233d7ad9bd7ff8946c81d781e9d9d47d2e2d24bc
[safe/jmp/linux-2.6] / tools / perf / util / event.c
1 #include <linux/types.h>
2 #include "event.h"
3 #include "debug.h"
4 #include "string.h"
5 #include "thread.h"
6
7 static pid_t event__synthesize_comm(pid_t pid, int full,
8                                     int (*process)(event_t *event))
9 {
10         event_t ev;
11         char filename[PATH_MAX];
12         char bf[BUFSIZ];
13         FILE *fp;
14         size_t size = 0;
15         DIR *tasks;
16         struct dirent dirent, *next;
17         pid_t tgid = 0;
18
19         snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
20
21         fp = fopen(filename, "r");
22         if (fp == NULL) {
23 out_race:
24                 /*
25                  * We raced with a task exiting - just return:
26                  */
27                 pr_debug("couldn't open %s\n", filename);
28                 return 0;
29         }
30
31         memset(&ev.comm, 0, sizeof(ev.comm));
32         while (!ev.comm.comm[0] || !ev.comm.pid) {
33                 if (fgets(bf, sizeof(bf), fp) == NULL)
34                         goto out_failure;
35
36                 if (memcmp(bf, "Name:", 5) == 0) {
37                         char *name = bf + 5;
38                         while (*name && isspace(*name))
39                                 ++name;
40                         size = strlen(name) - 1;
41                         memcpy(ev.comm.comm, name, size++);
42                 } else if (memcmp(bf, "Tgid:", 5) == 0) {
43                         char *tgids = bf + 5;
44                         while (*tgids && isspace(*tgids))
45                                 ++tgids;
46                         tgid = ev.comm.pid = atoi(tgids);
47                 }
48         }
49
50         ev.comm.header.type = PERF_RECORD_COMM;
51         size = ALIGN(size, sizeof(u64));
52         ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
53
54         if (!full) {
55                 ev.comm.tid = pid;
56
57                 process(&ev);
58                 goto out_fclose;
59         }
60
61         snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
62
63         tasks = opendir(filename);
64         if (tasks == NULL)
65                 goto out_race;
66
67         while (!readdir_r(tasks, &dirent, &next) && next) {
68                 char *end;
69                 pid = strtol(dirent.d_name, &end, 10);
70                 if (*end)
71                         continue;
72
73                 ev.comm.tid = pid;
74
75                 process(&ev);
76         }
77         closedir(tasks);
78
79 out_fclose:
80         fclose(fp);
81         return tgid;
82
83 out_failure:
84         pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
85         return -1;
86 }
87
88 static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
89                                          int (*process)(event_t *event))
90 {
91         char filename[PATH_MAX];
92         FILE *fp;
93
94         snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
95
96         fp = fopen(filename, "r");
97         if (fp == NULL) {
98                 /*
99                  * We raced with a task exiting - just return:
100                  */
101                 pr_debug("couldn't open %s\n", filename);
102                 return -1;
103         }
104
105         while (1) {
106                 char bf[BUFSIZ], *pbf = bf;
107                 event_t ev = {
108                         .header = { .type = PERF_RECORD_MMAP },
109                 };
110                 int n;
111                 size_t size;
112                 if (fgets(bf, sizeof(bf), fp) == NULL)
113                         break;
114
115                 /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
116                 n = hex2u64(pbf, &ev.mmap.start);
117                 if (n < 0)
118                         continue;
119                 pbf += n + 1;
120                 n = hex2u64(pbf, &ev.mmap.len);
121                 if (n < 0)
122                         continue;
123                 pbf += n + 3;
124                 if (*pbf == 'x') { /* vm_exec */
125                         char *execname = strchr(bf, '/');
126
127                         /* Catch VDSO */
128                         if (execname == NULL)
129                                 execname = strstr(bf, "[vdso]");
130
131                         if (execname == NULL)
132                                 continue;
133
134                         size = strlen(execname);
135                         execname[size - 1] = '\0'; /* Remove \n */
136                         memcpy(ev.mmap.filename, execname, size);
137                         size = ALIGN(size, sizeof(u64));
138                         ev.mmap.len -= ev.mmap.start;
139                         ev.mmap.header.size = (sizeof(ev.mmap) -
140                                                (sizeof(ev.mmap.filename) - size));
141                         ev.mmap.pid = tgid;
142                         ev.mmap.tid = pid;
143
144                         process(&ev);
145                 }
146         }
147
148         fclose(fp);
149         return 0;
150 }
151
152 int event__synthesize_thread(pid_t pid, int (*process)(event_t *event))
153 {
154         pid_t tgid = event__synthesize_comm(pid, 1, process);
155         if (tgid == -1)
156                 return -1;
157         return event__synthesize_mmap_events(pid, tgid, process);
158 }
159
160 void event__synthesize_threads(int (*process)(event_t *event))
161 {
162         DIR *proc;
163         struct dirent dirent, *next;
164
165         proc = opendir("/proc");
166
167         while (!readdir_r(proc, &dirent, &next) && next) {
168                 char *end;
169                 pid_t pid = strtol(dirent.d_name, &end, 10);
170
171                 if (*end) /* only interested in proper numerical dirents */
172                         continue;
173
174                 event__synthesize_thread(pid, process);
175         }
176
177         closedir(proc);
178 }
179
180 char *event__cwd;
181 int  event__cwdlen;
182
183 struct events_stats event__stats;
184
185 int event__process_comm(event_t *self)
186 {
187         struct thread *thread = threads__findnew(self->comm.pid);
188
189         dump_printf("PERF_RECORD_COMM: %s:%d\n",
190                     self->comm.comm, self->comm.pid);
191
192         if (thread == NULL || thread__set_comm(thread, self->comm.comm)) {
193                 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
194                 return -1;
195         }
196
197         return 0;
198 }
199
200 int event__process_lost(event_t *self)
201 {
202         dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
203         event__stats.lost += self->lost.lost;
204         return 0;
205 }
206
207 int event__process_mmap(event_t *self)
208 {
209         struct thread *thread = threads__findnew(self->mmap.pid);
210         struct map *map = map__new(&self->mmap, MAP__FUNCTION,
211                                    event__cwd, event__cwdlen);
212
213         dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
214                     self->mmap.pid, self->mmap.tid,
215                     (void *)(long)self->mmap.start,
216                     (void *)(long)self->mmap.len,
217                     (void *)(long)self->mmap.pgoff,
218                     self->mmap.filename);
219
220         if (thread == NULL || map == NULL)
221                 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
222         else
223                 thread__insert_map(thread, map);
224
225         return 0;
226 }
227
228 int event__process_task(event_t *self)
229 {
230         struct thread *thread = threads__findnew(self->fork.pid);
231         struct thread *parent = threads__findnew(self->fork.ppid);
232
233         dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
234                     self->fork.ppid, self->fork.ptid);
235         /*
236          * A thread clone will have the same PID for both parent and child.
237          */
238         if (thread == parent)
239                 return 0;
240
241         if (self->header.type == PERF_RECORD_EXIT)
242                 return 0;
243
244         if (thread == NULL || parent == NULL ||
245             thread__fork(thread, parent) < 0) {
246                 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
247                 return -1;
248         }
249
250         return 0;
251 }
252
253 void thread__find_addr_location(struct thread *self, u8 cpumode,
254                                 enum map_type type, u64 addr,
255                                 struct addr_location *al,
256                                 symbol_filter_t filter)
257 {
258         struct thread *thread = al->thread = self;
259
260         al->addr = addr;
261
262         if (cpumode & PERF_RECORD_MISC_KERNEL) {
263                 al->level = 'k';
264                 thread = kthread;
265         } else if (cpumode & PERF_RECORD_MISC_USER)
266                 al->level = '.';
267         else {
268                 al->level = 'H';
269                 al->map = NULL;
270                 al->sym = NULL;
271                 return;
272         }
273 try_again:
274         al->map = thread__find_map(thread, type, al->addr);
275         if (al->map == NULL) {
276                 /*
277                  * If this is outside of all known maps, and is a negative
278                  * address, try to look it up in the kernel dso, as it might be
279                  * a vsyscall or vdso (which executes in user-mode).
280                  *
281                  * XXX This is nasty, we should have a symbol list in the
282                  * "[vdso]" dso, but for now lets use the old trick of looking
283                  * in the whole kernel symbol list.
284                  */
285                 if ((long long)al->addr < 0 && thread != kthread) {
286                         thread = kthread;
287                         goto try_again;
288                 }
289                 al->sym = NULL;
290         } else {
291                 al->addr = al->map->map_ip(al->map, al->addr);
292                 al->sym = map__find_symbol(al->map, al->addr, filter);
293         }
294 }
295
296 int event__preprocess_sample(const event_t *self, struct addr_location *al,
297                              symbol_filter_t filter)
298 {
299         u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
300         struct thread *thread = threads__findnew(self->ip.pid);
301
302         if (thread == NULL)
303                 return -1;
304
305         dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
306
307         thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
308                                    self->ip.ip, al, filter);
309         dump_printf(" ...... dso: %s\n",
310                     al->map ? al->map->dso->long_name :
311                         al->level == 'H' ? "[hypervisor]" : "<not found>");
312         return 0;
313 }