diff options
Diffstat (limited to 'tools/perf/ui/browsers/hists.c')
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 885 |
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 | ||
253 | static 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 | |||
250 | static bool hist_entry__toggle_fold(struct hist_entry *he) | 285 | static 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 | ||
327 | static void hist_entry__init_have_children(struct hist_entry *he) | 362 | static 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 | ||
336 | static bool hist_browser__toggle_fold(struct hist_browser *browser) | 377 | static 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 | ||
425 | static void hist_entry__set_folding(struct hist_entry *he, bool unfold) | 498 | static 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 | |||
516 | static 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 | ||
452 | static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) | 568 | static 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 | ||
777 | static 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 | |||
660 | static int hist_browser__show_callchain_flat(struct hist_browser *browser, | 791 | static 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, | |||
763 | static int hist_browser__show_callchain_folded(struct hist_browser *browser, | 895 | static 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 | ||
847 | static int hist_browser__show_callchain(struct hist_browser *browser, | 980 | static 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 | ||
1046 | static 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 | |||
913 | struct hpp_arg { | 1085 | struct 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 | |||
1280 | static 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 | |||
1423 | show_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 | ||
1438 | static 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 | |||
1124 | static int advance_hpp_check(struct perf_hpp *hpp, int inc) | 1507 | static 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 | ||
1546 | static 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 | |||
1163 | static void hist_browser__show_headers(struct hist_browser *browser) | 1626 | static 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 | ||
1380 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, | 1872 | static 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 | |||
1929 | static 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 | |||
1782 | add_thread_opt(struct hist_browser *browser, struct popup_action *act, | 2354 | add_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 | |||
1825 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, | 2409 | add_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 | |||
1850 | add_map_opt(struct hist_browser *browser __maybe_unused, | 2434 | add_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, | |||
1952 | static int | 2536 | static int |
1953 | do_zoom_socket(struct hist_browser *browser, struct popup_action *act) | 2537 | do_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 | |||
1971 | add_socket_opt(struct hist_browser *browser, struct popup_action *act, | 2558 | add_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 | |||
2593 | static 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 | |||
2626 | next: | ||
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 | ||
2005 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 2635 | static 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]); |
2332 | add_exit_option: | 2985 | skip_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 | ||