summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorNamhyung Kim <namhyung@kernel.org>2014-03-03 20:46:34 -0500
committerJiri Olsa <jolsa@kernel.org>2014-05-21 05:45:35 -0400
commita7d945bc91602f916d2d0c794c179d9a784859e7 (patch)
tree40b0b84ec5ec0a624cbad51b7342a6ae44903def /tools
parent22af969e8cfc6ea46d3e1a774a16d7e19b8cf4db (diff)
perf report: Add -F option to specify output fields
The -F/--fields option is to allow user setup output field in any order. It can receive any sort keys and following (hpp) fields: overhead, overhead_sys, overhead_us, sample and period If guest profiling is enabled, overhead_guest_{sys,us} will be available too. The output fields also affect sort order unless you give -s/--sort option. And any keys specified on -s option, will also be added to the output field list automatically. $ perf report -F sym,sample,overhead ... # Symbol Samples Overhead # .......................... ............ ........ # [.] __cxa_atexit 2 2.50% [.] __libc_csu_init 4 5.00% [.] __new_exitfn 3 3.75% [.] _dl_check_map_versions 1 1.25% [.] _dl_name_match_p 4 5.00% [.] _dl_setup_hash 1 1.25% [.] _dl_sysdep_start 1 1.25% [.] _init 5 6.25% [.] _setjmp 6 7.50% [.] a 8 10.00% [.] b 8 10.00% [.] brk 1 1.25% [.] c 8 10.00% Note that, the example output above is captured after applying next patch which fixes sort/comparing behavior. Requested-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Namhyung Kim <namhyung@kernel.org> Acked-by: Ingo Molnar <mingo@kernel.org> Link: http://lkml.kernel.org/r/1400480762-22852-12-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Documentation/perf-report.txt10
-rw-r--r--tools/perf/builtin-report.c15
-rw-r--r--tools/perf/ui/hist.c59
-rw-r--r--tools/perf/util/hist.h4
-rw-r--r--tools/perf/util/sort.c209
-rw-r--r--tools/perf/util/sort.h2
6 files changed, 282 insertions, 17 deletions
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 9babe915b6c4..a1b5185402d5 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -107,6 +107,16 @@ OPTIONS
107 And default sort keys are changed to comm, dso_from, symbol_from, dso_to 107 And default sort keys are changed to comm, dso_from, symbol_from, dso_to
108 and symbol_to, see '--branch-stack'. 108 and symbol_to, see '--branch-stack'.
109 109
110-F::
111--fields=::
112 Specify output field - multiple keys can be specified in CSV format.
113 Following fields are available:
114 overhead, overhead_sys, overhead_us, sample and period.
115 Also it can contain any sort key(s).
116
117 By default, every sort keys not specified in -F will be appended
118 automatically.
119
110-p:: 120-p::
111--parent=<regex>:: 121--parent=<regex>::
112 A regex filter to identify parent. The parent is a caller of this 122 A regex filter to identify parent. The parent is a caller of this
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index c4dab7acbdbb..bc0eec1ce4be 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -701,6 +701,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
701 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 701 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
702 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." 702 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
703 " Please refer the man page for the complete list."), 703 " Please refer the man page for the complete list."),
704 OPT_STRING('F', "fields", &field_order, "key[,keys...]",
705 "output field(s): overhead, period, sample plus all of sort keys"),
704 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 706 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
705 "Show sample percentage for different cpu modes"), 707 "Show sample percentage for different cpu modes"),
706 OPT_STRING('p', "parent", &parent_pattern, "regex", 708 OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -814,17 +816,14 @@ repeat:
814 } 816 }
815 817
816 if (setup_sorting() < 0) { 818 if (setup_sorting() < 0) {
817 parse_options_usage(report_usage, options, "s", 1); 819 if (sort_order)
820 parse_options_usage(report_usage, options, "s", 1);
821 if (field_order)
822 parse_options_usage(sort_order ? NULL : report_usage,
823 options, "F", 1);
818 goto error; 824 goto error;
819 } 825 }
820 826
821 if (parent_pattern != default_parent_pattern) {
822 if (sort_dimension__add("parent") < 0)
823 goto error;
824 }
825
826 perf_hpp__init();
827
828 /* Force tty output for header output. */ 827 /* Force tty output for header output. */
829 if (report.header || report.header_only) 828 if (report.header || report.header_only)
830 use_browser = 0; 829 use_browser = 0;
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 24116a48298f..b114c6668865 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -355,6 +355,12 @@ void perf_hpp__init(void)
355 INIT_LIST_HEAD(&fmt->sort_list); 355 INIT_LIST_HEAD(&fmt->sort_list);
356 } 356 }
357 357
358 /*
359 * If user specified field order, no need to setup default fields.
360 */
361 if (field_order)
362 return;
363
358 perf_hpp__column_enable(PERF_HPP__OVERHEAD); 364 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
359 365
360 if (symbol_conf.show_cpu_utilization) { 366 if (symbol_conf.show_cpu_utilization) {
@@ -377,8 +383,6 @@ void perf_hpp__init(void)
377 list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; 383 list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
378 if (list_empty(list)) 384 if (list_empty(list))
379 list_add(list, &perf_hpp__sort_list); 385 list_add(list, &perf_hpp__sort_list);
380
381 perf_hpp__setup_output_field();
382} 386}
383 387
384void perf_hpp__column_register(struct perf_hpp_fmt *format) 388void perf_hpp__column_register(struct perf_hpp_fmt *format)
@@ -403,8 +407,55 @@ void perf_hpp__setup_output_field(void)
403 407
404 /* append sort keys to output field */ 408 /* append sort keys to output field */
405 perf_hpp__for_each_sort_list(fmt) { 409 perf_hpp__for_each_sort_list(fmt) {
406 if (list_empty(&fmt->list)) 410 if (!list_empty(&fmt->list))
407 perf_hpp__column_register(fmt); 411 continue;
412
413 /*
414 * sort entry fields are dynamically created,
415 * so they can share a same sort key even though
416 * the list is empty.
417 */
418 if (perf_hpp__is_sort_entry(fmt)) {
419 struct perf_hpp_fmt *pos;
420
421 perf_hpp__for_each_format(pos) {
422 if (perf_hpp__same_sort_entry(pos, fmt))
423 goto next;
424 }
425 }
426
427 perf_hpp__column_register(fmt);
428next:
429 continue;
430 }
431}
432
433void perf_hpp__append_sort_keys(void)
434{
435 struct perf_hpp_fmt *fmt;
436
437 /* append output fields to sort keys */
438 perf_hpp__for_each_format(fmt) {
439 if (!list_empty(&fmt->sort_list))
440 continue;
441
442 /*
443 * sort entry fields are dynamically created,
444 * so they can share a same sort key even though
445 * the list is empty.
446 */
447 if (perf_hpp__is_sort_entry(fmt)) {
448 struct perf_hpp_fmt *pos;
449
450 perf_hpp__for_each_sort_list(pos) {
451 if (perf_hpp__same_sort_entry(pos, fmt))
452 goto next;
453 }
454 }
455
456 perf_hpp__register_sort_field(fmt);
457next:
458 continue;
408 } 459 }
409} 460}
410 461
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 76bb72e4302b..f3713b79742d 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -197,6 +197,10 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format);
197void perf_hpp__column_enable(unsigned col); 197void perf_hpp__column_enable(unsigned col);
198void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); 198void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
199void perf_hpp__setup_output_field(void); 199void perf_hpp__setup_output_field(void);
200void perf_hpp__append_sort_keys(void);
201
202bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
203bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
200 204
201typedef u64 (*hpp_field_fn)(struct hist_entry *he); 205typedef u64 (*hpp_field_fn)(struct hist_entry *he);
202typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); 206typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index d64c1e58f1b1..b748b02fcb78 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -13,6 +13,7 @@ const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso
13const char default_top_sort_order[] = "dso,symbol"; 13const char default_top_sort_order[] = "dso,symbol";
14const char default_diff_sort_order[] = "dso,symbol"; 14const char default_diff_sort_order[] = "dso,symbol";
15const char *sort_order; 15const char *sort_order;
16const char *field_order;
16regex_t ignore_callees_regex; 17regex_t ignore_callees_regex;
17int have_ignore_callees = 0; 18int have_ignore_callees = 0;
18int sort__need_collapse = 0; 19int sort__need_collapse = 0;
@@ -1057,6 +1058,20 @@ struct hpp_sort_entry {
1057 struct sort_entry *se; 1058 struct sort_entry *se;
1058}; 1059};
1059 1060
1061bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1062{
1063 struct hpp_sort_entry *hse_a;
1064 struct hpp_sort_entry *hse_b;
1065
1066 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1067 return false;
1068
1069 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1070 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1071
1072 return hse_a->se == hse_b->se;
1073}
1074
1060static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 1075static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1061 struct perf_evsel *evsel) 1076 struct perf_evsel *evsel)
1062{ 1077{
@@ -1092,14 +1107,15 @@ static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1092 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); 1107 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1093} 1108}
1094 1109
1095static int __sort_dimension__add_hpp(struct sort_dimension *sd) 1110static struct hpp_sort_entry *
1111__sort_dimension__alloc_hpp(struct sort_dimension *sd)
1096{ 1112{
1097 struct hpp_sort_entry *hse; 1113 struct hpp_sort_entry *hse;
1098 1114
1099 hse = malloc(sizeof(*hse)); 1115 hse = malloc(sizeof(*hse));
1100 if (hse == NULL) { 1116 if (hse == NULL) {
1101 pr_err("Memory allocation failed\n"); 1117 pr_err("Memory allocation failed\n");
1102 return -1; 1118 return NULL;
1103 } 1119 }
1104 1120
1105 hse->se = sd->entry; 1121 hse->se = sd->entry;
@@ -1115,16 +1131,42 @@ static int __sort_dimension__add_hpp(struct sort_dimension *sd)
1115 INIT_LIST_HEAD(&hse->hpp.list); 1131 INIT_LIST_HEAD(&hse->hpp.list);
1116 INIT_LIST_HEAD(&hse->hpp.sort_list); 1132 INIT_LIST_HEAD(&hse->hpp.sort_list);
1117 1133
1134 return hse;
1135}
1136
1137bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1138{
1139 return format->header == __sort__hpp_header;
1140}
1141
1142static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1143{
1144 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1145
1146 if (hse == NULL)
1147 return -1;
1148
1118 perf_hpp__register_sort_field(&hse->hpp); 1149 perf_hpp__register_sort_field(&hse->hpp);
1119 return 0; 1150 return 0;
1120} 1151}
1121 1152
1153static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1154{
1155 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1156
1157 if (hse == NULL)
1158 return -1;
1159
1160 perf_hpp__column_register(&hse->hpp);
1161 return 0;
1162}
1163
1122static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) 1164static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
1123{ 1165{
1124 if (sd->taken) 1166 if (sd->taken)
1125 return 0; 1167 return 0;
1126 1168
1127 if (__sort_dimension__add_hpp(sd) < 0) 1169 if (__sort_dimension__add_hpp_sort(sd) < 0)
1128 return -1; 1170 return -1;
1129 1171
1130 if (sd->entry->se_collapse) 1172 if (sd->entry->se_collapse)
@@ -1149,6 +1191,28 @@ static int __hpp_dimension__add(struct hpp_dimension *hd)
1149 return 0; 1191 return 0;
1150} 1192}
1151 1193
1194static int __sort_dimension__add_output(struct sort_dimension *sd)
1195{
1196 if (sd->taken)
1197 return 0;
1198
1199 if (__sort_dimension__add_hpp_output(sd) < 0)
1200 return -1;
1201
1202 sd->taken = 1;
1203 return 0;
1204}
1205
1206static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1207{
1208 if (!hd->taken) {
1209 hd->taken = 1;
1210
1211 perf_hpp__column_register(hd->fmt);
1212 }
1213 return 0;
1214}
1215
1152int sort_dimension__add(const char *tok) 1216int sort_dimension__add(const char *tok)
1153{ 1217{
1154 unsigned int i; 1218 unsigned int i;
@@ -1237,14 +1301,23 @@ static const char *get_default_sort_order(void)
1237 return default_sort_orders[sort__mode]; 1301 return default_sort_orders[sort__mode];
1238} 1302}
1239 1303
1240int setup_sorting(void) 1304static int __setup_sorting(void)
1241{ 1305{
1242 char *tmp, *tok, *str; 1306 char *tmp, *tok, *str;
1243 const char *sort_keys = sort_order; 1307 const char *sort_keys = sort_order;
1244 int ret = 0; 1308 int ret = 0;
1245 1309
1246 if (sort_keys == NULL) 1310 if (sort_keys == NULL) {
1311 if (field_order) {
1312 /*
1313 * If user specified field order but no sort order,
1314 * we'll honor it and not add default sort orders.
1315 */
1316 return 0;
1317 }
1318
1247 sort_keys = get_default_sort_order(); 1319 sort_keys = get_default_sort_order();
1320 }
1248 1321
1249 str = strdup(sort_keys); 1322 str = strdup(sort_keys);
1250 if (str == NULL) { 1323 if (str == NULL) {
@@ -1331,3 +1404,129 @@ void sort__setup_elide(FILE *output)
1331 list_for_each_entry(se, &hist_entry__sort_list, list) 1404 list_for_each_entry(se, &hist_entry__sort_list, list)
1332 se->elide = false; 1405 se->elide = false;
1333} 1406}
1407
1408static int output_field_add(char *tok)
1409{
1410 unsigned int i;
1411
1412 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1413 struct sort_dimension *sd = &common_sort_dimensions[i];
1414
1415 if (strncasecmp(tok, sd->name, strlen(tok)))
1416 continue;
1417
1418 return __sort_dimension__add_output(sd);
1419 }
1420
1421 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1422 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1423
1424 if (strncasecmp(tok, hd->name, strlen(tok)))
1425 continue;
1426
1427 return __hpp_dimension__add_output(hd);
1428 }
1429
1430 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1431 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1432
1433 if (strncasecmp(tok, sd->name, strlen(tok)))
1434 continue;
1435
1436 return __sort_dimension__add_output(sd);
1437 }
1438
1439 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1440 struct sort_dimension *sd = &memory_sort_dimensions[i];
1441
1442 if (strncasecmp(tok, sd->name, strlen(tok)))
1443 continue;
1444
1445 return __sort_dimension__add_output(sd);
1446 }
1447
1448 return -ESRCH;
1449}
1450
1451static void reset_dimensions(void)
1452{
1453 unsigned int i;
1454
1455 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1456 common_sort_dimensions[i].taken = 0;
1457
1458 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1459 hpp_sort_dimensions[i].taken = 0;
1460
1461 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1462 bstack_sort_dimensions[i].taken = 0;
1463
1464 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1465 memory_sort_dimensions[i].taken = 0;
1466}
1467
1468static int __setup_output_field(void)
1469{
1470 char *tmp, *tok, *str;
1471 int ret = 0;
1472
1473 if (field_order == NULL)
1474 return 0;
1475
1476 reset_dimensions();
1477
1478 str = strdup(field_order);
1479 if (str == NULL) {
1480 error("Not enough memory to setup output fields");
1481 return -ENOMEM;
1482 }
1483
1484 for (tok = strtok_r(str, ", ", &tmp);
1485 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1486 ret = output_field_add(tok);
1487 if (ret == -EINVAL) {
1488 error("Invalid --fields key: `%s'", tok);
1489 break;
1490 } else if (ret == -ESRCH) {
1491 error("Unknown --fields key: `%s'", tok);
1492 break;
1493 }
1494 }
1495
1496 free(str);
1497 return ret;
1498}
1499
1500int setup_sorting(void)
1501{
1502 int err;
1503
1504 err = __setup_sorting();
1505 if (err < 0)
1506 return err;
1507
1508 if (parent_pattern != default_parent_pattern) {
1509 err = sort_dimension__add("parent");
1510 if (err < 0)
1511 return err;
1512 }
1513
1514 reset_dimensions();
1515
1516 /*
1517 * perf diff doesn't use default hpp output fields.
1518 */
1519 if (sort__mode != SORT_MODE__DIFF)
1520 perf_hpp__init();
1521
1522 err = __setup_output_field();
1523 if (err < 0)
1524 return err;
1525
1526 /* copy sort keys to output fields */
1527 perf_hpp__setup_output_field();
1528 /* and then copy output fields to sort keys */
1529 perf_hpp__append_sort_keys();
1530
1531 return 0;
1532}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 1a7295255dc9..89e5057ca378 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -25,6 +25,7 @@
25 25
26extern regex_t parent_regex; 26extern regex_t parent_regex;
27extern const char *sort_order; 27extern const char *sort_order;
28extern const char *field_order;
28extern const char default_parent_pattern[]; 29extern const char default_parent_pattern[];
29extern const char *parent_pattern; 30extern const char *parent_pattern;
30extern const char default_sort_order[]; 31extern const char default_sort_order[];
@@ -191,6 +192,7 @@ extern struct sort_entry sort_thread;
191extern struct list_head hist_entry__sort_list; 192extern struct list_head hist_entry__sort_list;
192 193
193int setup_sorting(void); 194int setup_sorting(void);
195int setup_output_field(void);
194extern int sort_dimension__add(const char *); 196extern int sort_dimension__add(const char *);
195void sort__setup_elide(FILE *fp); 197void sort__setup_elide(FILE *fp);
196 198