perf report: Support multiple events on the TUI

The hists__tty_browse_tree function was created with the loop to print
all events, and its equivalent, hists__tui_browse_tree, was created in a
similar fashion, where it is possible to switch among the multiple
events, if present, using TAB to go the next event, and shift+TAB
(UNTAB) to go to the previous.

The report TUI now shows as the window title the name of the event and a
leak was fixed wrt pstacks.

Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

+109 -41
+36 -24
tools/perf/builtin-report.c
··· 288 return ret + fprintf(fp, "\n#\n"); 289 } 290 291 static int __cmd_report(void) 292 { 293 int ret = -EINVAL; ··· 362 hists = rb_entry(next, struct hists, rb_node); 363 hists__collapse_resort(hists); 364 hists__output_resort(hists); 365 - if (use_browser > 0) 366 - hists__browse(hists, help, input_name); 367 - else { 368 - const char *evname = NULL; 369 - if (rb_first(&session->hists.entries) != 370 - rb_last(&session->hists.entries)) 371 - evname = __event_name(hists->type, hists->config); 372 - 373 - hists__fprintf_nr_sample_events(hists, evname, stdout); 374 - 375 - hists__fprintf(hists, NULL, false, stdout); 376 - fprintf(stdout, "\n\n"); 377 - } 378 - 379 next = rb_next(&hists->rb_node); 380 } 381 382 - if (use_browser <= 0 && sort_order == default_sort_order && 383 - parent_pattern == default_parent_pattern) { 384 - fprintf(stdout, "#\n# (%s)\n#\n", help); 385 386 - if (show_threads) { 387 - bool style = !strcmp(pretty_printing_style, "raw"); 388 - perf_read_values_display(stdout, &show_threads_values, 389 - style); 390 - perf_read_values_destroy(&show_threads_values); 391 - } 392 - } 393 out_delete: 394 perf_session__delete(session); 395 return ret;
··· 288 return ret + fprintf(fp, "\n#\n"); 289 } 290 291 + static int hists__tty_browse_tree(struct rb_root *tree, const char *help) 292 + { 293 + struct rb_node *next = rb_first(tree); 294 + 295 + while (next) { 296 + struct hists *hists = rb_entry(next, struct hists, rb_node); 297 + const char *evname = NULL; 298 + 299 + if (rb_first(&hists->entries) != rb_last(&hists->entries)) 300 + evname = __event_name(hists->type, hists->config); 301 + 302 + hists__fprintf_nr_sample_events(hists, evname, stdout); 303 + hists__fprintf(hists, NULL, false, stdout); 304 + fprintf(stdout, "\n\n"); 305 + next = rb_next(&hists->rb_node); 306 + } 307 + 308 + if (sort_order == default_sort_order && 309 + parent_pattern == default_parent_pattern) { 310 + fprintf(stdout, "#\n# (%s)\n#\n", help); 311 + 312 + if (show_threads) { 313 + bool style = !strcmp(pretty_printing_style, "raw"); 314 + perf_read_values_display(stdout, &show_threads_values, 315 + style); 316 + perf_read_values_destroy(&show_threads_values); 317 + } 318 + } 319 + 320 + return 0; 321 + } 322 + 323 static int __cmd_report(void) 324 { 325 int ret = -EINVAL; ··· 330 hists = rb_entry(next, struct hists, rb_node); 331 hists__collapse_resort(hists); 332 hists__output_resort(hists); 333 next = rb_next(&hists->rb_node); 334 } 335 336 + if (use_browser > 0) 337 + hists__tui_browse_tree(&session->hists_tree, help); 338 + else 339 + hists__tty_browse_tree(&session->hists_tree, help); 340 341 out_delete: 342 perf_session__delete(session); 343 return ret;
+12 -2
tools/perf/util/hist.h
··· 98 #ifdef NO_NEWT_SUPPORT 99 static inline int hists__browse(struct hists *self __used, 100 const char *helpline __used, 101 - const char *input_name __used) 102 { 103 return 0; 104 } 105 static inline int hist_entry__tui_annotate(struct hist_entry *self __used) 106 { 107 return 0; ··· 118 #else 119 #include <newt.h> 120 int hists__browse(struct hists *self, const char *helpline, 121 - const char *input_name); 122 int hist_entry__tui_annotate(struct hist_entry *self); 123 #define KEY_LEFT NEWT_KEY_LEFT 124 #define KEY_RIGHT NEWT_KEY_RIGHT 125 #endif 126 #endif /* __PERF_HIST_H */
··· 98 #ifdef NO_NEWT_SUPPORT 99 static inline int hists__browse(struct hists *self __used, 100 const char *helpline __used, 101 + const char *ev_name __used) 102 { 103 return 0; 104 } 105 + 106 + static inline int hists__tui_browse_tree(struct rb_root *self __used, 107 + const char *help __used) 108 + { 109 + return 0; 110 + } 111 + 112 static inline int hist_entry__tui_annotate(struct hist_entry *self __used) 113 { 114 return 0; ··· 111 #else 112 #include <newt.h> 113 int hists__browse(struct hists *self, const char *helpline, 114 + const char *ev_name); 115 int hist_entry__tui_annotate(struct hist_entry *self); 116 + 117 #define KEY_LEFT NEWT_KEY_LEFT 118 #define KEY_RIGHT NEWT_KEY_RIGHT 119 + 120 + int hists__tui_browse_tree(struct rb_root *self, const char *help); 121 #endif 122 #endif /* __PERF_HIST_H */
+61 -15
tools/perf/util/newt.c
··· 842 newtFormAddHotKey(self->form, 'h'); 843 newtFormAddHotKey(self->form, NEWT_KEY_F1); 844 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); 845 newtFormAddComponents(self->form, self->tree, NULL); 846 self->selection = newt__symbol_tree_get_current(self->tree); 847 ··· 875 return he ? he->thread : NULL; 876 } 877 878 - static int hist_browser__title(char *bf, size_t size, const char *input_name, 879 const struct dso *dso, const struct thread *thread) 880 { 881 int printed = 0; ··· 889 printed += snprintf(bf + printed, size - printed, 890 "%sDSO: %s", thread ? " " : "", 891 dso->short_name); 892 - return printed ?: snprintf(bf, size, "Report: %s", input_name); 893 } 894 895 - int hists__browse(struct hists *self, const char *helpline, const char *input_name) 896 { 897 struct hist_browser *browser = hist_browser__new(); 898 - struct pstack *fstack = pstack__new(2); 899 const struct thread *thread_filter = NULL; 900 const struct dso *dso_filter = NULL; 901 struct newtExitStruct es; 902 char msg[160]; 903 - int err = -1; 904 905 if (browser == NULL) 906 return -1; ··· 911 912 ui_helpline__push(helpline); 913 914 - hist_browser__title(msg, sizeof(msg), input_name, 915 dso_filter, thread_filter); 916 if (hist_browser__populate(browser, self, msg) < 0) 917 goto out_free_stack; ··· 929 dso = browser->selection->map ? browser->selection->map->dso : NULL; 930 931 if (es.reason == NEWT_EXIT_HOTKEY) { 932 - if (es.u.key == NEWT_KEY_F1) 933 - goto do_help; 934 935 - switch (toupper(es.u.key)) { 936 case 'A': 937 if (browser->selection->map == NULL && 938 browser->selection->map->dso->annotate_warned) ··· 968 continue; 969 default:; 970 } 971 - if (is_exit_key(es.u.key)) { 972 - if (es.u.key == NEWT_KEY_ESCAPE) { 973 if (dialog_yesno("Do you really want to exit?")) 974 break; 975 else ··· 1056 pstack__push(fstack, &dso_filter); 1057 } 1058 hists__filter_by_dso(self, dso_filter); 1059 - hist_browser__title(msg, sizeof(msg), input_name, 1060 dso_filter, thread_filter); 1061 if (hist_browser__populate(browser, self, msg) < 0) 1062 goto out; ··· 1075 pstack__push(fstack, &thread_filter); 1076 } 1077 hists__filter_by_thread(self, thread_filter); 1078 - hist_browser__title(msg, sizeof(msg), input_name, 1079 dso_filter, thread_filter); 1080 if (hist_browser__populate(browser, self, msg) < 0) 1081 goto out; 1082 } 1083 } 1084 - err = 0; 1085 out_free_stack: 1086 pstack__delete(fstack); 1087 out: 1088 hist_browser__delete(browser); 1089 - return err; 1090 } 1091 1092 static struct newtPercentTreeColors {
··· 842 newtFormAddHotKey(self->form, 'h'); 843 newtFormAddHotKey(self->form, NEWT_KEY_F1); 844 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); 845 + newtFormAddHotKey(self->form, NEWT_KEY_TAB); 846 + newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); 847 newtFormAddComponents(self->form, self->tree, NULL); 848 self->selection = newt__symbol_tree_get_current(self->tree); 849 ··· 873 return he ? he->thread : NULL; 874 } 875 876 + static int hist_browser__title(char *bf, size_t size, const char *ev_name, 877 const struct dso *dso, const struct thread *thread) 878 { 879 int printed = 0; ··· 887 printed += snprintf(bf + printed, size - printed, 888 "%sDSO: %s", thread ? " " : "", 889 dso->short_name); 890 + return printed ?: snprintf(bf, size, "Event: %s", ev_name); 891 } 892 893 + int hists__browse(struct hists *self, const char *helpline, const char *ev_name) 894 { 895 struct hist_browser *browser = hist_browser__new(); 896 + struct pstack *fstack; 897 const struct thread *thread_filter = NULL; 898 const struct dso *dso_filter = NULL; 899 struct newtExitStruct es; 900 char msg[160]; 901 + int key = -1; 902 903 if (browser == NULL) 904 return -1; ··· 909 910 ui_helpline__push(helpline); 911 912 + hist_browser__title(msg, sizeof(msg), ev_name, 913 dso_filter, thread_filter); 914 if (hist_browser__populate(browser, self, msg) < 0) 915 goto out_free_stack; ··· 927 dso = browser->selection->map ? browser->selection->map->dso : NULL; 928 929 if (es.reason == NEWT_EXIT_HOTKEY) { 930 + key = es.u.key; 931 932 + switch (key) { 933 + case NEWT_KEY_F1: 934 + goto do_help; 935 + case NEWT_KEY_TAB: 936 + case NEWT_KEY_UNTAB: 937 + /* 938 + * Exit the browser, let hists__browser_tree 939 + * go to the next or previous 940 + */ 941 + goto out_free_stack; 942 + default:; 943 + } 944 + 945 + key = toupper(key); 946 + switch (key) { 947 case 'A': 948 if (browser->selection->map == NULL && 949 browser->selection->map->dso->annotate_warned) ··· 953 continue; 954 default:; 955 } 956 + if (is_exit_key(key)) { 957 + if (key == NEWT_KEY_ESCAPE) { 958 if (dialog_yesno("Do you really want to exit?")) 959 break; 960 else ··· 1041 pstack__push(fstack, &dso_filter); 1042 } 1043 hists__filter_by_dso(self, dso_filter); 1044 + hist_browser__title(msg, sizeof(msg), ev_name, 1045 dso_filter, thread_filter); 1046 if (hist_browser__populate(browser, self, msg) < 0) 1047 goto out; ··· 1060 pstack__push(fstack, &thread_filter); 1061 } 1062 hists__filter_by_thread(self, thread_filter); 1063 + hist_browser__title(msg, sizeof(msg), ev_name, 1064 dso_filter, thread_filter); 1065 if (hist_browser__populate(browser, self, msg) < 0) 1066 goto out; 1067 } 1068 } 1069 out_free_stack: 1070 pstack__delete(fstack); 1071 out: 1072 hist_browser__delete(browser); 1073 + return key; 1074 + } 1075 + 1076 + int hists__tui_browse_tree(struct rb_root *self, const char *help) 1077 + { 1078 + struct rb_node *first = rb_first(self), *nd = first, *next; 1079 + int key = 0; 1080 + 1081 + while (nd) { 1082 + struct hists *hists = rb_entry(nd, struct hists, rb_node); 1083 + const char *ev_name = __event_name(hists->type, hists->config); 1084 + 1085 + key = hists__browse(hists, help, ev_name); 1086 + 1087 + if (is_exit_key(key)) 1088 + break; 1089 + 1090 + switch (key) { 1091 + case NEWT_KEY_TAB: 1092 + next = rb_next(nd); 1093 + if (next) 1094 + nd = next; 1095 + break; 1096 + case NEWT_KEY_UNTAB: 1097 + if (nd == first) 1098 + continue; 1099 + nd = rb_prev(nd); 1100 + default: 1101 + break; 1102 + } 1103 + } 1104 + 1105 + return key; 1106 } 1107 1108 static struct newtPercentTreeColors {