aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/ui/browsers/hists.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/ui/browsers/hists.c')
-rw-r--r--tools/perf/ui/browsers/hists.c885
1 files changed, 769 insertions, 116 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 08c09ad755d2..4b9816555946 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -32,6 +32,7 @@ struct hist_browser {
32 bool show_headers; 32 bool show_headers;
33 float min_pcnt; 33 float min_pcnt;
34 u64 nr_non_filtered_entries; 34 u64 nr_non_filtered_entries;
35 u64 nr_hierarchy_entries;
35 u64 nr_callchain_rows; 36 u64 nr_callchain_rows;
36}; 37};
37 38
@@ -58,11 +59,11 @@ static int hist_browser__get_folding(struct hist_browser *browser)
58 59
59 for (nd = rb_first(&hists->entries); 60 for (nd = rb_first(&hists->entries);
60 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 61 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
61 nd = rb_next(nd)) { 62 nd = rb_hierarchy_next(nd)) {
62 struct hist_entry *he = 63 struct hist_entry *he =
63 rb_entry(nd, struct hist_entry, rb_node); 64 rb_entry(nd, struct hist_entry, rb_node);
64 65
65 if (he->unfolded) 66 if (he->leaf && he->unfolded)
66 unfolded_rows += he->nr_rows; 67 unfolded_rows += he->nr_rows;
67 } 68 }
68 return unfolded_rows; 69 return unfolded_rows;
@@ -72,7 +73,9 @@ static u32 hist_browser__nr_entries(struct hist_browser *hb)
72{ 73{
73 u32 nr_entries; 74 u32 nr_entries;
74 75
75 if (hist_browser__has_filter(hb)) 76 if (symbol_conf.report_hierarchy)
77 nr_entries = hb->nr_hierarchy_entries;
78 else if (hist_browser__has_filter(hb))
76 nr_entries = hb->nr_non_filtered_entries; 79 nr_entries = hb->nr_non_filtered_entries;
77 else 80 else
78 nr_entries = hb->hists->nr_entries; 81 nr_entries = hb->hists->nr_entries;
@@ -247,6 +250,38 @@ static int callchain__count_rows(struct rb_root *chain)
247 return n; 250 return n;
248} 251}
249 252
253static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
254 bool include_children)
255{
256 int count = 0;
257 struct rb_node *node;
258 struct hist_entry *child;
259
260 if (he->leaf)
261 return callchain__count_rows(&he->sorted_chain);
262
263 if (he->has_no_entry)
264 return 1;
265
266 node = rb_first(&he->hroot_out);
267 while (node) {
268 float percent;
269
270 child = rb_entry(node, struct hist_entry, rb_node);
271 percent = hist_entry__get_percent_limit(child);
272
273 if (!child->filtered && percent >= hb->min_pcnt) {
274 count++;
275
276 if (include_children && child->unfolded)
277 count += hierarchy_count_rows(hb, child, true);
278 }
279
280 node = rb_next(node);
281 }
282 return count;
283}
284
250static bool hist_entry__toggle_fold(struct hist_entry *he) 285static bool hist_entry__toggle_fold(struct hist_entry *he)
251{ 286{
252 if (!he) 287 if (!he)
@@ -326,11 +361,17 @@ static void callchain__init_have_children(struct rb_root *root)
326 361
327static void hist_entry__init_have_children(struct hist_entry *he) 362static void hist_entry__init_have_children(struct hist_entry *he)
328{ 363{
329 if (!he->init_have_children) { 364 if (he->init_have_children)
365 return;
366
367 if (he->leaf) {
330 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); 368 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
331 callchain__init_have_children(&he->sorted_chain); 369 callchain__init_have_children(&he->sorted_chain);
332 he->init_have_children = true; 370 } else {
371 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
333 } 372 }
373
374 he->init_have_children = true;
334} 375}
335 376
336static bool hist_browser__toggle_fold(struct hist_browser *browser) 377static bool hist_browser__toggle_fold(struct hist_browser *browser)
@@ -349,17 +390,49 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
349 has_children = callchain_list__toggle_fold(cl); 390 has_children = callchain_list__toggle_fold(cl);
350 391
351 if (has_children) { 392 if (has_children) {
393 int child_rows = 0;
394
352 hist_entry__init_have_children(he); 395 hist_entry__init_have_children(he);
353 browser->b.nr_entries -= he->nr_rows; 396 browser->b.nr_entries -= he->nr_rows;
354 browser->nr_callchain_rows -= he->nr_rows;
355 397
356 if (he->unfolded) 398 if (he->leaf)
357 he->nr_rows = callchain__count_rows(&he->sorted_chain); 399 browser->nr_callchain_rows -= he->nr_rows;
358 else 400 else
401 browser->nr_hierarchy_entries -= he->nr_rows;
402
403 if (symbol_conf.report_hierarchy)
404 child_rows = hierarchy_count_rows(browser, he, true);
405
406 if (he->unfolded) {
407 if (he->leaf)
408 he->nr_rows = callchain__count_rows(&he->sorted_chain);
409 else
410 he->nr_rows = hierarchy_count_rows(browser, he, false);
411
412 /* account grand children */
413 if (symbol_conf.report_hierarchy)
414 browser->b.nr_entries += child_rows - he->nr_rows;
415
416 if (!he->leaf && he->nr_rows == 0) {
417 he->has_no_entry = true;
418 he->nr_rows = 1;
419 }
420 } else {
421 if (symbol_conf.report_hierarchy)
422 browser->b.nr_entries -= child_rows - he->nr_rows;
423
424 if (he->has_no_entry)
425 he->has_no_entry = false;
426
359 he->nr_rows = 0; 427 he->nr_rows = 0;
428 }
360 429
361 browser->b.nr_entries += he->nr_rows; 430 browser->b.nr_entries += he->nr_rows;
362 browser->nr_callchain_rows += he->nr_rows; 431
432 if (he->leaf)
433 browser->nr_callchain_rows += he->nr_rows;
434 else
435 browser->nr_hierarchy_entries += he->nr_rows;
363 436
364 return true; 437 return true;
365 } 438 }
@@ -422,13 +495,38 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)
422 return n; 495 return n;
423} 496}
424 497
425static void hist_entry__set_folding(struct hist_entry *he, bool unfold) 498static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
499 bool unfold __maybe_unused)
500{
501 float percent;
502 struct rb_node *nd;
503 struct hist_entry *child;
504 int n = 0;
505
506 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
507 child = rb_entry(nd, struct hist_entry, rb_node);
508 percent = hist_entry__get_percent_limit(child);
509 if (!child->filtered && percent >= hb->min_pcnt)
510 n++;
511 }
512
513 return n;
514}
515
516static void hist_entry__set_folding(struct hist_entry *he,
517 struct hist_browser *hb, bool unfold)
426{ 518{
427 hist_entry__init_have_children(he); 519 hist_entry__init_have_children(he);
428 he->unfolded = unfold ? he->has_children : false; 520 he->unfolded = unfold ? he->has_children : false;
429 521
430 if (he->has_children) { 522 if (he->has_children) {
431 int n = callchain__set_folding(&he->sorted_chain, unfold); 523 int n;
524
525 if (he->leaf)
526 n = callchain__set_folding(&he->sorted_chain, unfold);
527 else
528 n = hierarchy_set_folding(hb, he, unfold);
529
432 he->nr_rows = unfold ? n : 0; 530 he->nr_rows = unfold ? n : 0;
433 } else 531 } else
434 he->nr_rows = 0; 532 he->nr_rows = 0;
@@ -438,19 +536,38 @@ static void
438__hist_browser__set_folding(struct hist_browser *browser, bool unfold) 536__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
439{ 537{
440 struct rb_node *nd; 538 struct rb_node *nd;
441 struct hists *hists = browser->hists; 539 struct hist_entry *he;
540 double percent;
442 541
443 for (nd = rb_first(&hists->entries); 542 nd = rb_first(&browser->hists->entries);
444 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 543 while (nd) {
445 nd = rb_next(nd)) { 544 he = rb_entry(nd, struct hist_entry, rb_node);
446 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 545
447 hist_entry__set_folding(he, unfold); 546 /* set folding state even if it's currently folded */
448 browser->nr_callchain_rows += he->nr_rows; 547 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
548
549 hist_entry__set_folding(he, browser, unfold);
550
551 percent = hist_entry__get_percent_limit(he);
552 if (he->filtered || percent < browser->min_pcnt)
553 continue;
554
555 if (!he->depth || unfold)
556 browser->nr_hierarchy_entries++;
557 if (he->leaf)
558 browser->nr_callchain_rows += he->nr_rows;
559 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
560 browser->nr_hierarchy_entries++;
561 he->has_no_entry = true;
562 he->nr_rows = 1;
563 } else
564 he->has_no_entry = false;
449 } 565 }
450} 566}
451 567
452static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 568static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
453{ 569{
570 browser->nr_hierarchy_entries = 0;
454 browser->nr_callchain_rows = 0; 571 browser->nr_callchain_rows = 0;
455 __hist_browser__set_folding(browser, unfold); 572 __hist_browser__set_folding(browser, unfold);
456 573
@@ -657,9 +774,24 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
657 return 1; 774 return 1;
658} 775}
659 776
777static bool check_percent_display(struct rb_node *node, u64 parent_total)
778{
779 struct callchain_node *child;
780
781 if (node == NULL)
782 return false;
783
784 if (rb_next(node))
785 return true;
786
787 child = rb_entry(node, struct callchain_node, rb_node);
788 return callchain_cumul_hits(child) != parent_total;
789}
790
660static int hist_browser__show_callchain_flat(struct hist_browser *browser, 791static int hist_browser__show_callchain_flat(struct hist_browser *browser,
661 struct rb_root *root, 792 struct rb_root *root,
662 unsigned short row, u64 total, 793 unsigned short row, u64 total,
794 u64 parent_total,
663 print_callchain_entry_fn print, 795 print_callchain_entry_fn print,
664 struct callchain_print_arg *arg, 796 struct callchain_print_arg *arg,
665 check_output_full_fn is_output_full) 797 check_output_full_fn is_output_full)
@@ -669,7 +801,7 @@ static int hist_browser__show_callchain_flat(struct hist_browser *browser,
669 bool need_percent; 801 bool need_percent;
670 802
671 node = rb_first(root); 803 node = rb_first(root);
672 need_percent = node && rb_next(node); 804 need_percent = check_percent_display(node, parent_total);
673 805
674 while (node) { 806 while (node) {
675 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 807 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
@@ -763,6 +895,7 @@ static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
763static int hist_browser__show_callchain_folded(struct hist_browser *browser, 895static int hist_browser__show_callchain_folded(struct hist_browser *browser,
764 struct rb_root *root, 896 struct rb_root *root,
765 unsigned short row, u64 total, 897 unsigned short row, u64 total,
898 u64 parent_total,
766 print_callchain_entry_fn print, 899 print_callchain_entry_fn print,
767 struct callchain_print_arg *arg, 900 struct callchain_print_arg *arg,
768 check_output_full_fn is_output_full) 901 check_output_full_fn is_output_full)
@@ -772,7 +905,7 @@ static int hist_browser__show_callchain_folded(struct hist_browser *browser,
772 bool need_percent; 905 bool need_percent;
773 906
774 node = rb_first(root); 907 node = rb_first(root);
775 need_percent = node && rb_next(node); 908 need_percent = check_percent_display(node, parent_total);
776 909
777 while (node) { 910 while (node) {
778 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 911 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
@@ -844,20 +977,24 @@ next:
844 return row - first_row; 977 return row - first_row;
845} 978}
846 979
847static int hist_browser__show_callchain(struct hist_browser *browser, 980static int hist_browser__show_callchain_graph(struct hist_browser *browser,
848 struct rb_root *root, int level, 981 struct rb_root *root, int level,
849 unsigned short row, u64 total, 982 unsigned short row, u64 total,
983 u64 parent_total,
850 print_callchain_entry_fn print, 984 print_callchain_entry_fn print,
851 struct callchain_print_arg *arg, 985 struct callchain_print_arg *arg,
852 check_output_full_fn is_output_full) 986 check_output_full_fn is_output_full)
853{ 987{
854 struct rb_node *node; 988 struct rb_node *node;
855 int first_row = row, offset = level * LEVEL_OFFSET_STEP; 989 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
856 u64 new_total;
857 bool need_percent; 990 bool need_percent;
991 u64 percent_total = total;
992
993 if (callchain_param.mode == CHAIN_GRAPH_REL)
994 percent_total = parent_total;
858 995
859 node = rb_first(root); 996 node = rb_first(root);
860 need_percent = node && rb_next(node); 997 need_percent = check_percent_display(node, parent_total);
861 998
862 while (node) { 999 while (node) {
863 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 1000 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
@@ -878,7 +1015,7 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
878 folded_sign = callchain_list__folded(chain); 1015 folded_sign = callchain_list__folded(chain);
879 1016
880 row += hist_browser__show_callchain_list(browser, child, 1017 row += hist_browser__show_callchain_list(browser, child,
881 chain, row, total, 1018 chain, row, percent_total,
882 was_first && need_percent, 1019 was_first && need_percent,
883 offset + extra_offset, 1020 offset + extra_offset,
884 print, arg); 1021 print, arg);
@@ -893,13 +1030,9 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
893 if (folded_sign == '-') { 1030 if (folded_sign == '-') {
894 const int new_level = level + (extra_offset ? 2 : 1); 1031 const int new_level = level + (extra_offset ? 2 : 1);
895 1032
896 if (callchain_param.mode == CHAIN_GRAPH_REL) 1033 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
897 new_total = child->children_hit; 1034 new_level, row, total,
898 else 1035 child->children_hit,
899 new_total = total;
900
901 row += hist_browser__show_callchain(browser, &child->rb_root,
902 new_level, row, new_total,
903 print, arg, is_output_full); 1036 print, arg, is_output_full);
904 } 1037 }
905 if (is_output_full(browser, row)) 1038 if (is_output_full(browser, row))
@@ -910,6 +1043,45 @@ out:
910 return row - first_row; 1043 return row - first_row;
911} 1044}
912 1045
1046static int hist_browser__show_callchain(struct hist_browser *browser,
1047 struct hist_entry *entry, int level,
1048 unsigned short row,
1049 print_callchain_entry_fn print,
1050 struct callchain_print_arg *arg,
1051 check_output_full_fn is_output_full)
1052{
1053 u64 total = hists__total_period(entry->hists);
1054 u64 parent_total;
1055 int printed;
1056
1057 if (symbol_conf.cumulate_callchain)
1058 parent_total = entry->stat_acc->period;
1059 else
1060 parent_total = entry->stat.period;
1061
1062 if (callchain_param.mode == CHAIN_FLAT) {
1063 printed = hist_browser__show_callchain_flat(browser,
1064 &entry->sorted_chain, row,
1065 total, parent_total, print, arg,
1066 is_output_full);
1067 } else if (callchain_param.mode == CHAIN_FOLDED) {
1068 printed = hist_browser__show_callchain_folded(browser,
1069 &entry->sorted_chain, row,
1070 total, parent_total, print, arg,
1071 is_output_full);
1072 } else {
1073 printed = hist_browser__show_callchain_graph(browser,
1074 &entry->sorted_chain, level, row,
1075 total, parent_total, print, arg,
1076 is_output_full);
1077 }
1078
1079 if (arg->is_current_entry)
1080 browser->he_selection = entry;
1081
1082 return printed;
1083}
1084
913struct hpp_arg { 1085struct hpp_arg {
914 struct ui_browser *b; 1086 struct ui_browser *b;
915 char folded_sign; 1087 char folded_sign;
@@ -1006,7 +1178,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
1006 struct hist_entry *entry, 1178 struct hist_entry *entry,
1007 unsigned short row) 1179 unsigned short row)
1008{ 1180{
1009 char s[256];
1010 int printed = 0; 1181 int printed = 0;
1011 int width = browser->b.width; 1182 int width = browser->b.width;
1012 char folded_sign = ' '; 1183 char folded_sign = ' ';
@@ -1031,16 +1202,18 @@ static int hist_browser__show_entry(struct hist_browser *browser,
1031 .folded_sign = folded_sign, 1202 .folded_sign = folded_sign,
1032 .current_entry = current_entry, 1203 .current_entry = current_entry,
1033 }; 1204 };
1034 struct perf_hpp hpp = {
1035 .buf = s,
1036 .size = sizeof(s),
1037 .ptr = &arg,
1038 };
1039 int column = 0; 1205 int column = 0;
1040 1206
1041 hist_browser__gotorc(browser, row, 0); 1207 hist_browser__gotorc(browser, row, 0);
1042 1208
1043 perf_hpp__for_each_format(fmt) { 1209 hists__for_each_format(browser->hists, fmt) {
1210 char s[2048];
1211 struct perf_hpp hpp = {
1212 .buf = s,
1213 .size = sizeof(s),
1214 .ptr = &arg,
1215 };
1216
1044 if (perf_hpp__should_skip(fmt, entry->hists) || 1217 if (perf_hpp__should_skip(fmt, entry->hists) ||
1045 column++ < browser->b.horiz_scroll) 1218 column++ < browser->b.horiz_scroll)
1046 continue; 1219 continue;
@@ -1065,11 +1238,18 @@ static int hist_browser__show_entry(struct hist_browser *browser,
1065 } 1238 }
1066 1239
1067 if (fmt->color) { 1240 if (fmt->color) {
1068 width -= fmt->color(fmt, &hpp, entry); 1241 int ret = fmt->color(fmt, &hpp, entry);
1242 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1243 /*
1244 * fmt->color() already used ui_browser to
1245 * print the non alignment bits, skip it (+ret):
1246 */
1247 ui_browser__printf(&browser->b, "%s", s + ret);
1069 } else { 1248 } else {
1070 width -= fmt->entry(fmt, &hpp, entry); 1249 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1071 ui_browser__printf(&browser->b, "%s", s); 1250 ui_browser__printf(&browser->b, "%s", s);
1072 } 1251 }
1252 width -= hpp.buf - s;
1073 } 1253 }
1074 1254
1075 /* The scroll bar isn't being used */ 1255 /* The scroll bar isn't being used */
@@ -1084,43 +1264,246 @@ static int hist_browser__show_entry(struct hist_browser *browser,
1084 --row_offset; 1264 --row_offset;
1085 1265
1086 if (folded_sign == '-' && row != browser->b.rows) { 1266 if (folded_sign == '-' && row != browser->b.rows) {
1087 u64 total = hists__total_period(entry->hists);
1088 struct callchain_print_arg arg = { 1267 struct callchain_print_arg arg = {
1089 .row_offset = row_offset, 1268 .row_offset = row_offset,
1090 .is_current_entry = current_entry, 1269 .is_current_entry = current_entry,
1091 }; 1270 };
1092 1271
1093 if (callchain_param.mode == CHAIN_GRAPH_REL) { 1272 printed += hist_browser__show_callchain(browser, entry, 1, row,
1094 if (symbol_conf.cumulate_callchain)
1095 total = entry->stat_acc->period;
1096 else
1097 total = entry->stat.period;
1098 }
1099
1100 if (callchain_param.mode == CHAIN_FLAT) {
1101 printed += hist_browser__show_callchain_flat(browser,
1102 &entry->sorted_chain, row, total,
1103 hist_browser__show_callchain_entry, &arg,
1104 hist_browser__check_output_full);
1105 } else if (callchain_param.mode == CHAIN_FOLDED) {
1106 printed += hist_browser__show_callchain_folded(browser,
1107 &entry->sorted_chain, row, total,
1108 hist_browser__show_callchain_entry, &arg, 1273 hist_browser__show_callchain_entry, &arg,
1109 hist_browser__check_output_full); 1274 hist_browser__check_output_full);
1275 }
1276
1277 return printed;
1278}
1279
1280static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1281 struct hist_entry *entry,
1282 unsigned short row,
1283 int level)
1284{
1285 int printed = 0;
1286 int width = browser->b.width;
1287 char folded_sign = ' ';
1288 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1289 off_t row_offset = entry->row_offset;
1290 bool first = true;
1291 struct perf_hpp_fmt *fmt;
1292 struct perf_hpp_list_node *fmt_node;
1293 struct hpp_arg arg = {
1294 .b = &browser->b,
1295 .current_entry = current_entry,
1296 };
1297 int column = 0;
1298 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1299
1300 if (current_entry) {
1301 browser->he_selection = entry;
1302 browser->selection = &entry->ms;
1303 }
1304
1305 hist_entry__init_have_children(entry);
1306 folded_sign = hist_entry__folded(entry);
1307 arg.folded_sign = folded_sign;
1308
1309 if (entry->leaf && row_offset) {
1310 row_offset--;
1311 goto show_callchain;
1312 }
1313
1314 hist_browser__gotorc(browser, row, 0);
1315
1316 if (current_entry && browser->b.navkeypressed)
1317 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1318 else
1319 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1320
1321 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1322 width -= level * HIERARCHY_INDENT;
1323
1324 /* the first hpp_list_node is for overhead columns */
1325 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1326 struct perf_hpp_list_node, list);
1327 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1328 char s[2048];
1329 struct perf_hpp hpp = {
1330 .buf = s,
1331 .size = sizeof(s),
1332 .ptr = &arg,
1333 };
1334
1335 if (perf_hpp__should_skip(fmt, entry->hists) ||
1336 column++ < browser->b.horiz_scroll)
1337 continue;
1338
1339 if (current_entry && browser->b.navkeypressed) {
1340 ui_browser__set_color(&browser->b,
1341 HE_COLORSET_SELECTED);
1110 } else { 1342 } else {
1111 printed += hist_browser__show_callchain(browser, 1343 ui_browser__set_color(&browser->b,
1112 &entry->sorted_chain, 1, row, total, 1344 HE_COLORSET_NORMAL);
1113 hist_browser__show_callchain_entry, &arg, 1345 }
1114 hist_browser__check_output_full); 1346
1347 if (first) {
1348 ui_browser__printf(&browser->b, "%c", folded_sign);
1349 width--;
1350 first = false;
1351 } else {
1352 ui_browser__printf(&browser->b, " ");
1353 width -= 2;
1354 }
1355
1356 if (fmt->color) {
1357 int ret = fmt->color(fmt, &hpp, entry);
1358 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359 /*
1360 * fmt->color() already used ui_browser to
1361 * print the non alignment bits, skip it (+ret):
1362 */
1363 ui_browser__printf(&browser->b, "%s", s + ret);
1364 } else {
1365 int ret = fmt->entry(fmt, &hpp, entry);
1366 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1367 ui_browser__printf(&browser->b, "%s", s);
1368 }
1369 width -= hpp.buf - s;
1370 }
1371
1372 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1373 width -= hierarchy_indent;
1374
1375 if (column >= browser->b.horiz_scroll) {
1376 char s[2048];
1377 struct perf_hpp hpp = {
1378 .buf = s,
1379 .size = sizeof(s),
1380 .ptr = &arg,
1381 };
1382
1383 if (current_entry && browser->b.navkeypressed) {
1384 ui_browser__set_color(&browser->b,
1385 HE_COLORSET_SELECTED);
1386 } else {
1387 ui_browser__set_color(&browser->b,
1388 HE_COLORSET_NORMAL);
1115 } 1389 }
1116 1390
1117 if (arg.is_current_entry) 1391 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1118 browser->he_selection = entry; 1392 ui_browser__write_nstring(&browser->b, "", 2);
1393 width -= 2;
1394
1395 /*
1396 * No need to call hist_entry__snprintf_alignment()
1397 * since this fmt is always the last column in the
1398 * hierarchy mode.
1399 */
1400 if (fmt->color) {
1401 width -= fmt->color(fmt, &hpp, entry);
1402 } else {
1403 int i = 0;
1404
1405 width -= fmt->entry(fmt, &hpp, entry);
1406 ui_browser__printf(&browser->b, "%s", ltrim(s));
1407
1408 while (isspace(s[i++]))
1409 width++;
1410 }
1411 }
1412 }
1413
1414 /* The scroll bar isn't being used */
1415 if (!browser->b.navkeypressed)
1416 width += 1;
1417
1418 ui_browser__write_nstring(&browser->b, "", width);
1419
1420 ++row;
1421 ++printed;
1422
1423show_callchain:
1424 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1425 struct callchain_print_arg carg = {
1426 .row_offset = row_offset,
1427 };
1428
1429 printed += hist_browser__show_callchain(browser, entry,
1430 level + 1, row,
1431 hist_browser__show_callchain_entry, &carg,
1432 hist_browser__check_output_full);
1119 } 1433 }
1120 1434
1121 return printed; 1435 return printed;
1122} 1436}
1123 1437
1438static int hist_browser__show_no_entry(struct hist_browser *browser,
1439 unsigned short row, int level)
1440{
1441 int width = browser->b.width;
1442 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1443 bool first = true;
1444 int column = 0;
1445 int ret;
1446 struct perf_hpp_fmt *fmt;
1447 struct perf_hpp_list_node *fmt_node;
1448 int indent = browser->hists->nr_hpp_node - 2;
1449
1450 if (current_entry) {
1451 browser->he_selection = NULL;
1452 browser->selection = NULL;
1453 }
1454
1455 hist_browser__gotorc(browser, row, 0);
1456
1457 if (current_entry && browser->b.navkeypressed)
1458 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1459 else
1460 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1461
1462 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1463 width -= level * HIERARCHY_INDENT;
1464
1465 /* the first hpp_list_node is for overhead columns */
1466 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1467 struct perf_hpp_list_node, list);
1468 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1469 if (perf_hpp__should_skip(fmt, browser->hists) ||
1470 column++ < browser->b.horiz_scroll)
1471 continue;
1472
1473 ret = fmt->width(fmt, NULL, hists_to_evsel(browser->hists));
1474
1475 if (first) {
1476 /* for folded sign */
1477 first = false;
1478 ret++;
1479 } else {
1480 /* space between columns */
1481 ret += 2;
1482 }
1483
1484 ui_browser__write_nstring(&browser->b, "", ret);
1485 width -= ret;
1486 }
1487
1488 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1489 width -= indent * HIERARCHY_INDENT;
1490
1491 if (column >= browser->b.horiz_scroll) {
1492 char buf[32];
1493
1494 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1495 ui_browser__printf(&browser->b, " %s", buf);
1496 width -= ret + 2;
1497 }
1498
1499 /* The scroll bar isn't being used */
1500 if (!browser->b.navkeypressed)
1501 width += 1;
1502
1503 ui_browser__write_nstring(&browser->b, "", width);
1504 return 1;
1505}
1506
1124static int advance_hpp_check(struct perf_hpp *hpp, int inc) 1507static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1125{ 1508{
1126 advance_hpp(hpp, inc); 1509 advance_hpp(hpp, inc);
@@ -1144,7 +1527,7 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *
1144 return ret; 1527 return ret;
1145 } 1528 }
1146 1529
1147 perf_hpp__for_each_format(fmt) { 1530 hists__for_each_format(browser->hists, fmt) {
1148 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) 1531 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1149 continue; 1532 continue;
1150 1533
@@ -1160,11 +1543,96 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *
1160 return ret; 1543 return ret;
1161} 1544}
1162 1545
1546static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1547{
1548 struct hists *hists = browser->hists;
1549 struct perf_hpp dummy_hpp = {
1550 .buf = buf,
1551 .size = size,
1552 };
1553 struct perf_hpp_fmt *fmt;
1554 struct perf_hpp_list_node *fmt_node;
1555 size_t ret = 0;
1556 int column = 0;
1557 int indent = hists->nr_hpp_node - 2;
1558 bool first_node, first_col;
1559
1560 ret = scnprintf(buf, size, " ");
1561 if (advance_hpp_check(&dummy_hpp, ret))
1562 return ret;
1563
1564 /* the first hpp_list_node is for overhead columns */
1565 fmt_node = list_first_entry(&hists->hpp_formats,
1566 struct perf_hpp_list_node, list);
1567 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1568 if (column++ < browser->b.horiz_scroll)
1569 continue;
1570
1571 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1572 if (advance_hpp_check(&dummy_hpp, ret))
1573 break;
1574
1575 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1576 if (advance_hpp_check(&dummy_hpp, ret))
1577 break;
1578 }
1579
1580 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1581 indent * HIERARCHY_INDENT, "");
1582 if (advance_hpp_check(&dummy_hpp, ret))
1583 return ret;
1584
1585 first_node = true;
1586 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1587 if (!first_node) {
1588 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1589 if (advance_hpp_check(&dummy_hpp, ret))
1590 break;
1591 }
1592 first_node = false;
1593
1594 first_col = true;
1595 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1596 char *start;
1597
1598 if (perf_hpp__should_skip(fmt, hists))
1599 continue;
1600
1601 if (!first_col) {
1602 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1603 if (advance_hpp_check(&dummy_hpp, ret))
1604 break;
1605 }
1606 first_col = false;
1607
1608 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1609 dummy_hpp.buf[ret] = '\0';
1610 rtrim(dummy_hpp.buf);
1611
1612 start = ltrim(dummy_hpp.buf);
1613 ret = strlen(start);
1614
1615 if (start != dummy_hpp.buf)
1616 memmove(dummy_hpp.buf, start, ret + 1);
1617
1618 if (advance_hpp_check(&dummy_hpp, ret))
1619 break;
1620 }
1621 }
1622
1623 return ret;
1624}
1625
1163static void hist_browser__show_headers(struct hist_browser *browser) 1626static void hist_browser__show_headers(struct hist_browser *browser)
1164{ 1627{
1165 char headers[1024]; 1628 char headers[1024];
1166 1629
1167 hists_browser__scnprintf_headers(browser, headers, sizeof(headers)); 1630 if (symbol_conf.report_hierarchy)
1631 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1632 sizeof(headers));
1633 else
1634 hists_browser__scnprintf_headers(browser, headers,
1635 sizeof(headers));
1168 ui_browser__gotorc(&browser->b, 0, 0); 1636 ui_browser__gotorc(&browser->b, 0, 0);
1169 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1637 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1170 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1638 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
@@ -1196,18 +1664,34 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
1196 hb->he_selection = NULL; 1664 hb->he_selection = NULL;
1197 hb->selection = NULL; 1665 hb->selection = NULL;
1198 1666
1199 for (nd = browser->top; nd; nd = rb_next(nd)) { 1667 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1200 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1668 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1201 float percent; 1669 float percent;
1202 1670
1203 if (h->filtered) 1671 if (h->filtered) {
1672 /* let it move to sibling */
1673 h->unfolded = false;
1204 continue; 1674 continue;
1675 }
1205 1676
1206 percent = hist_entry__get_percent_limit(h); 1677 percent = hist_entry__get_percent_limit(h);
1207 if (percent < hb->min_pcnt) 1678 if (percent < hb->min_pcnt)
1208 continue; 1679 continue;
1209 1680
1210 row += hist_browser__show_entry(hb, h, row); 1681 if (symbol_conf.report_hierarchy) {
1682 row += hist_browser__show_hierarchy_entry(hb, h, row,
1683 h->depth);
1684 if (row == browser->rows)
1685 break;
1686
1687 if (h->has_no_entry) {
1688 hist_browser__show_no_entry(hb, row, h->depth + 1);
1689 row++;
1690 }
1691 } else {
1692 row += hist_browser__show_entry(hb, h, row);
1693 }
1694
1211 if (row == browser->rows) 1695 if (row == browser->rows)
1212 break; 1696 break;
1213 } 1697 }
@@ -1225,7 +1709,14 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
1225 if (!h->filtered && percent >= min_pcnt) 1709 if (!h->filtered && percent >= min_pcnt)
1226 return nd; 1710 return nd;
1227 1711
1228 nd = rb_next(nd); 1712 /*
1713 * If it's filtered, its all children also were filtered.
1714 * So move to sibling node.
1715 */
1716 if (rb_next(nd))
1717 nd = rb_next(nd);
1718 else
1719 nd = rb_hierarchy_next(nd);
1229 } 1720 }
1230 1721
1231 return NULL; 1722 return NULL;
@@ -1241,7 +1732,7 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1241 if (!h->filtered && percent >= min_pcnt) 1732 if (!h->filtered && percent >= min_pcnt)
1242 return nd; 1733 return nd;
1243 1734
1244 nd = rb_prev(nd); 1735 nd = rb_hierarchy_prev(nd);
1245 } 1736 }
1246 1737
1247 return NULL; 1738 return NULL;
@@ -1271,8 +1762,8 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
1271 nd = browser->top; 1762 nd = browser->top;
1272 goto do_offset; 1763 goto do_offset;
1273 case SEEK_END: 1764 case SEEK_END:
1274 nd = hists__filter_prev_entries(rb_last(browser->entries), 1765 nd = rb_hierarchy_last(rb_last(browser->entries));
1275 hb->min_pcnt); 1766 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1276 first = false; 1767 first = false;
1277 break; 1768 break;
1278 default: 1769 default:
@@ -1306,7 +1797,7 @@ do_offset:
1306 if (offset > 0) { 1797 if (offset > 0) {
1307 do { 1798 do {
1308 h = rb_entry(nd, struct hist_entry, rb_node); 1799 h = rb_entry(nd, struct hist_entry, rb_node);
1309 if (h->unfolded) { 1800 if (h->unfolded && h->leaf) {
1310 u16 remaining = h->nr_rows - h->row_offset; 1801 u16 remaining = h->nr_rows - h->row_offset;
1311 if (offset > remaining) { 1802 if (offset > remaining) {
1312 offset -= remaining; 1803 offset -= remaining;
@@ -1318,7 +1809,8 @@ do_offset:
1318 break; 1809 break;
1319 } 1810 }
1320 } 1811 }
1321 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt); 1812 nd = hists__filter_entries(rb_hierarchy_next(nd),
1813 hb->min_pcnt);
1322 if (nd == NULL) 1814 if (nd == NULL)
1323 break; 1815 break;
1324 --offset; 1816 --offset;
@@ -1327,7 +1819,7 @@ do_offset:
1327 } else if (offset < 0) { 1819 } else if (offset < 0) {
1328 while (1) { 1820 while (1) {
1329 h = rb_entry(nd, struct hist_entry, rb_node); 1821 h = rb_entry(nd, struct hist_entry, rb_node);
1330 if (h->unfolded) { 1822 if (h->unfolded && h->leaf) {
1331 if (first) { 1823 if (first) {
1332 if (-offset > h->row_offset) { 1824 if (-offset > h->row_offset) {
1333 offset += h->row_offset; 1825 offset += h->row_offset;
@@ -1351,7 +1843,7 @@ do_offset:
1351 } 1843 }
1352 } 1844 }
1353 1845
1354 nd = hists__filter_prev_entries(rb_prev(nd), 1846 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1355 hb->min_pcnt); 1847 hb->min_pcnt);
1356 if (nd == NULL) 1848 if (nd == NULL)
1357 break; 1849 break;
@@ -1364,7 +1856,7 @@ do_offset:
1364 * row_offset at its last entry. 1856 * row_offset at its last entry.
1365 */ 1857 */
1366 h = rb_entry(nd, struct hist_entry, rb_node); 1858 h = rb_entry(nd, struct hist_entry, rb_node);
1367 if (h->unfolded) 1859 if (h->unfolded && h->leaf)
1368 h->row_offset = h->nr_rows; 1860 h->row_offset = h->nr_rows;
1369 break; 1861 break;
1370 } 1862 }
@@ -1378,17 +1870,14 @@ do_offset:
1378} 1870}
1379 1871
1380static int hist_browser__fprintf_callchain(struct hist_browser *browser, 1872static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1381 struct hist_entry *he, FILE *fp) 1873 struct hist_entry *he, FILE *fp,
1874 int level)
1382{ 1875{
1383 u64 total = hists__total_period(he->hists);
1384 struct callchain_print_arg arg = { 1876 struct callchain_print_arg arg = {
1385 .fp = fp, 1877 .fp = fp,
1386 }; 1878 };
1387 1879
1388 if (symbol_conf.cumulate_callchain) 1880 hist_browser__show_callchain(browser, he, level, 0,
1389 total = he->stat_acc->period;
1390
1391 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1392 hist_browser__fprintf_callchain_entry, &arg, 1881 hist_browser__fprintf_callchain_entry, &arg,
1393 hist_browser__check_dump_full); 1882 hist_browser__check_dump_full);
1394 return arg.printed; 1883 return arg.printed;
@@ -1414,7 +1903,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
1414 if (symbol_conf.use_callchain) 1903 if (symbol_conf.use_callchain)
1415 printed += fprintf(fp, "%c ", folded_sign); 1904 printed += fprintf(fp, "%c ", folded_sign);
1416 1905
1417 perf_hpp__for_each_format(fmt) { 1906 hists__for_each_format(browser->hists, fmt) {
1418 if (perf_hpp__should_skip(fmt, he->hists)) 1907 if (perf_hpp__should_skip(fmt, he->hists))
1419 continue; 1908 continue;
1420 1909
@@ -1425,12 +1914,71 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
1425 first = false; 1914 first = false;
1426 1915
1427 ret = fmt->entry(fmt, &hpp, he); 1916 ret = fmt->entry(fmt, &hpp, he);
1917 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1428 advance_hpp(&hpp, ret); 1918 advance_hpp(&hpp, ret);
1429 } 1919 }
1430 printed += fprintf(fp, "%s\n", rtrim(s)); 1920 printed += fprintf(fp, "%s\n", s);
1431 1921
1432 if (folded_sign == '-') 1922 if (folded_sign == '-')
1433 printed += hist_browser__fprintf_callchain(browser, he, fp); 1923 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1924
1925 return printed;
1926}
1927
1928
1929static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1930 struct hist_entry *he,
1931 FILE *fp, int level)
1932{
1933 char s[8192];
1934 int printed = 0;
1935 char folded_sign = ' ';
1936 struct perf_hpp hpp = {
1937 .buf = s,
1938 .size = sizeof(s),
1939 };
1940 struct perf_hpp_fmt *fmt;
1941 struct perf_hpp_list_node *fmt_node;
1942 bool first = true;
1943 int ret;
1944 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1945
1946 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1947
1948 folded_sign = hist_entry__folded(he);
1949 printed += fprintf(fp, "%c", folded_sign);
1950
1951 /* the first hpp_list_node is for overhead columns */
1952 fmt_node = list_first_entry(&he->hists->hpp_formats,
1953 struct perf_hpp_list_node, list);
1954 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1955 if (!first) {
1956 ret = scnprintf(hpp.buf, hpp.size, " ");
1957 advance_hpp(&hpp, ret);
1958 } else
1959 first = false;
1960
1961 ret = fmt->entry(fmt, &hpp, he);
1962 advance_hpp(&hpp, ret);
1963 }
1964
1965 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1966 advance_hpp(&hpp, ret);
1967
1968 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1969 ret = scnprintf(hpp.buf, hpp.size, " ");
1970 advance_hpp(&hpp, ret);
1971
1972 ret = fmt->entry(fmt, &hpp, he);
1973 advance_hpp(&hpp, ret);
1974 }
1975
1976 printed += fprintf(fp, "%s\n", rtrim(s));
1977
1978 if (he->leaf && folded_sign == '-') {
1979 printed += hist_browser__fprintf_callchain(browser, he, fp,
1980 he->depth + 1);
1981 }
1434 1982
1435 return printed; 1983 return printed;
1436} 1984}
@@ -1444,8 +1992,16 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1444 while (nd) { 1992 while (nd) {
1445 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1993 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1446 1994
1447 printed += hist_browser__fprintf_entry(browser, h, fp); 1995 if (symbol_conf.report_hierarchy) {
1448 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt); 1996 printed += hist_browser__fprintf_hierarchy_entry(browser,
1997 h, fp,
1998 h->depth);
1999 } else {
2000 printed += hist_browser__fprintf_entry(browser, h, fp);
2001 }
2002
2003 nd = hists__filter_entries(rb_hierarchy_next(nd),
2004 browser->min_pcnt);
1449 } 2005 }
1450 2006
1451 return printed; 2007 return printed;
@@ -1580,11 +2136,18 @@ static int hists__browser_title(struct hists *hists,
1580 if (hists->uid_filter_str) 2136 if (hists->uid_filter_str)
1581 printed += snprintf(bf + printed, size - printed, 2137 printed += snprintf(bf + printed, size - printed,
1582 ", UID: %s", hists->uid_filter_str); 2138 ", UID: %s", hists->uid_filter_str);
1583 if (thread) 2139 if (thread) {
1584 printed += scnprintf(bf + printed, size - printed, 2140 if (sort__has_thread) {
2141 printed += scnprintf(bf + printed, size - printed,
1585 ", Thread: %s(%d)", 2142 ", Thread: %s(%d)",
1586 (thread->comm_set ? thread__comm_str(thread) : ""), 2143 (thread->comm_set ? thread__comm_str(thread) : ""),
1587 thread->tid); 2144 thread->tid);
2145 } else {
2146 printed += scnprintf(bf + printed, size - printed,
2147 ", Thread: %s",
2148 (thread->comm_set ? thread__comm_str(thread) : ""));
2149 }
2150 }
1588 if (dso) 2151 if (dso)
1589 printed += scnprintf(bf + printed, size - printed, 2152 printed += scnprintf(bf + printed, size - printed,
1590 ", DSO: %s", dso->short_name); 2153 ", DSO: %s", dso->short_name);
@@ -1759,15 +2322,24 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1759{ 2322{
1760 struct thread *thread = act->thread; 2323 struct thread *thread = act->thread;
1761 2324
2325 if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
2326 return 0;
2327
1762 if (browser->hists->thread_filter) { 2328 if (browser->hists->thread_filter) {
1763 pstack__remove(browser->pstack, &browser->hists->thread_filter); 2329 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1764 perf_hpp__set_elide(HISTC_THREAD, false); 2330 perf_hpp__set_elide(HISTC_THREAD, false);
1765 thread__zput(browser->hists->thread_filter); 2331 thread__zput(browser->hists->thread_filter);
1766 ui_helpline__pop(); 2332 ui_helpline__pop();
1767 } else { 2333 } else {
1768 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 2334 if (sort__has_thread) {
1769 thread->comm_set ? thread__comm_str(thread) : "", 2335 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1770 thread->tid); 2336 thread->comm_set ? thread__comm_str(thread) : "",
2337 thread->tid);
2338 } else {
2339 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2340 thread->comm_set ? thread__comm_str(thread) : "");
2341 }
2342
1771 browser->hists->thread_filter = thread__get(thread); 2343 browser->hists->thread_filter = thread__get(thread);
1772 perf_hpp__set_elide(HISTC_THREAD, false); 2344 perf_hpp__set_elide(HISTC_THREAD, false);
1773 pstack__push(browser->pstack, &browser->hists->thread_filter); 2345 pstack__push(browser->pstack, &browser->hists->thread_filter);
@@ -1782,13 +2354,22 @@ static int
1782add_thread_opt(struct hist_browser *browser, struct popup_action *act, 2354add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1783 char **optstr, struct thread *thread) 2355 char **optstr, struct thread *thread)
1784{ 2356{
1785 if (thread == NULL) 2357 int ret;
2358
2359 if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
1786 return 0; 2360 return 0;
1787 2361
1788 if (asprintf(optstr, "Zoom %s %s(%d) thread", 2362 if (sort__has_thread) {
1789 browser->hists->thread_filter ? "out of" : "into", 2363 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
1790 thread->comm_set ? thread__comm_str(thread) : "", 2364 browser->hists->thread_filter ? "out of" : "into",
1791 thread->tid) < 0) 2365 thread->comm_set ? thread__comm_str(thread) : "",
2366 thread->tid);
2367 } else {
2368 ret = asprintf(optstr, "Zoom %s %s thread",
2369 browser->hists->thread_filter ? "out of" : "into",
2370 thread->comm_set ? thread__comm_str(thread) : "");
2371 }
2372 if (ret < 0)
1792 return 0; 2373 return 0;
1793 2374
1794 act->thread = thread; 2375 act->thread = thread;
@@ -1801,6 +2382,9 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1801{ 2382{
1802 struct map *map = act->ms.map; 2383 struct map *map = act->ms.map;
1803 2384
2385 if (!sort__has_dso || map == NULL)
2386 return 0;
2387
1804 if (browser->hists->dso_filter) { 2388 if (browser->hists->dso_filter) {
1805 pstack__remove(browser->pstack, &browser->hists->dso_filter); 2389 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1806 perf_hpp__set_elide(HISTC_DSO, false); 2390 perf_hpp__set_elide(HISTC_DSO, false);
@@ -1825,7 +2409,7 @@ static int
1825add_dso_opt(struct hist_browser *browser, struct popup_action *act, 2409add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1826 char **optstr, struct map *map) 2410 char **optstr, struct map *map)
1827{ 2411{
1828 if (map == NULL) 2412 if (!sort__has_dso || map == NULL)
1829 return 0; 2413 return 0;
1830 2414
1831 if (asprintf(optstr, "Zoom %s %s DSO", 2415 if (asprintf(optstr, "Zoom %s %s DSO",
@@ -1850,7 +2434,7 @@ static int
1850add_map_opt(struct hist_browser *browser __maybe_unused, 2434add_map_opt(struct hist_browser *browser __maybe_unused,
1851 struct popup_action *act, char **optstr, struct map *map) 2435 struct popup_action *act, char **optstr, struct map *map)
1852{ 2436{
1853 if (map == NULL) 2437 if (!sort__has_dso || map == NULL)
1854 return 0; 2438 return 0;
1855 2439
1856 if (asprintf(optstr, "Browse map details") < 0) 2440 if (asprintf(optstr, "Browse map details") < 0)
@@ -1952,6 +2536,9 @@ add_exit_opt(struct hist_browser *browser __maybe_unused,
1952static int 2536static int
1953do_zoom_socket(struct hist_browser *browser, struct popup_action *act) 2537do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1954{ 2538{
2539 if (!sort__has_socket || act->socket < 0)
2540 return 0;
2541
1955 if (browser->hists->socket_filter > -1) { 2542 if (browser->hists->socket_filter > -1) {
1956 pstack__remove(browser->pstack, &browser->hists->socket_filter); 2543 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1957 browser->hists->socket_filter = -1; 2544 browser->hists->socket_filter = -1;
@@ -1971,7 +2558,7 @@ static int
1971add_socket_opt(struct hist_browser *browser, struct popup_action *act, 2558add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1972 char **optstr, int socket_id) 2559 char **optstr, int socket_id)
1973{ 2560{
1974 if (socket_id < 0) 2561 if (!sort__has_socket || socket_id < 0)
1975 return 0; 2562 return 0;
1976 2563
1977 if (asprintf(optstr, "Zoom %s Processor Socket %d", 2564 if (asprintf(optstr, "Zoom %s Processor Socket %d",
@@ -1989,17 +2576,60 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb)
1989 u64 nr_entries = 0; 2576 u64 nr_entries = 0;
1990 struct rb_node *nd = rb_first(&hb->hists->entries); 2577 struct rb_node *nd = rb_first(&hb->hists->entries);
1991 2578
1992 if (hb->min_pcnt == 0) { 2579 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
1993 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 2580 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1994 return; 2581 return;
1995 } 2582 }
1996 2583
1997 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2584 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1998 nr_entries++; 2585 nr_entries++;
1999 nd = rb_next(nd); 2586 nd = rb_hierarchy_next(nd);
2000 } 2587 }
2001 2588
2002 hb->nr_non_filtered_entries = nr_entries; 2589 hb->nr_non_filtered_entries = nr_entries;
2590 hb->nr_hierarchy_entries = nr_entries;
2591}
2592
2593static void hist_browser__update_percent_limit(struct hist_browser *hb,
2594 double percent)
2595{
2596 struct hist_entry *he;
2597 struct rb_node *nd = rb_first(&hb->hists->entries);
2598 u64 total = hists__total_period(hb->hists);
2599 u64 min_callchain_hits = total * (percent / 100);
2600
2601 hb->min_pcnt = callchain_param.min_percent = percent;
2602
2603 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2604 he = rb_entry(nd, struct hist_entry, rb_node);
2605
2606 if (he->has_no_entry) {
2607 he->has_no_entry = false;
2608 he->nr_rows = 0;
2609 }
2610
2611 if (!he->leaf || !symbol_conf.use_callchain)
2612 goto next;
2613
2614 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2615 total = he->stat.period;
2616
2617 if (symbol_conf.cumulate_callchain)
2618 total = he->stat_acc->period;
2619
2620 min_callchain_hits = total * (percent / 100);
2621 }
2622
2623 callchain_param.sort(&he->sorted_chain, he->callchain,
2624 min_callchain_hits, &callchain_param);
2625
2626next:
2627 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2628
2629 /* force to re-evaluate folding state of callchains */
2630 he->init_have_children = false;
2631 hist_entry__set_folding(he, hb, false);
2632 }
2003} 2633}
2004 2634
2005static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 2635static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
@@ -2037,6 +2667,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2037 "E Expand all callchains\n" \ 2667 "E Expand all callchains\n" \
2038 "F Toggle percentage of filtered entries\n" \ 2668 "F Toggle percentage of filtered entries\n" \
2039 "H Display column headers\n" \ 2669 "H Display column headers\n" \
2670 "L Change percent limit\n" \
2040 "m Display context menu\n" \ 2671 "m Display context menu\n" \
2041 "S Zoom into current Processor Socket\n" \ 2672 "S Zoom into current Processor Socket\n" \
2042 2673
@@ -2077,7 +2708,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2077 memset(options, 0, sizeof(options)); 2708 memset(options, 0, sizeof(options));
2078 memset(actions, 0, sizeof(actions)); 2709 memset(actions, 0, sizeof(actions));
2079 2710
2080 perf_hpp__for_each_format(fmt) { 2711 hists__for_each_format(browser->hists, fmt) {
2081 perf_hpp__reset_width(fmt, hists); 2712 perf_hpp__reset_width(fmt, hists);
2082 /* 2713 /*
2083 * This is done just once, and activates the horizontal scrolling 2714 * This is done just once, and activates the horizontal scrolling
@@ -2192,6 +2823,24 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2192 top->zero = !top->zero; 2823 top->zero = !top->zero;
2193 } 2824 }
2194 continue; 2825 continue;
2826 case 'L':
2827 if (ui_browser__input_window("Percent Limit",
2828 "Please enter the value you want to hide entries under that percent.",
2829 buf, "ENTER: OK, ESC: Cancel",
2830 delay_secs * 2) == K_ENTER) {
2831 char *end;
2832 double new_percent = strtod(buf, &end);
2833
2834 if (new_percent < 0 || new_percent > 100) {
2835 ui_browser__warning(&browser->b, delay_secs * 2,
2836 "Invalid percent: %.2f", new_percent);
2837 continue;
2838 }
2839
2840 hist_browser__update_percent_limit(browser, new_percent);
2841 hist_browser__reset(browser);
2842 }
2843 continue;
2195 case K_F1: 2844 case K_F1:
2196 case 'h': 2845 case 'h':
2197 case '?': 2846 case '?':
@@ -2263,10 +2912,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2263 continue; 2912 continue;
2264 } 2913 }
2265 2914
2266 if (!sort__has_sym) 2915 if (!sort__has_sym || browser->selection == NULL)
2267 goto add_exit_option;
2268
2269 if (browser->selection == NULL)
2270 goto skip_annotation; 2916 goto skip_annotation;
2271 2917
2272 if (sort__mode == SORT_MODE__BRANCH) { 2918 if (sort__mode == SORT_MODE__BRANCH) {
@@ -2306,11 +2952,16 @@ skip_annotation:
2306 &options[nr_options], 2952 &options[nr_options],
2307 socked_id); 2953 socked_id);
2308 /* perf script support */ 2954 /* perf script support */
2955 if (!is_report_browser(hbt))
2956 goto skip_scripting;
2957
2309 if (browser->he_selection) { 2958 if (browser->he_selection) {
2310 nr_options += add_script_opt(browser, 2959 if (sort__has_thread && thread) {
2311 &actions[nr_options], 2960 nr_options += add_script_opt(browser,
2312 &options[nr_options], 2961 &actions[nr_options],
2313 thread, NULL); 2962 &options[nr_options],
2963 thread, NULL);
2964 }
2314 /* 2965 /*
2315 * Note that browser->selection != NULL 2966 * Note that browser->selection != NULL
2316 * when browser->he_selection is not NULL, 2967 * when browser->he_selection is not NULL,
@@ -2320,16 +2971,18 @@ skip_annotation:
2320 * 2971 *
2321 * See hist_browser__show_entry. 2972 * See hist_browser__show_entry.
2322 */ 2973 */
2323 nr_options += add_script_opt(browser, 2974 if (sort__has_sym && browser->selection->sym) {
2324 &actions[nr_options], 2975 nr_options += add_script_opt(browser,
2325 &options[nr_options], 2976 &actions[nr_options],
2326 NULL, browser->selection->sym); 2977 &options[nr_options],
2978 NULL, browser->selection->sym);
2979 }
2327 } 2980 }
2328 nr_options += add_script_opt(browser, &actions[nr_options], 2981 nr_options += add_script_opt(browser, &actions[nr_options],
2329 &options[nr_options], NULL, NULL); 2982 &options[nr_options], NULL, NULL);
2330 nr_options += add_switch_opt(browser, &actions[nr_options], 2983 nr_options += add_switch_opt(browser, &actions[nr_options],
2331 &options[nr_options]); 2984 &options[nr_options]);
2332add_exit_option: 2985skip_scripting:
2333 nr_options += add_exit_opt(browser, &actions[nr_options], 2986 nr_options += add_exit_opt(browser, &actions[nr_options],
2334 &options[nr_options]); 2987 &options[nr_options]);
2335 2988