aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-report.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r--tools/perf/builtin-report.c127
1 files changed, 110 insertions, 17 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index b20a4b6e31b7..99274cec0adb 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -31,7 +31,7 @@
31static char const *input_name = "perf.data"; 31static char const *input_name = "perf.data";
32static char *vmlinux = NULL; 32static char *vmlinux = NULL;
33 33
34static char default_sort_order[] = "comm,dso"; 34static char default_sort_order[] = "comm,dso,symbol";
35static char *sort_order = default_sort_order; 35static char *sort_order = default_sort_order;
36static char *dso_list_str, *comm_list_str, *sym_list_str, 36static char *dso_list_str, *comm_list_str, *sym_list_str,
37 *col_width_list_str; 37 *col_width_list_str;
@@ -68,7 +68,7 @@ static int callchain;
68 68
69static 69static
70struct callchain_param callchain_param = { 70struct 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
@@ -99,6 +99,7 @@ struct comm_event {
99struct fork_event { 99struct fork_event {
100 struct perf_event_header header; 100 struct perf_event_header header;
101 u32 pid, ppid; 101 u32 pid, ppid;
102 u32 tid, ptid;
102}; 103};
103 104
104struct lost_event { 105struct lost_event {
@@ -111,7 +112,9 @@ struct read_event {
111 struct perf_event_header header; 112 struct perf_event_header header;
112 u32 pid,tid; 113 u32 pid,tid;
113 u64 value; 114 u64 value;
114 u64 format[3]; 115 u64 time_enabled;
116 u64 time_running;
117 u64 id;
115}; 118};
116 119
117typedef union event_union { 120typedef union event_union {
@@ -252,7 +255,7 @@ static int strcommon(const char *pathname)
252{ 255{
253 int n = 0; 256 int n = 0;
254 257
255 while (pathname[n] == cwd[n] && n < cwdlen) 258 while (n < cwdlen && pathname[n] == cwd[n])
256 ++n; 259 ++n;
257 260
258 return n; 261 return n;
@@ -697,7 +700,8 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
697 size_t ret = 0; 700 size_t ret = 0;
698 701
699 if (verbose) 702 if (verbose)
700 ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); 703 ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip,
704 dso__symtab_origin(self->dso));
701 705
702 ret += repsep_fprintf(fp, "[%c] ", self->level); 706 ret += repsep_fprintf(fp, "[%c] ", self->level);
703 if (self->sym) { 707 if (self->sym) {
@@ -887,6 +891,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
887 return ret; 891 return ret;
888} 892}
889 893
894static struct symbol *rem_sq_bracket;
895static struct callchain_list rem_hits;
896
897static 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
890static size_t 909static size_t
891callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 910callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
892 u64 total_samples, int depth, int depth_mask) 911 u64 total_samples, int depth, int depth_mask)
@@ -896,25 +915,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
896 struct callchain_list *chain; 915 struct callchain_list *chain;
897 int new_depth_mask = depth_mask; 916 int new_depth_mask = depth_mask;
898 u64 new_total; 917 u64 new_total;
918 u64 remaining;
899 size_t ret = 0; 919 size_t ret = 0;
900 int i; 920 int i;
901 921
902 if (callchain_param.mode == CHAIN_GRAPH_REL) 922 if (callchain_param.mode == CHAIN_GRAPH_REL)
903 new_total = self->cumul_hit; 923 new_total = self->children_hit;
904 else 924 else
905 new_total = total_samples; 925 new_total = total_samples;
906 926
927 remaining = new_total;
928
907 node = rb_first(&self->rb_root); 929 node = rb_first(&self->rb_root);
908 while (node) { 930 while (node) {
931 u64 cumul;
932
909 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;
910 936
911 /* 937 /*
912 * The depth mask manages the output of pipes that show 938 * The depth mask manages the output of pipes that show
913 * 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
914 * 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
915 */ 943 */
916 next = rb_next(node); 944 next = rb_next(node);
917 if (!next) 945 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
918 new_depth_mask &= ~(1 << (depth - 1)); 946 new_depth_mask &= ~(1 << (depth - 1));
919 947
920 /* 948 /*
@@ -929,7 +957,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
929 ret += ipchain__fprintf_graph(fp, chain, depth, 957 ret += ipchain__fprintf_graph(fp, chain, depth,
930 new_depth_mask, i++, 958 new_depth_mask, i++,
931 new_total, 959 new_total,
932 child->cumul_hit); 960 cumul);
933 } 961 }
934 ret += callchain__fprintf_graph(fp, child, new_total, 962 ret += callchain__fprintf_graph(fp, child, new_total,
935 depth + 1, 963 depth + 1,
@@ -937,6 +965,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
937 node = next; 965 node = next;
938 } 966 }
939 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
940 return ret; 981 return ret;
941} 982}
942 983
@@ -1357,6 +1398,8 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
1357 unsigned int width; 1398 unsigned int width;
1358 char *col_width = col_width_list_str; 1399 char *col_width = col_width_list_str;
1359 1400
1401 init_rem_hits();
1402
1360 fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); 1403 fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
1361 fprintf(fp, "#\n"); 1404 fprintf(fp, "#\n");
1362 1405
@@ -1423,11 +1466,13 @@ print_entries:
1423 if (sort_order == default_sort_order && 1466 if (sort_order == default_sort_order &&
1424 parent_pattern == default_parent_pattern) { 1467 parent_pattern == default_parent_pattern) {
1425 fprintf(fp, "#\n"); 1468 fprintf(fp, "#\n");
1426 fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); 1469 fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
1427 fprintf(fp, "#\n"); 1470 fprintf(fp, "#\n");
1428 } 1471 }
1429 fprintf(fp, "\n"); 1472 fprintf(fp, "\n");
1430 1473
1474 free(rem_sq_bracket);
1475
1431 return ret; 1476 return ret;
1432} 1477}
1433 1478
@@ -1608,15 +1653,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
1608} 1653}
1609 1654
1610static int 1655static int
1611process_fork_event(event_t *event, unsigned long offset, unsigned long head) 1656process_task_event(event_t *event, unsigned long offset, unsigned long head)
1612{ 1657{
1613 struct thread *thread = threads__findnew(event->fork.pid); 1658 struct thread *thread = threads__findnew(event->fork.pid);
1614 struct thread *parent = threads__findnew(event->fork.ppid); 1659 struct thread *parent = threads__findnew(event->fork.ppid);
1615 1660
1616 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", 1661 dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",
1617 (void *)(offset + head), 1662 (void *)(offset + head),
1618 (void *)(long)(event->header.size), 1663 (void *)(long)(event->header.size),
1619 event->fork.pid, event->fork.ppid); 1664 event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT",
1665 event->fork.pid, event->fork.tid,
1666 event->fork.ppid, event->fork.ptid);
1667
1668 /*
1669 * A thread clone will have the same PID for both
1670 * parent and child.
1671 */
1672 if (thread == parent)
1673 return 0;
1674
1675 if (event->header.type == PERF_EVENT_EXIT)
1676 return 0;
1620 1677
1621 if (!thread || !parent || thread__fork(thread, parent)) { 1678 if (!thread || !parent || thread__fork(thread, parent)) {
1622 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); 1679 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
@@ -1677,14 +1734,37 @@ static void trace_event(event_t *event)
1677 dprintf(".\n"); 1734 dprintf(".\n");
1678} 1735}
1679 1736
1737static struct perf_header *header;
1738
1739static struct perf_counter_attr *perf_header__find_attr(u64 id)
1740{
1741 int i;
1742
1743 for (i = 0; i < header->attrs; i++) {
1744 struct perf_header_attr *attr = header->attr[i];
1745 int j;
1746
1747 for (j = 0; j < attr->ids; j++) {
1748 if (attr->id[j] == id)
1749 return &attr->attr;
1750 }
1751 }
1752
1753 return NULL;
1754}
1755
1680static int 1756static int
1681process_read_event(event_t *event, unsigned long offset, unsigned long head) 1757process_read_event(event_t *event, unsigned long offset, unsigned long head)
1682{ 1758{
1683 dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", 1759 struct perf_counter_attr *attr = perf_header__find_attr(event->read.id);
1760
1761 dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n",
1684 (void *)(offset + head), 1762 (void *)(offset + head),
1685 (void *)(long)(event->header.size), 1763 (void *)(long)(event->header.size),
1686 event->read.pid, 1764 event->read.pid,
1687 event->read.tid, 1765 event->read.tid,
1766 attr ? __event_name(attr->type, attr->config)
1767 : "FAIL",
1688 event->read.value); 1768 event->read.value);
1689 1769
1690 return 0; 1770 return 0;
@@ -1706,7 +1786,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
1706 return process_comm_event(event, offset, head); 1786 return process_comm_event(event, offset, head);
1707 1787
1708 case PERF_EVENT_FORK: 1788 case PERF_EVENT_FORK:
1709 return process_fork_event(event, offset, head); 1789 case PERF_EVENT_EXIT:
1790 return process_task_event(event, offset, head);
1710 1791
1711 case PERF_EVENT_LOST: 1792 case PERF_EVENT_LOST:
1712 return process_lost_event(event, offset, head); 1793 return process_lost_event(event, offset, head);
@@ -1729,8 +1810,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
1729 return 0; 1810 return 0;
1730} 1811}
1731 1812
1732static struct perf_header *header;
1733
1734static u64 perf_header__sample_type(void) 1813static u64 perf_header__sample_type(void)
1735{ 1814{
1736 u64 sample_type = 0; 1815 u64 sample_type = 0;
@@ -1798,6 +1877,13 @@ static int __cmd_report(void)
1798 " -g?\n"); 1877 " -g?\n");
1799 exit(-1); 1878 exit(-1);
1800 } 1879 }
1880 } else if (callchain_param.mode != CHAIN_NONE && !callchain) {
1881 callchain = 1;
1882 if (register_callchain_param(&callchain_param) < 0) {
1883 fprintf(stderr, "Can't register callchain"
1884 " params\n");
1885 exit(-1);
1886 }
1801 } 1887 }
1802 1888
1803 if (load_kernel() < 0) { 1889 if (load_kernel() < 0) {
@@ -1936,6 +2022,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
1936 else if (!strncmp(tok, "fractal", strlen(arg))) 2022 else if (!strncmp(tok, "fractal", strlen(arg)))
1937 callchain_param.mode = CHAIN_GRAPH_REL; 2023 callchain_param.mode = CHAIN_GRAPH_REL;
1938 2024
2025 else if (!strncmp(tok, "none", strlen(arg))) {
2026 callchain_param.mode = CHAIN_NONE;
2027 callchain = 0;
2028
2029 return 0;
2030 }
2031
1939 else 2032 else
1940 return -1; 2033 return -1;
1941 2034