blob: 864f68f06a9e8b14ead19d2929b276a43dba6e8e [file] [log] [blame]
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -03001#define _GNU_SOURCE
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <sys/time.h>
5#include <unistd.h>
6#include <stdint.h>
7#include <stdbool.h>
8#include <stdlib.h>
9#include <string.h>
10#include <limits.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <errno.h>
14#include <ctype.h>
15#include <time.h>
16#include <getopt.h>
17#include <assert.h>
18#include <search.h>
19
20#include <sys/ioctl.h>
21#include <sys/poll.h>
22#include <sys/prctl.h>
23#include <sys/wait.h>
24#include <sys/mman.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27
28#include <linux/unistd.h>
29#include <linux/types.h>
30
31#include "../../include/linux/perf_counter.h"
32#include "list.h"
33
34#define SHOW_KERNEL 1
35#define SHOW_USER 2
36#define SHOW_HV 4
37
38static char const *input_name = "output.perf";
39static int input;
40static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
41
42static unsigned long page_size;
43static unsigned long mmap_window = 32;
44
45static const char *perf_event_names[] = {
46 [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP",
47 [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP",
48 [PERF_EVENT_COMM] = " PERF_EVENT_COMM",
49};
50
51struct ip_event {
52 struct perf_event_header header;
53 __u64 ip;
54 __u32 pid, tid;
55};
56struct mmap_event {
57 struct perf_event_header header;
58 __u32 pid, tid;
59 __u64 start;
60 __u64 len;
61 __u64 pgoff;
62 char filename[PATH_MAX];
63};
64struct comm_event {
65 struct perf_event_header header;
66 __u32 pid,tid;
67 char comm[16];
68};
69
70typedef union event_union {
71 struct perf_event_header header;
72 struct ip_event ip;
73 struct mmap_event mmap;
74 struct comm_event comm;
75} event_t;
76
77struct section {
78 struct list_head node;
79 uint64_t start;
80 uint64_t end;
81 uint64_t offset;
82 char name[0];
83};
84
85static struct section *section__new(uint64_t start, uint64_t size,
86 uint64_t offset, char *name)
87{
88 struct section *self = malloc(sizeof(*self) + strlen(name) + 1);
89
90 if (self != NULL) {
91 self->start = start;
92 self->end = start + size;
93 self->offset = offset;
94 strcpy(self->name, name);
95 }
96
97 return self;
98}
99
100static void section__delete(struct section *self)
101{
102 free(self);
103}
104
105struct symbol {
106 struct list_head node;
107 uint64_t start;
108 uint64_t end;
109 char name[0];
110};
111
112static struct symbol *symbol__new(uint64_t start, uint64_t len, const char *name)
113{
114 struct symbol *self = malloc(sizeof(*self) + strlen(name) + 1);
115
116 if (self != NULL) {
117 self->start = start;
118 self->end = start + len;
119 strcpy(self->name, name);
120 }
121
122 return self;
123}
124
125static void symbol__delete(struct symbol *self)
126{
127 free(self);
128}
129
130static size_t symbol__fprintf(struct symbol *self, FILE *fp)
131{
132 return fprintf(fp, " %lx-%lx %s\n",
133 self->start, self->end, self->name);
134}
135
136struct dso {
137 struct list_head node;
138 struct list_head sections;
139 struct list_head syms;
140 char name[0];
141};
142
143static struct dso *dso__new(const char *name)
144{
145 struct dso *self = malloc(sizeof(*self) + strlen(name) + 1);
146
147 if (self != NULL) {
148 strcpy(self->name, name);
149 INIT_LIST_HEAD(&self->sections);
150 INIT_LIST_HEAD(&self->syms);
151 }
152
153 return self;
154}
155
156static void dso__delete_sections(struct dso *self)
157{
158 struct section *pos, *n;
159
160 list_for_each_entry_safe(pos, n, &self->sections, node)
161 section__delete(pos);
162}
163
164static void dso__delete_symbols(struct dso *self)
165{
166 struct symbol *pos, *n;
167
168 list_for_each_entry_safe(pos, n, &self->syms, node)
169 symbol__delete(pos);
170}
171
172static void dso__delete(struct dso *self)
173{
174 dso__delete_sections(self);
175 dso__delete_symbols(self);
176 free(self);
177}
178
179static void dso__insert_symbol(struct dso *self, struct symbol *sym)
180{
181 list_add_tail(&sym->node, &self->syms);
182}
183
184static struct symbol *dso__find_symbol(struct dso *self, uint64_t ip)
185{
186 if (self == NULL)
187 return NULL;
188
189 struct symbol *pos;
190
191 list_for_each_entry(pos, &self->syms, node)
192 if (ip >= pos->start && ip <= pos->end)
193 return pos;
194
195 return NULL;
196}
197
198static int dso__load(struct dso *self)
199{
200 /* FIXME */
201 return 0;
202}
203
204static size_t dso__fprintf(struct dso *self, FILE *fp)
205{
206 struct symbol *pos;
207 size_t ret = fprintf(fp, "dso: %s\n", self->name);
208
209 list_for_each_entry(pos, &self->syms, node)
210 ret += symbol__fprintf(pos, fp);
211
212 return ret;
213}
214
215static LIST_HEAD(dsos);
216static struct dso *kernel_dso;
217
218static void dsos__add(struct dso *dso)
219{
220 list_add_tail(&dso->node, &dsos);
221}
222
223static struct dso *dsos__find(const char *name)
224{
225 struct dso *pos;
226
227 list_for_each_entry(pos, &dsos, node)
228 if (strcmp(pos->name, name) == 0)
229 return pos;
230 return NULL;
231}
232
233static struct dso *dsos__findnew(const char *name)
234{
235 struct dso *dso = dsos__find(name);
236
237 if (dso == NULL) {
238 dso = dso__new(name);
239 if (dso != NULL && dso__load(dso) < 0)
240 goto out_delete_dso;
241
242 dsos__add(dso);
243 }
244
245 return dso;
246
247out_delete_dso:
248 dso__delete(dso);
249 return NULL;
250}
251
252static void dsos__fprintf(FILE *fp)
253{
254 struct dso *pos;
255
256 list_for_each_entry(pos, &dsos, node)
257 dso__fprintf(pos, fp);
258}
259
260static int load_kallsyms(void)
261{
262 kernel_dso = dso__new("[kernel]");
263 if (kernel_dso == NULL)
264 return -1;
265
266 FILE *file = fopen("/proc/kallsyms", "r");
267
268 if (file == NULL)
269 goto out_delete_dso;
270
271 char *line = NULL;
272 size_t n;
273
274 while (!feof(file)) {
275 unsigned long long start;
276 char c, symbf[4096];
277
278 if (getline(&line, &n, file) < 0)
279 break;
280
281 if (!line)
282 goto out_delete_dso;
283
284 if (sscanf(line, "%llx %c %s", &start, &c, symbf) == 3) {
285 struct symbol *sym = symbol__new(start, 0x1000000, symbf);
286
287 if (sym == NULL)
288 goto out_delete_dso;
289
290 dso__insert_symbol(kernel_dso, sym);
291 }
292 }
293
294 dsos__add(kernel_dso);
295 free(line);
296 fclose(file);
297 return 0;
298
299out_delete_dso:
300 dso__delete(kernel_dso);
301 return -1;
302}
303
304struct map {
305 struct list_head node;
306 uint64_t start;
307 uint64_t end;
308 uint64_t pgoff;
309 struct dso *dso;
310};
311
312static struct map *map__new(struct mmap_event *event)
313{
314 struct map *self = malloc(sizeof(*self));
315
316 if (self != NULL) {
317 self->start = event->start;
318 self->end = event->start + event->len;
319 self->pgoff = event->pgoff;
320
321 self->dso = dsos__findnew(event->filename);
322 if (self->dso == NULL)
323 goto out_delete;
324 }
325 return self;
326out_delete:
327 free(self);
328 return NULL;
329}
330
331static size_t map__fprintf(struct map *self, FILE *fp)
332{
333 return fprintf(fp, " %lx-%lx %lx %s\n",
334 self->start, self->end, self->pgoff, self->dso->name);
335}
336
337struct symhist {
338 struct list_head node;
339 struct dso *dso;
340 struct symbol *sym;
341 uint32_t count;
342 char level;
343};
344
345static struct symhist *symhist__new(struct symbol *sym, struct dso *dso,
346 char level)
347{
348 struct symhist *self = malloc(sizeof(*self));
349
350 if (self != NULL) {
351 self->sym = sym;
352 self->dso = dso;
353 self->level = level;
354 self->count = 0;
355 }
356
357 return self;
358}
359
360static void symhist__delete(struct symhist *self)
361{
362 free(self);
363}
364
365static bool symhist__equal(struct symhist *self, struct symbol *sym,
366 struct dso *dso, char level)
367{
368 return self->level == level && self->sym == sym && self->dso == dso;
369}
370
371static void symhist__inc(struct symhist *self)
372{
373 ++self->count;
374}
375
376static size_t symhist__fprintf(struct symhist *self, FILE *fp)
377{
378 size_t ret = fprintf(fp, "[%c] ", self->level);
379
380 if (self->level != '.')
381 ret += fprintf(fp, "%s", self->sym->name);
382 else
383 ret += fprintf(fp, "%s: %s",
384 self->dso ? self->dso->name : "<unknown",
385 self->sym ? self->sym->name : "<unknown>");
386 return ret + fprintf(fp, ": %u\n", self->count);
387}
388
389struct thread {
390 struct list_head node;
391 struct list_head maps;
392 struct list_head symhists;
393 pid_t pid;
394 char *comm;
395};
396
397static struct thread *thread__new(pid_t pid)
398{
399 struct thread *self = malloc(sizeof(*self));
400
401 if (self != NULL) {
402 self->pid = pid;
403 self->comm = NULL;
404 INIT_LIST_HEAD(&self->maps);
405 INIT_LIST_HEAD(&self->symhists);
406 }
407
408 return self;
409}
410
411static void thread__insert_symhist(struct thread *self,
412 struct symhist *symhist)
413{
414 list_add_tail(&symhist->node, &self->symhists);
415}
416
417static struct symhist *thread__symhists_find(struct thread *self,
418 struct symbol *sym,
419 struct dso *dso, char level)
420{
421 struct symhist *pos;
422
423 list_for_each_entry(pos, &self->symhists, node)
424 if (symhist__equal(pos, sym, dso, level))
425 return pos;
426
427 return NULL;
428}
429
430static int thread__symbol_incnew(struct thread *self, struct symbol *sym,
431 struct dso *dso, char level)
432{
433 struct symhist *symhist = thread__symhists_find(self, sym, dso, level);
434
435 if (symhist == NULL) {
436 symhist = symhist__new(sym, dso, level);
437 if (symhist == NULL)
438 goto out_error;
439 thread__insert_symhist(self, symhist);
440 }
441
442 symhist__inc(symhist);
443 return 0;
444out_error:
445 return -ENOMEM;
446}
447
448static int thread__set_comm(struct thread *self, const char *comm)
449{
450 self->comm = strdup(comm);
451 return self->comm ? 0 : -ENOMEM;
452}
453
454static size_t thread__maps_fprintf(struct thread *self, FILE *fp)
455{
456 struct map *pos;
457 size_t ret = 0;
458
459 list_for_each_entry(pos, &self->maps, node)
460 ret += map__fprintf(pos, fp);
461
462 return ret;
463}
464
465static size_t thread__fprintf(struct thread *self, FILE *fp)
466{
467 struct symhist *pos;
468 int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm);
469
470 list_for_each_entry(pos, &self->symhists, node)
471 ret += symhist__fprintf(pos, fp);
472
473 return ret;
474}
475
476static LIST_HEAD(threads);
477
478static void threads__add(struct thread *thread)
479{
480 list_add_tail(&thread->node, &threads);
481}
482
483static struct thread *threads__find(pid_t pid)
484{
485 struct thread *pos;
486
487 list_for_each_entry(pos, &threads, node)
488 if (pos->pid == pid)
489 return pos;
490 return NULL;
491}
492
493static struct thread *threads__findnew(pid_t pid)
494{
495 struct thread *thread = threads__find(pid);
496
497 if (thread == NULL) {
498 thread = thread__new(pid);
499 if (thread != NULL)
500 threads__add(thread);
501 }
502
503 return thread;
504}
505
506static void thread__insert_map(struct thread *self, struct map *map)
507{
508 list_add_tail(&map->node, &self->maps);
509}
510
511static struct map *thread__find_map(struct thread *self, uint64_t ip)
512{
513 if (self == NULL)
514 return NULL;
515
516 struct map *pos;
517
518 list_for_each_entry(pos, &self->maps, node)
519 if (ip >= pos->start && ip <= pos->end)
520 return pos;
521
522 return NULL;
523}
524
525static void threads__fprintf(FILE *fp)
526{
527 struct thread *pos;
528
529 list_for_each_entry(pos, &threads, node)
530 thread__fprintf(pos, fp);
531}
532
533#if 0
534static std::string resolve_user_symbol(int pid, uint64_t ip)
535{
536 std::string sym = "<unknown>";
537
538 maps_t &m = maps[pid];
539 maps_t::const_iterator mi = m.upper_bound(map(ip));
540 if (mi == m.end())
541 return sym;
542
543 ip -= mi->start + mi->pgoff;
544
545 symbols_t &s = dsos[mi->dso].syms;
546 symbols_t::const_iterator si = s.upper_bound(symbol(ip));
547
548 sym = mi->dso + ": <unknown>";
549
550 if (si == s.begin())
551 return sym;
552 si--;
553
554 if (si->start <= ip && ip < si->end)
555 sym = mi->dso + ": " + si->name;
556#if 0
557 else if (si->start <= ip)
558 sym = mi->dso + ": ?" + si->name;
559#endif
560
561 return sym;
562}
563#endif
564
565static void display_help(void)
566{
567 printf(
568 "Usage: perf-report [<options>]\n"
569 " -i file --input=<file> # input file\n"
570 );
571
572 exit(0);
573}
574
575static void process_options(int argc, char *argv[])
576{
577 int error = 0;
578
579 for (;;) {
580 int option_index = 0;
581 /** Options for getopt */
582 static struct option long_options[] = {
583 {"input", required_argument, NULL, 'i'},
584 {"no-user", no_argument, NULL, 'u'},
585 {"no-kernel", no_argument, NULL, 'k'},
586 {"no-hv", no_argument, NULL, 'h'},
587 {NULL, 0, NULL, 0 }
588 };
589 int c = getopt_long(argc, argv, "+:i:kuh",
590 long_options, &option_index);
591 if (c == -1)
592 break;
593
594 switch (c) {
595 case 'i': input_name = strdup(optarg); break;
596 case 'k': show_mask &= ~SHOW_KERNEL; break;
597 case 'u': show_mask &= ~SHOW_USER; break;
598 case 'h': show_mask &= ~SHOW_HV; break;
599 default: error = 1; break;
600 }
601 }
602
603 if (error)
604 display_help();
605}
606
607int cmd_report(int argc, char **argv)
608{
609 unsigned long offset = 0;
610 unsigned long head = 0;
611 struct stat stat;
612 char *buf;
613 event_t *event;
614 int ret, rc = EXIT_FAILURE;
615 unsigned long total = 0;
616
617 page_size = getpagesize();
618
619 process_options(argc, argv);
620
621 input = open(input_name, O_RDONLY);
622 if (input < 0) {
623 perror("failed to open file");
624 exit(-1);
625 }
626
627 ret = fstat(input, &stat);
628 if (ret < 0) {
629 perror("failed to stat file");
630 exit(-1);
631 }
632
633 if (!stat.st_size) {
634 fprintf(stderr, "zero-sized file, nothing to do!\n");
635 exit(0);
636 }
637
638 if (load_kallsyms() < 0) {
639 perror("failed to open kallsyms");
640 return EXIT_FAILURE;
641 }
642
643remap:
644 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
645 MAP_SHARED, input, offset);
646 if (buf == MAP_FAILED) {
647 perror("failed to mmap file");
648 exit(-1);
649 }
650
651more:
652 event = (event_t *)(buf + head);
653
654 if (head + event->header.size >= page_size * mmap_window) {
655 unsigned long shift = page_size * (head / page_size);
656 int ret;
657
658 ret = munmap(buf, page_size * mmap_window);
659 assert(ret == 0);
660
661 offset += shift;
662 head -= shift;
663 goto remap;
664 }
665
666
667 if (!event->header.size) {
668 fprintf(stderr, "zero-sized event at file offset %ld\n", offset + head);
669 fprintf(stderr, "skipping %ld bytes of events.\n", stat.st_size - offset - head);
670 goto done;
671 }
672
673 head += event->header.size;
674
675 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
676 char level;
677 int show = 0;
678 struct dso *dso = NULL;
679 struct thread *thread = threads__findnew(event->ip.pid);
680
681 if (thread == NULL)
682 goto done;
683
684 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
685 show = SHOW_KERNEL;
686 level = 'k';
687 dso = kernel_dso;
688 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
689 show = SHOW_USER;
690 level = '.';
691 struct map *map = thread__find_map(thread, event->ip.ip);
692 if (map != NULL)
693 dso = map->dso;
694 } else {
695 show = SHOW_HV;
696 level = 'H';
697 }
698
699 if (show & show_mask) {
700 struct symbol *sym = dso__find_symbol(dso, event->ip.ip);
701
702 if (thread__symbol_incnew(thread, sym, dso, level))
703 goto done;
704 }
705 total++;
706 } else switch (event->header.type) {
707 case PERF_EVENT_MMAP: {
708 struct thread *thread = threads__findnew(event->mmap.pid);
709 struct map *map = map__new(&event->mmap);
710
711 if (thread == NULL || map == NULL )
712 goto done;
713 thread__insert_map(thread, map);
714 break;
715 }
716 case PERF_EVENT_COMM: {
717 struct thread *thread = threads__findnew(event->comm.pid);
718
719 if (thread == NULL ||
720 thread__set_comm(thread, event->comm.comm))
721 goto done;
722 break;
723 }
724 }
725
726 if (offset + head < stat.st_size)
727 goto more;
728
729 rc = EXIT_SUCCESS;
730done:
731 close(input);
732 //dsos__fprintf(stdout);
733 threads__fprintf(stdout);
734#if 0
735 std::map<std::string, int>::iterator hi = hist.begin();
736
737 while (hi != hist.end()) {
738 rev_hist.insert(std::pair<int, std::string>(hi->second, hi->first));
739 hist.erase(hi++);
740 }
741
742 std::multimap<int, std::string>::const_iterator ri = rev_hist.begin();
743
744 while (ri != rev_hist.end()) {
745 printf(" %5.2f %s\n", (100.0 * ri->first)/total, ri->second.c_str());
746 ri++;
747 }
748#endif
749 return rc;
750}
751