diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 236 |
1 files changed, 197 insertions, 39 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5eb5566f0c95..135b7837e6bf 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -15,8 +15,11 @@ | |||
15 | #include "util/rbtree.h" | 15 | #include "util/rbtree.h" |
16 | #include "util/symbol.h" | 16 | #include "util/symbol.h" |
17 | #include "util/string.h" | 17 | #include "util/string.h" |
18 | #include "util/callchain.h" | ||
19 | #include "util/strlist.h" | ||
18 | 20 | ||
19 | #include "perf.h" | 21 | #include "perf.h" |
22 | #include "util/header.h" | ||
20 | 23 | ||
21 | #include "util/parse-options.h" | 24 | #include "util/parse-options.h" |
22 | #include "util/parse-events.h" | 25 | #include "util/parse-events.h" |
@@ -30,6 +33,8 @@ static char *vmlinux = NULL; | |||
30 | 33 | ||
31 | static char default_sort_order[] = "comm,dso"; | 34 | static char default_sort_order[] = "comm,dso"; |
32 | static char *sort_order = default_sort_order; | 35 | static char *sort_order = default_sort_order; |
36 | static char *dso_list_str, *comm_list_str, *sym_list_str; | ||
37 | static struct strlist *dso_list, *comm_list, *sym_list; | ||
33 | 38 | ||
34 | static int input; | 39 | static int input; |
35 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 40 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
@@ -51,6 +56,9 @@ static char *parent_pattern = default_parent_pattern; | |||
51 | static regex_t parent_regex; | 56 | static regex_t parent_regex; |
52 | 57 | ||
53 | static int exclude_other = 1; | 58 | static int exclude_other = 1; |
59 | static int callchain; | ||
60 | |||
61 | static u64 sample_type; | ||
54 | 62 | ||
55 | struct ip_event { | 63 | struct ip_event { |
56 | struct perf_event_header header; | 64 | struct perf_event_header header; |
@@ -59,11 +67,6 @@ struct ip_event { | |||
59 | unsigned char __more_data[]; | 67 | unsigned char __more_data[]; |
60 | }; | 68 | }; |
61 | 69 | ||
62 | struct ip_callchain { | ||
63 | u64 nr; | ||
64 | u64 ips[0]; | ||
65 | }; | ||
66 | |||
67 | struct mmap_event { | 70 | struct mmap_event { |
68 | struct perf_event_header header; | 71 | struct perf_event_header header; |
69 | u32 pid, tid; | 72 | u32 pid, tid; |
@@ -97,6 +100,13 @@ struct lost_event { | |||
97 | u64 lost; | 100 | u64 lost; |
98 | }; | 101 | }; |
99 | 102 | ||
103 | struct read_event { | ||
104 | struct perf_event_header header; | ||
105 | u32 pid,tid; | ||
106 | u64 value; | ||
107 | u64 format[3]; | ||
108 | }; | ||
109 | |||
100 | typedef union event_union { | 110 | typedef union event_union { |
101 | struct perf_event_header header; | 111 | struct perf_event_header header; |
102 | struct ip_event ip; | 112 | struct ip_event ip; |
@@ -105,6 +115,7 @@ typedef union event_union { | |||
105 | struct fork_event fork; | 115 | struct fork_event fork; |
106 | struct period_event period; | 116 | struct period_event period; |
107 | struct lost_event lost; | 117 | struct lost_event lost; |
118 | struct read_event read; | ||
108 | } event_t; | 119 | } event_t; |
109 | 120 | ||
110 | static LIST_HEAD(dsos); | 121 | static LIST_HEAD(dsos); |
@@ -229,7 +240,7 @@ static u64 vdso__map_ip(struct map *map, u64 ip) | |||
229 | 240 | ||
230 | static inline int is_anon_memory(const char *filename) | 241 | static inline int is_anon_memory(const char *filename) |
231 | { | 242 | { |
232 | return strcmp(filename, "//anon") == 0; | 243 | return strcmp(filename, "//anon") == 0; |
233 | } | 244 | } |
234 | 245 | ||
235 | static struct map *map__new(struct mmap_event *event) | 246 | static struct map *map__new(struct mmap_event *event) |
@@ -400,9 +411,27 @@ static void thread__insert_map(struct thread *self, struct map *map) | |||
400 | 411 | ||
401 | list_for_each_entry_safe(pos, tmp, &self->maps, node) { | 412 | list_for_each_entry_safe(pos, tmp, &self->maps, node) { |
402 | if (map__overlap(pos, map)) { | 413 | if (map__overlap(pos, map)) { |
403 | list_del_init(&pos->node); | 414 | if (verbose >= 2) { |
404 | /* XXX leaks dsos */ | 415 | printf("overlapping maps:\n"); |
405 | free(pos); | 416 | map__fprintf(map, stdout); |
417 | map__fprintf(pos, stdout); | ||
418 | } | ||
419 | |||
420 | if (map->start <= pos->start && map->end > pos->start) | ||
421 | pos->start = map->end; | ||
422 | |||
423 | if (map->end >= pos->end && map->start < pos->end) | ||
424 | pos->end = map->start; | ||
425 | |||
426 | if (verbose >= 2) { | ||
427 | printf("after collision:\n"); | ||
428 | map__fprintf(pos, stdout); | ||
429 | } | ||
430 | |||
431 | if (pos->start >= pos->end) { | ||
432 | list_del_init(&pos->node); | ||
433 | free(pos); | ||
434 | } | ||
406 | } | 435 | } |
407 | } | 436 | } |
408 | 437 | ||
@@ -464,17 +493,19 @@ static size_t threads__fprintf(FILE *fp) | |||
464 | static struct rb_root hist; | 493 | static struct rb_root hist; |
465 | 494 | ||
466 | struct hist_entry { | 495 | struct hist_entry { |
467 | struct rb_node rb_node; | 496 | struct rb_node rb_node; |
468 | 497 | ||
469 | struct thread *thread; | 498 | struct thread *thread; |
470 | struct map *map; | 499 | struct map *map; |
471 | struct dso *dso; | 500 | struct dso *dso; |
472 | struct symbol *sym; | 501 | struct symbol *sym; |
473 | struct symbol *parent; | 502 | struct symbol *parent; |
474 | u64 ip; | 503 | u64 ip; |
475 | char level; | 504 | char level; |
476 | 505 | struct callchain_node callchain; | |
477 | u64 count; | 506 | struct rb_root sorted_chain; |
507 | |||
508 | u64 count; | ||
478 | }; | 509 | }; |
479 | 510 | ||
480 | /* | 511 | /* |
@@ -745,6 +776,48 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
745 | } | 776 | } |
746 | 777 | ||
747 | static size_t | 778 | static size_t |
779 | callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | ||
780 | { | ||
781 | struct callchain_list *chain; | ||
782 | size_t ret = 0; | ||
783 | |||
784 | if (!self) | ||
785 | return 0; | ||
786 | |||
787 | ret += callchain__fprintf(fp, self->parent, total_samples); | ||
788 | |||
789 | |||
790 | list_for_each_entry(chain, &self->val, list) | ||
791 | ret += fprintf(fp, " %p\n", (void *)chain->ip); | ||
792 | |||
793 | return ret; | ||
794 | } | ||
795 | |||
796 | static size_t | ||
797 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
798 | u64 total_samples) | ||
799 | { | ||
800 | struct rb_node *rb_node; | ||
801 | struct callchain_node *chain; | ||
802 | size_t ret = 0; | ||
803 | |||
804 | rb_node = rb_first(&self->sorted_chain); | ||
805 | while (rb_node) { | ||
806 | double percent; | ||
807 | |||
808 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
809 | percent = chain->hit * 100.0 / total_samples; | ||
810 | ret += fprintf(fp, " %6.2f%%\n", percent); | ||
811 | ret += callchain__fprintf(fp, chain, total_samples); | ||
812 | ret += fprintf(fp, "\n"); | ||
813 | rb_node = rb_next(rb_node); | ||
814 | } | ||
815 | |||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | |||
820 | static size_t | ||
748 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | 821 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) |
749 | { | 822 | { |
750 | struct sort_entry *se; | 823 | struct sort_entry *se; |
@@ -784,6 +857,9 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
784 | 857 | ||
785 | ret += fprintf(fp, "\n"); | 858 | ret += fprintf(fp, "\n"); |
786 | 859 | ||
860 | if (callchain) | ||
861 | hist_entry_callchain__fprintf(fp, self, total_samples); | ||
862 | |||
787 | return ret; | 863 | return ret; |
788 | } | 864 | } |
789 | 865 | ||
@@ -797,7 +873,7 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
797 | { | 873 | { |
798 | struct dso *dso = dsop ? *dsop : NULL; | 874 | struct dso *dso = dsop ? *dsop : NULL; |
799 | struct map *map = mapp ? *mapp : NULL; | 875 | struct map *map = mapp ? *mapp : NULL; |
800 | uint64_t ip = *ipp; | 876 | u64 ip = *ipp; |
801 | 877 | ||
802 | if (!thread) | 878 | if (!thread) |
803 | return NULL; | 879 | return NULL; |
@@ -814,7 +890,6 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
814 | *mapp = map; | 890 | *mapp = map; |
815 | got_map: | 891 | got_map: |
816 | ip = map->map_ip(map, ip); | 892 | ip = map->map_ip(map, ip); |
817 | *ipp = ip; | ||
818 | 893 | ||
819 | dso = map->dso; | 894 | dso = map->dso; |
820 | } else { | 895 | } else { |
@@ -828,6 +903,8 @@ got_map: | |||
828 | dso = kernel_dso; | 903 | dso = kernel_dso; |
829 | } | 904 | } |
830 | dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); | 905 | dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); |
906 | dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip); | ||
907 | *ipp = ip; | ||
831 | 908 | ||
832 | if (dsop) | 909 | if (dsop) |
833 | *dsop = dso; | 910 | *dsop = dso; |
@@ -867,6 +944,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
867 | .level = level, | 944 | .level = level, |
868 | .count = count, | 945 | .count = count, |
869 | .parent = NULL, | 946 | .parent = NULL, |
947 | .sorted_chain = RB_ROOT | ||
870 | }; | 948 | }; |
871 | int cmp; | 949 | int cmp; |
872 | 950 | ||
@@ -909,6 +987,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
909 | 987 | ||
910 | if (!cmp) { | 988 | if (!cmp) { |
911 | he->count += count; | 989 | he->count += count; |
990 | if (callchain) | ||
991 | append_chain(&he->callchain, chain); | ||
912 | return 0; | 992 | return 0; |
913 | } | 993 | } |
914 | 994 | ||
@@ -922,6 +1002,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
922 | if (!he) | 1002 | if (!he) |
923 | return -ENOMEM; | 1003 | return -ENOMEM; |
924 | *he = entry; | 1004 | *he = entry; |
1005 | if (callchain) { | ||
1006 | callchain_init(&he->callchain); | ||
1007 | append_chain(&he->callchain, chain); | ||
1008 | } | ||
925 | rb_link_node(&he->rb_node, parent, p); | 1009 | rb_link_node(&he->rb_node, parent, p); |
926 | rb_insert_color(&he->rb_node, &hist); | 1010 | rb_insert_color(&he->rb_node, &hist); |
927 | 1011 | ||
@@ -998,6 +1082,9 @@ static void output__insert_entry(struct hist_entry *he) | |||
998 | struct rb_node *parent = NULL; | 1082 | struct rb_node *parent = NULL; |
999 | struct hist_entry *iter; | 1083 | struct hist_entry *iter; |
1000 | 1084 | ||
1085 | if (callchain) | ||
1086 | sort_chain_to_rbtree(&he->sorted_chain, &he->callchain); | ||
1087 | |||
1001 | while (*p != NULL) { | 1088 | while (*p != NULL) { |
1002 | parent = *p; | 1089 | parent = *p; |
1003 | iter = rb_entry(parent, struct hist_entry, rb_node); | 1090 | iter = rb_entry(parent, struct hist_entry, rb_node); |
@@ -1115,7 +1202,7 @@ static int validate_chain(struct ip_callchain *chain, event_t *event) | |||
1115 | } | 1202 | } |
1116 | 1203 | ||
1117 | static int | 1204 | static int |
1118 | process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | 1205 | process_sample_event(event_t *event, unsigned long offset, unsigned long head) |
1119 | { | 1206 | { |
1120 | char level; | 1207 | char level; |
1121 | int show = 0; | 1208 | int show = 0; |
@@ -1127,12 +1214,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1127 | void *more_data = event->ip.__more_data; | 1214 | void *more_data = event->ip.__more_data; |
1128 | struct ip_callchain *chain = NULL; | 1215 | struct ip_callchain *chain = NULL; |
1129 | 1216 | ||
1130 | if (event->header.type & PERF_SAMPLE_PERIOD) { | 1217 | if (sample_type & PERF_SAMPLE_PERIOD) { |
1131 | period = *(u64 *)more_data; | 1218 | period = *(u64 *)more_data; |
1132 | more_data += sizeof(u64); | 1219 | more_data += sizeof(u64); |
1133 | } | 1220 | } |
1134 | 1221 | ||
1135 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n", | 1222 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", |
1136 | (void *)(offset + head), | 1223 | (void *)(offset + head), |
1137 | (void *)(long)(event->header.size), | 1224 | (void *)(long)(event->header.size), |
1138 | event->header.misc, | 1225 | event->header.misc, |
@@ -1140,7 +1227,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1140 | (void *)(long)ip, | 1227 | (void *)(long)ip, |
1141 | (long long)period); | 1228 | (long long)period); |
1142 | 1229 | ||
1143 | if (event->header.type & PERF_SAMPLE_CALLCHAIN) { | 1230 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { |
1144 | int i; | 1231 | int i; |
1145 | 1232 | ||
1146 | chain = (void *)more_data; | 1233 | chain = (void *)more_data; |
@@ -1166,6 +1253,9 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1166 | return -1; | 1253 | return -1; |
1167 | } | 1254 | } |
1168 | 1255 | ||
1256 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) | ||
1257 | return 0; | ||
1258 | |||
1169 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | 1259 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { |
1170 | show = SHOW_KERNEL; | 1260 | show = SHOW_KERNEL; |
1171 | level = 'k'; | 1261 | level = 'k'; |
@@ -1188,6 +1278,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1188 | if (show & show_mask) { | 1278 | if (show & show_mask) { |
1189 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1279 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
1190 | 1280 | ||
1281 | if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) | ||
1282 | return 0; | ||
1283 | |||
1284 | if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) | ||
1285 | return 0; | ||
1286 | |||
1191 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1287 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
1192 | eprintf("problem incrementing symbol count, skipping event\n"); | 1288 | eprintf("problem incrementing symbol count, skipping event\n"); |
1193 | return -1; | 1289 | return -1; |
@@ -1328,14 +1424,27 @@ static void trace_event(event_t *event) | |||
1328 | } | 1424 | } |
1329 | 1425 | ||
1330 | static int | 1426 | static int |
1427 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | ||
1428 | { | ||
1429 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | ||
1430 | (void *)(offset + head), | ||
1431 | (void *)(long)(event->header.size), | ||
1432 | event->read.pid, | ||
1433 | event->read.tid, | ||
1434 | event->read.value); | ||
1435 | |||
1436 | return 0; | ||
1437 | } | ||
1438 | |||
1439 | static int | ||
1331 | process_event(event_t *event, unsigned long offset, unsigned long head) | 1440 | process_event(event_t *event, unsigned long offset, unsigned long head) |
1332 | { | 1441 | { |
1333 | trace_event(event); | 1442 | trace_event(event); |
1334 | 1443 | ||
1335 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) | ||
1336 | return process_overflow_event(event, offset, head); | ||
1337 | |||
1338 | switch (event->header.type) { | 1444 | switch (event->header.type) { |
1445 | case PERF_EVENT_SAMPLE: | ||
1446 | return process_sample_event(event, offset, head); | ||
1447 | |||
1339 | case PERF_EVENT_MMAP: | 1448 | case PERF_EVENT_MMAP: |
1340 | return process_mmap_event(event, offset, head); | 1449 | return process_mmap_event(event, offset, head); |
1341 | 1450 | ||
@@ -1351,6 +1460,9 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1351 | case PERF_EVENT_LOST: | 1460 | case PERF_EVENT_LOST: |
1352 | return process_lost_event(event, offset, head); | 1461 | return process_lost_event(event, offset, head); |
1353 | 1462 | ||
1463 | case PERF_EVENT_READ: | ||
1464 | return process_read_event(event, offset, head); | ||
1465 | |||
1354 | /* | 1466 | /* |
1355 | * We dont process them right now but they are fine: | 1467 | * We dont process them right now but they are fine: |
1356 | */ | 1468 | */ |
@@ -1366,13 +1478,30 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1366 | return 0; | 1478 | return 0; |
1367 | } | 1479 | } |
1368 | 1480 | ||
1369 | static struct perf_file_header file_header; | 1481 | static struct perf_header *header; |
1482 | |||
1483 | static u64 perf_header__sample_type(void) | ||
1484 | { | ||
1485 | u64 sample_type = 0; | ||
1486 | int i; | ||
1487 | |||
1488 | for (i = 0; i < header->attrs; i++) { | ||
1489 | struct perf_header_attr *attr = header->attr[i]; | ||
1490 | |||
1491 | if (!sample_type) | ||
1492 | sample_type = attr->attr.sample_type; | ||
1493 | else if (sample_type != attr->attr.sample_type) | ||
1494 | die("non matching sample_type"); | ||
1495 | } | ||
1496 | |||
1497 | return sample_type; | ||
1498 | } | ||
1370 | 1499 | ||
1371 | static int __cmd_report(void) | 1500 | static int __cmd_report(void) |
1372 | { | 1501 | { |
1373 | int ret, rc = EXIT_FAILURE; | 1502 | int ret, rc = EXIT_FAILURE; |
1374 | unsigned long offset = 0; | 1503 | unsigned long offset = 0; |
1375 | unsigned long head = sizeof(file_header); | 1504 | unsigned long head, shift; |
1376 | struct stat stat; | 1505 | struct stat stat; |
1377 | event_t *event; | 1506 | event_t *event; |
1378 | uint32_t size; | 1507 | uint32_t size; |
@@ -1400,13 +1529,12 @@ static int __cmd_report(void) | |||
1400 | exit(0); | 1529 | exit(0); |
1401 | } | 1530 | } |
1402 | 1531 | ||
1403 | if (read(input, &file_header, sizeof(file_header)) == -1) { | 1532 | header = perf_header__read(input); |
1404 | perror("failed to read file headers"); | 1533 | head = header->data_offset; |
1405 | exit(-1); | ||
1406 | } | ||
1407 | 1534 | ||
1408 | if (sort__has_parent && | 1535 | sample_type = perf_header__sample_type(); |
1409 | !(file_header.sample_type & PERF_SAMPLE_CALLCHAIN)) { | 1536 | |
1537 | if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | ||
1410 | fprintf(stderr, "selected --sort parent, but no callchain data\n"); | 1538 | fprintf(stderr, "selected --sort parent, but no callchain data\n"); |
1411 | exit(-1); | 1539 | exit(-1); |
1412 | } | 1540 | } |
@@ -1426,6 +1554,11 @@ static int __cmd_report(void) | |||
1426 | cwd = NULL; | 1554 | cwd = NULL; |
1427 | cwdlen = 0; | 1555 | cwdlen = 0; |
1428 | } | 1556 | } |
1557 | |||
1558 | shift = page_size * (head / page_size); | ||
1559 | offset += shift; | ||
1560 | head -= shift; | ||
1561 | |||
1429 | remap: | 1562 | remap: |
1430 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | 1563 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, |
1431 | MAP_SHARED, input, offset); | 1564 | MAP_SHARED, input, offset); |
@@ -1442,9 +1575,10 @@ more: | |||
1442 | size = 8; | 1575 | size = 8; |
1443 | 1576 | ||
1444 | if (head + event->header.size >= page_size * mmap_window) { | 1577 | if (head + event->header.size >= page_size * mmap_window) { |
1445 | unsigned long shift = page_size * (head / page_size); | ||
1446 | int ret; | 1578 | int ret; |
1447 | 1579 | ||
1580 | shift = page_size * (head / page_size); | ||
1581 | |||
1448 | ret = munmap(buf, page_size * mmap_window); | 1582 | ret = munmap(buf, page_size * mmap_window); |
1449 | assert(ret == 0); | 1583 | assert(ret == 0); |
1450 | 1584 | ||
@@ -1482,7 +1616,7 @@ more: | |||
1482 | 1616 | ||
1483 | head += size; | 1617 | head += size; |
1484 | 1618 | ||
1485 | if (offset + head >= sizeof(file_header) + file_header.data_size) | 1619 | if (offset + head >= header->data_offset + header->data_size) |
1486 | goto done; | 1620 | goto done; |
1487 | 1621 | ||
1488 | if (offset + head < stat.st_size) | 1622 | if (offset + head < stat.st_size) |
@@ -1536,6 +1670,13 @@ static const struct option options[] = { | |||
1536 | "regex filter to identify parent, see: '--sort parent'"), | 1670 | "regex filter to identify parent, see: '--sort parent'"), |
1537 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 1671 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
1538 | "Only display entries with parent-match"), | 1672 | "Only display entries with parent-match"), |
1673 | OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"), | ||
1674 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | ||
1675 | "only consider symbols in these dsos"), | ||
1676 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | ||
1677 | "only consider symbols in these comms"), | ||
1678 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | ||
1679 | "only consider these symbols"), | ||
1539 | OPT_END() | 1680 | OPT_END() |
1540 | }; | 1681 | }; |
1541 | 1682 | ||
@@ -1554,6 +1695,19 @@ static void setup_sorting(void) | |||
1554 | free(str); | 1695 | free(str); |
1555 | } | 1696 | } |
1556 | 1697 | ||
1698 | static void setup_list(struct strlist **list, const char *list_str, | ||
1699 | const char *list_name) | ||
1700 | { | ||
1701 | if (list_str) { | ||
1702 | *list = strlist__new(true, list_str); | ||
1703 | if (!*list) { | ||
1704 | fprintf(stderr, "problems parsing %s list\n", | ||
1705 | list_name); | ||
1706 | exit(129); | ||
1707 | } | ||
1708 | } | ||
1709 | } | ||
1710 | |||
1557 | int cmd_report(int argc, const char **argv, const char *prefix) | 1711 | int cmd_report(int argc, const char **argv, const char *prefix) |
1558 | { | 1712 | { |
1559 | symbol__init(); | 1713 | symbol__init(); |
@@ -1575,6 +1729,10 @@ int cmd_report(int argc, const char **argv, const char *prefix) | |||
1575 | if (argc) | 1729 | if (argc) |
1576 | usage_with_options(report_usage, options); | 1730 | usage_with_options(report_usage, options); |
1577 | 1731 | ||
1732 | setup_list(&dso_list, dso_list_str, "dso"); | ||
1733 | setup_list(&comm_list, comm_list_str, "comm"); | ||
1734 | setup_list(&sym_list, sym_list_str, "symbol"); | ||
1735 | |||
1578 | setup_pager(); | 1736 | setup_pager(); |
1579 | 1737 | ||
1580 | return __cmd_report(); | 1738 | return __cmd_report(); |