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