diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 146 |
1 files changed, 124 insertions, 22 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b20a4b6e31b7..8b2ec882e6e0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -31,13 +31,14 @@ | |||
31 | static char const *input_name = "perf.data"; | 31 | static char const *input_name = "perf.data"; |
32 | static char *vmlinux = NULL; | 32 | static char *vmlinux = NULL; |
33 | 33 | ||
34 | static char default_sort_order[] = "comm,dso"; | 34 | static char default_sort_order[] = "comm,dso,symbol"; |
35 | 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, | 36 | static char *dso_list_str, *comm_list_str, *sym_list_str, |
37 | *col_width_list_str; | 37 | *col_width_list_str; |
38 | static struct strlist *dso_list, *comm_list, *sym_list; | 38 | static struct strlist *dso_list, *comm_list, *sym_list; |
39 | static char *field_sep; | 39 | static char *field_sep; |
40 | 40 | ||
41 | static int force; | ||
41 | static int input; | 42 | static int input; |
42 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 43 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
43 | 44 | ||
@@ -68,7 +69,7 @@ static int callchain; | |||
68 | 69 | ||
69 | static | 70 | static |
70 | struct callchain_param callchain_param = { | 71 | struct callchain_param callchain_param = { |
71 | .mode = CHAIN_GRAPH_ABS, | 72 | .mode = CHAIN_GRAPH_REL, |
72 | .min_percent = 0.5 | 73 | .min_percent = 0.5 |
73 | }; | 74 | }; |
74 | 75 | ||
@@ -99,6 +100,7 @@ struct comm_event { | |||
99 | struct fork_event { | 100 | struct fork_event { |
100 | struct perf_event_header header; | 101 | struct perf_event_header header; |
101 | u32 pid, ppid; | 102 | u32 pid, ppid; |
103 | u32 tid, ptid; | ||
102 | }; | 104 | }; |
103 | 105 | ||
104 | struct lost_event { | 106 | struct lost_event { |
@@ -111,7 +113,9 @@ struct read_event { | |||
111 | struct perf_event_header header; | 113 | struct perf_event_header header; |
112 | u32 pid,tid; | 114 | u32 pid,tid; |
113 | u64 value; | 115 | u64 value; |
114 | u64 format[3]; | 116 | u64 time_enabled; |
117 | u64 time_running; | ||
118 | u64 id; | ||
115 | }; | 119 | }; |
116 | 120 | ||
117 | typedef union event_union { | 121 | typedef union event_union { |
@@ -252,7 +256,7 @@ static int strcommon(const char *pathname) | |||
252 | { | 256 | { |
253 | int n = 0; | 257 | int n = 0; |
254 | 258 | ||
255 | while (pathname[n] == cwd[n] && n < cwdlen) | 259 | while (n < cwdlen && pathname[n] == cwd[n]) |
256 | ++n; | 260 | ++n; |
257 | 261 | ||
258 | return n; | 262 | return n; |
@@ -697,7 +701,8 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | |||
697 | size_t ret = 0; | 701 | size_t ret = 0; |
698 | 702 | ||
699 | if (verbose) | 703 | if (verbose) |
700 | ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); | 704 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
705 | dso__symtab_origin(self->dso)); | ||
701 | 706 | ||
702 | ret += repsep_fprintf(fp, "[%c] ", self->level); | 707 | ret += repsep_fprintf(fp, "[%c] ", self->level); |
703 | if (self->sym) { | 708 | if (self->sym) { |
@@ -887,6 +892,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | |||
887 | return ret; | 892 | return ret; |
888 | } | 893 | } |
889 | 894 | ||
895 | static struct symbol *rem_sq_bracket; | ||
896 | static struct callchain_list rem_hits; | ||
897 | |||
898 | static void init_rem_hits(void) | ||
899 | { | ||
900 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
901 | if (!rem_sq_bracket) { | ||
902 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
903 | return; | ||
904 | } | ||
905 | |||
906 | strcpy(rem_sq_bracket->name, "[...]"); | ||
907 | rem_hits.sym = rem_sq_bracket; | ||
908 | } | ||
909 | |||
890 | static size_t | 910 | static size_t |
891 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 911 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
892 | u64 total_samples, int depth, int depth_mask) | 912 | u64 total_samples, int depth, int depth_mask) |
@@ -896,25 +916,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
896 | struct callchain_list *chain; | 916 | struct callchain_list *chain; |
897 | int new_depth_mask = depth_mask; | 917 | int new_depth_mask = depth_mask; |
898 | u64 new_total; | 918 | u64 new_total; |
919 | u64 remaining; | ||
899 | size_t ret = 0; | 920 | size_t ret = 0; |
900 | int i; | 921 | int i; |
901 | 922 | ||
902 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 923 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
903 | new_total = self->cumul_hit; | 924 | new_total = self->children_hit; |
904 | else | 925 | else |
905 | new_total = total_samples; | 926 | new_total = total_samples; |
906 | 927 | ||
928 | remaining = new_total; | ||
929 | |||
907 | node = rb_first(&self->rb_root); | 930 | node = rb_first(&self->rb_root); |
908 | while (node) { | 931 | while (node) { |
932 | u64 cumul; | ||
933 | |||
909 | child = rb_entry(node, struct callchain_node, rb_node); | 934 | child = rb_entry(node, struct callchain_node, rb_node); |
935 | cumul = cumul_hits(child); | ||
936 | remaining -= cumul; | ||
910 | 937 | ||
911 | /* | 938 | /* |
912 | * The depth mask manages the output of pipes that show | 939 | * The depth mask manages the output of pipes that show |
913 | * the depth. We don't want to keep the pipes of the current | 940 | * the depth. We don't want to keep the pipes of the current |
914 | * level for the last child of this depth | 941 | * level for the last child of this depth. |
942 | * Except if we have remaining filtered hits. They will | ||
943 | * supersede the last child | ||
915 | */ | 944 | */ |
916 | next = rb_next(node); | 945 | next = rb_next(node); |
917 | if (!next) | 946 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) |
918 | new_depth_mask &= ~(1 << (depth - 1)); | 947 | new_depth_mask &= ~(1 << (depth - 1)); |
919 | 948 | ||
920 | /* | 949 | /* |
@@ -929,7 +958,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
929 | ret += ipchain__fprintf_graph(fp, chain, depth, | 958 | ret += ipchain__fprintf_graph(fp, chain, depth, |
930 | new_depth_mask, i++, | 959 | new_depth_mask, i++, |
931 | new_total, | 960 | new_total, |
932 | child->cumul_hit); | 961 | cumul); |
933 | } | 962 | } |
934 | ret += callchain__fprintf_graph(fp, child, new_total, | 963 | ret += callchain__fprintf_graph(fp, child, new_total, |
935 | depth + 1, | 964 | depth + 1, |
@@ -937,6 +966,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
937 | node = next; | 966 | node = next; |
938 | } | 967 | } |
939 | 968 | ||
969 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
970 | remaining && remaining != new_total) { | ||
971 | |||
972 | if (!rem_sq_bracket) | ||
973 | return ret; | ||
974 | |||
975 | new_depth_mask &= ~(1 << (depth - 1)); | ||
976 | |||
977 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
978 | new_depth_mask, 0, new_total, | ||
979 | remaining); | ||
980 | } | ||
981 | |||
940 | return ret; | 982 | return ret; |
941 | } | 983 | } |
942 | 984 | ||
@@ -1357,6 +1399,8 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1357 | unsigned int width; | 1399 | unsigned int width; |
1358 | char *col_width = col_width_list_str; | 1400 | char *col_width = col_width_list_str; |
1359 | 1401 | ||
1402 | init_rem_hits(); | ||
1403 | |||
1360 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); | 1404 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
1361 | fprintf(fp, "#\n"); | 1405 | fprintf(fp, "#\n"); |
1362 | 1406 | ||
@@ -1423,11 +1467,13 @@ print_entries: | |||
1423 | if (sort_order == default_sort_order && | 1467 | if (sort_order == default_sort_order && |
1424 | parent_pattern == default_parent_pattern) { | 1468 | parent_pattern == default_parent_pattern) { |
1425 | fprintf(fp, "#\n"); | 1469 | fprintf(fp, "#\n"); |
1426 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); | 1470 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); |
1427 | fprintf(fp, "#\n"); | 1471 | fprintf(fp, "#\n"); |
1428 | } | 1472 | } |
1429 | fprintf(fp, "\n"); | 1473 | fprintf(fp, "\n"); |
1430 | 1474 | ||
1475 | free(rem_sq_bracket); | ||
1476 | |||
1431 | return ret; | 1477 | return ret; |
1432 | } | 1478 | } |
1433 | 1479 | ||
@@ -1481,11 +1527,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1481 | more_data += sizeof(u64); | 1527 | more_data += sizeof(u64); |
1482 | } | 1528 | } |
1483 | 1529 | ||
1484 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", | 1530 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
1485 | (void *)(offset + head), | 1531 | (void *)(offset + head), |
1486 | (void *)(long)(event->header.size), | 1532 | (void *)(long)(event->header.size), |
1487 | event->header.misc, | 1533 | event->header.misc, |
1488 | event->ip.pid, | 1534 | event->ip.pid, event->ip.tid, |
1489 | (void *)(long)ip, | 1535 | (void *)(long)ip, |
1490 | (long long)period); | 1536 | (long long)period); |
1491 | 1537 | ||
@@ -1545,10 +1591,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1545 | if (show & show_mask) { | 1591 | if (show & show_mask) { |
1546 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1592 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
1547 | 1593 | ||
1548 | if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) | 1594 | if (dso_list && (!dso || !dso->name || |
1595 | !strlist__has_entry(dso_list, dso->name))) | ||
1549 | return 0; | 1596 | return 0; |
1550 | 1597 | ||
1551 | if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) | 1598 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) |
1552 | return 0; | 1599 | return 0; |
1553 | 1600 | ||
1554 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1601 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
@@ -1567,10 +1614,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
1567 | struct thread *thread = threads__findnew(event->mmap.pid); | 1614 | struct thread *thread = threads__findnew(event->mmap.pid); |
1568 | struct map *map = map__new(&event->mmap); | 1615 | struct map *map = map__new(&event->mmap); |
1569 | 1616 | ||
1570 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", | 1617 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
1571 | (void *)(offset + head), | 1618 | (void *)(offset + head), |
1572 | (void *)(long)(event->header.size), | 1619 | (void *)(long)(event->header.size), |
1573 | event->mmap.pid, | 1620 | event->mmap.pid, |
1621 | event->mmap.tid, | ||
1574 | (void *)(long)event->mmap.start, | 1622 | (void *)(long)event->mmap.start, |
1575 | (void *)(long)event->mmap.len, | 1623 | (void *)(long)event->mmap.len, |
1576 | (void *)(long)event->mmap.pgoff, | 1624 | (void *)(long)event->mmap.pgoff, |
@@ -1608,15 +1656,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |||
1608 | } | 1656 | } |
1609 | 1657 | ||
1610 | static int | 1658 | static int |
1611 | process_fork_event(event_t *event, unsigned long offset, unsigned long head) | 1659 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
1612 | { | 1660 | { |
1613 | struct thread *thread = threads__findnew(event->fork.pid); | 1661 | struct thread *thread = threads__findnew(event->fork.pid); |
1614 | struct thread *parent = threads__findnew(event->fork.ppid); | 1662 | struct thread *parent = threads__findnew(event->fork.ppid); |
1615 | 1663 | ||
1616 | dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", | 1664 | dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", |
1617 | (void *)(offset + head), | 1665 | (void *)(offset + head), |
1618 | (void *)(long)(event->header.size), | 1666 | (void *)(long)(event->header.size), |
1619 | event->fork.pid, event->fork.ppid); | 1667 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
1668 | event->fork.pid, event->fork.tid, | ||
1669 | event->fork.ppid, event->fork.ptid); | ||
1670 | |||
1671 | /* | ||
1672 | * A thread clone will have the same PID for both | ||
1673 | * parent and child. | ||
1674 | */ | ||
1675 | if (thread == parent) | ||
1676 | return 0; | ||
1677 | |||
1678 | if (event->header.type == PERF_EVENT_EXIT) | ||
1679 | return 0; | ||
1620 | 1680 | ||
1621 | if (!thread || !parent || thread__fork(thread, parent)) { | 1681 | if (!thread || !parent || thread__fork(thread, parent)) { |
1622 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 1682 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
@@ -1677,14 +1737,37 @@ static void trace_event(event_t *event) | |||
1677 | dprintf(".\n"); | 1737 | dprintf(".\n"); |
1678 | } | 1738 | } |
1679 | 1739 | ||
1740 | static struct perf_header *header; | ||
1741 | |||
1742 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
1743 | { | ||
1744 | int i; | ||
1745 | |||
1746 | for (i = 0; i < header->attrs; i++) { | ||
1747 | struct perf_header_attr *attr = header->attr[i]; | ||
1748 | int j; | ||
1749 | |||
1750 | for (j = 0; j < attr->ids; j++) { | ||
1751 | if (attr->id[j] == id) | ||
1752 | return &attr->attr; | ||
1753 | } | ||
1754 | } | ||
1755 | |||
1756 | return NULL; | ||
1757 | } | ||
1758 | |||
1680 | static int | 1759 | static int |
1681 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | 1760 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
1682 | { | 1761 | { |
1683 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | 1762 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
1763 | |||
1764 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
1684 | (void *)(offset + head), | 1765 | (void *)(offset + head), |
1685 | (void *)(long)(event->header.size), | 1766 | (void *)(long)(event->header.size), |
1686 | event->read.pid, | 1767 | event->read.pid, |
1687 | event->read.tid, | 1768 | event->read.tid, |
1769 | attr ? __event_name(attr->type, attr->config) | ||
1770 | : "FAIL", | ||
1688 | event->read.value); | 1771 | event->read.value); |
1689 | 1772 | ||
1690 | return 0; | 1773 | return 0; |
@@ -1706,7 +1789,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1706 | return process_comm_event(event, offset, head); | 1789 | return process_comm_event(event, offset, head); |
1707 | 1790 | ||
1708 | case PERF_EVENT_FORK: | 1791 | case PERF_EVENT_FORK: |
1709 | return process_fork_event(event, offset, head); | 1792 | case PERF_EVENT_EXIT: |
1793 | return process_task_event(event, offset, head); | ||
1710 | 1794 | ||
1711 | case PERF_EVENT_LOST: | 1795 | case PERF_EVENT_LOST: |
1712 | return process_lost_event(event, offset, head); | 1796 | return process_lost_event(event, offset, head); |
@@ -1729,8 +1813,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1729 | return 0; | 1813 | return 0; |
1730 | } | 1814 | } |
1731 | 1815 | ||
1732 | static struct perf_header *header; | ||
1733 | |||
1734 | static u64 perf_header__sample_type(void) | 1816 | static u64 perf_header__sample_type(void) |
1735 | { | 1817 | { |
1736 | u64 sample_type = 0; | 1818 | u64 sample_type = 0; |
@@ -1775,6 +1857,11 @@ static int __cmd_report(void) | |||
1775 | exit(-1); | 1857 | exit(-1); |
1776 | } | 1858 | } |
1777 | 1859 | ||
1860 | if (!force && (stat.st_uid != geteuid())) { | ||
1861 | fprintf(stderr, "file: %s not owned by current user\n", input_name); | ||
1862 | exit(-1); | ||
1863 | } | ||
1864 | |||
1778 | if (!stat.st_size) { | 1865 | if (!stat.st_size) { |
1779 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1866 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
1780 | exit(0); | 1867 | exit(0); |
@@ -1798,6 +1885,13 @@ static int __cmd_report(void) | |||
1798 | " -g?\n"); | 1885 | " -g?\n"); |
1799 | exit(-1); | 1886 | exit(-1); |
1800 | } | 1887 | } |
1888 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | ||
1889 | callchain = 1; | ||
1890 | if (register_callchain_param(&callchain_param) < 0) { | ||
1891 | fprintf(stderr, "Can't register callchain" | ||
1892 | " params\n"); | ||
1893 | exit(-1); | ||
1894 | } | ||
1801 | } | 1895 | } |
1802 | 1896 | ||
1803 | if (load_kernel() < 0) { | 1897 | if (load_kernel() < 0) { |
@@ -1936,6 +2030,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
1936 | else if (!strncmp(tok, "fractal", strlen(arg))) | 2030 | else if (!strncmp(tok, "fractal", strlen(arg))) |
1937 | callchain_param.mode = CHAIN_GRAPH_REL; | 2031 | callchain_param.mode = CHAIN_GRAPH_REL; |
1938 | 2032 | ||
2033 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
2034 | callchain_param.mode = CHAIN_NONE; | ||
2035 | callchain = 0; | ||
2036 | |||
2037 | return 0; | ||
2038 | } | ||
2039 | |||
1939 | else | 2040 | else |
1940 | return -1; | 2041 | return -1; |
1941 | 2042 | ||
@@ -1969,6 +2070,7 @@ static const struct option options[] = { | |||
1969 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 2070 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1970 | "dump raw trace in ASCII"), | 2071 | "dump raw trace in ASCII"), |
1971 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 2072 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
2073 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
1972 | OPT_BOOLEAN('m', "modules", &modules, | 2074 | OPT_BOOLEAN('m', "modules", &modules, |
1973 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 2075 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
1974 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | 2076 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, |