Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Arnaldo Carvalho de Melo and committed by
Ingo Molnar
52d422de 71a851b4

+164 -32
+12
tools/perf/Documentation/perf-report.txt
··· 33 33 Only consider these symbols. CSV that understands 34 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 + 36 48 SEE ALSO 37 49 -------- 38 50 linkperf:perf-stat[1]
+142 -32
tools/perf/builtin-report.c
··· 33 33 34 34 static char default_sort_order[] = "comm,dso"; 35 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 38 static struct strlist *dso_list, *comm_list, *sym_list; 39 + static char *field_sep; 38 40 39 41 static int input; 40 42 static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; ··· 130 128 struct lost_event lost; 131 129 struct read_event read; 132 130 } event_t; 131 + 132 + static int repsep_fprintf(FILE *fp, const char *fmt, ...) 133 + { 134 + int n; 135 + va_list ap; 136 + 137 + va_start(ap, fmt); 138 + if (!field_sep) 139 + n = vfprintf(fp, fmt, ap); 140 + else { 141 + char *bf = NULL; 142 + n = vasprintf(&bf, fmt, ap); 143 + if (n > 0) { 144 + char *sep = bf; 145 + while (1) { 146 + sep = strchr(sep, *field_sep); 147 + if (sep == NULL) 148 + break; 149 + *sep = '.'; 150 + } 151 + } 152 + fputs(bf, fp); 153 + free(bf); 154 + } 155 + va_end(ap); 156 + return n; 157 + } 133 158 134 159 static LIST_HEAD(dsos); 135 160 static struct dso *kernel_dso; ··· 389 360 return self; 390 361 } 391 362 363 + static unsigned int dsos__col_width, 364 + comms__col_width, 365 + threads__col_width; 366 + 392 367 static int thread__set_comm(struct thread *self, const char *comm) 393 368 { 394 369 if (self->comm) 395 370 free(self->comm); 396 371 self->comm = strdup(comm); 397 - return self->comm ? 0 : -ENOMEM; 372 + if (!self->comm) 373 + return -ENOMEM; 374 + 375 + if (!col_width_list_str && !field_sep && 376 + (!comm_list || strlist__has_entry(comm_list, comm))) { 377 + unsigned int slen = strlen(comm); 378 + if (slen > comms__col_width) { 379 + comms__col_width = slen; 380 + threads__col_width = slen + 6; 381 + } 382 + } 383 + 384 + return 0; 398 385 } 399 386 400 387 static size_t thread__fprintf(struct thread *self, FILE *fp) ··· 581 536 582 537 int64_t (*cmp)(struct hist_entry *, struct hist_entry *); 583 538 int64_t (*collapse)(struct hist_entry *, struct hist_entry *); 584 - size_t (*print)(FILE *fp, struct hist_entry *); 539 + size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); 540 + unsigned int *width; 585 541 }; 586 542 587 543 static int64_t cmp_null(void *l, void *r) ··· 604 558 } 605 559 606 560 static size_t 607 - sort__thread_print(FILE *fp, struct hist_entry *self) 561 + sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) 608 562 { 609 - return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); 563 + return repsep_fprintf(fp, "%*s:%5d", width - 6, 564 + self->thread->comm ?: "", self->thread->pid); 610 565 } 611 566 612 567 static struct sort_entry sort_thread = { 613 - .header = " Command: Pid", 568 + .header = "Command: Pid", 614 569 .cmp = sort__thread_cmp, 615 570 .print = sort__thread_print, 571 + .width = &threads__col_width, 616 572 }; 617 573 618 574 /* --sort comm */ ··· 638 590 } 639 591 640 592 static size_t 641 - sort__comm_print(FILE *fp, struct hist_entry *self) 593 + sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) 642 594 { 643 - return fprintf(fp, "%16s", self->thread->comm); 595 + return repsep_fprintf(fp, "%*s", width, self->thread->comm); 644 596 } 645 597 646 598 static struct sort_entry sort_comm = { 647 - .header = " Command", 599 + .header = "Command", 648 600 .cmp = sort__comm_cmp, 649 601 .collapse = sort__comm_collapse, 650 602 .print = sort__comm_print, 603 + .width = &comms__col_width, 651 604 }; 652 605 653 606 /* --sort dso */ ··· 666 617 } 667 618 668 619 static size_t 669 - sort__dso_print(FILE *fp, struct hist_entry *self) 620 + sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) 670 621 { 671 622 if (self->dso) 672 - return fprintf(fp, "%-25s", self->dso->name); 623 + return repsep_fprintf(fp, "%-*s", width, self->dso->name); 673 624 674 - return fprintf(fp, "%016llx ", (u64)self->ip); 625 + return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); 675 626 } 676 627 677 628 static struct sort_entry sort_dso = { 678 - .header = "Shared Object ", 629 + .header = "Shared Object", 679 630 .cmp = sort__dso_cmp, 680 631 .print = sort__dso_print, 632 + .width = &dsos__col_width, 681 633 }; 682 634 683 635 /* --sort symbol */ ··· 698 648 } 699 649 700 650 static size_t 701 - sort__sym_print(FILE *fp, struct hist_entry *self) 651 + sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) 702 652 { 703 653 size_t ret = 0; 704 654 705 655 if (verbose) 706 - ret += fprintf(fp, "%#018llx ", (u64)self->ip); 656 + ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); 707 657 708 658 if (self->sym) { 709 - ret += fprintf(fp, "[%c] %s", 659 + ret += repsep_fprintf(fp, "[%c] %s", 710 660 self->dso == kernel_dso ? 'k' : 711 661 self->dso == hypervisor_dso ? 'h' : '.', self->sym->name); 712 662 713 663 if (self->sym->module) 714 - ret += fprintf(fp, "\t[%s]", self->sym->module->name); 664 + ret += repsep_fprintf(fp, "\t[%s]", 665 + self->sym->module->name); 715 666 } else { 716 - ret += fprintf(fp, "%#016llx", (u64)self->ip); 667 + ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); 717 668 } 718 669 719 670 return ret; ··· 741 690 } 742 691 743 692 static size_t 744 - sort__parent_print(FILE *fp, struct hist_entry *self) 693 + sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) 745 694 { 746 - size_t ret = 0; 747 - 748 - ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); 749 - 750 - return ret; 695 + return repsep_fprintf(fp, "%-*s", width, 696 + self->parent ? self->parent->name : "[other]"); 751 697 } 752 698 699 + static unsigned int parent_symbol__col_width; 700 + 753 701 static struct sort_entry sort_parent = { 754 - .header = "Parent symbol ", 702 + .header = "Parent symbol", 755 703 .cmp = sort__parent_cmp, 756 704 .print = sort__parent_print, 705 + .width = &parent_symbol__col_width, 757 706 }; 758 707 759 708 static int sort__need_collapse = 0; ··· 1018 967 return 0; 1019 968 1020 969 if (total_samples) 1021 - ret = percent_color_fprintf(fp, " %6.2f%%", 1022 - (self->count * 100.0) / total_samples); 970 + ret = percent_color_fprintf(fp, 971 + field_sep ? "%.2f" : " %6.2f%%", 972 + (self->count * 100.0) / total_samples); 1023 973 else 1024 - ret = fprintf(fp, "%12Ld ", self->count); 974 + ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); 1025 975 1026 976 list_for_each_entry(se, &hist_entry__sort_list, list) { 1027 977 if (exclude_other && (se == &sort_parent)) 1028 978 continue; 1029 979 1030 - fprintf(fp, " "); 1031 - ret += se->print(fp, self); 980 + fprintf(fp, "%s", field_sep ?: " "); 981 + ret += se->print(fp, self, se->width ? *se->width : 0); 1032 982 } 1033 983 1034 984 ret += fprintf(fp, "\n"); ··· 1043 991 /* 1044 992 * 1045 993 */ 994 + 995 + static void dso__calc_col_width(struct dso *self) 996 + { 997 + if (!col_width_list_str && !field_sep && 998 + (!dso_list || strlist__has_entry(dso_list, self->name))) { 999 + unsigned int slen = strlen(self->name); 1000 + if (slen > dsos__col_width) 1001 + dsos__col_width = slen; 1002 + } 1003 + 1004 + self->slen_calculated = 1; 1005 + } 1046 1006 1047 1007 static struct symbol * 1048 1008 resolve_symbol(struct thread *thread, struct map **mapp, ··· 1075 1011 1076 1012 map = thread__find_map(thread, ip); 1077 1013 if (map != NULL) { 1014 + /* 1015 + * We have to do this here as we may have a dso 1016 + * with no symbol hit that has a name longer than 1017 + * the ones with symbols sampled. 1018 + */ 1019 + if (!map->dso->slen_calculated) 1020 + dso__calc_col_width(map->dso); 1021 + 1078 1022 if (mapp) 1079 1023 *mapp = map; 1080 1024 got_map: ··· 1354 1282 struct sort_entry *se; 1355 1283 struct rb_node *nd; 1356 1284 size_t ret = 0; 1285 + unsigned int width; 1286 + char *col_width = col_width_list_str; 1357 1287 1358 1288 fprintf(fp, "\n"); 1359 1289 fprintf(fp, "#\n"); ··· 1366 1292 list_for_each_entry(se, &hist_entry__sort_list, list) { 1367 1293 if (exclude_other && (se == &sort_parent)) 1368 1294 continue; 1369 - fprintf(fp, " %s", se->header); 1295 + if (field_sep) { 1296 + fprintf(fp, "%c%s", *field_sep, se->header); 1297 + continue; 1298 + } 1299 + width = strlen(se->header); 1300 + if (se->width) { 1301 + if (col_width_list_str) { 1302 + if (col_width) { 1303 + *se->width = atoi(col_width); 1304 + col_width = strchr(col_width, ','); 1305 + if (col_width) 1306 + ++col_width; 1307 + } 1308 + } 1309 + width = *se->width = max(*se->width, width); 1310 + } 1311 + fprintf(fp, " %*s", width, se->header); 1370 1312 } 1371 1313 fprintf(fp, "\n"); 1314 + 1315 + if (field_sep) 1316 + goto print_entries; 1372 1317 1373 1318 fprintf(fp, "# ........"); 1374 1319 list_for_each_entry(se, &hist_entry__sort_list, list) { ··· 1397 1304 continue; 1398 1305 1399 1306 fprintf(fp, " "); 1400 - for (i = 0; i < strlen(se->header); i++) 1307 + if (se->width) 1308 + width = *se->width; 1309 + else 1310 + width = strlen(se->header); 1311 + for (i = 0; i < width; i++) 1401 1312 fprintf(fp, "."); 1402 1313 } 1403 1314 fprintf(fp, "\n"); 1404 1315 1405 1316 fprintf(fp, "#\n"); 1406 1317 1318 + print_entries: 1407 1319 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { 1408 1320 pos = rb_entry(nd, struct hist_entry, rb_node); 1409 1321 ret += hist_entry__fprintf(fp, pos, total_samples); ··· 1998 1900 "only consider symbols in these comms"), 1999 1901 OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", 2000 1902 "only consider these symbols"), 1903 + OPT_STRING('w', "column-widths", &col_width_list_str, 1904 + "width[,width...]", 1905 + "don't try to adjust column width, use these fixed values"), 1906 + OPT_STRING('t', "field-separator", &field_sep, "separator", 1907 + "separator for columns, no spaces will be added between " 1908 + "columns '.' is reserved."), 2001 1909 OPT_END() 2002 1910 }; 2003 1911 ··· 2059 1955 setup_list(&dso_list, dso_list_str, "dso"); 2060 1956 setup_list(&comm_list, comm_list_str, "comm"); 2061 1957 setup_list(&sym_list, sym_list_str, "symbol"); 1958 + 1959 + if (field_sep && *field_sep == '.') { 1960 + fputs("'.' is the only non valid --field-separator argument\n", 1961 + stderr); 1962 + exit(129); 1963 + } 2062 1964 2063 1965 setup_pager(); 2064 1966
+8
tools/perf/util/include/linux/kernel.h
··· 18 18 (type *)((char *)__mptr - offsetof(type, member)); }) 19 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 29 #endif
+1
tools/perf/util/symbol.c
··· 65 65 self->syms = RB_ROOT; 66 66 self->sym_priv_size = sym_priv_size; 67 67 self->find_symbol = dso__find_symbol; 68 + self->slen_calculated = 0; 68 69 } 69 70 70 71 return self;
+1
tools/perf/util/symbol.h
··· 25 25 struct symbol *(*find_symbol)(struct dso *, u64 ip); 26 26 unsigned int sym_priv_size; 27 27 unsigned char adjust_symbols; 28 + unsigned char slen_calculated; 28 29 char name[0]; 29 30 }; 30 31