blob: 270e986c2d42a69a8207200eeef72e93774c2c07 [file] [log] [blame]
Ingo Molnar53cb8bc2009-05-26 09:17:18 +02001#include "util/util.h"
Ingo Molnar16f762a2009-05-27 09:10:38 +02002#include "builtin.h"
Ingo Molnar53cb8bc2009-05-26 09:17:18 +02003
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -03004#include "util/list.h"
Ingo Molnara930d2c2009-05-27 09:50:13 +02005#include "util/cache.h"
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -03006#include "util/rbtree.h"
Arnaldo Carvalho de Meloa2928c42009-05-28 14:55:04 -03007#include "util/symbol.h"
Arnaldo Carvalho de Meloa0055ae2009-06-01 17:50:19 -03008#include "util/string.h"
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -03009
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020010#include "perf.h"
11
12#include "util/parse-options.h"
13#include "util/parse-events.h"
14
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030015#define SHOW_KERNEL 1
16#define SHOW_USER 2
17#define SHOW_HV 4
18
Ingo Molnar23ac9cb2009-05-27 09:33:18 +020019static char const *input_name = "perf.data";
Peter Zijlstra450aaa22009-05-27 20:20:23 +020020static char *vmlinux = NULL;
Peter Zijlstraf70e87d2009-06-02 14:13:24 +020021static char *sort_order = "comm,dso";
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030022static int input;
23static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
24
Ingo Molnar97b07b62009-05-26 18:48:58 +020025static int dump_trace = 0;
Ingo Molnar16f762a2009-05-27 09:10:38 +020026static int verbose;
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -030027static int full_paths;
Ingo Molnar97b07b62009-05-26 18:48:58 +020028
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030029static unsigned long page_size;
30static unsigned long mmap_window = 32;
31
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020032const char *perf_event_names[] = {
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030033 [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP",
34 [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP",
35 [PERF_EVENT_COMM] = " PERF_EVENT_COMM",
36};
37
38struct ip_event {
39 struct perf_event_header header;
40 __u64 ip;
41 __u32 pid, tid;
42};
43struct mmap_event {
44 struct perf_event_header header;
45 __u32 pid, tid;
46 __u64 start;
47 __u64 len;
48 __u64 pgoff;
49 char filename[PATH_MAX];
50};
51struct comm_event {
52 struct perf_event_header header;
53 __u32 pid,tid;
54 char comm[16];
55};
56
57typedef union event_union {
58 struct perf_event_header header;
59 struct ip_event ip;
60 struct mmap_event mmap;
61 struct comm_event comm;
62} event_t;
63
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030064static LIST_HEAD(dsos);
65static struct dso *kernel_dso;
66
67static void dsos__add(struct dso *dso)
68{
69 list_add_tail(&dso->node, &dsos);
70}
71
72static struct dso *dsos__find(const char *name)
73{
74 struct dso *pos;
75
76 list_for_each_entry(pos, &dsos, node)
77 if (strcmp(pos->name, name) == 0)
78 return pos;
79 return NULL;
80}
81
82static struct dso *dsos__findnew(const char *name)
83{
84 struct dso *dso = dsos__find(name);
Peter Zijlstrab7a16ea2009-05-27 13:35:35 +020085 int nr;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030086
Ingo Molnar4593bba2009-06-02 15:34:25 +020087 if (dso)
88 return dso;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030089
Ingo Molnar4593bba2009-06-02 15:34:25 +020090 dso = dso__new(name, 0);
91 if (!dso)
92 goto out_delete_dso;
Peter Zijlstrab7a16ea2009-05-27 13:35:35 +020093
Ingo Molnar4593bba2009-06-02 15:34:25 +020094 nr = dso__load(dso, NULL);
95 if (nr < 0) {
96 fprintf(stderr, "Failed to open: %s\n", name);
97 goto out_delete_dso;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030098 }
Ingo Molnar4593bba2009-06-02 15:34:25 +020099 if (!nr && verbose) {
100 fprintf(stderr,
101 "No symbols found in: %s, maybe install a debug package?\n",
102 name);
103 }
104
105 dsos__add(dso);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300106
107 return dso;
108
109out_delete_dso:
110 dso__delete(dso);
111 return NULL;
112}
113
Ingo Molnar16f762a2009-05-27 09:10:38 +0200114static void dsos__fprintf(FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300115{
116 struct dso *pos;
117
118 list_for_each_entry(pos, &dsos, node)
119 dso__fprintf(pos, fp);
120}
121
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200122static int load_kernel(void)
123{
Arnaldo Carvalho de Meloa827c872009-05-28 14:55:19 -0300124 int err;
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200125
Arnaldo Carvalho de Melo0085c952009-05-28 14:55:13 -0300126 kernel_dso = dso__new("[kernel]", 0);
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200127 if (!kernel_dso)
Arnaldo Carvalho de Meloa2928c42009-05-28 14:55:04 -0300128 return -1;
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200129
Arnaldo Carvalho de Melo69ee69f2009-05-28 14:55:26 -0300130 err = dso__load_kernel(kernel_dso, vmlinux, NULL);
Arnaldo Carvalho de Meloa2928c42009-05-28 14:55:04 -0300131 if (err) {
132 dso__delete(kernel_dso);
133 kernel_dso = NULL;
134 } else
135 dsos__add(kernel_dso);
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200136
Arnaldo Carvalho de Meloa2928c42009-05-28 14:55:04 -0300137 return err;
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200138}
139
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300140static int strcommon(const char *pathname, const char *cwd, int cwdlen)
141{
142 int n = 0;
143
144 while (pathname[n] == cwd[n] && n < cwdlen)
145 ++n;
146
147 return n;
148}
149
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300150struct map {
151 struct list_head node;
152 uint64_t start;
153 uint64_t end;
154 uint64_t pgoff;
155 struct dso *dso;
156};
157
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300158static struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300159{
160 struct map *self = malloc(sizeof(*self));
161
162 if (self != NULL) {
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300163 const char *filename = event->filename;
164 char newfilename[PATH_MAX];
165
166 if (cwd) {
167 int n = strcommon(filename, cwd, cwdlen);
168 if (n == cwdlen) {
169 snprintf(newfilename, sizeof(newfilename),
170 ".%s", filename + n);
171 filename = newfilename;
172 }
173 }
174
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300175 self->start = event->start;
176 self->end = event->start + event->len;
177 self->pgoff = event->pgoff;
178
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300179 self->dso = dsos__findnew(filename);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300180 if (self->dso == NULL)
181 goto out_delete;
182 }
183 return self;
184out_delete:
185 free(self);
186 return NULL;
187}
188
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300189struct thread;
190
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300191struct thread {
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300192 struct rb_node rb_node;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300193 struct list_head maps;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300194 pid_t pid;
195 char *comm;
196};
197
198static struct thread *thread__new(pid_t pid)
199{
200 struct thread *self = malloc(sizeof(*self));
201
202 if (self != NULL) {
203 self->pid = pid;
Ingo Molnar0a520c62009-06-02 23:24:45 +0200204 self->comm = malloc(30);
205 if (self->comm)
206 sprintf(self->comm, ":%d", pid);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300207 INIT_LIST_HEAD(&self->maps);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300208 }
209
210 return self;
211}
212
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300213static int thread__set_comm(struct thread *self, const char *comm)
214{
215 self->comm = strdup(comm);
216 return self->comm ? 0 : -ENOMEM;
217}
218
Ingo Molnar16f762a2009-05-27 09:10:38 +0200219static struct rb_root threads;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300220
221static struct thread *threads__findnew(pid_t pid)
222{
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300223 struct rb_node **p = &threads.rb_node;
224 struct rb_node *parent = NULL;
225 struct thread *th;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300226
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300227 while (*p != NULL) {
228 parent = *p;
229 th = rb_entry(parent, struct thread, rb_node);
230
231 if (th->pid == pid)
232 return th;
233
234 if (pid < th->pid)
235 p = &(*p)->rb_left;
236 else
237 p = &(*p)->rb_right;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300238 }
239
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300240 th = thread__new(pid);
241 if (th != NULL) {
242 rb_link_node(&th->rb_node, parent, p);
243 rb_insert_color(&th->rb_node, &threads);
244 }
245 return th;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300246}
247
248static void thread__insert_map(struct thread *self, struct map *map)
249{
250 list_add_tail(&map->node, &self->maps);
251}
252
253static struct map *thread__find_map(struct thread *self, uint64_t ip)
254{
Ingo Molnar16f762a2009-05-27 09:10:38 +0200255 struct map *pos;
256
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300257 if (self == NULL)
258 return NULL;
259
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300260 list_for_each_entry(pos, &self->maps, node)
261 if (ip >= pos->start && ip <= pos->end)
262 return pos;
263
264 return NULL;
265}
266
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200267/*
268 * histogram, sorted on item, collects counts
269 */
270
271static struct rb_root hist;
272
273struct hist_entry {
274 struct rb_node rb_node;
275
276 struct thread *thread;
277 struct map *map;
278 struct dso *dso;
279 struct symbol *sym;
280 uint64_t ip;
281 char level;
282
283 uint32_t count;
284};
285
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200286/*
287 * configurable sorting bits
288 */
289
290struct sort_entry {
291 struct list_head list;
292
Peter Zijlstraca8cdee2009-05-28 11:08:33 +0200293 char *header;
294
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200295 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
296 size_t (*print)(FILE *fp, struct hist_entry *);
297};
298
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200299static int64_t
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200300sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
301{
302 return right->thread->pid - left->thread->pid;
303}
304
305static size_t
306sort__thread_print(FILE *fp, struct hist_entry *self)
307{
Ingo Molnarcf25c632009-06-02 22:12:14 +0200308 return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid);
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200309}
310
311static struct sort_entry sort_thread = {
Ingo Molnarcf25c632009-06-02 22:12:14 +0200312 .header = " Command: Pid ",
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200313 .cmp = sort__thread_cmp,
314 .print = sort__thread_print,
315};
316
317static int64_t
Peter Zijlstra992444b2009-05-27 20:20:27 +0200318sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
319{
320 char *comm_l = left->thread->comm;
321 char *comm_r = right->thread->comm;
322
323 if (!comm_l || !comm_r) {
324 if (!comm_l && !comm_r)
325 return 0;
326 else if (!comm_l)
327 return -1;
328 else
329 return 1;
330 }
331
332 return strcmp(comm_l, comm_r);
333}
334
335static size_t
336sort__comm_print(FILE *fp, struct hist_entry *self)
337{
Ingo Molnar0a520c62009-06-02 23:24:45 +0200338 return fprintf(fp, " %16s", self->thread->comm);
Peter Zijlstra992444b2009-05-27 20:20:27 +0200339}
340
341static struct sort_entry sort_comm = {
Ingo Molnar4593bba2009-06-02 15:34:25 +0200342 .header = " Command",
Peter Zijlstra992444b2009-05-27 20:20:27 +0200343 .cmp = sort__comm_cmp,
344 .print = sort__comm_print,
345};
346
347static int64_t
Peter Zijlstra55e5ec42009-05-27 20:20:28 +0200348sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
349{
350 struct dso *dso_l = left->dso;
351 struct dso *dso_r = right->dso;
352
353 if (!dso_l || !dso_r) {
354 if (!dso_l && !dso_r)
355 return 0;
356 else if (!dso_l)
357 return -1;
358 else
359 return 1;
360 }
361
362 return strcmp(dso_l->name, dso_r->name);
363}
364
365static size_t
366sort__dso_print(FILE *fp, struct hist_entry *self)
367{
Ingo Molnar0a520c62009-06-02 23:24:45 +0200368 if (self->dso)
369 return fprintf(fp, " %-25s", self->dso->name);
370
371 return fprintf(fp, " %016llx", (__u64)self->ip);
Peter Zijlstra55e5ec42009-05-27 20:20:28 +0200372}
373
374static struct sort_entry sort_dso = {
Ingo Molnarcf25c632009-06-02 22:12:14 +0200375 .header = " Shared Object ",
Peter Zijlstra55e5ec42009-05-27 20:20:28 +0200376 .cmp = sort__dso_cmp,
377 .print = sort__dso_print,
378};
379
380static int64_t
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200381sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300382{
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200383 uint64_t ip_l, ip_r;
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200384
385 if (left->sym == right->sym)
386 return 0;
387
388 ip_l = left->sym ? left->sym->start : left->ip;
389 ip_r = right->sym ? right->sym->start : right->ip;
390
391 return (int64_t)(ip_r - ip_l);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300392}
393
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200394static size_t
395sort__sym_print(FILE *fp, struct hist_entry *self)
396{
397 size_t ret = 0;
398
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200399 if (verbose)
Ingo Molnar0a520c62009-06-02 23:24:45 +0200400 ret += fprintf(fp, " %#018llx", (__u64)self->ip);
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200401
Ingo Molnar0a520c62009-06-02 23:24:45 +0200402 if (self->dso)
403 ret += fprintf(fp, " %s: ", self->dso->name);
404 else
405 ret += fprintf(fp, " %#016llx: ", (__u64)self->ip);
406
407 if (self->sym)
408 ret += fprintf(fp, "%s", self->sym->name);
409 else
410 ret += fprintf(fp, "%#016llx", (__u64)self->ip);
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200411
412 return ret;
413}
414
415static struct sort_entry sort_sym = {
Ingo Molnar4593bba2009-06-02 15:34:25 +0200416 .header = " Shared Object: Symbol",
Peter Zijlstraca8cdee2009-05-28 11:08:33 +0200417 .cmp = sort__sym_cmp,
418 .print = sort__sym_print,
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200419};
420
Peter Zijlstra37f440c2009-05-27 20:20:26 +0200421struct sort_dimension {
422 char *name;
423 struct sort_entry *entry;
424 int taken;
425};
426
427static struct sort_dimension sort_dimensions[] = {
428 { .name = "pid", .entry = &sort_thread, },
Peter Zijlstra992444b2009-05-27 20:20:27 +0200429 { .name = "comm", .entry = &sort_comm, },
Peter Zijlstra55e5ec42009-05-27 20:20:28 +0200430 { .name = "dso", .entry = &sort_dso, },
Peter Zijlstra37f440c2009-05-27 20:20:26 +0200431 { .name = "symbol", .entry = &sort_sym, },
432};
433
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200434static LIST_HEAD(hist_entry__sort_list);
435
Peter Zijlstra37f440c2009-05-27 20:20:26 +0200436static int sort_dimension__add(char *tok)
437{
438 int i;
439
440 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
441 struct sort_dimension *sd = &sort_dimensions[i];
442
443 if (sd->taken)
444 continue;
445
446 if (strcmp(tok, sd->name))
447 continue;
448
449 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
450 sd->taken = 1;
451 return 0;
452 }
453
454 return -ESRCH;
455}
456
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200457static void setup_sorting(void)
458{
Peter Zijlstra37f440c2009-05-27 20:20:26 +0200459 char *tmp, *tok, *str = strdup(sort_order);
460
461 for (tok = strtok_r(str, ", ", &tmp);
462 tok; tok = strtok_r(NULL, ", ", &tmp))
463 sort_dimension__add(tok);
464
465 free(str);
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200466}
467
468static int64_t
469hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
470{
471 struct sort_entry *se;
472 int64_t cmp = 0;
473
474 list_for_each_entry(se, &hist_entry__sort_list, list) {
475 cmp = se->cmp(left, right);
476 if (cmp)
477 break;
478 }
479
480 return cmp;
481}
482
483static size_t
484hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples)
485{
486 struct sort_entry *se;
487 size_t ret;
488
489 if (total_samples) {
Ingo Molnar2d655372009-05-27 21:36:22 +0200490 ret = fprintf(fp, " %5.2f%%",
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200491 (self->count * 100.0) / total_samples);
492 } else
493 ret = fprintf(fp, "%12d ", self->count);
494
495 list_for_each_entry(se, &hist_entry__sort_list, list)
496 ret += se->print(fp, self);
497
498 ret += fprintf(fp, "\n");
499
500 return ret;
501}
502
503/*
504 * collect histogram counts
505 */
506
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200507static int
508hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
509 struct symbol *sym, uint64_t ip, char level)
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300510{
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200511 struct rb_node **p = &hist.rb_node;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300512 struct rb_node *parent = NULL;
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200513 struct hist_entry *he;
514 struct hist_entry entry = {
515 .thread = thread,
516 .map = map,
517 .dso = dso,
518 .sym = sym,
519 .ip = ip,
520 .level = level,
521 .count = 1,
522 };
523 int cmp;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300524
525 while (*p != NULL) {
526 parent = *p;
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200527 he = rb_entry(parent, struct hist_entry, rb_node);
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300528
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200529 cmp = hist_entry__cmp(&entry, he);
530
531 if (!cmp) {
532 he->count++;
533 return 0;
534 }
535
536 if (cmp < 0)
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300537 p = &(*p)->rb_left;
538 else
539 p = &(*p)->rb_right;
540 }
541
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200542 he = malloc(sizeof(*he));
543 if (!he)
544 return -ENOMEM;
545 *he = entry;
546 rb_link_node(&he->rb_node, parent, p);
547 rb_insert_color(&he->rb_node, &hist);
548
549 return 0;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300550}
551
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200552/*
553 * reverse the map, sort on count.
554 */
555
556static struct rb_root output_hists;
557
558static void output__insert_entry(struct hist_entry *he)
559{
560 struct rb_node **p = &output_hists.rb_node;
561 struct rb_node *parent = NULL;
562 struct hist_entry *iter;
563
564 while (*p != NULL) {
565 parent = *p;
566 iter = rb_entry(parent, struct hist_entry, rb_node);
567
568 if (he->count > iter->count)
569 p = &(*p)->rb_left;
570 else
571 p = &(*p)->rb_right;
572 }
573
574 rb_link_node(&he->rb_node, parent, p);
575 rb_insert_color(&he->rb_node, &output_hists);
576}
577
578static void output__resort(void)
579{
580 struct rb_node *next = rb_first(&hist);
581 struct hist_entry *n;
582
583 while (next) {
584 n = rb_entry(next, struct hist_entry, rb_node);
585 next = rb_next(&n->rb_node);
586
587 rb_erase(&n->rb_node, &hist);
588 output__insert_entry(n);
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300589 }
590}
591
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200592static size_t output__fprintf(FILE *fp, uint64_t total_samples)
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300593{
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200594 struct hist_entry *pos;
Ingo Molnar2d655372009-05-27 21:36:22 +0200595 struct sort_entry *se;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300596 struct rb_node *nd;
597 size_t ret = 0;
598
Peter Zijlstraca8cdee2009-05-28 11:08:33 +0200599 fprintf(fp, "#\n");
600
601 fprintf(fp, "# Overhead");
602 list_for_each_entry(se, &hist_entry__sort_list, list)
603 fprintf(fp, " %s", se->header);
604 fprintf(fp, "\n");
605
606 fprintf(fp, "# ........");
Ingo Molnar2d655372009-05-27 21:36:22 +0200607 list_for_each_entry(se, &hist_entry__sort_list, list) {
Peter Zijlstraca8cdee2009-05-28 11:08:33 +0200608 int i;
609
Ingo Molnar4593bba2009-06-02 15:34:25 +0200610 fprintf(fp, " ");
611 for (i = 0; i < strlen(se->header)-1; i++)
Peter Zijlstraca8cdee2009-05-28 11:08:33 +0200612 fprintf(fp, ".");
Ingo Molnar2d655372009-05-27 21:36:22 +0200613 }
Peter Zijlstraca8cdee2009-05-28 11:08:33 +0200614 fprintf(fp, "\n");
615
616 fprintf(fp, "#\n");
Ingo Molnar2d655372009-05-27 21:36:22 +0200617
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200618 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
619 pos = rb_entry(nd, struct hist_entry, rb_node);
620 ret += hist_entry__fprintf(fp, pos, total_samples);
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300621 }
622
623 return ret;
624}
625
Peter Zijlstra436224a2009-06-02 21:02:36 +0200626static void register_idle_thread(void)
627{
628 struct thread *thread = threads__findnew(0);
629
630 if (thread == NULL ||
631 thread__set_comm(thread, "[idle]")) {
632 fprintf(stderr, "problem inserting idle task.\n");
633 exit(-1);
634 }
635}
636
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200637
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200638static int __cmd_report(void)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300639{
640 unsigned long offset = 0;
641 unsigned long head = 0;
642 struct stat stat;
643 char *buf;
644 event_t *event;
645 int ret, rc = EXIT_FAILURE;
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200646 uint32_t size;
Ingo Molnarf49515b2009-05-26 19:03:36 +0200647 unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300648 char cwd[PATH_MAX], *cwdp = cwd;
649 int cwdlen;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300650
Peter Zijlstra436224a2009-06-02 21:02:36 +0200651 register_idle_thread();
652
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300653 input = open(input_name, O_RDONLY);
654 if (input < 0) {
655 perror("failed to open file");
656 exit(-1);
657 }
658
659 ret = fstat(input, &stat);
660 if (ret < 0) {
661 perror("failed to stat file");
662 exit(-1);
663 }
664
665 if (!stat.st_size) {
666 fprintf(stderr, "zero-sized file, nothing to do!\n");
667 exit(0);
668 }
669
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200670 if (load_kernel() < 0) {
Arnaldo Carvalho de Meloa2928c42009-05-28 14:55:04 -0300671 perror("failed to load kernel symbols");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300672 return EXIT_FAILURE;
673 }
674
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300675 if (!full_paths) {
676 if (getcwd(cwd, sizeof(cwd)) == NULL) {
677 perror("failed to get the current directory");
678 return EXIT_FAILURE;
679 }
680 cwdlen = strlen(cwd);
Mike Galbraith10a28252009-06-02 11:04:44 +0200681 } else {
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300682 cwdp = NULL;
Mike Galbraith10a28252009-06-02 11:04:44 +0200683 cwdlen = 0;
684 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300685remap:
686 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
687 MAP_SHARED, input, offset);
688 if (buf == MAP_FAILED) {
689 perror("failed to mmap file");
690 exit(-1);
691 }
692
693more:
694 event = (event_t *)(buf + head);
695
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200696 size = event->header.size;
697 if (!size)
698 size = 8;
699
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300700 if (head + event->header.size >= page_size * mmap_window) {
701 unsigned long shift = page_size * (head / page_size);
702 int ret;
703
704 ret = munmap(buf, page_size * mmap_window);
705 assert(ret == 0);
706
707 offset += shift;
708 head -= shift;
709 goto remap;
710 }
711
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200712 size = event->header.size;
713 if (!size)
714 goto broken_event;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300715
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300716 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
717 char level;
718 int show = 0;
719 struct dso *dso = NULL;
720 struct thread *thread = threads__findnew(event->ip.pid);
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200721 uint64_t ip = event->ip.ip;
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200722 struct map *map = NULL;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300723
Ingo Molnar97b07b62009-05-26 18:48:58 +0200724 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200725 fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
726 (void *)(offset + head),
727 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200728 event->header.misc,
729 event->ip.pid,
Ingo Molnar16f762a2009-05-27 09:10:38 +0200730 (void *)(long)ip);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200731 }
732
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300733 if (thread == NULL) {
Ingo Molnar55717312009-05-27 22:13:17 +0200734 fprintf(stderr, "problem processing %d event, skipping it.\n",
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300735 event->header.type);
Ingo Molnar55717312009-05-27 22:13:17 +0200736 goto broken_event;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300737 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300738
739 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
740 show = SHOW_KERNEL;
741 level = 'k';
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200742
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300743 dso = kernel_dso;
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200744
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300745 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
Ingo Molnar16f762a2009-05-27 09:10:38 +0200746
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300747 show = SHOW_USER;
748 level = '.';
Ingo Molnar16f762a2009-05-27 09:10:38 +0200749
750 map = thread__find_map(thread, ip);
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200751 if (map != NULL) {
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300752 dso = map->dso;
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200753 ip -= map->start + map->pgoff;
754 }
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200755
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300756 } else {
757 show = SHOW_HV;
758 level = 'H';
759 }
760
761 if (show & show_mask) {
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200762 struct symbol *sym = dso__find_symbol(dso, ip);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300763
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200764 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
765 fprintf(stderr,
Ingo Molnar55717312009-05-27 22:13:17 +0200766 "problem incrementing symbol count, skipping event\n");
767 goto broken_event;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300768 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300769 }
770 total++;
771 } else switch (event->header.type) {
772 case PERF_EVENT_MMAP: {
773 struct thread *thread = threads__findnew(event->mmap.pid);
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300774 struct map *map = map__new(&event->mmap, cwdp, cwdlen);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300775
Ingo Molnar97b07b62009-05-26 18:48:58 +0200776 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200777 fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
778 (void *)(offset + head),
779 (void *)(long)(event->header.size),
Ingo Molnar16f762a2009-05-27 09:10:38 +0200780 (void *)(long)event->mmap.start,
781 (void *)(long)event->mmap.len,
782 (void *)(long)event->mmap.pgoff,
Ingo Molnar97b07b62009-05-26 18:48:58 +0200783 event->mmap.filename);
784 }
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300785 if (thread == NULL || map == NULL) {
Ingo Molnar0a520c62009-06-02 23:24:45 +0200786 if (verbose)
787 fprintf(stderr, "problem processing PERF_EVENT_MMAP, skipping event.\n");
Ingo Molnar55717312009-05-27 22:13:17 +0200788 goto broken_event;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300789 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300790 thread__insert_map(thread, map);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200791 total_mmap++;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300792 break;
793 }
794 case PERF_EVENT_COMM: {
795 struct thread *thread = threads__findnew(event->comm.pid);
796
Ingo Molnar97b07b62009-05-26 18:48:58 +0200797 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200798 fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n",
799 (void *)(offset + head),
800 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200801 event->comm.comm, event->comm.pid);
802 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300803 if (thread == NULL ||
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300804 thread__set_comm(thread, event->comm.comm)) {
Ingo Molnar55717312009-05-27 22:13:17 +0200805 fprintf(stderr, "problem processing PERF_EVENT_COMM, skipping event.\n");
806 goto broken_event;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300807 }
Ingo Molnar97b07b62009-05-26 18:48:58 +0200808 total_comm++;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300809 break;
810 }
Ingo Molnar97b07b62009-05-26 18:48:58 +0200811 default: {
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200812broken_event:
Peter Zijlstrab7a16ea2009-05-27 13:35:35 +0200813 if (dump_trace)
814 fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
815 (void *)(offset + head),
816 (void *)(long)(event->header.size),
817 event->header.type);
818
Ingo Molnar3e706112009-05-26 18:53:17 +0200819 total_unknown++;
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200820
821 /*
822 * assume we lost track of the stream, check alignment, and
823 * increment a single u64 in the hope to catch on again 'soon'.
824 */
825
826 if (unlikely(head & 7))
827 head &= ~7ULL;
828
829 size = 8;
Ingo Molnar97b07b62009-05-26 18:48:58 +0200830 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300831 }
832
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200833 head += size;
Ingo Molnarf49515b2009-05-26 19:03:36 +0200834
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300835 if (offset + head < stat.st_size)
836 goto more;
837
838 rc = EXIT_SUCCESS;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300839 close(input);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200840
841 if (dump_trace) {
Ingo Molnar3e706112009-05-26 18:53:17 +0200842 fprintf(stderr, " IP events: %10ld\n", total);
843 fprintf(stderr, " mmap events: %10ld\n", total_mmap);
844 fprintf(stderr, " comm events: %10ld\n", total_comm);
845 fprintf(stderr, " unknown events: %10ld\n", total_unknown);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200846
847 return 0;
848 }
849
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200850 if (verbose >= 2)
Ingo Molnar16f762a2009-05-27 09:10:38 +0200851 dsos__fprintf(stdout);
Ingo Molnar16f762a2009-05-27 09:10:38 +0200852
Peter Zijlstrae7fb08b2009-05-27 20:20:24 +0200853 output__resort();
854 output__fprintf(stdout, total);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300855
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300856 return rc;
857}
858
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200859static const char * const report_usage[] = {
860 "perf report [<options>] <command>",
861 NULL
862};
863
864static const struct option options[] = {
865 OPT_STRING('i', "input", &input_name, "file",
866 "input file name"),
Arnaldo Carvalho de Melo815e7772009-05-26 19:46:14 -0300867 OPT_BOOLEAN('v', "verbose", &verbose,
868 "be more verbose (show symbol address, etc)"),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200869 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
870 "dump raw trace in ASCII"),
Peter Zijlstra450aaa22009-05-27 20:20:23 +0200871 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
Ingo Molnar63299f02009-05-28 10:52:00 +0200872 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
873 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
Arnaldo Carvalho de Melob78c07d2009-05-29 13:48:59 -0300874 OPT_BOOLEAN('P', "full-paths", &full_paths,
875 "Don't shorten the pathnames taking into account the cwd"),
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200876 OPT_END()
877};
878
879int cmd_report(int argc, const char **argv, const char *prefix)
880{
Arnaldo Carvalho de Meloa2928c42009-05-28 14:55:04 -0300881 symbol__init();
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200882
883 page_size = getpagesize();
884
885 parse_options(argc, argv, options, report_usage, 0);
886
Peter Zijlstra1aa16732009-05-27 20:20:25 +0200887 setup_sorting();
888
Ingo Molnara930d2c2009-05-27 09:50:13 +0200889 setup_pager();
890
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200891 return __cmd_report();
892}