217478ddca954a9d3fc4c4c99bd9f3c93e498bb1
[kmemtrace-user.git] / kmemtraced.c
1 /*
2  * Copyright (C) 2008 Pekka Enberg, Eduard - Gabriel Munteanu
3  *
4  * This file is released under GPL version 2.
5  */
6
7 #define _GNU_SOURCE
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <limits.h>
12 #include <pthread.h>
13 #include <poll.h>
14 #include <sched.h>
15 #include <signal.h>
16 #include <stdarg.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <sys/mman.h>
22 #include <sys/types.h>
23 #include <sys/syscall.h>
24 #include <sys/sendfile.h>
25 #include <unistd.h>
26
27 #include "common.h"
28 #include "kmemtrace.h"
29
30 static volatile int terminate;
31
32 static void write_str(const char *filename, const char *value)
33 {
34         int fd;
35
36         fd = open(filename, O_RDWR);
37         if (fd < 0)
38                 panic("Could not open() file %s: %s\n", filename, strerror(errno));
39
40         if (write(fd, value, strlen(value)) < 0)
41                 panic("Could not write() to file %s: %s\n", filename, strerror(errno));
42
43         close(fd);
44 }
45
46 static int open_channel(int cpu)
47 {
48         char filename[PATH_MAX];
49         int fd;
50
51         sprintf(filename, "/sys/kernel/debug/kmemtrace/cpu%d", cpu);
52         fd = open(filename, O_RDONLY | O_NONBLOCK);
53         if (fd < 0)
54                 panic("Could not open() file %s: %s\n", filename, strerror(errno));
55         return fd;
56 }
57
58 static int open_log(int cpu)
59 {
60         char filename[PATH_MAX];
61         int fd;
62
63         sprintf(filename, "cpu%d.out", cpu);
64         fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC,
65                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
66         if (fd < 0)
67                 panic("Could not open() file %s: %s\n",
68                       filename, strerror(errno));
69         
70         return fd;
71 }
72
73 static void reader_set_affinity(unsigned long cpu)
74 {
75         int err;
76         cpu_set_t cpumask;
77
78         CPU_ZERO(&cpumask);
79         CPU_SET(cpu, &cpumask);
80         err = sched_setaffinity(syscall(SYS_gettid),
81                                 sizeof(cpu_set_t), &cpumask);
82
83         if (err == -1)
84                 panic("reader_set_affinity: %s\n", strerror(errno));
85 }
86
87 static void *reader_thread(void *data)
88 {
89         unsigned long cpu = (unsigned long) data;
90         int relay_fd, log_fd;
91         int pipe_fd[2];
92         long retval;
93
94         reader_set_affinity(cpu);
95         
96         relay_fd = open_channel(cpu);
97         log_fd = open_log(cpu);
98
99         if (pipe(pipe_fd))
100                 panic("pipe() failed: %s\n", strerror(errno));
101
102         do {
103                 /*
104                  * We don't strip extra features (due to ABI changes) from
105                  * events; do that when actually parsing the data.
106                  */
107                 retval = splice(relay_fd, NULL, pipe_fd[1], NULL,
108                                 128, SPLICE_F_MOVE);
109                 if (retval < 0)
110                         panic("splice() (from) failed: %s\n",
111                               strerror(errno));
112                 retval = splice(pipe_fd[0], NULL, log_fd, NULL,
113                                 128, SPLICE_F_MOVE);
114                 if (retval < 0)
115                         panic("splice() (to) failed: %s\n", strerror(errno));
116         } while (!terminate);
117
118         return NULL;
119 }
120
121 static void copy_kallsyms(void)
122 {
123         int in_fd, out_fd;
124         char buf[256];
125         ssize_t count;
126
127         in_fd = open("/proc/kallsyms", O_RDONLY);
128         if (in_fd == -1)
129                 goto err;
130
131         out_fd = open("kallsyms", O_CREAT | O_WRONLY | O_TRUNC,
132                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
133         if (out_fd == -1) {
134                 close(in_fd);
135                 goto err;
136         }
137
138         while ((count = read(in_fd, buf, 256)) > 0)
139                 write(out_fd, buf, count);
140         if (count < 0)
141                 goto err;
142
143         return;
144
145 err:
146         panic("copy_kallsyms: %s\n", strerror(errno));
147 }
148
149 int main(int argc, char *argv[])
150 {
151         unsigned long nr_cpus;
152         pthread_t *readers;
153         sigset_t signals;
154         unsigned long i;
155         int signal;
156
157         sigemptyset(&signals);
158         sigaddset(&signals, SIGINT);
159         sigaddset(&signals, SIGTERM);
160         pthread_sigmask(SIG_BLOCK, &signals, NULL);
161
162         nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
163
164         readers = calloc(nr_cpus, sizeof(pthread_t));
165         if (!readers)
166                 panic("Out of memory!\n");
167
168         write_str("/sys/kernel/debug/kmemtrace/enabled", "0");
169
170         printf("Copying /proc/kallsyms...\n");
171         copy_kallsyms();
172
173         for (i = 0; i < nr_cpus; i++) {
174                 int err;
175
176                 err = pthread_create(&readers[i], NULL, reader_thread,
177                                      (void *) i);
178                 if (err)
179                         panic("Could not pthread_create(): %s!\n",
180                               strerror(errno));
181         }
182
183         printf("Logging... Press Control-C to stop.\n");
184
185         while (sigwait(&signals, &signal) == 0) {
186                 if (signal == SIGINT || signal == SIGTERM)
187                         /*
188                          * No synchronization needed, we wait for
189                          * threads to end.
190                          */
191                         terminate = 1;
192                         break;
193         }
194
195         write_str("/sys/kernel/debug/kmemtrace/enabled", "0");
196
197         for (i = 0; i < nr_cpus; i++)
198                 pthread_join(readers[i], NULL);
199         
200         return EXIT_SUCCESS;
201 }