Root/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
10static pid_t event__synthesize_comm(pid_t pid, int full,
11                    event__handler_t process,
12                    struct perf_session *session)
13{
14    event_t ev;
15    char filename[PATH_MAX];
16    char bf[BUFSIZ];
17    FILE *fp;
18    size_t size = 0;
19    DIR *tasks;
20    struct dirent dirent, *next;
21    pid_t tgid = 0;
22
23    snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
24
25    fp = fopen(filename, "r");
26    if (fp == NULL) {
27out_race:
28        /*
29         * We raced with a task exiting - just return:
30         */
31        pr_debug("couldn't open %s\n", filename);
32        return 0;
33    }
34
35    memset(&ev.comm, 0, sizeof(ev.comm));
36    while (!ev.comm.comm[0] || !ev.comm.pid) {
37        if (fgets(bf, sizeof(bf), fp) == NULL)
38            goto out_failure;
39
40        if (memcmp(bf, "Name:", 5) == 0) {
41            char *name = bf + 5;
42            while (*name && isspace(*name))
43                ++name;
44            size = strlen(name) - 1;
45            memcpy(ev.comm.comm, name, size++);
46        } else if (memcmp(bf, "Tgid:", 5) == 0) {
47            char *tgids = bf + 5;
48            while (*tgids && isspace(*tgids))
49                ++tgids;
50            tgid = ev.comm.pid = atoi(tgids);
51        }
52    }
53
54    ev.comm.header.type = PERF_RECORD_COMM;
55    size = ALIGN(size, sizeof(u64));
56    ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
57
58    if (!full) {
59        ev.comm.tid = pid;
60
61        process(&ev, session);
62        goto out_fclose;
63    }
64
65    snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
66
67    tasks = opendir(filename);
68    if (tasks == NULL)
69        goto out_race;
70
71    while (!readdir_r(tasks, &dirent, &next) && next) {
72        char *end;
73        pid = strtol(dirent.d_name, &end, 10);
74        if (*end)
75            continue;
76
77        ev.comm.tid = pid;
78
79        process(&ev, session);
80    }
81    closedir(tasks);
82
83out_fclose:
84    fclose(fp);
85    return tgid;
86
87out_failure:
88    pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
89    return -1;
90}
91
92static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
93                     event__handler_t process,
94                     struct perf_session *session)
95{
96    char filename[PATH_MAX];
97    FILE *fp;
98
99    snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
100
101    fp = fopen(filename, "r");
102    if (fp == NULL) {
103        /*
104         * We raced with a task exiting - just return:
105         */
106        pr_debug("couldn't open %s\n", filename);
107        return -1;
108    }
109
110    while (1) {
111        char bf[BUFSIZ], *pbf = bf;
112        event_t ev = {
113            .header = {
114                .type = PERF_RECORD_MMAP,
115                .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */
116             },
117        };
118        int n;
119        size_t size;
120        if (fgets(bf, sizeof(bf), fp) == NULL)
121            break;
122
123        /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
124        n = hex2u64(pbf, &ev.mmap.start);
125        if (n < 0)
126            continue;
127        pbf += n + 1;
128        n = hex2u64(pbf, &ev.mmap.len);
129        if (n < 0)
130            continue;
131        pbf += n + 3;
132        if (*pbf == 'x') { /* vm_exec */
133            char *execname = strchr(bf, '/');
134
135            /* Catch VDSO */
136            if (execname == NULL)
137                execname = strstr(bf, "[vdso]");
138
139            if (execname == NULL)
140                continue;
141
142            size = strlen(execname);
143            execname[size - 1] = '\0'; /* Remove \n */
144            memcpy(ev.mmap.filename, execname, size);
145            size = ALIGN(size, sizeof(u64));
146            ev.mmap.len -= ev.mmap.start;
147            ev.mmap.header.size = (sizeof(ev.mmap) -
148                           (sizeof(ev.mmap.filename) - size));
149            ev.mmap.pid = tgid;
150            ev.mmap.tid = pid;
151
152            process(&ev, session);
153        }
154    }
155
156    fclose(fp);
157    return 0;
158}
159
160int event__synthesize_modules(event__handler_t process,
161                  struct perf_session *session)
162{
163    struct rb_node *nd;
164
165    for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]);
166         nd; nd = rb_next(nd)) {
167        event_t ev;
168        size_t size;
169        struct map *pos = rb_entry(nd, struct map, rb_node);
170
171        if (pos->dso->kernel)
172            continue;
173
174        size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
175        memset(&ev, 0, sizeof(ev));
176        ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */
177        ev.mmap.header.type = PERF_RECORD_MMAP;
178        ev.mmap.header.size = (sizeof(ev.mmap) -
179                        (sizeof(ev.mmap.filename) - size));
180        ev.mmap.start = pos->start;
181        ev.mmap.len = pos->end - pos->start;
182
183        memcpy(ev.mmap.filename, pos->dso->long_name,
184               pos->dso->long_name_len + 1);
185        process(&ev, session);
186    }
187
188    return 0;
189}
190
191int event__synthesize_thread(pid_t pid, event__handler_t process,
192                 struct perf_session *session)
193{
194    pid_t tgid = event__synthesize_comm(pid, 1, process, session);
195    if (tgid == -1)
196        return -1;
197    return event__synthesize_mmap_events(pid, tgid, process, session);
198}
199
200void event__synthesize_threads(event__handler_t process,
201                   struct perf_session *session)
202{
203    DIR *proc;
204    struct dirent dirent, *next;
205
206    proc = opendir("/proc");
207
208    while (!readdir_r(proc, &dirent, &next) && next) {
209        char *end;
210        pid_t pid = strtol(dirent.d_name, &end, 10);
211
212        if (*end) /* only interested in proper numerical dirents */
213            continue;
214
215        event__synthesize_thread(pid, process, session);
216    }
217
218    closedir(proc);
219}
220
221struct process_symbol_args {
222    const char *name;
223    u64 start;
224};
225
226static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
227{
228    struct process_symbol_args *args = arg;
229
230    /*
231     * Must be a function or at least an alias, as in PARISC64, where "_text" is
232     * an 'A' to the same address as "_stext".
233     */
234    if (!(symbol_type__is_a(type, MAP__FUNCTION) ||
235          type == 'A') || strcmp(name, args->name))
236        return 0;
237
238    args->start = start;
239    return 1;
240}
241
242int event__synthesize_kernel_mmap(event__handler_t process,
243                  struct perf_session *session,
244                  const char *symbol_name)
245{
246    size_t size;
247    event_t ev = {
248        .header = {
249            .type = PERF_RECORD_MMAP,
250            .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */
251        },
252    };
253    /*
254     * We should get this from /sys/kernel/sections/.text, but till that is
255     * available use this, and after it is use this as a fallback for older
256     * kernels.
257     */
258    struct process_symbol_args args = { .name = symbol_name, };
259
260    if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0)
261        return -ENOENT;
262
263    size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
264            "[kernel.kallsyms.%s]", symbol_name) + 1;
265    size = ALIGN(size, sizeof(u64));
266    ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size));
267    ev.mmap.pgoff = args.start;
268    ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start;
269    ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ;
270
271    return process(&ev, session);
272}
273
274static void thread__comm_adjust(struct thread *self)
275{
276    char *comm = self->comm;
277
278    if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
279        (!symbol_conf.comm_list ||
280         strlist__has_entry(symbol_conf.comm_list, comm))) {
281        unsigned int slen = strlen(comm);
282
283        if (slen > comms__col_width) {
284            comms__col_width = slen;
285            threads__col_width = slen + 6;
286        }
287    }
288}
289
290static int thread__set_comm_adjust(struct thread *self, const char *comm)
291{
292    int ret = thread__set_comm(self, comm);
293
294    if (ret)
295        return ret;
296
297    thread__comm_adjust(self);
298
299    return 0;
300}
301
302int event__process_comm(event_t *self, struct perf_session *session)
303{
304    struct thread *thread = perf_session__findnew(session, self->comm.pid);
305
306    dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid);
307
308    if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) {
309        dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
310        return -1;
311    }
312
313    return 0;
314}
315
316int event__process_lost(event_t *self, struct perf_session *session)
317{
318    dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
319    session->events_stats.lost += self->lost.lost;
320    return 0;
321}
322
323int event__process_mmap(event_t *self, struct perf_session *session)
324{
325    struct thread *thread;
326    struct map *map;
327
328    dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n",
329            self->mmap.pid, self->mmap.tid, self->mmap.start,
330            self->mmap.len, self->mmap.pgoff, self->mmap.filename);
331
332    if (self->mmap.pid == 0) {
333        static const char kmmap_prefix[] = "[kernel.kallsyms.";
334
335        if (self->mmap.filename[0] == '/') {
336            char short_module_name[1024];
337            char *name = strrchr(self->mmap.filename, '/'), *dot;
338
339            if (name == NULL)
340                goto out_problem;
341
342            ++name; /* skip / */
343            dot = strrchr(name, '.');
344            if (dot == NULL)
345                goto out_problem;
346
347            snprintf(short_module_name, sizeof(short_module_name),
348                 "[%.*s]", (int)(dot - name), name);
349            strxfrchar(short_module_name, '-', '_');
350
351            map = perf_session__new_module_map(session,
352                               self->mmap.start,
353                               self->mmap.filename);
354            if (map == NULL)
355                goto out_problem;
356
357            name = strdup(short_module_name);
358            if (name == NULL)
359                goto out_problem;
360
361            map->dso->short_name = name;
362            map->end = map->start + self->mmap.len;
363        } else if (memcmp(self->mmap.filename, kmmap_prefix,
364                sizeof(kmmap_prefix) - 1) == 0) {
365            const char *symbol_name = (self->mmap.filename +
366                           sizeof(kmmap_prefix) - 1);
367            /*
368             * Should be there already, from the build-id table in
369             * the header.
370             */
371            struct dso *kernel = __dsos__findnew(&dsos__kernel,
372                                 "[kernel.kallsyms]");
373            if (kernel == NULL)
374                goto out_problem;
375
376            kernel->kernel = 1;
377            if (__perf_session__create_kernel_maps(session, kernel) < 0)
378                goto out_problem;
379
380            session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start;
381            session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
382            /*
383             * Be a bit paranoid here, some perf.data file came with
384             * a zero sized synthesized MMAP event for the kernel.
385             */
386            if (session->vmlinux_maps[MAP__FUNCTION]->end == 0)
387                session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL;
388
389            perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name,
390                                 self->mmap.pgoff);
391        }
392        return 0;
393    }
394
395    thread = perf_session__findnew(session, self->mmap.pid);
396    map = map__new(&self->mmap, MAP__FUNCTION,
397               session->cwd, session->cwdlen);
398
399    if (thread == NULL || map == NULL)
400        goto out_problem;
401
402    thread__insert_map(thread, map);
403    return 0;
404
405out_problem:
406    dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
407    return 0;
408}
409
410int event__process_task(event_t *self, struct perf_session *session)
411{
412    struct thread *thread = perf_session__findnew(session, self->fork.pid);
413    struct thread *parent = perf_session__findnew(session, self->fork.ppid);
414
415    dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
416            self->fork.ppid, self->fork.ptid);
417    /*
418     * A thread clone will have the same PID for both parent and child.
419     */
420    if (thread == parent)
421        return 0;
422
423    if (self->header.type == PERF_RECORD_EXIT)
424        return 0;
425
426    if (thread == NULL || parent == NULL ||
427        thread__fork(thread, parent) < 0) {
428        dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
429        return -1;
430    }
431
432    return 0;
433}
434
435void thread__find_addr_map(struct thread *self,
436               struct perf_session *session, u8 cpumode,
437               enum map_type type, u64 addr,
438               struct addr_location *al)
439{
440    struct map_groups *mg = &self->mg;
441
442    al->thread = self;
443    al->addr = addr;
444
445    if (cpumode == PERF_RECORD_MISC_KERNEL) {
446        al->level = 'k';
447        mg = &session->kmaps;
448    } else if (cpumode == PERF_RECORD_MISC_USER)
449        al->level = '.';
450    else {
451        al->level = 'H';
452        al->map = NULL;
453        return;
454    }
455try_again:
456    al->map = map_groups__find(mg, type, al->addr);
457    if (al->map == NULL) {
458        /*
459         * If this is outside of all known maps, and is a negative
460         * address, try to look it up in the kernel dso, as it might be
461         * a vsyscall or vdso (which executes in user-mode).
462         *
463         * XXX This is nasty, we should have a symbol list in the
464         * "[vdso]" dso, but for now lets use the old trick of looking
465         * in the whole kernel symbol list.
466         */
467        if ((long long)al->addr < 0 && mg != &session->kmaps) {
468            mg = &session->kmaps;
469            goto try_again;
470        }
471    } else
472        al->addr = al->map->map_ip(al->map, al->addr);
473}
474
475void thread__find_addr_location(struct thread *self,
476                struct perf_session *session, u8 cpumode,
477                enum map_type type, u64 addr,
478                struct addr_location *al,
479                symbol_filter_t filter)
480{
481    thread__find_addr_map(self, session, cpumode, type, addr, al);
482    if (al->map != NULL)
483        al->sym = map__find_symbol(al->map, al->addr, filter);
484    else
485        al->sym = NULL;
486}
487
488static void dso__calc_col_width(struct dso *self)
489{
490    if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
491        (!symbol_conf.dso_list ||
492         strlist__has_entry(symbol_conf.dso_list, self->name))) {
493        unsigned int slen = strlen(self->name);
494        if (slen > dsos__col_width)
495            dsos__col_width = slen;
496    }
497
498    self->slen_calculated = 1;
499}
500
501int event__preprocess_sample(const event_t *self, struct perf_session *session,
502                 struct addr_location *al, symbol_filter_t filter)
503{
504    u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
505    struct thread *thread = perf_session__findnew(session, self->ip.pid);
506
507    if (thread == NULL)
508        return -1;
509
510    if (symbol_conf.comm_list &&
511        !strlist__has_entry(symbol_conf.comm_list, thread->comm))
512        goto out_filtered;
513
514    dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
515
516    thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION,
517                   self->ip.ip, al, filter);
518    dump_printf(" ...... dso: %s\n",
519            al->map ? al->map->dso->long_name :
520            al->level == 'H' ? "[hypervisor]" : "<not found>");
521    /*
522     * We have to do this here as we may have a dso with no symbol hit that
523     * has a name longer than the ones with symbols sampled.
524     */
525    if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated)
526        dso__calc_col_width(al->map->dso);
527
528    if (symbol_conf.dso_list &&
529        (!al->map || !al->map->dso ||
530         !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) ||
531           (al->map->dso->short_name != al->map->dso->long_name &&
532        strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name)))))
533        goto out_filtered;
534
535    if (symbol_conf.sym_list && al->sym &&
536        !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
537        goto out_filtered;
538
539    al->filtered = false;
540    return 0;
541
542out_filtered:
543    al->filtered = true;
544    return 0;
545}
546
547int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
548{
549    u64 *array = event->sample.array;
550
551    if (type & PERF_SAMPLE_IP) {
552        data->ip = event->ip.ip;
553        array++;
554    }
555
556    if (type & PERF_SAMPLE_TID) {
557        u32 *p = (u32 *)array;
558        data->pid = p[0];
559        data->tid = p[1];
560        array++;
561    }
562
563    if (type & PERF_SAMPLE_TIME) {
564        data->time = *array;
565        array++;
566    }
567
568    if (type & PERF_SAMPLE_ADDR) {
569        data->addr = *array;
570        array++;
571    }
572
573    if (type & PERF_SAMPLE_ID) {
574        data->id = *array;
575        array++;
576    }
577
578    if (type & PERF_SAMPLE_STREAM_ID) {
579        data->stream_id = *array;
580        array++;
581    }
582
583    if (type & PERF_SAMPLE_CPU) {
584        u32 *p = (u32 *)array;
585        data->cpu = *p;
586        array++;
587    }
588
589    if (type & PERF_SAMPLE_PERIOD) {
590        data->period = *array;
591        array++;
592    }
593
594    if (type & PERF_SAMPLE_READ) {
595        pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
596        return -1;
597    }
598
599    if (type & PERF_SAMPLE_CALLCHAIN) {
600        data->callchain = (struct ip_callchain *)array;
601        array += 1 + data->callchain->nr;
602    }
603
604    if (type & PERF_SAMPLE_RAW) {
605        u32 *p = (u32 *)array;
606        data->raw_size = *p;
607        p++;
608        data->raw_data = p;
609    }
610
611    return 0;
612}
613

Archive Download this file



interactive