perf top: Replace CTRL+z with 'f' as hotkey for enable/disable events
[deliverable/linux.git] / tools / perf / ui / browsers / hists.c
CommitLineData
d1b4f249 1#include <stdio.h>
d1b4f249
ACM
2#include "../libslang.h"
3#include <stdlib.h>
4#include <string.h>
d1b4f249
ACM
5#include <linux/rbtree.h>
6
aca7a94d
NK
7#include "../../util/evsel.h"
8#include "../../util/evlist.h"
9#include "../../util/hist.h"
10#include "../../util/pstack.h"
11#include "../../util/sort.h"
12#include "../../util/util.h"
42337a22 13#include "../../util/top.h"
68d80758 14#include "../../arch/common.h"
d1b4f249
ACM
15
16#include "../browser.h"
17#include "../helpline.h"
18#include "../util.h"
4610e413 19#include "../ui.h"
d1b4f249 20#include "map.h"
d755330c 21#include "annotate.h"
d1b4f249 22
d1b4f249
ACM
23struct hist_browser {
24 struct ui_browser b;
25 struct hists *hists;
26 struct hist_entry *he_selection;
27 struct map_symbol *selection;
c2a51ab8 28 struct hist_browser_timer *hbt;
01f00a1c 29 struct pstack *pstack;
b1a9ceef 30 struct perf_session_env *env;
aff3f3f6 31 int print_seq;
a7cb8863 32 bool show_dso;
025bf7ea 33 bool show_headers;
064f1981 34 float min_pcnt;
112f761f 35 u64 nr_non_filtered_entries;
c3b78952 36 u64 nr_callchain_rows;
d1b4f249
ACM
37};
38
f5951d56
NK
39extern void hist_browser__init_hpp(void);
40
1e378ebd
TS
41static int hists__browser_title(struct hists *hists,
42 struct hist_browser_timer *hbt,
43 char *bf, size_t size);
112f761f 44static void hist_browser__update_nr_entries(struct hist_browser *hb);
81cce8de 45
c3b78952 46static struct rb_node *hists__filter_entries(struct rb_node *nd,
c3b78952
NK
47 float min_pcnt);
48
268397cb
NK
49static bool hist_browser__has_filter(struct hist_browser *hb)
50{
51 return hists__has_filter(hb->hists) || hb->min_pcnt;
52}
53
4fabf3d1
HK
54static int hist_browser__get_folding(struct hist_browser *browser)
55{
56 struct rb_node *nd;
57 struct hists *hists = browser->hists;
58 int unfolded_rows = 0;
59
60 for (nd = rb_first(&hists->entries);
61 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
62 nd = rb_next(nd)) {
63 struct hist_entry *he =
64 rb_entry(nd, struct hist_entry, rb_node);
65
3698dab1 66 if (he->unfolded)
4fabf3d1
HK
67 unfolded_rows += he->nr_rows;
68 }
69 return unfolded_rows;
70}
71
c3b78952
NK
72static u32 hist_browser__nr_entries(struct hist_browser *hb)
73{
74 u32 nr_entries;
75
76 if (hist_browser__has_filter(hb))
77 nr_entries = hb->nr_non_filtered_entries;
78 else
79 nr_entries = hb->hists->nr_entries;
80
4fabf3d1 81 hb->nr_callchain_rows = hist_browser__get_folding(hb);
c3b78952
NK
82 return nr_entries + hb->nr_callchain_rows;
83}
84
025bf7ea
ACM
85static void hist_browser__update_rows(struct hist_browser *hb)
86{
87 struct ui_browser *browser = &hb->b;
88 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
89
90 browser->rows = browser->height - header_offset;
91 /*
92 * Verify if we were at the last line and that line isn't
93 * visibe because we now show the header line(s).
94 */
95 index_row = browser->index - browser->top_idx;
96 if (index_row >= browser->rows)
97 browser->index -= index_row - browser->rows + 1;
98}
99
357cfff1 100static void hist_browser__refresh_dimensions(struct ui_browser *browser)
d1b4f249 101{
357cfff1
ACM
102 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
103
d1b4f249 104 /* 3 == +/- toggle symbol before actual hist_entry rendering */
357cfff1
ACM
105 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
106 /*
107 * FIXME: Just keeping existing behaviour, but this really should be
108 * before updating browser->width, as it will invalidate the
109 * calculation above. Fix this and the fallout in another
110 * changeset.
111 */
112 ui_browser__refresh_dimensions(browser);
025bf7ea 113 hist_browser__update_rows(hb);
d1b4f249
ACM
114}
115
ca3ff33b
ACM
116static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
117{
025bf7ea
ACM
118 u16 header_offset = browser->show_headers ? 1 : 0;
119
120 ui_browser__gotorc(&browser->b, row + header_offset, column);
ca3ff33b
ACM
121}
122
05e8b080 123static void hist_browser__reset(struct hist_browser *browser)
d1b4f249 124{
c3b78952
NK
125 /*
126 * The hists__remove_entry_filter() already folds non-filtered
127 * entries so we can assume it has 0 callchain rows.
128 */
129 browser->nr_callchain_rows = 0;
130
268397cb 131 hist_browser__update_nr_entries(browser);
c3b78952 132 browser->b.nr_entries = hist_browser__nr_entries(browser);
357cfff1 133 hist_browser__refresh_dimensions(&browser->b);
05e8b080 134 ui_browser__reset_index(&browser->b);
d1b4f249
ACM
135}
136
137static char tree__folded_sign(bool unfolded)
138{
139 return unfolded ? '-' : '+';
140}
141
05e8b080 142static char hist_entry__folded(const struct hist_entry *he)
d1b4f249 143{
3698dab1 144 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
d1b4f249
ACM
145}
146
05e8b080 147static char callchain_list__folded(const struct callchain_list *cl)
d1b4f249 148{
3698dab1 149 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
d1b4f249
ACM
150}
151
3698dab1 152static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
3c916cc2 153{
3698dab1 154 cl->unfolded = unfold ? cl->has_children : false;
3c916cc2
ACM
155}
156
05e8b080 157static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
d1b4f249
ACM
158{
159 int n = 0;
160 struct rb_node *nd;
161
05e8b080 162 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
163 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
164 struct callchain_list *chain;
165 char folded_sign = ' '; /* No children */
166
167 list_for_each_entry(chain, &child->val, list) {
168 ++n;
169 /* We need this because we may not have children */
170 folded_sign = callchain_list__folded(chain);
171 if (folded_sign == '+')
172 break;
173 }
174
175 if (folded_sign == '-') /* Have children and they're unfolded */
176 n += callchain_node__count_rows_rb_tree(child);
177 }
178
179 return n;
180}
181
182static int callchain_node__count_rows(struct callchain_node *node)
183{
184 struct callchain_list *chain;
185 bool unfolded = false;
186 int n = 0;
187
188 list_for_each_entry(chain, &node->val, list) {
189 ++n;
3698dab1 190 unfolded = chain->unfolded;
d1b4f249
ACM
191 }
192
193 if (unfolded)
194 n += callchain_node__count_rows_rb_tree(node);
195
196 return n;
197}
198
199static int callchain__count_rows(struct rb_root *chain)
200{
201 struct rb_node *nd;
202 int n = 0;
203
204 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
205 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
206 n += callchain_node__count_rows(node);
207 }
208
209 return n;
210}
211
3698dab1 212static bool hist_entry__toggle_fold(struct hist_entry *he)
d1b4f249 213{
3698dab1 214 if (!he)
8493fe1d
JO
215 return false;
216
3698dab1 217 if (!he->has_children)
d1b4f249
ACM
218 return false;
219
3698dab1
NK
220 he->unfolded = !he->unfolded;
221 return true;
222}
223
224static bool callchain_list__toggle_fold(struct callchain_list *cl)
225{
226 if (!cl)
227 return false;
228
229 if (!cl->has_children)
230 return false;
231
232 cl->unfolded = !cl->unfolded;
d1b4f249
ACM
233 return true;
234}
235
05e8b080 236static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
d1b4f249 237{
05e8b080 238 struct rb_node *nd = rb_first(&node->rb_root);
d1b4f249 239
05e8b080 240 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
241 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
242 struct callchain_list *chain;
293db47f 243 bool first = true;
d1b4f249
ACM
244
245 list_for_each_entry(chain, &child->val, list) {
246 if (first) {
247 first = false;
3698dab1 248 chain->has_children = chain->list.next != &child->val ||
293db47f 249 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249 250 } else
3698dab1 251 chain->has_children = chain->list.next == &child->val &&
293db47f 252 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
253 }
254
255 callchain_node__init_have_children_rb_tree(child);
256 }
257}
258
a7444af6
NK
259static void callchain_node__init_have_children(struct callchain_node *node,
260 bool has_sibling)
d1b4f249
ACM
261{
262 struct callchain_list *chain;
263
a7444af6 264 chain = list_entry(node->val.next, struct callchain_list, list);
3698dab1 265 chain->has_children = has_sibling;
a7444af6 266
82162b5a
NK
267 if (!list_empty(&node->val)) {
268 chain = list_entry(node->val.prev, struct callchain_list, list);
3698dab1 269 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
82162b5a 270 }
d1b4f249 271
05e8b080 272 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
273}
274
05e8b080 275static void callchain__init_have_children(struct rb_root *root)
d1b4f249 276{
a7444af6
NK
277 struct rb_node *nd = rb_first(root);
278 bool has_sibling = nd && rb_next(nd);
d1b4f249 279
05e8b080 280 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249 281 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
a7444af6 282 callchain_node__init_have_children(node, has_sibling);
d1b4f249
ACM
283 }
284}
285
05e8b080 286static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 287{
05e8b080 288 if (!he->init_have_children) {
3698dab1 289 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
05e8b080
ACM
290 callchain__init_have_children(&he->sorted_chain);
291 he->init_have_children = true;
d1b4f249
ACM
292 }
293}
294
05e8b080 295static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 296{
3698dab1
NK
297 struct hist_entry *he = browser->he_selection;
298 struct map_symbol *ms = browser->selection;
299 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
300 bool has_children;
301
302 if (ms == &he->ms)
303 has_children = hist_entry__toggle_fold(he);
304 else
305 has_children = callchain_list__toggle_fold(cl);
d1b4f249 306
3698dab1 307 if (has_children) {
d1b4f249 308 hist_entry__init_have_children(he);
c3b78952
NK
309 browser->b.nr_entries -= he->nr_rows;
310 browser->nr_callchain_rows -= he->nr_rows;
d1b4f249 311
3698dab1 312 if (he->unfolded)
d1b4f249
ACM
313 he->nr_rows = callchain__count_rows(&he->sorted_chain);
314 else
315 he->nr_rows = 0;
c3b78952
NK
316
317 browser->b.nr_entries += he->nr_rows;
318 browser->nr_callchain_rows += he->nr_rows;
d1b4f249
ACM
319
320 return true;
321 }
322
323 /* If it doesn't have children, no toggling performed */
324 return false;
325}
326
05e8b080 327static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
328{
329 int n = 0;
330 struct rb_node *nd;
331
05e8b080 332 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
333 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334 struct callchain_list *chain;
335 bool has_children = false;
336
337 list_for_each_entry(chain, &child->val, list) {
338 ++n;
3698dab1
NK
339 callchain_list__set_folding(chain, unfold);
340 has_children = chain->has_children;
3c916cc2
ACM
341 }
342
343 if (has_children)
344 n += callchain_node__set_folding_rb_tree(child, unfold);
345 }
346
347 return n;
348}
349
350static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
351{
352 struct callchain_list *chain;
353 bool has_children = false;
354 int n = 0;
355
356 list_for_each_entry(chain, &node->val, list) {
357 ++n;
3698dab1
NK
358 callchain_list__set_folding(chain, unfold);
359 has_children = chain->has_children;
3c916cc2
ACM
360 }
361
362 if (has_children)
363 n += callchain_node__set_folding_rb_tree(node, unfold);
364
365 return n;
366}
367
368static int callchain__set_folding(struct rb_root *chain, bool unfold)
369{
370 struct rb_node *nd;
371 int n = 0;
372
373 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
374 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
375 n += callchain_node__set_folding(node, unfold);
376 }
377
378 return n;
379}
380
05e8b080 381static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
3c916cc2 382{
05e8b080 383 hist_entry__init_have_children(he);
3698dab1 384 he->unfolded = unfold ? he->has_children : false;
3c916cc2 385
3698dab1 386 if (he->has_children) {
05e8b080
ACM
387 int n = callchain__set_folding(&he->sorted_chain, unfold);
388 he->nr_rows = unfold ? n : 0;
3c916cc2 389 } else
05e8b080 390 he->nr_rows = 0;
3c916cc2
ACM
391}
392
c3b78952
NK
393static void
394__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2
ACM
395{
396 struct rb_node *nd;
c3b78952 397 struct hists *hists = browser->hists;
3c916cc2 398
c3b78952 399 for (nd = rb_first(&hists->entries);
14135663 400 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
c3b78952 401 nd = rb_next(nd)) {
3c916cc2
ACM
402 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
403 hist_entry__set_folding(he, unfold);
c3b78952 404 browser->nr_callchain_rows += he->nr_rows;
3c916cc2
ACM
405 }
406}
407
05e8b080 408static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 409{
c3b78952
NK
410 browser->nr_callchain_rows = 0;
411 __hist_browser__set_folding(browser, unfold);
412
413 browser->b.nr_entries = hist_browser__nr_entries(browser);
3c916cc2 414 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 415 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
416}
417
7b27509f
ACM
418static void ui_browser__warn_lost_events(struct ui_browser *browser)
419{
420 ui_browser__warning(browser, 4,
421 "Events are being lost, check IO/CPU overload!\n\n"
422 "You may want to run 'perf' using a RT scheduler policy:\n\n"
423 " perf top -r 80\n\n"
424 "Or reduce the sampling frequency.");
425}
426
c2a51ab8 427static int hist_browser__run(struct hist_browser *browser)
d1b4f249 428{
b50e003d 429 int key;
81cce8de 430 char title[160];
c2a51ab8 431 struct hist_browser_timer *hbt = browser->hbt;
9783adf7 432 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 433
05e8b080 434 browser->b.entries = &browser->hists->entries;
c3b78952 435 browser->b.nr_entries = hist_browser__nr_entries(browser);
d1b4f249 436
1e378ebd 437 hists__browser_title(browser->hists, hbt, title, sizeof(title));
d1b4f249 438
05e8b080 439 if (ui_browser__show(&browser->b, title,
59e8fe32 440 "Press '?' for help on key bindings") < 0)
d1b4f249
ACM
441 return -1;
442
d1b4f249 443 while (1) {
05e8b080 444 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 445
b50e003d 446 switch (key) {
fa5df943
NK
447 case K_TIMER: {
448 u64 nr_entries;
9783adf7 449 hbt->timer(hbt->arg);
fa5df943 450
c3b78952 451 if (hist_browser__has_filter(browser))
112f761f 452 hist_browser__update_nr_entries(browser);
fa5df943 453
c3b78952 454 nr_entries = hist_browser__nr_entries(browser);
fa5df943 455 ui_browser__update_nr_entries(&browser->b, nr_entries);
7b27509f 456
05e8b080
ACM
457 if (browser->hists->stats.nr_lost_warned !=
458 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
459 browser->hists->stats.nr_lost_warned =
460 browser->hists->stats.nr_events[PERF_RECORD_LOST];
461 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
462 }
463
1e378ebd
TS
464 hists__browser_title(browser->hists,
465 hbt, title, sizeof(title));
05e8b080 466 ui_browser__show_title(&browser->b, title);
81cce8de 467 continue;
fa5df943 468 }
4694153c 469 case 'D': { /* Debug */
d1b4f249 470 static int seq;
05e8b080 471 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
472 struct hist_entry, rb_node);
473 ui_helpline__pop();
62c95ae3 474 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
475 seq++, browser->b.nr_entries,
476 browser->hists->nr_entries,
62c95ae3 477 browser->b.rows,
05e8b080
ACM
478 browser->b.index,
479 browser->b.top_idx,
d1b4f249
ACM
480 h->row_offset, h->nr_rows);
481 }
3c916cc2
ACM
482 break;
483 case 'C':
484 /* Collapse the whole world. */
05e8b080 485 hist_browser__set_folding(browser, false);
3c916cc2
ACM
486 break;
487 case 'E':
488 /* Expand the whole world. */
05e8b080 489 hist_browser__set_folding(browser, true);
3c916cc2 490 break;
025bf7ea
ACM
491 case 'H':
492 browser->show_headers = !browser->show_headers;
493 hist_browser__update_rows(browser);
494 break;
cf958003 495 case K_ENTER:
05e8b080 496 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
497 break;
498 /* fall thru */
499 default:
b50e003d 500 goto out;
d1b4f249
ACM
501 }
502 }
b50e003d 503out:
05e8b080 504 ui_browser__hide(&browser->b);
b50e003d 505 return key;
d1b4f249
ACM
506}
507
39ee533f
NK
508struct callchain_print_arg {
509 /* for hists browser */
510 off_t row_offset;
511 bool is_current_entry;
512
513 /* for file dump */
514 FILE *fp;
515 int printed;
516};
517
518typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
519 struct callchain_list *chain,
520 const char *str, int offset,
521 unsigned short row,
522 struct callchain_print_arg *arg);
523
f4536ddd
NK
524static void hist_browser__show_callchain_entry(struct hist_browser *browser,
525 struct callchain_list *chain,
39ee533f
NK
526 const char *str, int offset,
527 unsigned short row,
528 struct callchain_print_arg *arg)
f4536ddd
NK
529{
530 int color, width;
39ee533f 531 char folded_sign = callchain_list__folded(chain);
70e97278 532 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
f4536ddd
NK
533
534 color = HE_COLORSET_NORMAL;
535 width = browser->b.width - (offset + 2);
536 if (ui_browser__is_current_entry(&browser->b, row)) {
537 browser->selection = &chain->ms;
538 color = HE_COLORSET_SELECTED;
39ee533f 539 arg->is_current_entry = true;
f4536ddd
NK
540 }
541
542 ui_browser__set_color(&browser->b, color);
543 hist_browser__gotorc(browser, row, 0);
544 slsmg_write_nstring(" ", offset);
70e97278
ACM
545 slsmg_printf("%c", folded_sign);
546 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
f4536ddd
NK
547 slsmg_write_nstring(str, width);
548}
549
39ee533f
NK
550static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
551 struct callchain_list *chain,
552 const char *str, int offset,
553 unsigned short row __maybe_unused,
554 struct callchain_print_arg *arg)
555{
556 char folded_sign = callchain_list__folded(chain);
557
558 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
559 folded_sign, str);
560}
561
562typedef bool (*check_output_full_fn)(struct hist_browser *browser,
563 unsigned short row);
564
565static bool hist_browser__check_output_full(struct hist_browser *browser,
566 unsigned short row)
567{
568 return browser->b.rows == row;
569}
570
571static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
572 unsigned short row __maybe_unused)
573{
574 return false;
575}
576
d1b4f249
ACM
577#define LEVEL_OFFSET_STEP 3
578
c09a7e75
NK
579static int hist_browser__show_callchain(struct hist_browser *browser,
580 struct rb_root *root, int level,
39ee533f
NK
581 unsigned short row, u64 total,
582 print_callchain_entry_fn print,
583 struct callchain_print_arg *arg,
584 check_output_full_fn is_output_full)
d1b4f249
ACM
585{
586 struct rb_node *node;
f4536ddd 587 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
36e15dd4 588 u64 new_total;
4087d11c 589 bool need_percent;
d1b4f249 590
c09a7e75 591 node = rb_first(root);
c09e31cc 592 need_percent = node && rb_next(node);
4087d11c 593
d1b4f249
ACM
594 while (node) {
595 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
596 struct rb_node *next = rb_next(node);
f08c3154 597 u64 cumul = callchain_cumul_hits(child);
d1b4f249
ACM
598 struct callchain_list *chain;
599 char folded_sign = ' ';
600 int first = true;
601 int extra_offset = 0;
602
d1b4f249 603 list_for_each_entry(chain, &child->val, list) {
a7cb8863 604 char bf[1024], *alloc_str;
d1b4f249 605 const char *str;
d1b4f249
ACM
606 bool was_first = first;
607
163caed9 608 if (first)
d1b4f249 609 first = false;
4087d11c 610 else if (need_percent)
d1b4f249 611 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
612
613 folded_sign = callchain_list__folded(chain);
39ee533f
NK
614 if (arg->row_offset != 0) {
615 arg->row_offset--;
d1b4f249
ACM
616 goto do_next;
617 }
618
619 alloc_str = NULL;
a7cb8863
ACM
620 str = callchain_list__sym_name(chain, bf, sizeof(bf),
621 browser->show_dso);
c09a7e75 622
4087d11c 623 if (was_first && need_percent) {
c09a7e75 624 double percent = cumul * 100.0 / total;
d1b4f249
ACM
625
626 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
627 str = "Not enough memory!";
628 else
629 str = alloc_str;
630 }
631
39ee533f
NK
632 print(browser, chain, str, offset + extra_offset, row, arg);
633
d1b4f249
ACM
634 free(alloc_str);
635
39ee533f 636 if (is_output_full(browser, ++row))
d1b4f249
ACM
637 goto out;
638do_next:
639 if (folded_sign == '+')
640 break;
641 }
642
643 if (folded_sign == '-') {
644 const int new_level = level + (extra_offset ? 2 : 1);
d1b4f249 645
c09a7e75
NK
646 if (callchain_param.mode == CHAIN_GRAPH_REL)
647 new_total = child->children_hit;
648 else
649 new_total = total;
d1b4f249 650
c09a7e75 651 row += hist_browser__show_callchain(browser, &child->rb_root,
39ee533f
NK
652 new_level, row, new_total,
653 print, arg, is_output_full);
d1b4f249 654 }
39ee533f 655 if (is_output_full(browser, row))
d1b4f249 656 break;
c09a7e75 657 node = next;
d1b4f249 658 }
c09a7e75 659out:
d1b4f249
ACM
660 return row - first_row;
661}
662
89701460
NK
663struct hpp_arg {
664 struct ui_browser *b;
665 char folded_sign;
666 bool current_entry;
667};
668
2f6d9009
NK
669static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
670{
671 struct hpp_arg *arg = hpp->ptr;
d675107c 672 int ret, len;
2f6d9009
NK
673 va_list args;
674 double percent;
371d8c40 675
2f6d9009 676 va_start(args, fmt);
d675107c 677 len = va_arg(args, int);
2f6d9009
NK
678 percent = va_arg(args, double);
679 va_end(args);
371d8c40 680
2f6d9009 681 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
371d8c40 682
d675107c 683 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
2f6d9009 684 slsmg_printf("%s", hpp->buf);
5aed9d24 685
2f6d9009 686 advance_hpp(hpp, ret);
5aed9d24
NK
687 return ret;
688}
689
fb821c9e 690#define __HPP_COLOR_PERCENT_FN(_type, _field) \
5aed9d24
NK
691static u64 __hpp_get_##_field(struct hist_entry *he) \
692{ \
693 return he->stat._field; \
694} \
695 \
2c5d4b4a 696static int \
5b591669 697hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
2c5d4b4a
JO
698 struct perf_hpp *hpp, \
699 struct hist_entry *he) \
f5951d56 700{ \
5b591669
NK
701 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
702 __hpp__slsmg_color_printf, true); \
f5951d56
NK
703}
704
0434ddd2
NK
705#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
706static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
707{ \
708 return he->stat_acc->_field; \
709} \
710 \
711static int \
5b591669 712hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
0434ddd2
NK
713 struct perf_hpp *hpp, \
714 struct hist_entry *he) \
715{ \
716 if (!symbol_conf.cumulate_callchain) { \
5b591669 717 int len = fmt->user_len ?: fmt->len; \
d675107c 718 int ret = scnprintf(hpp->buf, hpp->size, \
5b591669 719 "%*s", len, "N/A"); \
0434ddd2
NK
720 slsmg_printf("%s", hpp->buf); \
721 \
722 return ret; \
723 } \
5b591669
NK
724 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
725 " %*.2f%%", __hpp__slsmg_color_printf, true); \
0434ddd2
NK
726}
727
fb821c9e
NK
728__HPP_COLOR_PERCENT_FN(overhead, period)
729__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
730__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
731__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
732__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 733__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 734
5aed9d24 735#undef __HPP_COLOR_PERCENT_FN
0434ddd2 736#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
737
738void hist_browser__init_hpp(void)
739{
f5951d56
NK
740 perf_hpp__format[PERF_HPP__OVERHEAD].color =
741 hist_browser__hpp_color_overhead;
742 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
743 hist_browser__hpp_color_overhead_sys;
744 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
745 hist_browser__hpp_color_overhead_us;
746 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
747 hist_browser__hpp_color_overhead_guest_sys;
748 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
749 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
750 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
751 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
752}
753
05e8b080 754static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
755 struct hist_entry *entry,
756 unsigned short row)
757{
758 char s[256];
1240005e 759 int printed = 0;
67d25916 760 int width = browser->b.width;
d1b4f249 761 char folded_sign = ' ';
05e8b080 762 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 763 off_t row_offset = entry->row_offset;
63a1a3d8 764 bool first = true;
1240005e 765 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
766
767 if (current_entry) {
05e8b080
ACM
768 browser->he_selection = entry;
769 browser->selection = &entry->ms;
d1b4f249
ACM
770 }
771
772 if (symbol_conf.use_callchain) {
163caed9 773 hist_entry__init_have_children(entry);
d1b4f249
ACM
774 folded_sign = hist_entry__folded(entry);
775 }
776
777 if (row_offset == 0) {
89701460 778 struct hpp_arg arg = {
fb821c9e 779 .b = &browser->b,
89701460
NK
780 .folded_sign = folded_sign,
781 .current_entry = current_entry,
782 };
f5951d56
NK
783 struct perf_hpp hpp = {
784 .buf = s,
785 .size = sizeof(s),
89701460 786 .ptr = &arg,
f5951d56 787 };
d1b4f249 788
ca3ff33b 789 hist_browser__gotorc(browser, row, 0);
c172f742 790
1240005e 791 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
792 if (perf_hpp__should_skip(fmt))
793 continue;
794
fb821c9e
NK
795 if (current_entry && browser->b.navkeypressed) {
796 ui_browser__set_color(&browser->b,
797 HE_COLORSET_SELECTED);
798 } else {
799 ui_browser__set_color(&browser->b,
800 HE_COLORSET_NORMAL);
801 }
802
803 if (first) {
804 if (symbol_conf.use_callchain) {
805 slsmg_printf("%c ", folded_sign);
806 width -= 2;
807 }
808 first = false;
809 } else {
f5951d56
NK
810 slsmg_printf(" ");
811 width -= 2;
812 }
c172f742 813
1240005e 814 if (fmt->color) {
2c5d4b4a 815 width -= fmt->color(fmt, &hpp, entry);
f5951d56 816 } else {
2c5d4b4a 817 width -= fmt->entry(fmt, &hpp, entry);
f5951d56
NK
818 slsmg_printf("%s", s);
819 }
2cf9cebf
ACM
820 }
821
f5951d56
NK
822 /* The scroll bar isn't being used */
823 if (!browser->b.navkeypressed)
824 width += 1;
825
26d8b338
NK
826 slsmg_write_nstring("", width);
827
d1b4f249
ACM
828 ++row;
829 ++printed;
830 } else
831 --row_offset;
832
62c95ae3 833 if (folded_sign == '-' && row != browser->b.rows) {
c09a7e75 834 u64 total = hists__total_period(entry->hists);
39ee533f
NK
835 struct callchain_print_arg arg = {
836 .row_offset = row_offset,
837 .is_current_entry = current_entry,
838 };
c09a7e75 839
4087d11c
NK
840 if (callchain_param.mode == CHAIN_GRAPH_REL) {
841 if (symbol_conf.cumulate_callchain)
842 total = entry->stat_acc->period;
843 else
844 total = entry->stat.period;
845 }
846
c09a7e75 847 printed += hist_browser__show_callchain(browser,
39ee533f
NK
848 &entry->sorted_chain, 1, row, total,
849 hist_browser__show_callchain_entry, &arg,
850 hist_browser__check_output_full);
c09a7e75 851
39ee533f 852 if (arg.is_current_entry)
05e8b080 853 browser->he_selection = entry;
d1b4f249
ACM
854 }
855
856 return printed;
857}
858
81a888fe
JO
859static int advance_hpp_check(struct perf_hpp *hpp, int inc)
860{
861 advance_hpp(hpp, inc);
862 return hpp->size <= 0;
863}
864
865static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
866{
867 struct perf_hpp dummy_hpp = {
868 .buf = buf,
869 .size = size,
870 };
871 struct perf_hpp_fmt *fmt;
872 size_t ret = 0;
873
874 if (symbol_conf.use_callchain) {
875 ret = scnprintf(buf, size, " ");
876 if (advance_hpp_check(&dummy_hpp, ret))
877 return ret;
878 }
879
880 perf_hpp__for_each_format(fmt) {
881 if (perf_hpp__should_skip(fmt))
882 continue;
883
81a888fe
JO
884 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
885 if (advance_hpp_check(&dummy_hpp, ret))
886 break;
887
888 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
889 if (advance_hpp_check(&dummy_hpp, ret))
890 break;
891 }
892
893 return ret;
894}
895
025bf7ea
ACM
896static void hist_browser__show_headers(struct hist_browser *browser)
897{
81a888fe
JO
898 char headers[1024];
899
900 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
025bf7ea
ACM
901 ui_browser__gotorc(&browser->b, 0, 0);
902 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
81a888fe 903 slsmg_write_nstring(headers, browser->b.width + 1);
025bf7ea
ACM
904}
905
437cfe7a
ACM
906static void ui_browser__hists_init_top(struct ui_browser *browser)
907{
908 if (browser->top == NULL) {
909 struct hist_browser *hb;
910
911 hb = container_of(browser, struct hist_browser, b);
912 browser->top = rb_first(&hb->hists->entries);
913 }
914}
915
05e8b080 916static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
917{
918 unsigned row = 0;
025bf7ea 919 u16 header_offset = 0;
d1b4f249 920 struct rb_node *nd;
05e8b080 921 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 922
025bf7ea
ACM
923 if (hb->show_headers) {
924 hist_browser__show_headers(hb);
925 header_offset = 1;
926 }
927
05e8b080 928 ui_browser__hists_init_top(browser);
d1b4f249 929
05e8b080 930 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249 931 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 932 float percent;
d1b4f249
ACM
933
934 if (h->filtered)
935 continue;
936
14135663 937 percent = hist_entry__get_percent_limit(h);
064f1981
NK
938 if (percent < hb->min_pcnt)
939 continue;
940
d1b4f249 941 row += hist_browser__show_entry(hb, h, row);
62c95ae3 942 if (row == browser->rows)
d1b4f249
ACM
943 break;
944 }
945
025bf7ea 946 return row + header_offset;
d1b4f249
ACM
947}
948
064f1981 949static struct rb_node *hists__filter_entries(struct rb_node *nd,
064f1981 950 float min_pcnt)
d1b4f249
ACM
951{
952 while (nd != NULL) {
953 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 954 float percent = hist_entry__get_percent_limit(h);
064f1981 955
c0f1527b 956 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
957 return nd;
958
959 nd = rb_next(nd);
960 }
961
962 return NULL;
963}
964
064f1981 965static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
064f1981 966 float min_pcnt)
d1b4f249
ACM
967{
968 while (nd != NULL) {
969 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 970 float percent = hist_entry__get_percent_limit(h);
064f1981
NK
971
972 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
973 return nd;
974
975 nd = rb_prev(nd);
976 }
977
978 return NULL;
979}
980
05e8b080 981static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
982 off_t offset, int whence)
983{
984 struct hist_entry *h;
985 struct rb_node *nd;
986 bool first = true;
064f1981
NK
987 struct hist_browser *hb;
988
989 hb = container_of(browser, struct hist_browser, b);
d1b4f249 990
05e8b080 991 if (browser->nr_entries == 0)
60098917
ACM
992 return;
993
05e8b080 994 ui_browser__hists_init_top(browser);
437cfe7a 995
d1b4f249
ACM
996 switch (whence) {
997 case SEEK_SET:
064f1981 998 nd = hists__filter_entries(rb_first(browser->entries),
14135663 999 hb->min_pcnt);
d1b4f249
ACM
1000 break;
1001 case SEEK_CUR:
05e8b080 1002 nd = browser->top;
d1b4f249
ACM
1003 goto do_offset;
1004 case SEEK_END:
064f1981 1005 nd = hists__filter_prev_entries(rb_last(browser->entries),
14135663 1006 hb->min_pcnt);
d1b4f249
ACM
1007 first = false;
1008 break;
1009 default:
1010 return;
1011 }
1012
1013 /*
1014 * Moves not relative to the first visible entry invalidates its
1015 * row_offset:
1016 */
05e8b080 1017 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
1018 h->row_offset = 0;
1019
1020 /*
1021 * Here we have to check if nd is expanded (+), if it is we can't go
1022 * the next top level hist_entry, instead we must compute an offset of
1023 * what _not_ to show and not change the first visible entry.
1024 *
1025 * This offset increments when we are going from top to bottom and
1026 * decreases when we're going from bottom to top.
1027 *
1028 * As we don't have backpointers to the top level in the callchains
1029 * structure, we need to always print the whole hist_entry callchain,
1030 * skipping the first ones that are before the first visible entry
1031 * and stop when we printed enough lines to fill the screen.
1032 */
1033do_offset:
1034 if (offset > 0) {
1035 do {
1036 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1037 if (h->unfolded) {
d1b4f249
ACM
1038 u16 remaining = h->nr_rows - h->row_offset;
1039 if (offset > remaining) {
1040 offset -= remaining;
1041 h->row_offset = 0;
1042 } else {
1043 h->row_offset += offset;
1044 offset = 0;
05e8b080 1045 browser->top = nd;
d1b4f249
ACM
1046 break;
1047 }
1048 }
14135663 1049 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
d1b4f249
ACM
1050 if (nd == NULL)
1051 break;
1052 --offset;
05e8b080 1053 browser->top = nd;
d1b4f249
ACM
1054 } while (offset != 0);
1055 } else if (offset < 0) {
1056 while (1) {
1057 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1058 if (h->unfolded) {
d1b4f249
ACM
1059 if (first) {
1060 if (-offset > h->row_offset) {
1061 offset += h->row_offset;
1062 h->row_offset = 0;
1063 } else {
1064 h->row_offset += offset;
1065 offset = 0;
05e8b080 1066 browser->top = nd;
d1b4f249
ACM
1067 break;
1068 }
1069 } else {
1070 if (-offset > h->nr_rows) {
1071 offset += h->nr_rows;
1072 h->row_offset = 0;
1073 } else {
1074 h->row_offset = h->nr_rows + offset;
1075 offset = 0;
05e8b080 1076 browser->top = nd;
d1b4f249
ACM
1077 break;
1078 }
1079 }
1080 }
1081
14135663 1082 nd = hists__filter_prev_entries(rb_prev(nd),
064f1981 1083 hb->min_pcnt);
d1b4f249
ACM
1084 if (nd == NULL)
1085 break;
1086 ++offset;
05e8b080 1087 browser->top = nd;
d1b4f249
ACM
1088 if (offset == 0) {
1089 /*
1090 * Last unfiltered hist_entry, check if it is
1091 * unfolded, if it is then we should have
1092 * row_offset at its last entry.
1093 */
1094 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1095 if (h->unfolded)
d1b4f249
ACM
1096 h->row_offset = h->nr_rows;
1097 break;
1098 }
1099 first = false;
1100 }
1101 } else {
05e8b080 1102 browser->top = nd;
d1b4f249
ACM
1103 h = rb_entry(nd, struct hist_entry, rb_node);
1104 h->row_offset = 0;
1105 }
1106}
1107
aff3f3f6 1108static int hist_browser__fprintf_callchain(struct hist_browser *browser,
39ee533f 1109 struct hist_entry *he, FILE *fp)
aff3f3f6 1110{
39ee533f
NK
1111 u64 total = hists__total_period(he->hists);
1112 struct callchain_print_arg arg = {
1113 .fp = fp,
1114 };
aff3f3f6 1115
39ee533f
NK
1116 if (symbol_conf.cumulate_callchain)
1117 total = he->stat_acc->period;
aff3f3f6 1118
39ee533f
NK
1119 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1120 hist_browser__fprintf_callchain_entry, &arg,
1121 hist_browser__check_dump_full);
1122 return arg.printed;
aff3f3f6
ACM
1123}
1124
1125static int hist_browser__fprintf_entry(struct hist_browser *browser,
1126 struct hist_entry *he, FILE *fp)
1127{
1128 char s[8192];
aff3f3f6
ACM
1129 int printed = 0;
1130 char folded_sign = ' ';
26d8b338
NK
1131 struct perf_hpp hpp = {
1132 .buf = s,
1133 .size = sizeof(s),
1134 };
1135 struct perf_hpp_fmt *fmt;
1136 bool first = true;
1137 int ret;
aff3f3f6
ACM
1138
1139 if (symbol_conf.use_callchain)
1140 folded_sign = hist_entry__folded(he);
1141
aff3f3f6
ACM
1142 if (symbol_conf.use_callchain)
1143 printed += fprintf(fp, "%c ", folded_sign);
1144
26d8b338 1145 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
1146 if (perf_hpp__should_skip(fmt))
1147 continue;
1148
26d8b338
NK
1149 if (!first) {
1150 ret = scnprintf(hpp.buf, hpp.size, " ");
1151 advance_hpp(&hpp, ret);
1152 } else
1153 first = false;
aff3f3f6 1154
26d8b338
NK
1155 ret = fmt->entry(fmt, &hpp, he);
1156 advance_hpp(&hpp, ret);
1157 }
aff3f3f6
ACM
1158 printed += fprintf(fp, "%s\n", rtrim(s));
1159
1160 if (folded_sign == '-')
39ee533f 1161 printed += hist_browser__fprintf_callchain(browser, he, fp);
aff3f3f6
ACM
1162
1163 return printed;
1164}
1165
1166static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1167{
064f1981 1168 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 1169 browser->min_pcnt);
aff3f3f6
ACM
1170 int printed = 0;
1171
1172 while (nd) {
1173 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1174
1175 printed += hist_browser__fprintf_entry(browser, h, fp);
14135663 1176 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
aff3f3f6
ACM
1177 }
1178
1179 return printed;
1180}
1181
1182static int hist_browser__dump(struct hist_browser *browser)
1183{
1184 char filename[64];
1185 FILE *fp;
1186
1187 while (1) {
1188 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1189 if (access(filename, F_OK))
1190 break;
1191 /*
1192 * XXX: Just an arbitrary lazy upper limit
1193 */
1194 if (++browser->print_seq == 8192) {
1195 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1196 return -1;
1197 }
1198 }
1199
1200 fp = fopen(filename, "w");
1201 if (fp == NULL) {
1202 char bf[64];
4cc49d4d
KS
1203 const char *err = strerror_r(errno, bf, sizeof(bf));
1204 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1205 return -1;
1206 }
1207
1208 ++browser->print_seq;
1209 hist_browser__fprintf(browser, fp);
1210 fclose(fp);
1211 ui_helpline__fpush("%s written!", filename);
1212
1213 return 0;
1214}
1215
c2a51ab8 1216static struct hist_browser *hist_browser__new(struct hists *hists,
b1a9ceef
NK
1217 struct hist_browser_timer *hbt,
1218 struct perf_session_env *env)
d1b4f249 1219{
05e8b080 1220 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1221
05e8b080
ACM
1222 if (browser) {
1223 browser->hists = hists;
1224 browser->b.refresh = hist_browser__refresh;
357cfff1 1225 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
05e8b080
ACM
1226 browser->b.seek = ui_browser__hists_seek;
1227 browser->b.use_navkeypressed = true;
c8302367 1228 browser->show_headers = symbol_conf.show_hist_headers;
c2a51ab8 1229 browser->hbt = hbt;
b1a9ceef 1230 browser->env = env;
d1b4f249
ACM
1231 }
1232
05e8b080 1233 return browser;
d1b4f249
ACM
1234}
1235
05e8b080 1236static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1237{
05e8b080 1238 free(browser);
d1b4f249
ACM
1239}
1240
05e8b080 1241static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1242{
05e8b080 1243 return browser->he_selection;
d1b4f249
ACM
1244}
1245
05e8b080 1246static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1247{
05e8b080 1248 return browser->he_selection->thread;
d1b4f249
ACM
1249}
1250
1e378ebd
TS
1251/* Check whether the browser is for 'top' or 'report' */
1252static inline bool is_report_browser(void *timer)
1253{
1254 return timer == NULL;
1255}
1256
1257static int hists__browser_title(struct hists *hists,
1258 struct hist_browser_timer *hbt,
1259 char *bf, size_t size)
d1b4f249 1260{
469917ce
ACM
1261 char unit;
1262 int printed;
05e8b080
ACM
1263 const struct dso *dso = hists->dso_filter;
1264 const struct thread *thread = hists->thread_filter;
1265 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1266 u64 nr_events = hists->stats.total_period;
717e263f 1267 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 1268 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
1269 char buf[512];
1270 size_t buflen = sizeof(buf);
1271
f2148330
NK
1272 if (symbol_conf.filter_relative) {
1273 nr_samples = hists->stats.nr_non_filtered_samples;
1274 nr_events = hists->stats.total_non_filtered_period;
1275 }
1276
759ff497 1277 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1278 struct perf_evsel *pos;
1279
1280 perf_evsel__group_desc(evsel, buf, buflen);
1281 ev_name = buf;
1282
1283 for_each_group_member(pos, evsel) {
4ea062ed
ACM
1284 struct hists *pos_hists = evsel__hists(pos);
1285
f2148330 1286 if (symbol_conf.filter_relative) {
4ea062ed
ACM
1287 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1288 nr_events += pos_hists->stats.total_non_filtered_period;
f2148330 1289 } else {
4ea062ed
ACM
1290 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1291 nr_events += pos_hists->stats.total_period;
f2148330 1292 }
717e263f
NK
1293 }
1294 }
cc686280
AR
1295
1296 nr_samples = convert_unit(nr_samples, &unit);
1297 printed = scnprintf(bf, size,
e641f696 1298 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
cc686280 1299 nr_samples, unit, ev_name, nr_events);
469917ce 1300
d1b4f249 1301
05e8b080 1302 if (hists->uid_filter_str)
0d37aa34 1303 printed += snprintf(bf + printed, size - printed,
05e8b080 1304 ", UID: %s", hists->uid_filter_str);
d1b4f249 1305 if (thread)
e7f01d1e 1306 printed += scnprintf(bf + printed, size - printed,
469917ce 1307 ", Thread: %s(%d)",
b9c5143a 1308 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1309 thread->tid);
d1b4f249 1310 if (dso)
e7f01d1e 1311 printed += scnprintf(bf + printed, size - printed,
469917ce 1312 ", DSO: %s", dso->short_name);
1e378ebd
TS
1313 if (!is_report_browser(hbt)) {
1314 struct perf_top *top = hbt->arg;
1315
1316 if (top->zero)
1317 printed += scnprintf(bf + printed, size - printed, " [z]");
1318 }
1319
469917ce 1320 return printed;
d1b4f249
ACM
1321}
1322
24bff2dc
SE
1323static inline void free_popup_options(char **options, int n)
1324{
1325 int i;
1326
04662523
ACM
1327 for (i = 0; i < n; ++i)
1328 zfree(&options[i]);
24bff2dc
SE
1329}
1330
341487ab
FT
1331/*
1332 * Only runtime switching of perf data file will make "input_name" point
1333 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1334 * whether we need to call free() for current "input_name" during the switch.
1335 */
1336static bool is_input_name_malloced = false;
1337
1338static int switch_data_file(void)
1339{
1340 char *pwd, *options[32], *abs_path[32], *tmp;
1341 DIR *pwd_dir;
1342 int nr_options = 0, choice = -1, ret = -1;
1343 struct dirent *dent;
1344
1345 pwd = getenv("PWD");
1346 if (!pwd)
1347 return ret;
1348
1349 pwd_dir = opendir(pwd);
1350 if (!pwd_dir)
1351 return ret;
1352
1353 memset(options, 0, sizeof(options));
1354 memset(options, 0, sizeof(abs_path));
1355
1356 while ((dent = readdir(pwd_dir))) {
1357 char path[PATH_MAX];
1358 u64 magic;
1359 char *name = dent->d_name;
1360 FILE *file;
1361
1362 if (!(dent->d_type == DT_REG))
1363 continue;
1364
1365 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1366
1367 file = fopen(path, "r");
1368 if (!file)
1369 continue;
1370
1371 if (fread(&magic, 1, 8, file) < 8)
1372 goto close_file_and_continue;
1373
1374 if (is_perf_magic(magic)) {
1375 options[nr_options] = strdup(name);
1376 if (!options[nr_options])
1377 goto close_file_and_continue;
1378
1379 abs_path[nr_options] = strdup(path);
1380 if (!abs_path[nr_options]) {
74cf249d 1381 zfree(&options[nr_options]);
341487ab
FT
1382 ui__warning("Can't search all data files due to memory shortage.\n");
1383 fclose(file);
1384 break;
1385 }
1386
1387 nr_options++;
1388 }
1389
1390close_file_and_continue:
1391 fclose(file);
1392 if (nr_options >= 32) {
1393 ui__warning("Too many perf data files in PWD!\n"
1394 "Only the first 32 files will be listed.\n");
1395 break;
1396 }
1397 }
1398 closedir(pwd_dir);
1399
1400 if (nr_options) {
1401 choice = ui__popup_menu(nr_options, options);
1402 if (choice < nr_options && choice >= 0) {
1403 tmp = strdup(abs_path[choice]);
1404 if (tmp) {
1405 if (is_input_name_malloced)
1406 free((void *)input_name);
1407 input_name = tmp;
1408 is_input_name_malloced = true;
1409 ret = 0;
1410 } else
1411 ui__warning("Data switch failed due to memory shortage!\n");
1412 }
1413 }
1414
1415 free_popup_options(options, nr_options);
1416 free_popup_options(abs_path, nr_options);
1417 return ret;
1418}
1419
ea7cd592
NK
1420struct popup_action {
1421 struct thread *thread;
1422 struct dso *dso;
1423 struct map_symbol ms;
1424
1425 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1426};
1427
bc7cad42 1428static int
ea7cd592 1429do_annotate(struct hist_browser *browser, struct popup_action *act)
bc7cad42
NK
1430{
1431 struct perf_evsel *evsel;
1432 struct annotation *notes;
1433 struct hist_entry *he;
1434 int err;
1435
1436 if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1437 return 0;
1438
ea7cd592 1439 notes = symbol__annotation(act->ms.sym);
bc7cad42
NK
1440 if (!notes->src)
1441 return 0;
1442
1443 evsel = hists_to_evsel(browser->hists);
ea7cd592 1444 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
bc7cad42
NK
1445 he = hist_browser__selected_entry(browser);
1446 /*
1447 * offer option to annotate the other branch source or target
1448 * (if they exists) when returning from annotate
1449 */
1450 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1451 return 1;
1452
1453 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1454 if (err)
1455 ui_browser__handle_resize(&browser->b);
1456 return 0;
1457}
1458
1459static int
ea7cd592
NK
1460add_annotate_opt(struct hist_browser *browser __maybe_unused,
1461 struct popup_action *act, char **optstr,
1462 struct map *map, struct symbol *sym)
bc7cad42 1463{
ea7cd592
NK
1464 if (sym == NULL || map->dso->annotate_warned)
1465 return 0;
1466
1467 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1468 return 0;
1469
1470 act->ms.map = map;
1471 act->ms.sym = sym;
1472 act->fn = do_annotate;
1473 return 1;
1474}
1475
1476static int
1477do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1478{
1479 struct thread *thread = act->thread;
1480
bc7cad42
NK
1481 if (browser->hists->thread_filter) {
1482 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1483 perf_hpp__set_elide(HISTC_THREAD, false);
1484 thread__zput(browser->hists->thread_filter);
1485 ui_helpline__pop();
1486 } else {
1487 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1488 thread->comm_set ? thread__comm_str(thread) : "",
1489 thread->tid);
1490 browser->hists->thread_filter = thread__get(thread);
1491 perf_hpp__set_elide(HISTC_THREAD, false);
1492 pstack__push(browser->pstack, &browser->hists->thread_filter);
1493 }
1494
1495 hists__filter_by_thread(browser->hists);
1496 hist_browser__reset(browser);
1497 return 0;
1498}
1499
1500static int
ea7cd592
NK
1501add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1502 char **optstr, struct thread *thread)
1503{
1504 if (thread == NULL)
1505 return 0;
1506
1507 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1508 browser->hists->thread_filter ? "out of" : "into",
1509 thread->comm_set ? thread__comm_str(thread) : "",
1510 thread->tid) < 0)
1511 return 0;
1512
1513 act->thread = thread;
1514 act->fn = do_zoom_thread;
1515 return 1;
1516}
1517
1518static int
1519do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
bc7cad42 1520{
ea7cd592
NK
1521 struct dso *dso = act->dso;
1522
bc7cad42
NK
1523 if (browser->hists->dso_filter) {
1524 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1525 perf_hpp__set_elide(HISTC_DSO, false);
1526 browser->hists->dso_filter = NULL;
1527 ui_helpline__pop();
1528 } else {
1529 if (dso == NULL)
1530 return 0;
1531 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1532 dso->kernel ? "the Kernel" : dso->short_name);
1533 browser->hists->dso_filter = dso;
1534 perf_hpp__set_elide(HISTC_DSO, true);
1535 pstack__push(browser->pstack, &browser->hists->dso_filter);
1536 }
1537
1538 hists__filter_by_dso(browser->hists);
1539 hist_browser__reset(browser);
1540 return 0;
1541}
1542
1543static int
ea7cd592
NK
1544add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1545 char **optstr, struct dso *dso)
bc7cad42 1546{
ea7cd592
NK
1547 if (dso == NULL)
1548 return 0;
1549
1550 if (asprintf(optstr, "Zoom %s %s DSO",
1551 browser->hists->dso_filter ? "out of" : "into",
1552 dso->kernel ? "the Kernel" : dso->short_name) < 0)
1553 return 0;
1554
1555 act->dso = dso;
1556 act->fn = do_zoom_dso;
1557 return 1;
1558}
1559
1560static int
1561do_browse_map(struct hist_browser *browser __maybe_unused,
1562 struct popup_action *act)
1563{
1564 map__browse(act->ms.map);
bc7cad42
NK
1565 return 0;
1566}
1567
ea7cd592
NK
1568static int
1569add_map_opt(struct hist_browser *browser __maybe_unused,
1570 struct popup_action *act, char **optstr, struct map *map)
1571{
1572 if (map == NULL)
1573 return 0;
1574
1575 if (asprintf(optstr, "Browse map details") < 0)
1576 return 0;
1577
1578 act->ms.map = map;
1579 act->fn = do_browse_map;
1580 return 1;
1581}
1582
bc7cad42
NK
1583static int
1584do_run_script(struct hist_browser *browser __maybe_unused,
ea7cd592 1585 struct popup_action *act)
bc7cad42
NK
1586{
1587 char script_opt[64];
1588 memset(script_opt, 0, sizeof(script_opt));
1589
ea7cd592 1590 if (act->thread) {
bc7cad42 1591 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
ea7cd592
NK
1592 thread__comm_str(act->thread));
1593 } else if (act->ms.sym) {
bc7cad42 1594 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
ea7cd592 1595 act->ms.sym->name);
bc7cad42
NK
1596 }
1597
1598 script_browse(script_opt);
1599 return 0;
1600}
1601
1602static int
ea7cd592
NK
1603add_script_opt(struct hist_browser *browser __maybe_unused,
1604 struct popup_action *act, char **optstr,
1605 struct thread *thread, struct symbol *sym)
1606{
1607 if (thread) {
1608 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1609 thread__comm_str(thread)) < 0)
1610 return 0;
1611 } else if (sym) {
1612 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1613 sym->name) < 0)
1614 return 0;
1615 } else {
1616 if (asprintf(optstr, "Run scripts for all samples") < 0)
1617 return 0;
1618 }
1619
1620 act->thread = thread;
1621 act->ms.sym = sym;
1622 act->fn = do_run_script;
1623 return 1;
1624}
1625
1626static int
1627do_switch_data(struct hist_browser *browser __maybe_unused,
1628 struct popup_action *act __maybe_unused)
bc7cad42
NK
1629{
1630 if (switch_data_file()) {
1631 ui__warning("Won't switch the data files due to\n"
1632 "no valid data file get selected!\n");
ea7cd592 1633 return 0;
bc7cad42
NK
1634 }
1635
1636 return K_SWITCH_INPUT_DATA;
1637}
1638
ea7cd592
NK
1639static int
1640add_switch_opt(struct hist_browser *browser,
1641 struct popup_action *act, char **optstr)
1642{
1643 if (!is_report_browser(browser->hbt))
1644 return 0;
1645
1646 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1647 return 0;
1648
1649 act->fn = do_switch_data;
1650 return 1;
1651}
1652
1653static int
1654do_exit_browser(struct hist_browser *browser __maybe_unused,
1655 struct popup_action *act __maybe_unused)
1656{
1657 return 0;
1658}
1659
1660static int
1661add_exit_opt(struct hist_browser *browser __maybe_unused,
1662 struct popup_action *act, char **optstr)
1663{
1664 if (asprintf(optstr, "Exit") < 0)
1665 return 0;
1666
1667 act->fn = do_exit_browser;
1668 return 1;
1669}
1670
112f761f 1671static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
1672{
1673 u64 nr_entries = 0;
1674 struct rb_node *nd = rb_first(&hb->hists->entries);
1675
268397cb
NK
1676 if (hb->min_pcnt == 0) {
1677 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1678 return;
1679 }
1680
14135663 1681 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 1682 nr_entries++;
c481f930 1683 nd = rb_next(nd);
064f1981
NK
1684 }
1685
112f761f 1686 hb->nr_non_filtered_entries = nr_entries;
064f1981 1687}
341487ab 1688
34958544 1689static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 1690 const char *helpline,
81cce8de 1691 bool left_exits,
68d80758 1692 struct hist_browser_timer *hbt,
064f1981 1693 float min_pcnt,
68d80758 1694 struct perf_session_env *env)
d1b4f249 1695{
4ea062ed 1696 struct hists *hists = evsel__hists(evsel);
b1a9ceef 1697 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
a68c2c58 1698 struct branch_info *bi;
f2b487db
NK
1699#define MAX_OPTIONS 16
1700 char *options[MAX_OPTIONS];
ea7cd592 1701 struct popup_action actions[MAX_OPTIONS];
24bff2dc 1702 int nr_options = 0;
d1b4f249 1703 int key = -1;
938a23ae 1704 char buf[64];
9783adf7 1705 int delay_secs = hbt ? hbt->refresh : 0;
59dc9f25 1706 struct perf_hpp_fmt *fmt;
d1b4f249 1707
e8e684a5
NK
1708#define HIST_BROWSER_HELP_COMMON \
1709 "h/?/F1 Show this window\n" \
1710 "UP/DOWN/PGUP\n" \
1711 "PGDN/SPACE Navigate\n" \
1712 "q/ESC/CTRL+C Exit browser\n\n" \
1713 "For multiple event sessions:\n\n" \
1714 "TAB/UNTAB Switch events\n\n" \
1715 "For symbolic views (--sort has sym):\n\n" \
1716 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1717 "<- Zoom out\n" \
1718 "a Annotate current symbol\n" \
1719 "C Collapse all callchains\n" \
1720 "d Zoom into current DSO\n" \
1721 "E Expand all callchains\n" \
105eb30f 1722 "F Toggle percentage of filtered entries\n" \
025bf7ea 1723 "H Display column headers\n" \
e8e684a5
NK
1724
1725 /* help messages are sorted by lexical order of the hotkey */
1726 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 1727 "i Show header information\n"
e8e684a5
NK
1728 "P Print histograms to perf.hist.N\n"
1729 "r Run available scripts\n"
1730 "s Switch to another data file in PWD\n"
1731 "t Zoom into current Thread\n"
1732 "V Verbose (DSO names in callchains, etc)\n"
1733 "/ Filter symbol by name";
1734 const char top_help[] = HIST_BROWSER_HELP_COMMON
1735 "P Print histograms to perf.hist.N\n"
1736 "t Zoom into current Thread\n"
1737 "V Verbose (DSO names in callchains, etc)\n"
42337a22 1738 "z Toggle zeroing of samples\n"
fbb7997e 1739 "f Enable/Disable events\n"
e8e684a5
NK
1740 "/ Filter symbol by name";
1741
d1b4f249
ACM
1742 if (browser == NULL)
1743 return -1;
1744
ed426915
NK
1745 /* reset abort key so that it can get Ctrl-C as a key */
1746 SLang_reset_tty();
1747 SLang_init_tty(0, 0, 0);
1748
064f1981
NK
1749 if (min_pcnt) {
1750 browser->min_pcnt = min_pcnt;
112f761f 1751 hist_browser__update_nr_entries(browser);
064f1981
NK
1752 }
1753
01f00a1c
NK
1754 browser->pstack = pstack__new(2);
1755 if (browser->pstack == NULL)
d1b4f249
ACM
1756 goto out;
1757
1758 ui_helpline__push(helpline);
1759
24bff2dc 1760 memset(options, 0, sizeof(options));
ea7cd592 1761 memset(actions, 0, sizeof(actions));
24bff2dc 1762
59dc9f25
NK
1763 perf_hpp__for_each_format(fmt)
1764 perf_hpp__reset_width(fmt, hists);
1765
5b591669
NK
1766 if (symbol_conf.col_width_list_str)
1767 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1768
d1b4f249 1769 while (1) {
f3b623b8 1770 struct thread *thread = NULL;
bc7cad42 1771 struct dso *dso = NULL;
ea7cd592 1772 int choice = 0;
d1b4f249 1773
24bff2dc
SE
1774 nr_options = 0;
1775
c2a51ab8 1776 key = hist_browser__run(browser);
d1b4f249 1777
60098917
ACM
1778 if (browser->he_selection != NULL) {
1779 thread = hist_browser__selected_thread(browser);
1780 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1781 }
b50e003d 1782 switch (key) {
cf958003
ACM
1783 case K_TAB:
1784 case K_UNTAB:
e4419b8e
DA
1785 if (nr_events == 1)
1786 continue;
b50e003d
ACM
1787 /*
1788 * Exit the browser, let hists__browser_tree
1789 * go to the next or previous
1790 */
1791 goto out_free_stack;
1792 case 'a':
9c796ec8 1793 if (!sort__has_sym) {
7b27509f 1794 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1795 "Annotation is only available for symbolic views, "
a68c2c58 1796 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1797 continue;
1798 }
1799
60098917 1800 if (browser->selection == NULL ||
db9a9cbc 1801 browser->selection->sym == NULL ||
b50e003d 1802 browser->selection->map->dso->annotate_warned)
d1b4f249 1803 continue;
bc7cad42 1804
ea7cd592
NK
1805 actions->ms.map = browser->selection->map;
1806 actions->ms.sym = browser->selection->sym;
1807 do_annotate(browser, actions);
bc7cad42 1808 continue;
aff3f3f6
ACM
1809 case 'P':
1810 hist_browser__dump(browser);
1811 continue;
b50e003d 1812 case 'd':
ea7cd592
NK
1813 actions->dso = dso;
1814 do_zoom_dso(browser, actions);
bc7cad42 1815 continue;
a7cb8863
ACM
1816 case 'V':
1817 browser->show_dso = !browser->show_dso;
1818 continue;
b50e003d 1819 case 't':
ea7cd592
NK
1820 actions->thread = thread;
1821 do_zoom_thread(browser, actions);
bc7cad42 1822 continue;
5a5626b1 1823 case '/':
938a23ae
NK
1824 if (ui_browser__input_window("Symbol to show",
1825 "Please enter the name of symbol you want to see",
1826 buf, "ENTER: OK, ESC: Cancel",
1827 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1828 hists->symbol_filter_str = *buf ? buf : NULL;
1829 hists__filter_by_symbol(hists);
938a23ae
NK
1830 hist_browser__reset(browser);
1831 }
1832 continue;
cdbab7c2 1833 case 'r':
ea7cd592
NK
1834 if (is_report_browser(hbt)) {
1835 actions->thread = NULL;
1836 actions->ms.sym = NULL;
1837 do_run_script(browser, actions);
1838 }
c77d8d70 1839 continue;
341487ab 1840 case 's':
bc7cad42 1841 if (is_report_browser(hbt)) {
ea7cd592 1842 key = do_switch_data(browser, actions);
bc7cad42
NK
1843 if (key == K_SWITCH_INPUT_DATA)
1844 goto out_free_stack;
1845 }
341487ab 1846 continue;
6dd60135
NK
1847 case 'i':
1848 /* env->arch is NULL for live-mode (i.e. perf top) */
1849 if (env->arch)
1850 tui__header_window(env);
1851 continue;
105eb30f
NK
1852 case 'F':
1853 symbol_conf.filter_relative ^= 1;
1854 continue;
42337a22
NK
1855 case 'z':
1856 if (!is_report_browser(hbt)) {
1857 struct perf_top *top = hbt->arg;
1858
1859 top->zero = !top->zero;
1860 }
1861 continue;
cf958003 1862 case K_F1:
b50e003d
ACM
1863 case 'h':
1864 case '?':
4610e413 1865 ui_browser__help_window(&browser->b,
e8e684a5 1866 is_report_browser(hbt) ? report_help : top_help);
b50e003d 1867 continue;
cf958003
ACM
1868 case K_ENTER:
1869 case K_RIGHT:
b50e003d
ACM
1870 /* menu */
1871 break;
cf958003 1872 case K_LEFT: {
b50e003d 1873 const void *top;
d1b4f249 1874
01f00a1c 1875 if (pstack__empty(browser->pstack)) {
7f0030b2
ACM
1876 /*
1877 * Go back to the perf_evsel_menu__run or other user
1878 */
1879 if (left_exits)
1880 goto out_free_stack;
d1b4f249 1881 continue;
7f0030b2 1882 }
6422184b 1883 top = pstack__peek(browser->pstack);
bc7cad42 1884 if (top == &browser->hists->dso_filter) {
6422184b
NK
1885 /*
1886 * No need to set actions->dso here since
1887 * it's just to remove the current filter.
1888 * Ditto for thread below.
1889 */
1890 do_zoom_dso(browser, actions);
bc7cad42 1891 }
6422184b
NK
1892 if (top == &browser->hists->thread_filter)
1893 do_zoom_thread(browser, actions);
b50e003d
ACM
1894 continue;
1895 }
cf958003 1896 case K_ESC:
7f0030b2 1897 if (!left_exits &&
4610e413
ACM
1898 !ui_browser__dialog_yesno(&browser->b,
1899 "Do you really want to exit?"))
b50e003d
ACM
1900 continue;
1901 /* Fall thru */
ed7e5662
ACM
1902 case 'q':
1903 case CTRL('c'):
fbb7997e 1904 case 'f':
b50e003d 1905 goto out_free_stack;
ed7e5662
ACM
1906 default:
1907 continue;
d1b4f249
ACM
1908 }
1909
9c796ec8 1910 if (!sort__has_sym)
724c9c9f
ACM
1911 goto add_exit_option;
1912
0ba332f7
ACM
1913 if (browser->selection == NULL)
1914 goto skip_annotation;
1915
55369fc1 1916 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58 1917 bi = browser->he_selection->branch_info;
0ba332f7
ACM
1918
1919 if (bi == NULL)
1920 goto skip_annotation;
1921
ea7cd592
NK
1922 nr_options += add_annotate_opt(browser,
1923 &actions[nr_options],
1924 &options[nr_options],
1925 bi->from.map,
1926 bi->from.sym);
1927 if (bi->to.sym != bi->from.sym)
1928 nr_options += add_annotate_opt(browser,
1929 &actions[nr_options],
1930 &options[nr_options],
1931 bi->to.map,
1932 bi->to.sym);
a68c2c58 1933 } else {
ea7cd592
NK
1934 nr_options += add_annotate_opt(browser,
1935 &actions[nr_options],
1936 &options[nr_options],
1937 browser->selection->map,
1938 browser->selection->sym);
a68c2c58 1939 }
0ba332f7 1940skip_annotation:
ea7cd592
NK
1941 nr_options += add_thread_opt(browser, &actions[nr_options],
1942 &options[nr_options], thread);
1943 nr_options += add_dso_opt(browser, &actions[nr_options],
1944 &options[nr_options], dso);
1945 nr_options += add_map_opt(browser, &actions[nr_options],
1946 &options[nr_options],
1947 browser->selection->map);
cdbab7c2
FT
1948
1949 /* perf script support */
1950 if (browser->he_selection) {
ea7cd592
NK
1951 nr_options += add_script_opt(browser,
1952 &actions[nr_options],
1953 &options[nr_options],
1954 thread, NULL);
1955 nr_options += add_script_opt(browser,
1956 &actions[nr_options],
1957 &options[nr_options],
1958 NULL, browser->selection->sym);
cdbab7c2 1959 }
ea7cd592
NK
1960 nr_options += add_script_opt(browser, &actions[nr_options],
1961 &options[nr_options], NULL, NULL);
1962 nr_options += add_switch_opt(browser, &actions[nr_options],
1963 &options[nr_options]);
724c9c9f 1964add_exit_option:
ea7cd592
NK
1965 nr_options += add_exit_opt(browser, &actions[nr_options],
1966 &options[nr_options]);
d1b4f249 1967
ea7cd592
NK
1968 do {
1969 struct popup_action *act;
68d80758 1970
ea7cd592
NK
1971 choice = ui__popup_menu(nr_options, options);
1972 if (choice == -1 || choice >= nr_options)
1973 break;
a68c2c58 1974
ea7cd592
NK
1975 act = &actions[choice];
1976 key = act->fn(browser, act);
1977 } while (key == 1);
a68c2c58 1978
ea7cd592
NK
1979 if (key == K_SWITCH_INPUT_DATA)
1980 break;
d1b4f249
ACM
1981 }
1982out_free_stack:
01f00a1c 1983 pstack__delete(browser->pstack);
d1b4f249
ACM
1984out:
1985 hist_browser__delete(browser);
f2b487db 1986 free_popup_options(options, MAX_OPTIONS);
d1b4f249
ACM
1987 return key;
1988}
1989
7f0030b2
ACM
1990struct perf_evsel_menu {
1991 struct ui_browser b;
1992 struct perf_evsel *selection;
7b27509f 1993 bool lost_events, lost_events_warned;
064f1981 1994 float min_pcnt;
68d80758 1995 struct perf_session_env *env;
7f0030b2
ACM
1996};
1997
1998static void perf_evsel_menu__write(struct ui_browser *browser,
1999 void *entry, int row)
2000{
2001 struct perf_evsel_menu *menu = container_of(browser,
2002 struct perf_evsel_menu, b);
2003 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
4ea062ed 2004 struct hists *hists = evsel__hists(evsel);
7f0030b2 2005 bool current_entry = ui_browser__is_current_entry(browser, row);
4ea062ed 2006 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 2007 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 2008 char bf[256], unit;
7b27509f
ACM
2009 const char *warn = " ";
2010 size_t printed;
7f0030b2
ACM
2011
2012 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2013 HE_COLORSET_NORMAL);
2014
759ff497 2015 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
2016 struct perf_evsel *pos;
2017
2018 ev_name = perf_evsel__group_name(evsel);
2019
2020 for_each_group_member(pos, evsel) {
4ea062ed
ACM
2021 struct hists *pos_hists = evsel__hists(pos);
2022 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
717e263f
NK
2023 }
2024 }
2025
7f0030b2 2026 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 2027 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
2028 unit, unit == ' ' ? "" : " ", ev_name);
2029 slsmg_printf("%s", bf);
2030
4ea062ed 2031 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
7b27509f
ACM
2032 if (nr_events != 0) {
2033 menu->lost_events = true;
2034 if (!current_entry)
2035 ui_browser__set_color(browser, HE_COLORSET_TOP);
2036 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
2037 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2038 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
2039 warn = bf;
2040 }
2041
2042 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
2043
2044 if (current_entry)
2045 menu->selection = evsel;
2046}
2047
34958544
ACM
2048static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2049 int nr_events, const char *help,
9783adf7 2050 struct hist_browser_timer *hbt)
d1b4f249 2051{
7f0030b2 2052 struct perf_evlist *evlist = menu->b.priv;
e248de33 2053 struct perf_evsel *pos;
dd00d486 2054 const char *title = "Available samples";
9783adf7 2055 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 2056 int key;
d1b4f249 2057
7f0030b2
ACM
2058 if (ui_browser__show(&menu->b, title,
2059 "ESC: exit, ENTER|->: Browse histograms") < 0)
2060 return -1;
2061
7f0030b2 2062 while (1) {
3af6e338 2063 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
2064
2065 switch (key) {
cf958003 2066 case K_TIMER:
9783adf7 2067 hbt->timer(hbt->arg);
7b27509f
ACM
2068
2069 if (!menu->lost_events_warned && menu->lost_events) {
2070 ui_browser__warn_lost_events(&menu->b);
2071 menu->lost_events_warned = true;
2072 }
81cce8de 2073 continue;
cf958003
ACM
2074 case K_RIGHT:
2075 case K_ENTER:
7f0030b2
ACM
2076 if (!menu->selection)
2077 continue;
2078 pos = menu->selection;
2079browse_hists:
18eaf0b8
ACM
2080 perf_evlist__set_selected(evlist, pos);
2081 /*
2082 * Give the calling tool a chance to populate the non
2083 * default evsel resorted hists tree.
2084 */
9783adf7
NK
2085 if (hbt)
2086 hbt->timer(hbt->arg);
34958544 2087 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 2088 true, hbt,
064f1981 2089 menu->min_pcnt,
68d80758 2090 menu->env);
7f0030b2 2091 ui_browser__show_title(&menu->b, title);
18eaf0b8 2092 switch (key) {
cf958003 2093 case K_TAB:
18eaf0b8 2094 if (pos->node.next == &evlist->entries)
9a354cdc 2095 pos = perf_evlist__first(evlist);
18eaf0b8 2096 else
9a354cdc 2097 pos = perf_evsel__next(pos);
18eaf0b8 2098 goto browse_hists;
cf958003 2099 case K_UNTAB:
18eaf0b8 2100 if (pos->node.prev == &evlist->entries)
9a354cdc 2101 pos = perf_evlist__last(evlist);
18eaf0b8 2102 else
d87fcb4a 2103 pos = perf_evsel__prev(pos);
18eaf0b8 2104 goto browse_hists;
cf958003 2105 case K_ESC:
4610e413
ACM
2106 if (!ui_browser__dialog_yesno(&menu->b,
2107 "Do you really want to exit?"))
18eaf0b8
ACM
2108 continue;
2109 /* Fall thru */
341487ab 2110 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
2111 case 'q':
2112 case CTRL('c'):
2113 goto out;
2114 default:
2115 continue;
2116 }
cf958003 2117 case K_LEFT:
7f0030b2 2118 continue;
cf958003 2119 case K_ESC:
4610e413
ACM
2120 if (!ui_browser__dialog_yesno(&menu->b,
2121 "Do you really want to exit?"))
ed7e5662
ACM
2122 continue;
2123 /* Fall thru */
7f0030b2
ACM
2124 case 'q':
2125 case CTRL('c'):
2126 goto out;
d1b4f249 2127 default:
18eaf0b8 2128 continue;
d1b4f249
ACM
2129 }
2130 }
2131
7f0030b2
ACM
2132out:
2133 ui_browser__hide(&menu->b);
2134 return key;
2135}
2136
316c7136 2137static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
2138 void *entry)
2139{
2140 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2141
2142 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2143 return true;
2144
2145 return false;
2146}
2147
7f0030b2 2148static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 2149 int nr_entries, const char *help,
68d80758 2150 struct hist_browser_timer *hbt,
064f1981 2151 float min_pcnt,
68d80758 2152 struct perf_session_env *env)
7f0030b2
ACM
2153{
2154 struct perf_evsel *pos;
2155 struct perf_evsel_menu menu = {
2156 .b = {
2157 .entries = &evlist->entries,
2158 .refresh = ui_browser__list_head_refresh,
2159 .seek = ui_browser__list_head_seek,
2160 .write = perf_evsel_menu__write,
fc24d7c2
NK
2161 .filter = filter_group_entries,
2162 .nr_entries = nr_entries,
7f0030b2
ACM
2163 .priv = evlist,
2164 },
064f1981 2165 .min_pcnt = min_pcnt,
68d80758 2166 .env = env,
7f0030b2
ACM
2167 };
2168
2169 ui_helpline__push("Press ESC to exit");
2170
0050f7aa 2171 evlist__for_each(evlist, pos) {
7289f83c 2172 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
2173 size_t line_len = strlen(ev_name) + 7;
2174
2175 if (menu.b.width < line_len)
2176 menu.b.width = line_len;
7f0030b2
ACM
2177 }
2178
fc24d7c2 2179 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
2180}
2181
81cce8de 2182int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 2183 struct hist_browser_timer *hbt,
064f1981 2184 float min_pcnt,
68d80758 2185 struct perf_session_env *env)
7f0030b2 2186{
fc24d7c2
NK
2187 int nr_entries = evlist->nr_entries;
2188
2189single_entry:
2190 if (nr_entries == 1) {
9a354cdc 2191 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
2192
2193 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 2194 false, hbt, min_pcnt,
064f1981 2195 env);
7f0030b2
ACM
2196 }
2197
fc24d7c2
NK
2198 if (symbol_conf.event_group) {
2199 struct perf_evsel *pos;
2200
2201 nr_entries = 0;
0050f7aa 2202 evlist__for_each(evlist, pos) {
fc24d7c2
NK
2203 if (perf_evsel__is_group_leader(pos))
2204 nr_entries++;
0050f7aa 2205 }
fc24d7c2
NK
2206
2207 if (nr_entries == 1)
2208 goto single_entry;
2209 }
2210
2211 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 2212 hbt, min_pcnt, env);
d1b4f249 2213}
This page took 0.296347 seconds and 5 git commands to generate.