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