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