aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2009-07-10 21:47:28 -0400
committerIngo Molnar <mingo@elte.hu>2009-07-11 04:24:11 -0400
commit52d422de22b26d96bbdfbc605eb31c2994df6d0b (patch)
treed335960701bc5b3cbefb842672d0d162d8684633
parent71a851b4d2a815adcfac09c1adda7ef6811fde66 (diff)
perf report: Adjust column width to the values sampled
Auto-adjust column width of perf report output to the longest occuring string length. Example: [acme@doppio pahole]$ perf report --sort comm,dso,symbol | head -13 12.79% pahole /usr/lib64/libdw-0.141.so [.] __libdw_find_attr 8.90% pahole /lib64/libc-2.10.1.so [.] _int_malloc 8.68% pahole /usr/lib64/libdw-0.141.so [.] __libdw_form_val_len 8.15% pahole /lib64/libc-2.10.1.so [.] __GI_strcmp 6.80% pahole /lib64/libc-2.10.1.so [.] __tsearch 5.54% pahole ./build/libdwarves.so.1.0.0 [.] tag__recode_dwarf_type [acme@doppio pahole]$ [acme@doppio pahole]$ perf report --sort comm,dso,symbol -d /lib64/libc-2.10.1.so | head -10 21.92% pahole /lib64/libc-2.10.1.so [.] _int_malloc 20.08% pahole /lib64/libc-2.10.1.so [.] __GI_strcmp 16.75% pahole /lib64/libc-2.10.1.so [.] __tsearch [acme@doppio pahole]$ Also add these extra options to control the new behaviour: -w, --field-width Force each column width to the provided list, for large terminal readability. -t, --field-separator: Use a special separator character and don't pad with spaces, replacing all occurances of this separator in symbol names (and other output) with a '.' character, that thus it's the only non valid separator. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <20090711014728.GH3452@ghostprotocols.net> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/Documentation/perf-report.txt12
-rw-r--r--tools/perf/builtin-report.c174
-rw-r--r--tools/perf/util/include/linux/kernel.h8
-rw-r--r--tools/perf/util/symbol.c1
-rw-r--r--tools/perf/util/symbol.h1
5 files changed, 164 insertions, 32 deletions
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 8aa3f8c88707..05774dfbd14f 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -33,6 +33,18 @@ OPTIONS
33 Only consider these symbols. CSV that understands 33 Only consider these symbols. CSV that understands
34 file://filename entries. 34 file://filename entries.
35 35
36-w::
37--field-width=::
38 Force each column width to the provided list, for large terminal
39 readability.
40
41-t::
42--field-separator=::
43
44 Use a special separator character and don't pad with spaces, replacing
45 all occurances of this separator in symbol names (and other output)
46 with a '.' character, that thus it's the only non valid separator.
47
36SEE ALSO 48SEE ALSO
37-------- 49--------
38linkperf:perf-stat[1] 50linkperf:perf-stat[1]
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 4e5cc266311e..740da43f313e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -33,8 +33,10 @@ static char *vmlinux = NULL;
33 33
34static char default_sort_order[] = "comm,dso"; 34static char default_sort_order[] = "comm,dso";
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;
37static struct strlist *dso_list, *comm_list, *sym_list; 38static struct strlist *dso_list, *comm_list, *sym_list;
39static char *field_sep;
38 40
39static int input; 41static int input;
40static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; 42static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@@ -129,6 +131,33 @@ typedef union event_union {
129 struct read_event read; 131 struct read_event read;
130} event_t; 132} event_t;
131 133
134static int repsep_fprintf(FILE *fp, const char *fmt, ...)
135{
136 int n;
137 va_list ap;
138
139 va_start(ap, fmt);
140 if (!field_sep)
141 n = vfprintf(fp, fmt, ap);
142 else {
143 char *bf = NULL;
144 n = vasprintf(&bf, fmt, ap);
145 if (n > 0) {
146 char *sep = bf;
147 while (1) {
148 sep = strchr(sep, *field_sep);
149 if (sep == NULL)
150 break;
151 *sep = '.';
152 }
153 }
154 fputs(bf, fp);
155 free(bf);
156 }
157 va_end(ap);
158 return n;
159}
160
132static LIST_HEAD(dsos); 161static LIST_HEAD(dsos);
133static struct dso *kernel_dso; 162static struct dso *kernel_dso;
134static struct dso *vdso; 163static struct dso *vdso;
@@ -360,12 +389,28 @@ static struct thread *thread__new(pid_t pid)
360 return self; 389 return self;
361} 390}
362 391
392static unsigned int dsos__col_width,
393 comms__col_width,
394 threads__col_width;
395
363static int thread__set_comm(struct thread *self, const char *comm) 396static int thread__set_comm(struct thread *self, const char *comm)
364{ 397{
365 if (self->comm) 398 if (self->comm)
366 free(self->comm); 399 free(self->comm);
367 self->comm = strdup(comm); 400 self->comm = strdup(comm);
368 return self->comm ? 0 : -ENOMEM; 401 if (!self->comm)
402 return -ENOMEM;
403
404 if (!col_width_list_str && !field_sep &&
405 (!comm_list || strlist__has_entry(comm_list, comm))) {
406 unsigned int slen = strlen(comm);
407 if (slen > comms__col_width) {
408 comms__col_width = slen;
409 threads__col_width = slen + 6;
410 }
411 }
412
413 return 0;
369} 414}
370 415
371static size_t thread__fprintf(struct thread *self, FILE *fp) 416static size_t thread__fprintf(struct thread *self, FILE *fp)
@@ -536,7 +581,8 @@ struct sort_entry {
536 581
537 int64_t (*cmp)(struct hist_entry *, struct hist_entry *); 582 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
538 int64_t (*collapse)(struct hist_entry *, struct hist_entry *); 583 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
539 size_t (*print)(FILE *fp, struct hist_entry *); 584 size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width);
585 unsigned int *width;
540}; 586};
541 587
542static int64_t cmp_null(void *l, void *r) 588static int64_t cmp_null(void *l, void *r)
@@ -558,15 +604,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
558} 604}
559 605
560static size_t 606static size_t
561sort__thread_print(FILE *fp, struct hist_entry *self) 607sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)
562{ 608{
563 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); 609 return repsep_fprintf(fp, "%*s:%5d", width - 6,
610 self->thread->comm ?: "", self->thread->pid);
564} 611}
565 612
566static struct sort_entry sort_thread = { 613static struct sort_entry sort_thread = {
567 .header = " Command: Pid", 614 .header = "Command: Pid",
568 .cmp = sort__thread_cmp, 615 .cmp = sort__thread_cmp,
569 .print = sort__thread_print, 616 .print = sort__thread_print,
617 .width = &threads__col_width,
570}; 618};
571 619
572/* --sort comm */ 620/* --sort comm */
@@ -590,16 +638,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
590} 638}
591 639
592static size_t 640static size_t
593sort__comm_print(FILE *fp, struct hist_entry *self) 641sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)
594{ 642{
595 return fprintf(fp, "%16s", self->thread->comm); 643 return repsep_fprintf(fp, "%*s", width, self->thread->comm);
596} 644}
597 645
598static struct sort_entry sort_comm = { 646static struct sort_entry sort_comm = {
599 .header = " Command", 647 .header = "Command",
600 .cmp = sort__comm_cmp, 648 .cmp = sort__comm_cmp,
601 .collapse = sort__comm_collapse, 649 .collapse = sort__comm_collapse,
602 .print = sort__comm_print, 650 .print = sort__comm_print,
651 .width = &comms__col_width,
603}; 652};
604 653
605/* --sort dso */ 654/* --sort dso */
@@ -617,18 +666,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
617} 666}
618 667
619static size_t 668static size_t
620sort__dso_print(FILE *fp, struct hist_entry *self) 669sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)
621{ 670{
622 if (self->dso) 671 if (self->dso)
623 return fprintf(fp, "%-25s", self->dso->name); 672 return repsep_fprintf(fp, "%-*s", width, self->dso->name);
624 673
625 return fprintf(fp, "%016llx ", (u64)self->ip); 674 return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);
626} 675}
627 676
628static struct sort_entry sort_dso = { 677static struct sort_entry sort_dso = {
629 .header = "Shared Object ", 678 .header = "Shared Object",
630 .cmp = sort__dso_cmp, 679 .cmp = sort__dso_cmp,
631 .print = sort__dso_print, 680 .print = sort__dso_print,
681 .width = &dsos__col_width,
632}; 682};
633 683
634/* --sort symbol */ 684/* --sort symbol */
@@ -648,22 +698,23 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
648} 698}
649 699
650static size_t 700static size_t
651sort__sym_print(FILE *fp, struct hist_entry *self) 701sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
652{ 702{
653 size_t ret = 0; 703 size_t ret = 0;
654 704
655 if (verbose) 705 if (verbose)
656 ret += fprintf(fp, "%#018llx ", (u64)self->ip); 706 ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip);
657 707
658 if (self->sym) { 708 if (self->sym) {
659 ret += fprintf(fp, "[%c] %s", 709 ret += repsep_fprintf(fp, "[%c] %s",
660 self->dso == kernel_dso ? 'k' : 710 self->dso == kernel_dso ? 'k' :
661 self->dso == hypervisor_dso ? 'h' : '.', self->sym->name); 711 self->dso == hypervisor_dso ? 'h' : '.', self->sym->name);
662 712
663 if (self->sym->module) 713 if (self->sym->module)
664 ret += fprintf(fp, "\t[%s]", self->sym->module->name); 714 ret += repsep_fprintf(fp, "\t[%s]",
715 self->sym->module->name);
665 } else { 716 } else {
666 ret += fprintf(fp, "%#016llx", (u64)self->ip); 717 ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);
667 } 718 }
668 719
669 return ret; 720 return ret;
@@ -690,19 +741,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
690} 741}
691 742
692static size_t 743static size_t
693sort__parent_print(FILE *fp, struct hist_entry *self) 744sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)
694{ 745{
695 size_t ret = 0; 746 return repsep_fprintf(fp, "%-*s", width,
696 747 self->parent ? self->parent->name : "[other]");
697 ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]");
698
699 return ret;
700} 748}
701 749
750static unsigned int parent_symbol__col_width;
751
702static struct sort_entry sort_parent = { 752static struct sort_entry sort_parent = {
703 .header = "Parent symbol ", 753 .header = "Parent symbol",
704 .cmp = sort__parent_cmp, 754 .cmp = sort__parent_cmp,
705 .print = sort__parent_print, 755 .print = sort__parent_print,
756 .width = &parent_symbol__col_width,
706}; 757};
707 758
708static int sort__need_collapse = 0; 759static int sort__need_collapse = 0;
@@ -967,17 +1018,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
967 return 0; 1018 return 0;
968 1019
969 if (total_samples) 1020 if (total_samples)
970 ret = percent_color_fprintf(fp, " %6.2f%%", 1021 ret = percent_color_fprintf(fp,
971 (self->count * 100.0) / total_samples); 1022 field_sep ? "%.2f" : " %6.2f%%",
1023 (self->count * 100.0) / total_samples);
972 else 1024 else
973 ret = fprintf(fp, "%12Ld ", self->count); 1025 ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
974 1026
975 list_for_each_entry(se, &hist_entry__sort_list, list) { 1027 list_for_each_entry(se, &hist_entry__sort_list, list) {
976 if (exclude_other && (se == &sort_parent)) 1028 if (exclude_other && (se == &sort_parent))
977 continue; 1029 continue;
978 1030
979 fprintf(fp, " "); 1031 fprintf(fp, "%s", field_sep ?: " ");
980 ret += se->print(fp, self); 1032 ret += se->print(fp, self, se->width ? *se->width : 0);
981 } 1033 }
982 1034
983 ret += fprintf(fp, "\n"); 1035 ret += fprintf(fp, "\n");
@@ -992,6 +1044,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
992 * 1044 *
993 */ 1045 */
994 1046
1047static void dso__calc_col_width(struct dso *self)
1048{
1049 if (!col_width_list_str && !field_sep &&
1050 (!dso_list || strlist__has_entry(dso_list, self->name))) {
1051 unsigned int slen = strlen(self->name);
1052 if (slen > dsos__col_width)
1053 dsos__col_width = slen;
1054 }
1055
1056 self->slen_calculated = 1;
1057}
1058
995static struct symbol * 1059static struct symbol *
996resolve_symbol(struct thread *thread, struct map **mapp, 1060resolve_symbol(struct thread *thread, struct map **mapp,
997 struct dso **dsop, u64 *ipp) 1061 struct dso **dsop, u64 *ipp)
@@ -1011,6 +1075,14 @@ resolve_symbol(struct thread *thread, struct map **mapp,
1011 1075
1012 map = thread__find_map(thread, ip); 1076 map = thread__find_map(thread, ip);
1013 if (map != NULL) { 1077 if (map != NULL) {
1078 /*
1079 * We have to do this here as we may have a dso
1080 * with no symbol hit that has a name longer than
1081 * the ones with symbols sampled.
1082 */
1083 if (!map->dso->slen_calculated)
1084 dso__calc_col_width(map->dso);
1085
1014 if (mapp) 1086 if (mapp)
1015 *mapp = map; 1087 *mapp = map;
1016got_map: 1088got_map:
@@ -1282,6 +1354,8 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
1282 struct sort_entry *se; 1354 struct sort_entry *se;
1283 struct rb_node *nd; 1355 struct rb_node *nd;
1284 size_t ret = 0; 1356 size_t ret = 0;
1357 unsigned int width;
1358 char *col_width = col_width_list_str;
1285 1359
1286 fprintf(fp, "\n"); 1360 fprintf(fp, "\n");
1287 fprintf(fp, "#\n"); 1361 fprintf(fp, "#\n");
@@ -1292,10 +1366,29 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
1292 list_for_each_entry(se, &hist_entry__sort_list, list) { 1366 list_for_each_entry(se, &hist_entry__sort_list, list) {
1293 if (exclude_other && (se == &sort_parent)) 1367 if (exclude_other && (se == &sort_parent))
1294 continue; 1368 continue;
1295 fprintf(fp, " %s", se->header); 1369 if (field_sep) {
1370 fprintf(fp, "%c%s", *field_sep, se->header);
1371 continue;
1372 }
1373 width = strlen(se->header);
1374 if (se->width) {
1375 if (col_width_list_str) {
1376 if (col_width) {
1377 *se->width = atoi(col_width);
1378 col_width = strchr(col_width, ',');
1379 if (col_width)
1380 ++col_width;
1381 }
1382 }
1383 width = *se->width = max(*se->width, width);
1384 }
1385 fprintf(fp, " %*s", width, se->header);
1296 } 1386 }
1297 fprintf(fp, "\n"); 1387 fprintf(fp, "\n");
1298 1388
1389 if (field_sep)
1390 goto print_entries;
1391
1299 fprintf(fp, "# ........"); 1392 fprintf(fp, "# ........");
1300 list_for_each_entry(se, &hist_entry__sort_list, list) { 1393 list_for_each_entry(se, &hist_entry__sort_list, list) {
1301 unsigned int i; 1394 unsigned int i;
@@ -1304,13 +1397,18 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
1304 continue; 1397 continue;
1305 1398
1306 fprintf(fp, " "); 1399 fprintf(fp, " ");
1307 for (i = 0; i < strlen(se->header); i++) 1400 if (se->width)
1401 width = *se->width;
1402 else
1403 width = strlen(se->header);
1404 for (i = 0; i < width; i++)
1308 fprintf(fp, "."); 1405 fprintf(fp, ".");
1309 } 1406 }
1310 fprintf(fp, "\n"); 1407 fprintf(fp, "\n");
1311 1408
1312 fprintf(fp, "#\n"); 1409 fprintf(fp, "#\n");
1313 1410
1411print_entries:
1314 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { 1412 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
1315 pos = rb_entry(nd, struct hist_entry, rb_node); 1413 pos = rb_entry(nd, struct hist_entry, rb_node);
1316 ret += hist_entry__fprintf(fp, pos, total_samples); 1414 ret += hist_entry__fprintf(fp, pos, total_samples);
@@ -1900,6 +1998,12 @@ static const struct option options[] = {
1900 "only consider symbols in these comms"), 1998 "only consider symbols in these comms"),
1901 OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", 1999 OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
1902 "only consider these symbols"), 2000 "only consider these symbols"),
2001 OPT_STRING('w', "column-widths", &col_width_list_str,
2002 "width[,width...]",
2003 "don't try to adjust column width, use these fixed values"),
2004 OPT_STRING('t', "field-separator", &field_sep, "separator",
2005 "separator for columns, no spaces will be added between "
2006 "columns '.' is reserved."),
1903 OPT_END() 2007 OPT_END()
1904}; 2008};
1905 2009
@@ -1956,6 +2060,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
1956 setup_list(&comm_list, comm_list_str, "comm"); 2060 setup_list(&comm_list, comm_list_str, "comm");
1957 setup_list(&sym_list, sym_list_str, "symbol"); 2061 setup_list(&sym_list, sym_list_str, "symbol");
1958 2062
2063 if (field_sep && *field_sep == '.') {
2064 fputs("'.' is the only non valid --field-separator argument\n",
2065 stderr);
2066 exit(129);
2067 }
2068
1959 setup_pager(); 2069 setup_pager();
1960 2070
1961 return __cmd_report(); 2071 return __cmd_report();
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
index 99c1b3d1edd9..a6b87390cb52 100644
--- a/tools/perf/util/include/linux/kernel.h
+++ b/tools/perf/util/include/linux/kernel.h
@@ -18,4 +18,12 @@
18 (type *)((char *)__mptr - offsetof(type, member)); }) 18 (type *)((char *)__mptr - offsetof(type, member)); })
19#endif 19#endif
20 20
21#ifndef max
22#define max(x, y) ({ \
23 typeof(x) _max1 = (x); \
24 typeof(y) _max2 = (y); \
25 (void) (&_max1 == &_max2); \
26 _max1 > _max2 ? _max1 : _max2; })
27#endif
28
21#endif 29#endif
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 4683b67b5ee4..8efe7e41109a 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -65,6 +65,7 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)
65 self->syms = RB_ROOT; 65 self->syms = RB_ROOT;
66 self->sym_priv_size = sym_priv_size; 66 self->sym_priv_size = sym_priv_size;
67 self->find_symbol = dso__find_symbol; 67 self->find_symbol = dso__find_symbol;
68 self->slen_calculated = 0;
68 } 69 }
69 70
70 return self; 71 return self;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 7918cffb23cd..2f92b21c712d 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -25,6 +25,7 @@ struct dso {
25 struct symbol *(*find_symbol)(struct dso *, u64 ip); 25 struct symbol *(*find_symbol)(struct dso *, u64 ip);
26 unsigned int sym_priv_size; 26 unsigned int sym_priv_size;
27 unsigned char adjust_symbols; 27 unsigned char adjust_symbols;
28 unsigned char slen_calculated;
28 char name[0]; 29 char name[0];
29}; 30};
30 31