diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 111 |
1 files changed, 96 insertions, 15 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8cb58d68a006..b53a60fc12de 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -68,7 +68,7 @@ static int callchain; | |||
68 | 68 | ||
69 | static | 69 | static |
70 | struct callchain_param callchain_param = { | 70 | struct callchain_param callchain_param = { |
71 | .mode = CHAIN_GRAPH_ABS, | 71 | .mode = CHAIN_GRAPH_REL, |
72 | .min_percent = 0.5 | 72 | .min_percent = 0.5 |
73 | }; | 73 | }; |
74 | 74 | ||
@@ -112,7 +112,9 @@ struct read_event { | |||
112 | struct perf_event_header header; | 112 | struct perf_event_header header; |
113 | u32 pid,tid; | 113 | u32 pid,tid; |
114 | u64 value; | 114 | u64 value; |
115 | u64 format[3]; | 115 | u64 time_enabled; |
116 | u64 time_running; | ||
117 | u64 id; | ||
116 | }; | 118 | }; |
117 | 119 | ||
118 | typedef union event_union { | 120 | typedef union event_union { |
@@ -698,7 +700,8 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | |||
698 | size_t ret = 0; | 700 | size_t ret = 0; |
699 | 701 | ||
700 | if (verbose) | 702 | if (verbose) |
701 | ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); | 703 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
704 | dso__symtab_origin(self->dso)); | ||
702 | 705 | ||
703 | ret += repsep_fprintf(fp, "[%c] ", self->level); | 706 | ret += repsep_fprintf(fp, "[%c] ", self->level); |
704 | if (self->sym) { | 707 | if (self->sym) { |
@@ -888,6 +891,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | |||
888 | return ret; | 891 | return ret; |
889 | } | 892 | } |
890 | 893 | ||
894 | static struct symbol *rem_sq_bracket; | ||
895 | static struct callchain_list rem_hits; | ||
896 | |||
897 | static void init_rem_hits(void) | ||
898 | { | ||
899 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
900 | if (!rem_sq_bracket) { | ||
901 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
902 | return; | ||
903 | } | ||
904 | |||
905 | strcpy(rem_sq_bracket->name, "[...]"); | ||
906 | rem_hits.sym = rem_sq_bracket; | ||
907 | } | ||
908 | |||
891 | static size_t | 909 | static size_t |
892 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 910 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
893 | u64 total_samples, int depth, int depth_mask) | 911 | u64 total_samples, int depth, int depth_mask) |
@@ -897,25 +915,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
897 | struct callchain_list *chain; | 915 | struct callchain_list *chain; |
898 | int new_depth_mask = depth_mask; | 916 | int new_depth_mask = depth_mask; |
899 | u64 new_total; | 917 | u64 new_total; |
918 | u64 remaining; | ||
900 | size_t ret = 0; | 919 | size_t ret = 0; |
901 | int i; | 920 | int i; |
902 | 921 | ||
903 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 922 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
904 | new_total = self->cumul_hit; | 923 | new_total = self->children_hit; |
905 | else | 924 | else |
906 | new_total = total_samples; | 925 | new_total = total_samples; |
907 | 926 | ||
927 | remaining = new_total; | ||
928 | |||
908 | node = rb_first(&self->rb_root); | 929 | node = rb_first(&self->rb_root); |
909 | while (node) { | 930 | while (node) { |
931 | u64 cumul; | ||
932 | |||
910 | child = rb_entry(node, struct callchain_node, rb_node); | 933 | child = rb_entry(node, struct callchain_node, rb_node); |
934 | cumul = cumul_hits(child); | ||
935 | remaining -= cumul; | ||
911 | 936 | ||
912 | /* | 937 | /* |
913 | * The depth mask manages the output of pipes that show | 938 | * The depth mask manages the output of pipes that show |
914 | * the depth. We don't want to keep the pipes of the current | 939 | * the depth. We don't want to keep the pipes of the current |
915 | * level for the last child of this depth | 940 | * level for the last child of this depth. |
941 | * Except if we have remaining filtered hits. They will | ||
942 | * supersede the last child | ||
916 | */ | 943 | */ |
917 | next = rb_next(node); | 944 | next = rb_next(node); |
918 | if (!next) | 945 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) |
919 | new_depth_mask &= ~(1 << (depth - 1)); | 946 | new_depth_mask &= ~(1 << (depth - 1)); |
920 | 947 | ||
921 | /* | 948 | /* |
@@ -930,7 +957,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
930 | ret += ipchain__fprintf_graph(fp, chain, depth, | 957 | ret += ipchain__fprintf_graph(fp, chain, depth, |
931 | new_depth_mask, i++, | 958 | new_depth_mask, i++, |
932 | new_total, | 959 | new_total, |
933 | child->cumul_hit); | 960 | cumul); |
934 | } | 961 | } |
935 | ret += callchain__fprintf_graph(fp, child, new_total, | 962 | ret += callchain__fprintf_graph(fp, child, new_total, |
936 | depth + 1, | 963 | depth + 1, |
@@ -938,6 +965,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
938 | node = next; | 965 | node = next; |
939 | } | 966 | } |
940 | 967 | ||
968 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
969 | remaining && remaining != new_total) { | ||
970 | |||
971 | if (!rem_sq_bracket) | ||
972 | return ret; | ||
973 | |||
974 | new_depth_mask &= ~(1 << (depth - 1)); | ||
975 | |||
976 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
977 | new_depth_mask, 0, new_total, | ||
978 | remaining); | ||
979 | } | ||
980 | |||
941 | return ret; | 981 | return ret; |
942 | } | 982 | } |
943 | 983 | ||
@@ -1358,6 +1398,8 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1358 | unsigned int width; | 1398 | unsigned int width; |
1359 | char *col_width = col_width_list_str; | 1399 | char *col_width = col_width_list_str; |
1360 | 1400 | ||
1401 | init_rem_hits(); | ||
1402 | |||
1361 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); | 1403 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
1362 | fprintf(fp, "#\n"); | 1404 | fprintf(fp, "#\n"); |
1363 | 1405 | ||
@@ -1429,6 +1471,8 @@ print_entries: | |||
1429 | } | 1471 | } |
1430 | fprintf(fp, "\n"); | 1472 | fprintf(fp, "\n"); |
1431 | 1473 | ||
1474 | free(rem_sq_bracket); | ||
1475 | |||
1432 | return ret; | 1476 | return ret; |
1433 | } | 1477 | } |
1434 | 1478 | ||
@@ -1482,11 +1526,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1482 | more_data += sizeof(u64); | 1526 | more_data += sizeof(u64); |
1483 | } | 1527 | } |
1484 | 1528 | ||
1485 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", | 1529 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
1486 | (void *)(offset + head), | 1530 | (void *)(offset + head), |
1487 | (void *)(long)(event->header.size), | 1531 | (void *)(long)(event->header.size), |
1488 | event->header.misc, | 1532 | event->header.misc, |
1489 | event->ip.pid, | 1533 | event->ip.pid, event->ip.tid, |
1490 | (void *)(long)ip, | 1534 | (void *)(long)ip, |
1491 | (long long)period); | 1535 | (long long)period); |
1492 | 1536 | ||
@@ -1546,10 +1590,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1546 | if (show & show_mask) { | 1590 | if (show & show_mask) { |
1547 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1591 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
1548 | 1592 | ||
1549 | if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) | 1593 | if (dso_list && (!dso || !dso->name || |
1594 | !strlist__has_entry(dso_list, dso->name))) | ||
1550 | return 0; | 1595 | return 0; |
1551 | 1596 | ||
1552 | if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) | 1597 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) |
1553 | return 0; | 1598 | return 0; |
1554 | 1599 | ||
1555 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1600 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
@@ -1568,10 +1613,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
1568 | struct thread *thread = threads__findnew(event->mmap.pid); | 1613 | struct thread *thread = threads__findnew(event->mmap.pid); |
1569 | struct map *map = map__new(&event->mmap); | 1614 | struct map *map = map__new(&event->mmap); |
1570 | 1615 | ||
1571 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", | 1616 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
1572 | (void *)(offset + head), | 1617 | (void *)(offset + head), |
1573 | (void *)(long)(event->header.size), | 1618 | (void *)(long)(event->header.size), |
1574 | event->mmap.pid, | 1619 | event->mmap.pid, |
1620 | event->mmap.tid, | ||
1575 | (void *)(long)event->mmap.start, | 1621 | (void *)(long)event->mmap.start, |
1576 | (void *)(long)event->mmap.len, | 1622 | (void *)(long)event->mmap.len, |
1577 | (void *)(long)event->mmap.pgoff, | 1623 | (void *)(long)event->mmap.pgoff, |
@@ -1690,14 +1736,37 @@ static void trace_event(event_t *event) | |||
1690 | dprintf(".\n"); | 1736 | dprintf(".\n"); |
1691 | } | 1737 | } |
1692 | 1738 | ||
1739 | static struct perf_header *header; | ||
1740 | |||
1741 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
1742 | { | ||
1743 | int i; | ||
1744 | |||
1745 | for (i = 0; i < header->attrs; i++) { | ||
1746 | struct perf_header_attr *attr = header->attr[i]; | ||
1747 | int j; | ||
1748 | |||
1749 | for (j = 0; j < attr->ids; j++) { | ||
1750 | if (attr->id[j] == id) | ||
1751 | return &attr->attr; | ||
1752 | } | ||
1753 | } | ||
1754 | |||
1755 | return NULL; | ||
1756 | } | ||
1757 | |||
1693 | static int | 1758 | static int |
1694 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | 1759 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
1695 | { | 1760 | { |
1696 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | 1761 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
1762 | |||
1763 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
1697 | (void *)(offset + head), | 1764 | (void *)(offset + head), |
1698 | (void *)(long)(event->header.size), | 1765 | (void *)(long)(event->header.size), |
1699 | event->read.pid, | 1766 | event->read.pid, |
1700 | event->read.tid, | 1767 | event->read.tid, |
1768 | attr ? __event_name(attr->type, attr->config) | ||
1769 | : "FAIL", | ||
1701 | event->read.value); | 1770 | event->read.value); |
1702 | 1771 | ||
1703 | return 0; | 1772 | return 0; |
@@ -1743,8 +1812,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1743 | return 0; | 1812 | return 0; |
1744 | } | 1813 | } |
1745 | 1814 | ||
1746 | static struct perf_header *header; | ||
1747 | |||
1748 | static u64 perf_header__sample_type(void) | 1815 | static u64 perf_header__sample_type(void) |
1749 | { | 1816 | { |
1750 | u64 sample_type = 0; | 1817 | u64 sample_type = 0; |
@@ -1812,6 +1879,13 @@ static int __cmd_report(void) | |||
1812 | " -g?\n"); | 1879 | " -g?\n"); |
1813 | exit(-1); | 1880 | exit(-1); |
1814 | } | 1881 | } |
1882 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | ||
1883 | callchain = 1; | ||
1884 | if (register_callchain_param(&callchain_param) < 0) { | ||
1885 | fprintf(stderr, "Can't register callchain" | ||
1886 | " params\n"); | ||
1887 | exit(-1); | ||
1888 | } | ||
1815 | } | 1889 | } |
1816 | 1890 | ||
1817 | if (load_kernel() < 0) { | 1891 | if (load_kernel() < 0) { |
@@ -1950,6 +2024,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
1950 | else if (!strncmp(tok, "fractal", strlen(arg))) | 2024 | else if (!strncmp(tok, "fractal", strlen(arg))) |
1951 | callchain_param.mode = CHAIN_GRAPH_REL; | 2025 | callchain_param.mode = CHAIN_GRAPH_REL; |
1952 | 2026 | ||
2027 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
2028 | callchain_param.mode = CHAIN_NONE; | ||
2029 | callchain = 0; | ||
2030 | |||
2031 | return 0; | ||
2032 | } | ||
2033 | |||
1953 | else | 2034 | else |
1954 | return -1; | 2035 | return -1; |
1955 | 2036 | ||