Merge branch 'for-fsnotify' into for-linus
[pandora-kernel.git] / tools / perf / util / event.c
1 #include <linux/types.h>
2 #include "event.h"
3 #include "debug.h"
4 #include "session.h"
5 #include "sort.h"
6 #include "string.h"
7 #include "strlist.h"
8 #include "thread.h"
9
10 static pid_t event__synthesize_comm(pid_t pid, int full,
11                                     int (*process)(event_t *event,
12                                                    struct perf_session *session),
13                                     struct perf_session *session)
14 {
15         event_t ev;
16         char filename[PATH_MAX];
17         char bf[BUFSIZ];
18         FILE *fp;
19         size_t size = 0;
20         DIR *tasks;
21         struct dirent dirent, *next;
22         pid_t tgid = 0;
23
24         snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
25
26         fp = fopen(filename, "r");
27         if (fp == NULL) {
28 out_race:
29                 /*
30                  * We raced with a task exiting - just return:
31                  */
32                 pr_debug("couldn't open %s\n", filename);
33                 return 0;
34         }
35
36         memset(&ev.comm, 0, sizeof(ev.comm));
37         while (!ev.comm.comm[0] || !ev.comm.pid) {
38                 if (fgets(bf, sizeof(bf), fp) == NULL)
39                         goto out_failure;
40
41                 if (memcmp(bf, "Name:", 5) == 0) {
42                         char *name = bf + 5;
43                         while (*name && isspace(*name))
44                                 ++name;
45                         size = strlen(name) - 1;
46                         memcpy(ev.comm.comm, name, size++);
47                 } else if (memcmp(bf, "Tgid:", 5) == 0) {
48                         char *tgids = bf + 5;
49                         while (*tgids && isspace(*tgids))
50                                 ++tgids;
51                         tgid = ev.comm.pid = atoi(tgids);
52                 }
53         }
54
55         ev.comm.header.type = PERF_RECORD_COMM;
56         size = ALIGN(size, sizeof(u64));
57         ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
58
59         if (!full) {
60                 ev.comm.tid = pid;
61
62                 process(&ev, session);
63                 goto out_fclose;
64         }
65
66         snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
67
68         tasks = opendir(filename);
69         if (tasks == NULL)
70                 goto out_race;
71
72         while (!readdir_r(tasks, &dirent, &next) && next) {
73                 char *end;
74                 pid = strtol(dirent.d_name, &end, 10);
75                 if (*end)
76                         continue;
77
78                 ev.comm.tid = pid;
79
80                 process(&ev, session);
81         }
82         closedir(tasks);
83
84 out_fclose:
85         fclose(fp);
86         return tgid;
87
88 out_failure:
89         pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
90         return -1;
91 }
92
93 static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
94                                          int (*process)(event_t *event,
95                                                         struct perf_session *session),
96                                          struct perf_session *session)
97 {
98         char filename[PATH_MAX];
99         FILE *fp;
100
101         snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
102
103         fp = fopen(filename, "r");
104         if (fp == NULL) {
105                 /*
106                  * We raced with a task exiting - just return:
107                  */
108                 pr_debug("couldn't open %s\n", filename);
109                 return -1;
110         }
111
112         while (1) {
113                 char bf[BUFSIZ], *pbf = bf;
114                 event_t ev = {
115                         .header = { .type = PERF_RECORD_MMAP },
116                 };
117                 int n;
118                 size_t size;
119                 if (fgets(bf, sizeof(bf), fp) == NULL)
120                         break;
121
122                 /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
123                 n = hex2u64(pbf, &ev.mmap.start);
124                 if (n < 0)
125                         continue;
126                 pbf += n + 1;
127                 n = hex2u64(pbf, &ev.mmap.len);
128                 if (n < 0)
129                         continue;
130                 pbf += n + 3;
131                 if (*pbf == 'x') { /* vm_exec */
132                         char *execname = strchr(bf, '/');
133
134                         /* Catch VDSO */
135                         if (execname == NULL)
136                                 execname = strstr(bf, "[vdso]");
137
138                         if (execname == NULL)
139                                 continue;
140
141                         size = strlen(execname);
142                         execname[size - 1] = '\0'; /* Remove \n */
143                         memcpy(ev.mmap.filename, execname, size);
144                         size = ALIGN(size, sizeof(u64));
145                         ev.mmap.len -= ev.mmap.start;
146                         ev.mmap.header.size = (sizeof(ev.mmap) -
147                                                (sizeof(ev.mmap.filename) - size));
148                         ev.mmap.pid = tgid;
149                         ev.mmap.tid = pid;
150
151                         process(&ev, session);
152                 }
153         }
154
155         fclose(fp);
156         return 0;
157 }
158
159 int event__synthesize_thread(pid_t pid,
160                              int (*process)(event_t *event,
161                                             struct perf_session *session),
162                              struct perf_session *session)
163 {
164         pid_t tgid = event__synthesize_comm(pid, 1, process, session);
165         if (tgid == -1)
166                 return -1;
167         return event__synthesize_mmap_events(pid, tgid, process, session);
168 }
169
170 void event__synthesize_threads(int (*process)(event_t *event,
171                                               struct perf_session *session),
172                                struct perf_session *session)
173 {
174         DIR *proc;
175         struct dirent dirent, *next;
176
177         proc = opendir("/proc");
178
179         while (!readdir_r(proc, &dirent, &next) && next) {
180                 char *end;
181                 pid_t pid = strtol(dirent.d_name, &end, 10);
182
183                 if (*end) /* only interested in proper numerical dirents */
184                         continue;
185
186                 event__synthesize_thread(pid, process, session);
187         }
188
189         closedir(proc);
190 }
191
192 static void thread__comm_adjust(struct thread *self)
193 {
194         char *comm = self->comm;
195
196         if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
197             (!symbol_conf.comm_list ||
198              strlist__has_entry(symbol_conf.comm_list, comm))) {
199                 unsigned int slen = strlen(comm);
200
201                 if (slen > comms__col_width) {
202                         comms__col_width = slen;
203                         threads__col_width = slen + 6;
204                 }
205         }
206 }
207
208 static int thread__set_comm_adjust(struct thread *self, const char *comm)
209 {
210         int ret = thread__set_comm(self, comm);
211
212         if (ret)
213                 return ret;
214
215         thread__comm_adjust(self);
216
217         return 0;
218 }
219
220 int event__process_comm(event_t *self, struct perf_session *session)
221 {
222         struct thread *thread = perf_session__findnew(session, self->comm.pid);
223
224         dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid);
225
226         if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) {
227                 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
228                 return -1;
229         }
230
231         return 0;
232 }
233
234 int event__process_lost(event_t *self, struct perf_session *session)
235 {
236         dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
237         session->events_stats.lost += self->lost.lost;
238         return 0;
239 }
240
241 int event__process_mmap(event_t *self, struct perf_session *session)
242 {
243         struct thread *thread = perf_session__findnew(session, self->mmap.pid);
244         struct map *map = map__new(&self->mmap, MAP__FUNCTION,
245                                    session->cwd, session->cwdlen);
246
247         dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
248                     self->mmap.pid, self->mmap.tid,
249                     (void *)(long)self->mmap.start,
250                     (void *)(long)self->mmap.len,
251                     (void *)(long)self->mmap.pgoff,
252                     self->mmap.filename);
253
254         if (thread == NULL || map == NULL)
255                 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
256         else
257                 thread__insert_map(thread, map);
258
259         return 0;
260 }
261
262 int event__process_task(event_t *self, struct perf_session *session)
263 {
264         struct thread *thread = perf_session__findnew(session, self->fork.pid);
265         struct thread *parent = perf_session__findnew(session, self->fork.ppid);
266
267         dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
268                     self->fork.ppid, self->fork.ptid);
269         /*
270          * A thread clone will have the same PID for both parent and child.
271          */
272         if (thread == parent)
273                 return 0;
274
275         if (self->header.type == PERF_RECORD_EXIT)
276                 return 0;
277
278         if (thread == NULL || parent == NULL ||
279             thread__fork(thread, parent) < 0) {
280                 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
281                 return -1;
282         }
283
284         return 0;
285 }
286
287 void thread__find_addr_location(struct thread *self,
288                                 struct perf_session *session, u8 cpumode,
289                                 enum map_type type, u64 addr,
290                                 struct addr_location *al,
291                                 symbol_filter_t filter)
292 {
293         struct map_groups *mg = &self->mg;
294
295         al->thread = self;
296         al->addr = addr;
297
298         if (cpumode == PERF_RECORD_MISC_KERNEL) {
299                 al->level = 'k';
300                 mg = &session->kmaps;
301         } else if (cpumode == PERF_RECORD_MISC_USER)
302                 al->level = '.';
303         else {
304                 al->level = 'H';
305                 al->map = NULL;
306                 al->sym = NULL;
307                 return;
308         }
309 try_again:
310         al->map = map_groups__find(mg, type, al->addr);
311         if (al->map == NULL) {
312                 /*
313                  * If this is outside of all known maps, and is a negative
314                  * address, try to look it up in the kernel dso, as it might be
315                  * a vsyscall or vdso (which executes in user-mode).
316                  *
317                  * XXX This is nasty, we should have a symbol list in the
318                  * "[vdso]" dso, but for now lets use the old trick of looking
319                  * in the whole kernel symbol list.
320                  */
321                 if ((long long)al->addr < 0 && mg != &session->kmaps) {
322                         mg = &session->kmaps;
323                         goto try_again;
324                 }
325                 al->sym = NULL;
326         } else {
327                 al->addr = al->map->map_ip(al->map, al->addr);
328                 al->sym = map__find_symbol(al->map, session, al->addr, filter);
329         }
330 }
331
332 static void dso__calc_col_width(struct dso *self)
333 {
334         if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
335             (!symbol_conf.dso_list ||
336              strlist__has_entry(symbol_conf.dso_list, self->name))) {
337                 unsigned int slen = strlen(self->name);
338                 if (slen > dsos__col_width)
339                         dsos__col_width = slen;
340         }
341
342         self->slen_calculated = 1;
343 }
344
345 int event__preprocess_sample(const event_t *self, struct perf_session *session,
346                              struct addr_location *al, symbol_filter_t filter)
347 {
348         u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
349         struct thread *thread = perf_session__findnew(session, self->ip.pid);
350
351         if (thread == NULL)
352                 return -1;
353
354         if (symbol_conf.comm_list &&
355             !strlist__has_entry(symbol_conf.comm_list, thread->comm))
356                 goto out_filtered;
357
358         dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
359
360         thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION,
361                                    self->ip.ip, al, filter);
362         dump_printf(" ...... dso: %s\n",
363                     al->map ? al->map->dso->long_name :
364                         al->level == 'H' ? "[hypervisor]" : "<not found>");
365         /*
366          * We have to do this here as we may have a dso with no symbol hit that
367          * has a name longer than the ones with symbols sampled.
368          */
369         if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated)
370                 dso__calc_col_width(al->map->dso);
371
372         if (symbol_conf.dso_list &&
373             (!al->map || !al->map->dso ||
374              !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) ||
375                (al->map->dso->short_name != al->map->dso->long_name &&
376                 strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name)))))
377                 goto out_filtered;
378
379         if (symbol_conf.sym_list && al->sym &&
380             !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
381                 goto out_filtered;
382
383         al->filtered = false;
384         return 0;
385
386 out_filtered:
387         al->filtered = true;
388         return 0;
389 }
390
391 int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
392 {
393         u64 *array = event->sample.array;
394
395         if (type & PERF_SAMPLE_IP) {
396                 data->ip = event->ip.ip;
397                 array++;
398         }
399
400         if (type & PERF_SAMPLE_TID) {
401                 u32 *p = (u32 *)array;
402                 data->pid = p[0];
403                 data->tid = p[1];
404                 array++;
405         }
406
407         if (type & PERF_SAMPLE_TIME) {
408                 data->time = *array;
409                 array++;
410         }
411
412         if (type & PERF_SAMPLE_ADDR) {
413                 data->addr = *array;
414                 array++;
415         }
416
417         if (type & PERF_SAMPLE_ID) {
418                 data->id = *array;
419                 array++;
420         }
421
422         if (type & PERF_SAMPLE_STREAM_ID) {
423                 data->stream_id = *array;
424                 array++;
425         }
426
427         if (type & PERF_SAMPLE_CPU) {
428                 u32 *p = (u32 *)array;
429                 data->cpu = *p;
430                 array++;
431         }
432
433         if (type & PERF_SAMPLE_PERIOD) {
434                 data->period = *array;
435                 array++;
436         }
437
438         if (type & PERF_SAMPLE_READ) {
439                 pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
440                 return -1;
441         }
442
443         if (type & PERF_SAMPLE_CALLCHAIN) {
444                 data->callchain = (struct ip_callchain *)array;
445                 array += 1 + data->callchain->nr;
446         }
447
448         if (type & PERF_SAMPLE_RAW) {
449                 u32 *p = (u32 *)array;
450                 data->raw_size = *p;
451                 p++;
452                 data->raw_data = p;
453         }
454
455         return 0;
456 }