perf hists browser: Remove superfluous null check on map
[deliverable/linux.git] / tools / perf / ui / browsers / hists.c
CommitLineData
d1b4f249 1#include <stdio.h>
d1b4f249
ACM
2#include <stdlib.h>
3#include <string.h>
d1b4f249
ACM
4#include <linux/rbtree.h>
5
aca7a94d
NK
6#include "../../util/evsel.h"
7#include "../../util/evlist.h"
8#include "../../util/hist.h"
9#include "../../util/pstack.h"
10#include "../../util/sort.h"
11#include "../../util/util.h"
42337a22 12#include "../../util/top.h"
68d80758 13#include "../../arch/common.h"
d1b4f249 14
f758990f 15#include "../browsers/hists.h"
d1b4f249
ACM
16#include "../helpline.h"
17#include "../util.h"
4610e413 18#include "../ui.h"
d1b4f249 19#include "map.h"
d755330c 20#include "annotate.h"
d1b4f249 21
f5951d56
NK
22extern void hist_browser__init_hpp(void);
23
5b91a86f
JO
24static int perf_evsel_browser_title(struct hist_browser *browser,
25 char *bf, size_t size);
112f761f 26static void hist_browser__update_nr_entries(struct hist_browser *hb);
81cce8de 27
c3b78952 28static struct rb_node *hists__filter_entries(struct rb_node *nd,
c3b78952
NK
29 float min_pcnt);
30
268397cb
NK
31static bool hist_browser__has_filter(struct hist_browser *hb)
32{
9c0fa8dd 33 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
268397cb
NK
34}
35
4fabf3d1
HK
36static int hist_browser__get_folding(struct hist_browser *browser)
37{
38 struct rb_node *nd;
39 struct hists *hists = browser->hists;
40 int unfolded_rows = 0;
41
42 for (nd = rb_first(&hists->entries);
43 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
f5b763fe 44 nd = rb_hierarchy_next(nd)) {
4fabf3d1
HK
45 struct hist_entry *he =
46 rb_entry(nd, struct hist_entry, rb_node);
47
f5b763fe 48 if (he->leaf && he->unfolded)
4fabf3d1
HK
49 unfolded_rows += he->nr_rows;
50 }
51 return unfolded_rows;
52}
53
c3b78952
NK
54static u32 hist_browser__nr_entries(struct hist_browser *hb)
55{
56 u32 nr_entries;
57
f5b763fe
NK
58 if (symbol_conf.report_hierarchy)
59 nr_entries = hb->nr_hierarchy_entries;
60 else if (hist_browser__has_filter(hb))
c3b78952
NK
61 nr_entries = hb->nr_non_filtered_entries;
62 else
63 nr_entries = hb->hists->nr_entries;
64
4fabf3d1 65 hb->nr_callchain_rows = hist_browser__get_folding(hb);
c3b78952
NK
66 return nr_entries + hb->nr_callchain_rows;
67}
68
025bf7ea
ACM
69static void hist_browser__update_rows(struct hist_browser *hb)
70{
71 struct ui_browser *browser = &hb->b;
f8e6710d
JO
72 struct hists *hists = hb->hists;
73 struct perf_hpp_list *hpp_list = hists->hpp_list;
74 u16 header_offset, index_row;
025bf7ea 75
f8e6710d 76 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
025bf7ea
ACM
77 browser->rows = browser->height - header_offset;
78 /*
79 * Verify if we were at the last line and that line isn't
80 * visibe because we now show the header line(s).
81 */
82 index_row = browser->index - browser->top_idx;
83 if (index_row >= browser->rows)
84 browser->index -= index_row - browser->rows + 1;
85}
86
357cfff1 87static void hist_browser__refresh_dimensions(struct ui_browser *browser)
d1b4f249 88{
357cfff1
ACM
89 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
90
d1b4f249 91 /* 3 == +/- toggle symbol before actual hist_entry rendering */
357cfff1
ACM
92 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
93 /*
94 * FIXME: Just keeping existing behaviour, but this really should be
95 * before updating browser->width, as it will invalidate the
96 * calculation above. Fix this and the fallout in another
97 * changeset.
98 */
99 ui_browser__refresh_dimensions(browser);
025bf7ea 100 hist_browser__update_rows(hb);
d1b4f249
ACM
101}
102
ca3ff33b
ACM
103static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
104{
f8e6710d
JO
105 struct hists *hists = browser->hists;
106 struct perf_hpp_list *hpp_list = hists->hpp_list;
107 u16 header_offset;
025bf7ea 108
f8e6710d 109 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
025bf7ea 110 ui_browser__gotorc(&browser->b, row + header_offset, column);
ca3ff33b
ACM
111}
112
05e8b080 113static void hist_browser__reset(struct hist_browser *browser)
d1b4f249 114{
c3b78952
NK
115 /*
116 * The hists__remove_entry_filter() already folds non-filtered
117 * entries so we can assume it has 0 callchain rows.
118 */
119 browser->nr_callchain_rows = 0;
120
268397cb 121 hist_browser__update_nr_entries(browser);
c3b78952 122 browser->b.nr_entries = hist_browser__nr_entries(browser);
357cfff1 123 hist_browser__refresh_dimensions(&browser->b);
05e8b080 124 ui_browser__reset_index(&browser->b);
d1b4f249
ACM
125}
126
127static char tree__folded_sign(bool unfolded)
128{
129 return unfolded ? '-' : '+';
130}
131
05e8b080 132static char hist_entry__folded(const struct hist_entry *he)
d1b4f249 133{
3698dab1 134 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
d1b4f249
ACM
135}
136
05e8b080 137static char callchain_list__folded(const struct callchain_list *cl)
d1b4f249 138{
3698dab1 139 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
d1b4f249
ACM
140}
141
3698dab1 142static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
3c916cc2 143{
3698dab1 144 cl->unfolded = unfold ? cl->has_children : false;
3c916cc2
ACM
145}
146
05e8b080 147static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
d1b4f249
ACM
148{
149 int n = 0;
150 struct rb_node *nd;
151
05e8b080 152 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
153 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
154 struct callchain_list *chain;
155 char folded_sign = ' '; /* No children */
156
157 list_for_each_entry(chain, &child->val, list) {
158 ++n;
159 /* We need this because we may not have children */
160 folded_sign = callchain_list__folded(chain);
161 if (folded_sign == '+')
162 break;
163 }
164
165 if (folded_sign == '-') /* Have children and they're unfolded */
166 n += callchain_node__count_rows_rb_tree(child);
167 }
168
169 return n;
170}
171
4b3a3212
NK
172static int callchain_node__count_flat_rows(struct callchain_node *node)
173{
174 struct callchain_list *chain;
175 char folded_sign = 0;
176 int n = 0;
177
178 list_for_each_entry(chain, &node->parent_val, list) {
179 if (!folded_sign) {
180 /* only check first chain list entry */
181 folded_sign = callchain_list__folded(chain);
182 if (folded_sign == '+')
183 return 1;
184 }
185 n++;
186 }
187
188 list_for_each_entry(chain, &node->val, list) {
189 if (!folded_sign) {
190 /* node->parent_val list might be empty */
191 folded_sign = callchain_list__folded(chain);
192 if (folded_sign == '+')
193 return 1;
194 }
195 n++;
196 }
197
198 return n;
199}
200
8c430a34
NK
201static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
202{
203 return 1;
204}
205
d1b4f249
ACM
206static int callchain_node__count_rows(struct callchain_node *node)
207{
208 struct callchain_list *chain;
209 bool unfolded = false;
210 int n = 0;
211
4b3a3212
NK
212 if (callchain_param.mode == CHAIN_FLAT)
213 return callchain_node__count_flat_rows(node);
8c430a34
NK
214 else if (callchain_param.mode == CHAIN_FOLDED)
215 return callchain_node__count_folded_rows(node);
4b3a3212 216
d1b4f249
ACM
217 list_for_each_entry(chain, &node->val, list) {
218 ++n;
3698dab1 219 unfolded = chain->unfolded;
d1b4f249
ACM
220 }
221
222 if (unfolded)
223 n += callchain_node__count_rows_rb_tree(node);
224
225 return n;
226}
227
228static int callchain__count_rows(struct rb_root *chain)
229{
230 struct rb_node *nd;
231 int n = 0;
232
233 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
234 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
235 n += callchain_node__count_rows(node);
236 }
237
238 return n;
239}
240
f5b763fe
NK
241static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
242 bool include_children)
243{
244 int count = 0;
245 struct rb_node *node;
246 struct hist_entry *child;
247
248 if (he->leaf)
249 return callchain__count_rows(&he->sorted_chain);
250
79dded87
NK
251 if (he->has_no_entry)
252 return 1;
253
f5b763fe
NK
254 node = rb_first(&he->hroot_out);
255 while (node) {
256 float percent;
257
258 child = rb_entry(node, struct hist_entry, rb_node);
259 percent = hist_entry__get_percent_limit(child);
260
261 if (!child->filtered && percent >= hb->min_pcnt) {
262 count++;
263
264 if (include_children && child->unfolded)
265 count += hierarchy_count_rows(hb, child, true);
266 }
267
268 node = rb_next(node);
269 }
270 return count;
271}
272
3698dab1 273static bool hist_entry__toggle_fold(struct hist_entry *he)
d1b4f249 274{
3698dab1 275 if (!he)
8493fe1d
JO
276 return false;
277
3698dab1 278 if (!he->has_children)
d1b4f249
ACM
279 return false;
280
3698dab1
NK
281 he->unfolded = !he->unfolded;
282 return true;
283}
284
285static bool callchain_list__toggle_fold(struct callchain_list *cl)
286{
287 if (!cl)
288 return false;
289
290 if (!cl->has_children)
291 return false;
292
293 cl->unfolded = !cl->unfolded;
d1b4f249
ACM
294 return true;
295}
296
05e8b080 297static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
d1b4f249 298{
05e8b080 299 struct rb_node *nd = rb_first(&node->rb_root);
d1b4f249 300
05e8b080 301 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
302 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
303 struct callchain_list *chain;
293db47f 304 bool first = true;
d1b4f249
ACM
305
306 list_for_each_entry(chain, &child->val, list) {
307 if (first) {
308 first = false;
3698dab1 309 chain->has_children = chain->list.next != &child->val ||
293db47f 310 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249 311 } else
3698dab1 312 chain->has_children = chain->list.next == &child->val &&
293db47f 313 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
314 }
315
316 callchain_node__init_have_children_rb_tree(child);
317 }
318}
319
a7444af6
NK
320static void callchain_node__init_have_children(struct callchain_node *node,
321 bool has_sibling)
d1b4f249
ACM
322{
323 struct callchain_list *chain;
324
a7444af6 325 chain = list_entry(node->val.next, struct callchain_list, list);
3698dab1 326 chain->has_children = has_sibling;
a7444af6 327
90989035 328 if (!list_empty(&node->val)) {
82162b5a 329 chain = list_entry(node->val.prev, struct callchain_list, list);
3698dab1 330 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
82162b5a 331 }
d1b4f249 332
05e8b080 333 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
334}
335
05e8b080 336static void callchain__init_have_children(struct rb_root *root)
d1b4f249 337{
a7444af6
NK
338 struct rb_node *nd = rb_first(root);
339 bool has_sibling = nd && rb_next(nd);
d1b4f249 340
05e8b080 341 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249 342 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
a7444af6 343 callchain_node__init_have_children(node, has_sibling);
8c430a34
NK
344 if (callchain_param.mode == CHAIN_FLAT ||
345 callchain_param.mode == CHAIN_FOLDED)
4b3a3212 346 callchain_node__make_parent_list(node);
d1b4f249
ACM
347 }
348}
349
05e8b080 350static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 351{
f5b763fe
NK
352 if (he->init_have_children)
353 return;
354
355 if (he->leaf) {
3698dab1 356 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
05e8b080 357 callchain__init_have_children(&he->sorted_chain);
f5b763fe
NK
358 } else {
359 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
d1b4f249 360 }
f5b763fe
NK
361
362 he->init_have_children = true;
d1b4f249
ACM
363}
364
05e8b080 365static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 366{
3698dab1
NK
367 struct hist_entry *he = browser->he_selection;
368 struct map_symbol *ms = browser->selection;
369 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
370 bool has_children;
371
4938cf0c
WN
372 if (!he || !ms)
373 return false;
374
3698dab1
NK
375 if (ms == &he->ms)
376 has_children = hist_entry__toggle_fold(he);
377 else
378 has_children = callchain_list__toggle_fold(cl);
d1b4f249 379
3698dab1 380 if (has_children) {
f5b763fe
NK
381 int child_rows = 0;
382
d1b4f249 383 hist_entry__init_have_children(he);
c3b78952 384 browser->b.nr_entries -= he->nr_rows;
d1b4f249 385
f5b763fe
NK
386 if (he->leaf)
387 browser->nr_callchain_rows -= he->nr_rows;
d1b4f249 388 else
f5b763fe
NK
389 browser->nr_hierarchy_entries -= he->nr_rows;
390
391 if (symbol_conf.report_hierarchy)
392 child_rows = hierarchy_count_rows(browser, he, true);
393
394 if (he->unfolded) {
395 if (he->leaf)
396 he->nr_rows = callchain__count_rows(&he->sorted_chain);
397 else
398 he->nr_rows = hierarchy_count_rows(browser, he, false);
399
400 /* account grand children */
401 if (symbol_conf.report_hierarchy)
402 browser->b.nr_entries += child_rows - he->nr_rows;
79dded87
NK
403
404 if (!he->leaf && he->nr_rows == 0) {
405 he->has_no_entry = true;
406 he->nr_rows = 1;
407 }
f5b763fe
NK
408 } else {
409 if (symbol_conf.report_hierarchy)
410 browser->b.nr_entries -= child_rows - he->nr_rows;
411
79dded87
NK
412 if (he->has_no_entry)
413 he->has_no_entry = false;
414
d1b4f249 415 he->nr_rows = 0;
f5b763fe 416 }
c3b78952
NK
417
418 browser->b.nr_entries += he->nr_rows;
f5b763fe
NK
419
420 if (he->leaf)
421 browser->nr_callchain_rows += he->nr_rows;
422 else
423 browser->nr_hierarchy_entries += he->nr_rows;
d1b4f249
ACM
424
425 return true;
426 }
427
428 /* If it doesn't have children, no toggling performed */
429 return false;
430}
431
05e8b080 432static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
433{
434 int n = 0;
435 struct rb_node *nd;
436
05e8b080 437 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
439 struct callchain_list *chain;
440 bool has_children = false;
441
442 list_for_each_entry(chain, &child->val, list) {
443 ++n;
3698dab1
NK
444 callchain_list__set_folding(chain, unfold);
445 has_children = chain->has_children;
3c916cc2
ACM
446 }
447
448 if (has_children)
449 n += callchain_node__set_folding_rb_tree(child, unfold);
450 }
451
452 return n;
453}
454
455static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
456{
457 struct callchain_list *chain;
458 bool has_children = false;
459 int n = 0;
460
461 list_for_each_entry(chain, &node->val, list) {
462 ++n;
3698dab1
NK
463 callchain_list__set_folding(chain, unfold);
464 has_children = chain->has_children;
3c916cc2
ACM
465 }
466
467 if (has_children)
468 n += callchain_node__set_folding_rb_tree(node, unfold);
469
470 return n;
471}
472
473static int callchain__set_folding(struct rb_root *chain, bool unfold)
474{
475 struct rb_node *nd;
476 int n = 0;
477
478 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
479 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
480 n += callchain_node__set_folding(node, unfold);
481 }
482
483 return n;
484}
485
492b1010
NK
486static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
487 bool unfold __maybe_unused)
488{
489 float percent;
490 struct rb_node *nd;
491 struct hist_entry *child;
492 int n = 0;
493
494 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
495 child = rb_entry(nd, struct hist_entry, rb_node);
496 percent = hist_entry__get_percent_limit(child);
497 if (!child->filtered && percent >= hb->min_pcnt)
498 n++;
499 }
500
501 return n;
502}
503
504static void hist_entry__set_folding(struct hist_entry *he,
505 struct hist_browser *hb, bool unfold)
3c916cc2 506{
05e8b080 507 hist_entry__init_have_children(he);
3698dab1 508 he->unfolded = unfold ? he->has_children : false;
3c916cc2 509
3698dab1 510 if (he->has_children) {
492b1010
NK
511 int n;
512
513 if (he->leaf)
514 n = callchain__set_folding(&he->sorted_chain, unfold);
515 else
516 n = hierarchy_set_folding(hb, he, unfold);
517
05e8b080 518 he->nr_rows = unfold ? n : 0;
3c916cc2 519 } else
05e8b080 520 he->nr_rows = 0;
3c916cc2
ACM
521}
522
c3b78952
NK
523static void
524__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2
ACM
525{
526 struct rb_node *nd;
492b1010
NK
527 struct hist_entry *he;
528 double percent;
3c916cc2 529
492b1010
NK
530 nd = rb_first(&browser->hists->entries);
531 while (nd) {
532 he = rb_entry(nd, struct hist_entry, rb_node);
533
534 /* set folding state even if it's currently folded */
535 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
536
537 hist_entry__set_folding(he, browser, unfold);
538
539 percent = hist_entry__get_percent_limit(he);
540 if (he->filtered || percent < browser->min_pcnt)
541 continue;
542
543 if (!he->depth || unfold)
544 browser->nr_hierarchy_entries++;
545 if (he->leaf)
546 browser->nr_callchain_rows += he->nr_rows;
79dded87
NK
547 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
548 browser->nr_hierarchy_entries++;
549 he->has_no_entry = true;
550 he->nr_rows = 1;
551 } else
552 he->has_no_entry = false;
3c916cc2
ACM
553 }
554}
555
05e8b080 556static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 557{
492b1010 558 browser->nr_hierarchy_entries = 0;
c3b78952
NK
559 browser->nr_callchain_rows = 0;
560 __hist_browser__set_folding(browser, unfold);
561
562 browser->b.nr_entries = hist_browser__nr_entries(browser);
3c916cc2 563 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 564 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
565}
566
7b27509f
ACM
567static void ui_browser__warn_lost_events(struct ui_browser *browser)
568{
569 ui_browser__warning(browser, 4,
570 "Events are being lost, check IO/CPU overload!\n\n"
571 "You may want to run 'perf' using a RT scheduler policy:\n\n"
572 " perf top -r 80\n\n"
573 "Or reduce the sampling frequency.");
574}
575
5b91a86f
JO
576static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
577{
578 return browser->title ? browser->title(browser, bf, size) : 0;
579}
580
dabd2012 581int hist_browser__run(struct hist_browser *browser, const char *help)
d1b4f249 582{
b50e003d 583 int key;
81cce8de 584 char title[160];
c2a51ab8 585 struct hist_browser_timer *hbt = browser->hbt;
9783adf7 586 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 587
05e8b080 588 browser->b.entries = &browser->hists->entries;
c3b78952 589 browser->b.nr_entries = hist_browser__nr_entries(browser);
d1b4f249 590
5b91a86f 591 hist_browser__title(browser, title, sizeof(title));
d1b4f249 592
090cff3e 593 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
d1b4f249
ACM
594 return -1;
595
d1b4f249 596 while (1) {
05e8b080 597 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 598
b50e003d 599 switch (key) {
fa5df943
NK
600 case K_TIMER: {
601 u64 nr_entries;
9783adf7 602 hbt->timer(hbt->arg);
fa5df943 603
c3b78952 604 if (hist_browser__has_filter(browser))
112f761f 605 hist_browser__update_nr_entries(browser);
fa5df943 606
c3b78952 607 nr_entries = hist_browser__nr_entries(browser);
fa5df943 608 ui_browser__update_nr_entries(&browser->b, nr_entries);
7b27509f 609
05e8b080
ACM
610 if (browser->hists->stats.nr_lost_warned !=
611 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
612 browser->hists->stats.nr_lost_warned =
613 browser->hists->stats.nr_events[PERF_RECORD_LOST];
614 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
615 }
616
5b91a86f 617 hist_browser__title(browser, title, sizeof(title));
05e8b080 618 ui_browser__show_title(&browser->b, title);
81cce8de 619 continue;
fa5df943 620 }
4694153c 621 case 'D': { /* Debug */
d1b4f249 622 static int seq;
05e8b080 623 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
624 struct hist_entry, rb_node);
625 ui_helpline__pop();
62c95ae3 626 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
627 seq++, browser->b.nr_entries,
628 browser->hists->nr_entries,
62c95ae3 629 browser->b.rows,
05e8b080
ACM
630 browser->b.index,
631 browser->b.top_idx,
d1b4f249
ACM
632 h->row_offset, h->nr_rows);
633 }
3c916cc2
ACM
634 break;
635 case 'C':
636 /* Collapse the whole world. */
05e8b080 637 hist_browser__set_folding(browser, false);
3c916cc2
ACM
638 break;
639 case 'E':
640 /* Expand the whole world. */
05e8b080 641 hist_browser__set_folding(browser, true);
3c916cc2 642 break;
025bf7ea
ACM
643 case 'H':
644 browser->show_headers = !browser->show_headers;
645 hist_browser__update_rows(browser);
646 break;
cf958003 647 case K_ENTER:
05e8b080 648 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
649 break;
650 /* fall thru */
651 default:
b50e003d 652 goto out;
d1b4f249
ACM
653 }
654 }
b50e003d 655out:
05e8b080 656 ui_browser__hide(&browser->b);
b50e003d 657 return key;
d1b4f249
ACM
658}
659
39ee533f
NK
660struct callchain_print_arg {
661 /* for hists browser */
662 off_t row_offset;
663 bool is_current_entry;
664
665 /* for file dump */
666 FILE *fp;
667 int printed;
668};
669
670typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
671 struct callchain_list *chain,
672 const char *str, int offset,
673 unsigned short row,
674 struct callchain_print_arg *arg);
675
f4536ddd
NK
676static void hist_browser__show_callchain_entry(struct hist_browser *browser,
677 struct callchain_list *chain,
39ee533f
NK
678 const char *str, int offset,
679 unsigned short row,
680 struct callchain_print_arg *arg)
f4536ddd
NK
681{
682 int color, width;
39ee533f 683 char folded_sign = callchain_list__folded(chain);
70e97278 684 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
f4536ddd
NK
685
686 color = HE_COLORSET_NORMAL;
687 width = browser->b.width - (offset + 2);
688 if (ui_browser__is_current_entry(&browser->b, row)) {
689 browser->selection = &chain->ms;
690 color = HE_COLORSET_SELECTED;
39ee533f 691 arg->is_current_entry = true;
f4536ddd
NK
692 }
693
694 ui_browser__set_color(&browser->b, color);
695 hist_browser__gotorc(browser, row, 0);
26270a00 696 ui_browser__write_nstring(&browser->b, " ", offset);
517dfdb3 697 ui_browser__printf(&browser->b, "%c", folded_sign);
70e97278 698 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
26270a00 699 ui_browser__write_nstring(&browser->b, str, width);
f4536ddd
NK
700}
701
39ee533f
NK
702static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
703 struct callchain_list *chain,
704 const char *str, int offset,
705 unsigned short row __maybe_unused,
706 struct callchain_print_arg *arg)
707{
708 char folded_sign = callchain_list__folded(chain);
709
710 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
711 folded_sign, str);
712}
713
714typedef bool (*check_output_full_fn)(struct hist_browser *browser,
715 unsigned short row);
716
717static bool hist_browser__check_output_full(struct hist_browser *browser,
718 unsigned short row)
719{
720 return browser->b.rows == row;
721}
722
723static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
724 unsigned short row __maybe_unused)
725{
726 return false;
727}
728
d1b4f249
ACM
729#define LEVEL_OFFSET_STEP 3
730
18bb8381
NK
731static int hist_browser__show_callchain_list(struct hist_browser *browser,
732 struct callchain_node *node,
733 struct callchain_list *chain,
734 unsigned short row, u64 total,
735 bool need_percent, int offset,
736 print_callchain_entry_fn print,
737 struct callchain_print_arg *arg)
738{
739 char bf[1024], *alloc_str;
740 const char *str;
741
742 if (arg->row_offset != 0) {
743 arg->row_offset--;
744 return 0;
745 }
746
747 alloc_str = NULL;
748 str = callchain_list__sym_name(chain, bf, sizeof(bf),
749 browser->show_dso);
750
751 if (need_percent) {
752 char buf[64];
753
754 callchain_node__scnprintf_value(node, buf, sizeof(buf),
755 total);
756
757 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
758 str = "Not enough memory!";
759 else
760 str = alloc_str;
761 }
762
763 print(browser, chain, str, offset, row, arg);
764
765 free(alloc_str);
766 return 1;
767}
768
59c624e2
NK
769static bool check_percent_display(struct rb_node *node, u64 parent_total)
770{
771 struct callchain_node *child;
772
773 if (node == NULL)
774 return false;
775
776 if (rb_next(node))
777 return true;
778
779 child = rb_entry(node, struct callchain_node, rb_node);
780 return callchain_cumul_hits(child) != parent_total;
781}
782
4b3a3212
NK
783static int hist_browser__show_callchain_flat(struct hist_browser *browser,
784 struct rb_root *root,
785 unsigned short row, u64 total,
59c624e2 786 u64 parent_total,
4b3a3212
NK
787 print_callchain_entry_fn print,
788 struct callchain_print_arg *arg,
789 check_output_full_fn is_output_full)
790{
791 struct rb_node *node;
792 int first_row = row, offset = LEVEL_OFFSET_STEP;
793 bool need_percent;
794
795 node = rb_first(root);
59c624e2 796 need_percent = check_percent_display(node, parent_total);
4b3a3212
NK
797
798 while (node) {
799 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
800 struct rb_node *next = rb_next(node);
801 struct callchain_list *chain;
802 char folded_sign = ' ';
803 int first = true;
804 int extra_offset = 0;
805
806 list_for_each_entry(chain, &child->parent_val, list) {
807 bool was_first = first;
808
809 if (first)
810 first = false;
811 else if (need_percent)
812 extra_offset = LEVEL_OFFSET_STEP;
813
814 folded_sign = callchain_list__folded(chain);
815
816 row += hist_browser__show_callchain_list(browser, child,
817 chain, row, total,
818 was_first && need_percent,
819 offset + extra_offset,
820 print, arg);
821
822 if (is_output_full(browser, row))
823 goto out;
824
825 if (folded_sign == '+')
826 goto next;
827 }
828
829 list_for_each_entry(chain, &child->val, list) {
830 bool was_first = first;
831
832 if (first)
833 first = false;
834 else if (need_percent)
835 extra_offset = LEVEL_OFFSET_STEP;
836
837 folded_sign = callchain_list__folded(chain);
838
839 row += hist_browser__show_callchain_list(browser, child,
840 chain, row, total,
841 was_first && need_percent,
842 offset + extra_offset,
843 print, arg);
844
845 if (is_output_full(browser, row))
846 goto out;
847
848 if (folded_sign == '+')
849 break;
850 }
851
852next:
853 if (is_output_full(browser, row))
854 break;
855 node = next;
856 }
857out:
858 return row - first_row;
859}
860
8c430a34
NK
861static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
862 struct callchain_list *chain,
863 char *value_str, char *old_str)
864{
865 char bf[1024];
866 const char *str;
867 char *new;
868
869 str = callchain_list__sym_name(chain, bf, sizeof(bf),
870 browser->show_dso);
871 if (old_str) {
872 if (asprintf(&new, "%s%s%s", old_str,
873 symbol_conf.field_sep ?: ";", str) < 0)
874 new = NULL;
875 } else {
876 if (value_str) {
877 if (asprintf(&new, "%s %s", value_str, str) < 0)
878 new = NULL;
879 } else {
880 if (asprintf(&new, "%s", str) < 0)
881 new = NULL;
882 }
883 }
884 return new;
885}
886
887static int hist_browser__show_callchain_folded(struct hist_browser *browser,
888 struct rb_root *root,
889 unsigned short row, u64 total,
59c624e2 890 u64 parent_total,
8c430a34
NK
891 print_callchain_entry_fn print,
892 struct callchain_print_arg *arg,
893 check_output_full_fn is_output_full)
894{
895 struct rb_node *node;
896 int first_row = row, offset = LEVEL_OFFSET_STEP;
897 bool need_percent;
898
899 node = rb_first(root);
59c624e2 900 need_percent = check_percent_display(node, parent_total);
8c430a34
NK
901
902 while (node) {
903 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
904 struct rb_node *next = rb_next(node);
905 struct callchain_list *chain, *first_chain = NULL;
906 int first = true;
907 char *value_str = NULL, *value_str_alloc = NULL;
908 char *chain_str = NULL, *chain_str_alloc = NULL;
909
910 if (arg->row_offset != 0) {
911 arg->row_offset--;
912 goto next;
913 }
914
915 if (need_percent) {
916 char buf[64];
917
918 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
919 if (asprintf(&value_str, "%s", buf) < 0) {
920 value_str = (char *)"<...>";
921 goto do_print;
922 }
923 value_str_alloc = value_str;
924 }
925
926 list_for_each_entry(chain, &child->parent_val, list) {
927 chain_str = hist_browser__folded_callchain_str(browser,
928 chain, value_str, chain_str);
929 if (first) {
930 first = false;
931 first_chain = chain;
932 }
933
934 if (chain_str == NULL) {
935 chain_str = (char *)"Not enough memory!";
936 goto do_print;
937 }
938
939 chain_str_alloc = chain_str;
940 }
941
942 list_for_each_entry(chain, &child->val, list) {
943 chain_str = hist_browser__folded_callchain_str(browser,
944 chain, value_str, chain_str);
945 if (first) {
946 first = false;
947 first_chain = chain;
948 }
949
950 if (chain_str == NULL) {
951 chain_str = (char *)"Not enough memory!";
952 goto do_print;
953 }
954
955 chain_str_alloc = chain_str;
956 }
957
958do_print:
959 print(browser, first_chain, chain_str, offset, row++, arg);
960 free(value_str_alloc);
961 free(chain_str_alloc);
962
963next:
964 if (is_output_full(browser, row))
965 break;
966 node = next;
967 }
968
969 return row - first_row;
970}
971
0c841c6c 972static int hist_browser__show_callchain_graph(struct hist_browser *browser,
c09a7e75 973 struct rb_root *root, int level,
39ee533f 974 unsigned short row, u64 total,
5eca104e 975 u64 parent_total,
39ee533f
NK
976 print_callchain_entry_fn print,
977 struct callchain_print_arg *arg,
978 check_output_full_fn is_output_full)
d1b4f249
ACM
979{
980 struct rb_node *node;
f4536ddd 981 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
4087d11c 982 bool need_percent;
5eca104e
NK
983 u64 percent_total = total;
984
985 if (callchain_param.mode == CHAIN_GRAPH_REL)
986 percent_total = parent_total;
d1b4f249 987
c09a7e75 988 node = rb_first(root);
59c624e2 989 need_percent = check_percent_display(node, parent_total);
4087d11c 990
d1b4f249
ACM
991 while (node) {
992 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
993 struct rb_node *next = rb_next(node);
d1b4f249
ACM
994 struct callchain_list *chain;
995 char folded_sign = ' ';
996 int first = true;
997 int extra_offset = 0;
998
d1b4f249 999 list_for_each_entry(chain, &child->val, list) {
d1b4f249
ACM
1000 bool was_first = first;
1001
163caed9 1002 if (first)
d1b4f249 1003 first = false;
4087d11c 1004 else if (need_percent)
d1b4f249 1005 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
1006
1007 folded_sign = callchain_list__folded(chain);
c09a7e75 1008
18bb8381 1009 row += hist_browser__show_callchain_list(browser, child,
5eca104e 1010 chain, row, percent_total,
18bb8381
NK
1011 was_first && need_percent,
1012 offset + extra_offset,
1013 print, arg);
d1b4f249 1014
18bb8381 1015 if (is_output_full(browser, row))
d1b4f249 1016 goto out;
18bb8381 1017
d1b4f249
ACM
1018 if (folded_sign == '+')
1019 break;
1020 }
1021
1022 if (folded_sign == '-') {
1023 const int new_level = level + (extra_offset ? 2 : 1);
d1b4f249 1024
0c841c6c 1025 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
5eca104e
NK
1026 new_level, row, total,
1027 child->children_hit,
39ee533f 1028 print, arg, is_output_full);
d1b4f249 1029 }
39ee533f 1030 if (is_output_full(browser, row))
d1b4f249 1031 break;
c09a7e75 1032 node = next;
d1b4f249 1033 }
c09a7e75 1034out:
d1b4f249
ACM
1035 return row - first_row;
1036}
1037
0c841c6c
NK
1038static int hist_browser__show_callchain(struct hist_browser *browser,
1039 struct hist_entry *entry, int level,
1040 unsigned short row,
1041 print_callchain_entry_fn print,
1042 struct callchain_print_arg *arg,
1043 check_output_full_fn is_output_full)
1044{
1045 u64 total = hists__total_period(entry->hists);
5eca104e 1046 u64 parent_total;
0c841c6c
NK
1047 int printed;
1048
5eca104e
NK
1049 if (symbol_conf.cumulate_callchain)
1050 parent_total = entry->stat_acc->period;
1051 else
1052 parent_total = entry->stat.period;
0c841c6c
NK
1053
1054 if (callchain_param.mode == CHAIN_FLAT) {
1055 printed = hist_browser__show_callchain_flat(browser,
5eca104e
NK
1056 &entry->sorted_chain, row,
1057 total, parent_total, print, arg,
1058 is_output_full);
0c841c6c
NK
1059 } else if (callchain_param.mode == CHAIN_FOLDED) {
1060 printed = hist_browser__show_callchain_folded(browser,
5eca104e
NK
1061 &entry->sorted_chain, row,
1062 total, parent_total, print, arg,
1063 is_output_full);
0c841c6c
NK
1064 } else {
1065 printed = hist_browser__show_callchain_graph(browser,
5eca104e
NK
1066 &entry->sorted_chain, level, row,
1067 total, parent_total, print, arg,
1068 is_output_full);
0c841c6c
NK
1069 }
1070
1071 if (arg->is_current_entry)
1072 browser->he_selection = entry;
1073
1074 return printed;
1075}
1076
89701460
NK
1077struct hpp_arg {
1078 struct ui_browser *b;
1079 char folded_sign;
1080 bool current_entry;
1081};
1082
2f6d9009
NK
1083static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1084{
1085 struct hpp_arg *arg = hpp->ptr;
d675107c 1086 int ret, len;
2f6d9009
NK
1087 va_list args;
1088 double percent;
371d8c40 1089
2f6d9009 1090 va_start(args, fmt);
d675107c 1091 len = va_arg(args, int);
2f6d9009
NK
1092 percent = va_arg(args, double);
1093 va_end(args);
371d8c40 1094
2f6d9009 1095 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
371d8c40 1096
d675107c 1097 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
517dfdb3 1098 ui_browser__printf(arg->b, "%s", hpp->buf);
5aed9d24 1099
2f6d9009 1100 advance_hpp(hpp, ret);
5aed9d24
NK
1101 return ret;
1102}
1103
fb821c9e 1104#define __HPP_COLOR_PERCENT_FN(_type, _field) \
5aed9d24
NK
1105static u64 __hpp_get_##_field(struct hist_entry *he) \
1106{ \
1107 return he->stat._field; \
1108} \
1109 \
2c5d4b4a 1110static int \
5b591669 1111hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
2c5d4b4a
JO
1112 struct perf_hpp *hpp, \
1113 struct hist_entry *he) \
f5951d56 1114{ \
5b591669
NK
1115 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1116 __hpp__slsmg_color_printf, true); \
f5951d56
NK
1117}
1118
0434ddd2
NK
1119#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1120static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1121{ \
1122 return he->stat_acc->_field; \
1123} \
1124 \
1125static int \
5b591669 1126hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
0434ddd2
NK
1127 struct perf_hpp *hpp, \
1128 struct hist_entry *he) \
1129{ \
1130 if (!symbol_conf.cumulate_callchain) { \
517dfdb3 1131 struct hpp_arg *arg = hpp->ptr; \
5b591669 1132 int len = fmt->user_len ?: fmt->len; \
d675107c 1133 int ret = scnprintf(hpp->buf, hpp->size, \
5b591669 1134 "%*s", len, "N/A"); \
517dfdb3 1135 ui_browser__printf(arg->b, "%s", hpp->buf); \
0434ddd2
NK
1136 \
1137 return ret; \
1138 } \
5b591669
NK
1139 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1140 " %*.2f%%", __hpp__slsmg_color_printf, true); \
0434ddd2
NK
1141}
1142
fb821c9e
NK
1143__HPP_COLOR_PERCENT_FN(overhead, period)
1144__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1145__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1146__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1147__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 1148__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 1149
5aed9d24 1150#undef __HPP_COLOR_PERCENT_FN
0434ddd2 1151#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
1152
1153void hist_browser__init_hpp(void)
1154{
f5951d56
NK
1155 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1156 hist_browser__hpp_color_overhead;
1157 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1158 hist_browser__hpp_color_overhead_sys;
1159 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1160 hist_browser__hpp_color_overhead_us;
1161 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1162 hist_browser__hpp_color_overhead_guest_sys;
1163 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1164 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
1165 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1166 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
1167}
1168
05e8b080 1169static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
1170 struct hist_entry *entry,
1171 unsigned short row)
1172{
1240005e 1173 int printed = 0;
67d25916 1174 int width = browser->b.width;
d1b4f249 1175 char folded_sign = ' ';
05e8b080 1176 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 1177 off_t row_offset = entry->row_offset;
63a1a3d8 1178 bool first = true;
1240005e 1179 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
1180
1181 if (current_entry) {
05e8b080
ACM
1182 browser->he_selection = entry;
1183 browser->selection = &entry->ms;
d1b4f249
ACM
1184 }
1185
1186 if (symbol_conf.use_callchain) {
163caed9 1187 hist_entry__init_have_children(entry);
d1b4f249
ACM
1188 folded_sign = hist_entry__folded(entry);
1189 }
1190
1191 if (row_offset == 0) {
89701460 1192 struct hpp_arg arg = {
fb821c9e 1193 .b = &browser->b,
89701460
NK
1194 .folded_sign = folded_sign,
1195 .current_entry = current_entry,
1196 };
c6c3c02d 1197 int column = 0;
d1b4f249 1198
ca3ff33b 1199 hist_browser__gotorc(browser, row, 0);
c172f742 1200
f0786af5 1201 hists__for_each_format(browser->hists, fmt) {
89fee709
ACM
1202 char s[2048];
1203 struct perf_hpp hpp = {
1204 .buf = s,
1205 .size = sizeof(s),
1206 .ptr = &arg,
1207 };
1208
361459f1
NK
1209 if (perf_hpp__should_skip(fmt, entry->hists) ||
1210 column++ < browser->b.horiz_scroll)
e67d49a7
NK
1211 continue;
1212
fb821c9e
NK
1213 if (current_entry && browser->b.navkeypressed) {
1214 ui_browser__set_color(&browser->b,
1215 HE_COLORSET_SELECTED);
1216 } else {
1217 ui_browser__set_color(&browser->b,
1218 HE_COLORSET_NORMAL);
1219 }
1220
1221 if (first) {
1222 if (symbol_conf.use_callchain) {
517dfdb3 1223 ui_browser__printf(&browser->b, "%c ", folded_sign);
fb821c9e
NK
1224 width -= 2;
1225 }
1226 first = false;
1227 } else {
517dfdb3 1228 ui_browser__printf(&browser->b, " ");
f5951d56
NK
1229 width -= 2;
1230 }
c172f742 1231
1240005e 1232 if (fmt->color) {
89fee709
ACM
1233 int ret = fmt->color(fmt, &hpp, entry);
1234 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1235 /*
1236 * fmt->color() already used ui_browser to
1237 * print the non alignment bits, skip it (+ret):
1238 */
1239 ui_browser__printf(&browser->b, "%s", s + ret);
f5951d56 1240 } else {
89fee709 1241 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
517dfdb3 1242 ui_browser__printf(&browser->b, "%s", s);
f5951d56 1243 }
89fee709 1244 width -= hpp.buf - s;
2cf9cebf
ACM
1245 }
1246
f5951d56
NK
1247 /* The scroll bar isn't being used */
1248 if (!browser->b.navkeypressed)
1249 width += 1;
1250
26270a00 1251 ui_browser__write_nstring(&browser->b, "", width);
26d8b338 1252
d1b4f249
ACM
1253 ++row;
1254 ++printed;
1255 } else
1256 --row_offset;
1257
62c95ae3 1258 if (folded_sign == '-' && row != browser->b.rows) {
39ee533f
NK
1259 struct callchain_print_arg arg = {
1260 .row_offset = row_offset,
1261 .is_current_entry = current_entry,
1262 };
c09a7e75 1263
0c841c6c 1264 printed += hist_browser__show_callchain(browser, entry, 1, row,
39ee533f
NK
1265 hist_browser__show_callchain_entry, &arg,
1266 hist_browser__check_output_full);
d1b4f249
ACM
1267 }
1268
1269 return printed;
1270}
1271
d0506edb
NK
1272static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1273 struct hist_entry *entry,
1274 unsigned short row,
2dbbe9f2 1275 int level)
d0506edb
NK
1276{
1277 int printed = 0;
1278 int width = browser->b.width;
1279 char folded_sign = ' ';
1280 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1281 off_t row_offset = entry->row_offset;
1282 bool first = true;
1283 struct perf_hpp_fmt *fmt;
a61a22f6 1284 struct perf_hpp_list_node *fmt_node;
d0506edb
NK
1285 struct hpp_arg arg = {
1286 .b = &browser->b,
1287 .current_entry = current_entry,
1288 };
1289 int column = 0;
2dbbe9f2 1290 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
d0506edb
NK
1291
1292 if (current_entry) {
1293 browser->he_selection = entry;
1294 browser->selection = &entry->ms;
1295 }
1296
1297 hist_entry__init_have_children(entry);
1298 folded_sign = hist_entry__folded(entry);
1299 arg.folded_sign = folded_sign;
1300
1301 if (entry->leaf && row_offset) {
1302 row_offset--;
1303 goto show_callchain;
1304 }
1305
1306 hist_browser__gotorc(browser, row, 0);
1307
1308 if (current_entry && browser->b.navkeypressed)
1309 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1310 else
1311 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1312
1313 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1314 width -= level * HIERARCHY_INDENT;
1315
a61a22f6
NK
1316 /* the first hpp_list_node is for overhead columns */
1317 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1318 struct perf_hpp_list_node, list);
1319 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
d0506edb
NK
1320 char s[2048];
1321 struct perf_hpp hpp = {
1322 .buf = s,
1323 .size = sizeof(s),
1324 .ptr = &arg,
1325 };
1326
1327 if (perf_hpp__should_skip(fmt, entry->hists) ||
1328 column++ < browser->b.horiz_scroll)
1329 continue;
1330
d0506edb
NK
1331 if (current_entry && browser->b.navkeypressed) {
1332 ui_browser__set_color(&browser->b,
1333 HE_COLORSET_SELECTED);
1334 } else {
1335 ui_browser__set_color(&browser->b,
1336 HE_COLORSET_NORMAL);
1337 }
1338
1339 if (first) {
1340 ui_browser__printf(&browser->b, "%c", folded_sign);
1341 width--;
1342 first = false;
1343 } else {
1344 ui_browser__printf(&browser->b, " ");
1345 width -= 2;
1346 }
1347
1348 if (fmt->color) {
1349 int ret = fmt->color(fmt, &hpp, entry);
1350 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1351 /*
1352 * fmt->color() already used ui_browser to
1353 * print the non alignment bits, skip it (+ret):
1354 */
1355 ui_browser__printf(&browser->b, "%s", s + ret);
1356 } else {
1357 int ret = fmt->entry(fmt, &hpp, entry);
1358 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359 ui_browser__printf(&browser->b, "%s", s);
1360 }
1361 width -= hpp.buf - s;
1362 }
1363
1364 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1365 width -= hierarchy_indent;
1366
1367 if (column >= browser->b.horiz_scroll) {
1368 char s[2048];
1369 struct perf_hpp hpp = {
1370 .buf = s,
1371 .size = sizeof(s),
1372 .ptr = &arg,
1373 };
1374
1375 if (current_entry && browser->b.navkeypressed) {
1376 ui_browser__set_color(&browser->b,
1377 HE_COLORSET_SELECTED);
1378 } else {
1379 ui_browser__set_color(&browser->b,
1380 HE_COLORSET_NORMAL);
1381 }
1382
1b2dbbf4
NK
1383 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1384 ui_browser__write_nstring(&browser->b, "", 2);
1385 width -= 2;
d0506edb 1386
1b2dbbf4
NK
1387 /*
1388 * No need to call hist_entry__snprintf_alignment()
1389 * since this fmt is always the last column in the
1390 * hierarchy mode.
1391 */
1392 if (fmt->color) {
1393 width -= fmt->color(fmt, &hpp, entry);
1394 } else {
1395 int i = 0;
cb1fab91 1396
1b2dbbf4
NK
1397 width -= fmt->entry(fmt, &hpp, entry);
1398 ui_browser__printf(&browser->b, "%s", ltrim(s));
cb1fab91 1399
1b2dbbf4
NK
1400 while (isspace(s[i++]))
1401 width++;
1402 }
d0506edb
NK
1403 }
1404 }
1405
1406 /* The scroll bar isn't being used */
1407 if (!browser->b.navkeypressed)
1408 width += 1;
1409
1410 ui_browser__write_nstring(&browser->b, "", width);
1411
1412 ++row;
1413 ++printed;
1414
1415show_callchain:
1416 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1417 struct callchain_print_arg carg = {
1418 .row_offset = row_offset,
1419 };
1420
1421 printed += hist_browser__show_callchain(browser, entry,
1422 level + 1, row,
1423 hist_browser__show_callchain_entry, &carg,
1424 hist_browser__check_output_full);
1425 }
1426
1427 return printed;
1428}
1429
79dded87 1430static int hist_browser__show_no_entry(struct hist_browser *browser,
2dbbe9f2 1431 unsigned short row, int level)
79dded87
NK
1432{
1433 int width = browser->b.width;
1434 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1435 bool first = true;
1436 int column = 0;
1437 int ret;
1438 struct perf_hpp_fmt *fmt;
a61a22f6 1439 struct perf_hpp_list_node *fmt_node;
2dbbe9f2 1440 int indent = browser->hists->nr_hpp_node - 2;
79dded87
NK
1441
1442 if (current_entry) {
1443 browser->he_selection = NULL;
1444 browser->selection = NULL;
1445 }
1446
1447 hist_browser__gotorc(browser, row, 0);
1448
1449 if (current_entry && browser->b.navkeypressed)
1450 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1451 else
1452 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1453
1454 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1455 width -= level * HIERARCHY_INDENT;
1456
a61a22f6
NK
1457 /* the first hpp_list_node is for overhead columns */
1458 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1459 struct perf_hpp_list_node, list);
1460 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
79dded87
NK
1461 if (perf_hpp__should_skip(fmt, browser->hists) ||
1462 column++ < browser->b.horiz_scroll)
1463 continue;
1464
da1b0407 1465 ret = fmt->width(fmt, NULL, browser->hists);
79dded87
NK
1466
1467 if (first) {
1468 /* for folded sign */
1469 first = false;
1470 ret++;
1471 } else {
1472 /* space between columns */
1473 ret += 2;
1474 }
1475
1476 ui_browser__write_nstring(&browser->b, "", ret);
1477 width -= ret;
1478 }
1479
2dbbe9f2
NK
1480 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1481 width -= indent * HIERARCHY_INDENT;
79dded87
NK
1482
1483 if (column >= browser->b.horiz_scroll) {
1484 char buf[32];
1485
1486 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1487 ui_browser__printf(&browser->b, " %s", buf);
1488 width -= ret + 2;
1489 }
1490
1491 /* The scroll bar isn't being used */
1492 if (!browser->b.navkeypressed)
1493 width += 1;
1494
1495 ui_browser__write_nstring(&browser->b, "", width);
1496 return 1;
1497}
1498
81a888fe
JO
1499static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1500{
1501 advance_hpp(hpp, inc);
1502 return hpp->size <= 0;
1503}
1504
69705b35
JO
1505static int
1506hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1507 size_t size, int line)
81a888fe 1508{
c6c3c02d 1509 struct hists *hists = browser->hists;
81a888fe
JO
1510 struct perf_hpp dummy_hpp = {
1511 .buf = buf,
1512 .size = size,
1513 };
1514 struct perf_hpp_fmt *fmt;
1515 size_t ret = 0;
c6c3c02d 1516 int column = 0;
29659ab4 1517 int span = 0;
81a888fe
JO
1518
1519 if (symbol_conf.use_callchain) {
1520 ret = scnprintf(buf, size, " ");
1521 if (advance_hpp_check(&dummy_hpp, ret))
1522 return ret;
1523 }
1524
f0786af5 1525 hists__for_each_format(browser->hists, fmt) {
361459f1 1526 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
81a888fe
JO
1527 continue;
1528
29659ab4 1529 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
81a888fe
JO
1530 if (advance_hpp_check(&dummy_hpp, ret))
1531 break;
1532
29659ab4
JO
1533 if (span)
1534 continue;
1535
81a888fe
JO
1536 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1537 if (advance_hpp_check(&dummy_hpp, ret))
1538 break;
1539 }
1540
1541 return ret;
1542}
1543
d8b92400
NK
1544static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1545{
1546 struct hists *hists = browser->hists;
1547 struct perf_hpp dummy_hpp = {
1548 .buf = buf,
1549 .size = size,
1550 };
1551 struct perf_hpp_fmt *fmt;
a61a22f6 1552 struct perf_hpp_list_node *fmt_node;
d8b92400
NK
1553 size_t ret = 0;
1554 int column = 0;
2dbbe9f2 1555 int indent = hists->nr_hpp_node - 2;
a61a22f6 1556 bool first_node, first_col;
d8b92400
NK
1557
1558 ret = scnprintf(buf, size, " ");
1559 if (advance_hpp_check(&dummy_hpp, ret))
1560 return ret;
1561
a61a22f6
NK
1562 /* the first hpp_list_node is for overhead columns */
1563 fmt_node = list_first_entry(&hists->hpp_formats,
1564 struct perf_hpp_list_node, list);
1565 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
d8b92400
NK
1566 if (column++ < browser->b.horiz_scroll)
1567 continue;
1568
29659ab4 1569 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
d8b92400
NK
1570 if (advance_hpp_check(&dummy_hpp, ret))
1571 break;
1572
1573 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1574 if (advance_hpp_check(&dummy_hpp, ret))
1575 break;
1576 }
1577
1578 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
2dbbe9f2 1579 indent * HIERARCHY_INDENT, "");
d8b92400
NK
1580 if (advance_hpp_check(&dummy_hpp, ret))
1581 return ret;
1582
a61a22f6
NK
1583 first_node = true;
1584 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1585 if (!first_node) {
d8b92400
NK
1586 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1587 if (advance_hpp_check(&dummy_hpp, ret))
1588 break;
1589 }
a61a22f6 1590 first_node = false;
d8b92400 1591
a61a22f6
NK
1592 first_col = true;
1593 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1594 char *start;
d8b92400 1595
a61a22f6
NK
1596 if (perf_hpp__should_skip(fmt, hists))
1597 continue;
cb1fab91 1598
a61a22f6
NK
1599 if (!first_col) {
1600 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1601 if (advance_hpp_check(&dummy_hpp, ret))
1602 break;
1603 }
1604 first_col = false;
cb1fab91 1605
29659ab4 1606 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
a61a22f6 1607 dummy_hpp.buf[ret] = '\0';
a61a22f6 1608
7d6a7e78 1609 start = trim(dummy_hpp.buf);
a61a22f6
NK
1610 ret = strlen(start);
1611
1612 if (start != dummy_hpp.buf)
1613 memmove(dummy_hpp.buf, start, ret + 1);
1614
1615 if (advance_hpp_check(&dummy_hpp, ret))
1616 break;
1617 }
d8b92400
NK
1618 }
1619
1620 return ret;
1621}
1622
01b4770d 1623static void hists_browser__hierarchy_headers(struct hist_browser *browser)
025bf7ea 1624{
81a888fe
JO
1625 char headers[1024];
1626
01b4770d
JO
1627 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1628 sizeof(headers));
1629
025bf7ea
ACM
1630 ui_browser__gotorc(&browser->b, 0, 0);
1631 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
26270a00 1632 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
025bf7ea
ACM
1633}
1634
01b4770d
JO
1635static void hists_browser__headers(struct hist_browser *browser)
1636{
69705b35
JO
1637 struct hists *hists = browser->hists;
1638 struct perf_hpp_list *hpp_list = hists->hpp_list;
01b4770d 1639
69705b35 1640 int line;
01b4770d 1641
69705b35
JO
1642 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1643 char headers[1024];
1644
1645 hists_browser__scnprintf_headers(browser, headers,
1646 sizeof(headers), line);
1647
1648 ui_browser__gotorc(&browser->b, line, 0);
1649 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1650 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1651 }
01b4770d
JO
1652}
1653
1654static void hist_browser__show_headers(struct hist_browser *browser)
1655{
1656 if (symbol_conf.report_hierarchy)
1657 hists_browser__hierarchy_headers(browser);
1658 else
1659 hists_browser__headers(browser);
1660}
1661
437cfe7a
ACM
1662static void ui_browser__hists_init_top(struct ui_browser *browser)
1663{
1664 if (browser->top == NULL) {
1665 struct hist_browser *hb;
1666
1667 hb = container_of(browser, struct hist_browser, b);
1668 browser->top = rb_first(&hb->hists->entries);
1669 }
1670}
1671
05e8b080 1672static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
1673{
1674 unsigned row = 0;
025bf7ea 1675 u16 header_offset = 0;
d1b4f249 1676 struct rb_node *nd;
05e8b080 1677 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
f8e6710d 1678 struct hists *hists = hb->hists;
d1b4f249 1679
025bf7ea 1680 if (hb->show_headers) {
f8e6710d
JO
1681 struct perf_hpp_list *hpp_list = hists->hpp_list;
1682
025bf7ea 1683 hist_browser__show_headers(hb);
f8e6710d 1684 header_offset = hpp_list->nr_header_lines;
025bf7ea
ACM
1685 }
1686
05e8b080 1687 ui_browser__hists_init_top(browser);
979d2cac
WN
1688 hb->he_selection = NULL;
1689 hb->selection = NULL;
d1b4f249 1690
d0506edb 1691 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
d1b4f249 1692 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1693 float percent;
d1b4f249 1694
d0506edb
NK
1695 if (h->filtered) {
1696 /* let it move to sibling */
1697 h->unfolded = false;
d1b4f249 1698 continue;
d0506edb 1699 }
d1b4f249 1700
14135663 1701 percent = hist_entry__get_percent_limit(h);
064f1981
NK
1702 if (percent < hb->min_pcnt)
1703 continue;
1704
d0506edb
NK
1705 if (symbol_conf.report_hierarchy) {
1706 row += hist_browser__show_hierarchy_entry(hb, h, row,
2dbbe9f2 1707 h->depth);
79dded87
NK
1708 if (row == browser->rows)
1709 break;
1710
1711 if (h->has_no_entry) {
a61a22f6 1712 hist_browser__show_no_entry(hb, row, h->depth + 1);
79dded87
NK
1713 row++;
1714 }
d0506edb
NK
1715 } else {
1716 row += hist_browser__show_entry(hb, h, row);
1717 }
1718
62c95ae3 1719 if (row == browser->rows)
d1b4f249
ACM
1720 break;
1721 }
1722
025bf7ea 1723 return row + header_offset;
d1b4f249
ACM
1724}
1725
064f1981 1726static struct rb_node *hists__filter_entries(struct rb_node *nd,
064f1981 1727 float min_pcnt)
d1b4f249
ACM
1728{
1729 while (nd != NULL) {
1730 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1731 float percent = hist_entry__get_percent_limit(h);
064f1981 1732
c0f1527b 1733 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1734 return nd;
1735
d0506edb
NK
1736 /*
1737 * If it's filtered, its all children also were filtered.
1738 * So move to sibling node.
1739 */
1740 if (rb_next(nd))
1741 nd = rb_next(nd);
1742 else
1743 nd = rb_hierarchy_next(nd);
d1b4f249
ACM
1744 }
1745
1746 return NULL;
1747}
1748
064f1981 1749static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
064f1981 1750 float min_pcnt)
d1b4f249
ACM
1751{
1752 while (nd != NULL) {
1753 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1754 float percent = hist_entry__get_percent_limit(h);
064f1981
NK
1755
1756 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1757 return nd;
1758
d0506edb 1759 nd = rb_hierarchy_prev(nd);
d1b4f249
ACM
1760 }
1761
1762 return NULL;
1763}
1764
05e8b080 1765static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
1766 off_t offset, int whence)
1767{
1768 struct hist_entry *h;
1769 struct rb_node *nd;
1770 bool first = true;
064f1981
NK
1771 struct hist_browser *hb;
1772
1773 hb = container_of(browser, struct hist_browser, b);
d1b4f249 1774
05e8b080 1775 if (browser->nr_entries == 0)
60098917
ACM
1776 return;
1777
05e8b080 1778 ui_browser__hists_init_top(browser);
437cfe7a 1779
d1b4f249
ACM
1780 switch (whence) {
1781 case SEEK_SET:
064f1981 1782 nd = hists__filter_entries(rb_first(browser->entries),
14135663 1783 hb->min_pcnt);
d1b4f249
ACM
1784 break;
1785 case SEEK_CUR:
05e8b080 1786 nd = browser->top;
d1b4f249
ACM
1787 goto do_offset;
1788 case SEEK_END:
d0506edb
NK
1789 nd = rb_hierarchy_last(rb_last(browser->entries));
1790 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
d1b4f249
ACM
1791 first = false;
1792 break;
1793 default:
1794 return;
1795 }
1796
1797 /*
1798 * Moves not relative to the first visible entry invalidates its
1799 * row_offset:
1800 */
05e8b080 1801 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
1802 h->row_offset = 0;
1803
1804 /*
1805 * Here we have to check if nd is expanded (+), if it is we can't go
1806 * the next top level hist_entry, instead we must compute an offset of
1807 * what _not_ to show and not change the first visible entry.
1808 *
1809 * This offset increments when we are going from top to bottom and
1810 * decreases when we're going from bottom to top.
1811 *
1812 * As we don't have backpointers to the top level in the callchains
1813 * structure, we need to always print the whole hist_entry callchain,
1814 * skipping the first ones that are before the first visible entry
1815 * and stop when we printed enough lines to fill the screen.
1816 */
1817do_offset:
837eeb75
WN
1818 if (!nd)
1819 return;
1820
d1b4f249
ACM
1821 if (offset > 0) {
1822 do {
1823 h = rb_entry(nd, struct hist_entry, rb_node);
d0506edb 1824 if (h->unfolded && h->leaf) {
d1b4f249
ACM
1825 u16 remaining = h->nr_rows - h->row_offset;
1826 if (offset > remaining) {
1827 offset -= remaining;
1828 h->row_offset = 0;
1829 } else {
1830 h->row_offset += offset;
1831 offset = 0;
05e8b080 1832 browser->top = nd;
d1b4f249
ACM
1833 break;
1834 }
1835 }
d0506edb
NK
1836 nd = hists__filter_entries(rb_hierarchy_next(nd),
1837 hb->min_pcnt);
d1b4f249
ACM
1838 if (nd == NULL)
1839 break;
1840 --offset;
05e8b080 1841 browser->top = nd;
d1b4f249
ACM
1842 } while (offset != 0);
1843 } else if (offset < 0) {
1844 while (1) {
1845 h = rb_entry(nd, struct hist_entry, rb_node);
d0506edb 1846 if (h->unfolded && h->leaf) {
d1b4f249
ACM
1847 if (first) {
1848 if (-offset > h->row_offset) {
1849 offset += h->row_offset;
1850 h->row_offset = 0;
1851 } else {
1852 h->row_offset += offset;
1853 offset = 0;
05e8b080 1854 browser->top = nd;
d1b4f249
ACM
1855 break;
1856 }
1857 } else {
1858 if (-offset > h->nr_rows) {
1859 offset += h->nr_rows;
1860 h->row_offset = 0;
1861 } else {
1862 h->row_offset = h->nr_rows + offset;
1863 offset = 0;
05e8b080 1864 browser->top = nd;
d1b4f249
ACM
1865 break;
1866 }
1867 }
1868 }
1869
d0506edb 1870 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
064f1981 1871 hb->min_pcnt);
d1b4f249
ACM
1872 if (nd == NULL)
1873 break;
1874 ++offset;
05e8b080 1875 browser->top = nd;
d1b4f249
ACM
1876 if (offset == 0) {
1877 /*
1878 * Last unfiltered hist_entry, check if it is
1879 * unfolded, if it is then we should have
1880 * row_offset at its last entry.
1881 */
1882 h = rb_entry(nd, struct hist_entry, rb_node);
d0506edb 1883 if (h->unfolded && h->leaf)
d1b4f249
ACM
1884 h->row_offset = h->nr_rows;
1885 break;
1886 }
1887 first = false;
1888 }
1889 } else {
05e8b080 1890 browser->top = nd;
d1b4f249
ACM
1891 h = rb_entry(nd, struct hist_entry, rb_node);
1892 h->row_offset = 0;
1893 }
1894}
1895
aff3f3f6 1896static int hist_browser__fprintf_callchain(struct hist_browser *browser,
d0506edb
NK
1897 struct hist_entry *he, FILE *fp,
1898 int level)
aff3f3f6 1899{
39ee533f
NK
1900 struct callchain_print_arg arg = {
1901 .fp = fp,
1902 };
aff3f3f6 1903
d0506edb 1904 hist_browser__show_callchain(browser, he, level, 0,
39ee533f
NK
1905 hist_browser__fprintf_callchain_entry, &arg,
1906 hist_browser__check_dump_full);
1907 return arg.printed;
aff3f3f6
ACM
1908}
1909
1910static int hist_browser__fprintf_entry(struct hist_browser *browser,
1911 struct hist_entry *he, FILE *fp)
1912{
1913 char s[8192];
aff3f3f6
ACM
1914 int printed = 0;
1915 char folded_sign = ' ';
26d8b338
NK
1916 struct perf_hpp hpp = {
1917 .buf = s,
1918 .size = sizeof(s),
1919 };
1920 struct perf_hpp_fmt *fmt;
1921 bool first = true;
1922 int ret;
aff3f3f6 1923
1b6b678e 1924 if (symbol_conf.use_callchain) {
aff3f3f6 1925 folded_sign = hist_entry__folded(he);
aff3f3f6 1926 printed += fprintf(fp, "%c ", folded_sign);
1b6b678e 1927 }
aff3f3f6 1928
f0786af5 1929 hists__for_each_format(browser->hists, fmt) {
361459f1 1930 if (perf_hpp__should_skip(fmt, he->hists))
e67d49a7
NK
1931 continue;
1932
26d8b338
NK
1933 if (!first) {
1934 ret = scnprintf(hpp.buf, hpp.size, " ");
1935 advance_hpp(&hpp, ret);
1936 } else
1937 first = false;
aff3f3f6 1938
26d8b338 1939 ret = fmt->entry(fmt, &hpp, he);
89fee709 1940 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
26d8b338
NK
1941 advance_hpp(&hpp, ret);
1942 }
89fee709 1943 printed += fprintf(fp, "%s\n", s);
aff3f3f6
ACM
1944
1945 if (folded_sign == '-')
d0506edb
NK
1946 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1947
1948 return printed;
1949}
1950
1951
1952static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1953 struct hist_entry *he,
325a6283 1954 FILE *fp, int level)
d0506edb
NK
1955{
1956 char s[8192];
1957 int printed = 0;
1958 char folded_sign = ' ';
1959 struct perf_hpp hpp = {
1960 .buf = s,
1961 .size = sizeof(s),
1962 };
1963 struct perf_hpp_fmt *fmt;
325a6283 1964 struct perf_hpp_list_node *fmt_node;
d0506edb
NK
1965 bool first = true;
1966 int ret;
325a6283 1967 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
d0506edb
NK
1968
1969 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1970
1971 folded_sign = hist_entry__folded(he);
1972 printed += fprintf(fp, "%c", folded_sign);
1973
325a6283
NK
1974 /* the first hpp_list_node is for overhead columns */
1975 fmt_node = list_first_entry(&he->hists->hpp_formats,
1976 struct perf_hpp_list_node, list);
1977 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
d0506edb
NK
1978 if (!first) {
1979 ret = scnprintf(hpp.buf, hpp.size, " ");
1980 advance_hpp(&hpp, ret);
1981 } else
1982 first = false;
1983
1984 ret = fmt->entry(fmt, &hpp, he);
1985 advance_hpp(&hpp, ret);
1986 }
1987
1988 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1989 advance_hpp(&hpp, ret);
1990
1b2dbbf4
NK
1991 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1992 ret = scnprintf(hpp.buf, hpp.size, " ");
1993 advance_hpp(&hpp, ret);
1994
1995 ret = fmt->entry(fmt, &hpp, he);
1996 advance_hpp(&hpp, ret);
1997 }
d0506edb
NK
1998
1999 printed += fprintf(fp, "%s\n", rtrim(s));
2000
2001 if (he->leaf && folded_sign == '-') {
2002 printed += hist_browser__fprintf_callchain(browser, he, fp,
2003 he->depth + 1);
2004 }
aff3f3f6
ACM
2005
2006 return printed;
2007}
2008
2009static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2010{
064f1981 2011 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 2012 browser->min_pcnt);
aff3f3f6
ACM
2013 int printed = 0;
2014
2015 while (nd) {
2016 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2017
d0506edb
NK
2018 if (symbol_conf.report_hierarchy) {
2019 printed += hist_browser__fprintf_hierarchy_entry(browser,
2020 h, fp,
325a6283 2021 h->depth);
d0506edb
NK
2022 } else {
2023 printed += hist_browser__fprintf_entry(browser, h, fp);
2024 }
2025
2026 nd = hists__filter_entries(rb_hierarchy_next(nd),
2027 browser->min_pcnt);
aff3f3f6
ACM
2028 }
2029
2030 return printed;
2031}
2032
2033static int hist_browser__dump(struct hist_browser *browser)
2034{
2035 char filename[64];
2036 FILE *fp;
2037
2038 while (1) {
2039 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2040 if (access(filename, F_OK))
2041 break;
2042 /*
2043 * XXX: Just an arbitrary lazy upper limit
2044 */
2045 if (++browser->print_seq == 8192) {
2046 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2047 return -1;
2048 }
2049 }
2050
2051 fp = fopen(filename, "w");
2052 if (fp == NULL) {
2053 char bf[64];
c8b5f2c9 2054 const char *err = str_error_r(errno, bf, sizeof(bf));
4cc49d4d 2055 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
2056 return -1;
2057 }
2058
2059 ++browser->print_seq;
2060 hist_browser__fprintf(browser, fp);
2061 fclose(fp);
2062 ui_helpline__fpush("%s written!", filename);
2063
2064 return 0;
2065}
2066
fcd86426
JO
2067void hist_browser__init(struct hist_browser *browser,
2068 struct hists *hists)
d1b4f249 2069{
fcd86426 2070 struct perf_hpp_fmt *fmt;
b1c7a8f7 2071
fcd86426
JO
2072 browser->hists = hists;
2073 browser->b.refresh = hist_browser__refresh;
2074 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2075 browser->b.seek = ui_browser__hists_seek;
2076 browser->b.use_navkeypressed = true;
2077 browser->show_headers = symbol_conf.show_hist_headers;
b1c7a8f7 2078
fcd86426
JO
2079 hists__for_each_format(hists, fmt) {
2080 perf_hpp__reset_width(fmt, hists);
2081 ++browser->b.columns;
d1b4f249 2082 }
fcd86426
JO
2083}
2084
2085struct hist_browser *hist_browser__new(struct hists *hists)
2086{
2087 struct hist_browser *browser = zalloc(sizeof(*browser));
2088
2089 if (browser)
2090 hist_browser__init(browser, hists);
d1b4f249 2091
05e8b080 2092 return browser;
d1b4f249
ACM
2093}
2094
a6ec894d
JO
2095static struct hist_browser *
2096perf_evsel_browser__new(struct perf_evsel *evsel,
2097 struct hist_browser_timer *hbt,
2098 struct perf_env *env)
2099{
2100 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2101
2102 if (browser) {
2103 browser->hbt = hbt;
2104 browser->env = env;
2105 browser->title = perf_evsel_browser_title;
2106 }
2107 return browser;
2108}
2109
dabd2012 2110void hist_browser__delete(struct hist_browser *browser)
d1b4f249 2111{
05e8b080 2112 free(browser);
d1b4f249
ACM
2113}
2114
05e8b080 2115static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 2116{
05e8b080 2117 return browser->he_selection;
d1b4f249
ACM
2118}
2119
05e8b080 2120static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 2121{
05e8b080 2122 return browser->he_selection->thread;
d1b4f249
ACM
2123}
2124
1e378ebd
TS
2125/* Check whether the browser is for 'top' or 'report' */
2126static inline bool is_report_browser(void *timer)
2127{
2128 return timer == NULL;
2129}
2130
5b91a86f 2131static int perf_evsel_browser_title(struct hist_browser *browser,
1e378ebd 2132 char *bf, size_t size)
d1b4f249 2133{
5b91a86f
JO
2134 struct hist_browser_timer *hbt = browser->hbt;
2135 struct hists *hists = browser->hists;
469917ce
ACM
2136 char unit;
2137 int printed;
05e8b080
ACM
2138 const struct dso *dso = hists->dso_filter;
2139 const struct thread *thread = hists->thread_filter;
84734b06 2140 int socket_id = hists->socket_filter;
05e8b080
ACM
2141 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2142 u64 nr_events = hists->stats.total_period;
717e263f 2143 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 2144 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
2145 char buf[512];
2146 size_t buflen = sizeof(buf);
9e207ddf
KL
2147 char ref[30] = " show reference callgraph, ";
2148 bool enable_ref = false;
717e263f 2149
f2148330
NK
2150 if (symbol_conf.filter_relative) {
2151 nr_samples = hists->stats.nr_non_filtered_samples;
2152 nr_events = hists->stats.total_non_filtered_period;
2153 }
2154
759ff497 2155 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
2156 struct perf_evsel *pos;
2157
2158 perf_evsel__group_desc(evsel, buf, buflen);
2159 ev_name = buf;
2160
2161 for_each_group_member(pos, evsel) {
4ea062ed
ACM
2162 struct hists *pos_hists = evsel__hists(pos);
2163
f2148330 2164 if (symbol_conf.filter_relative) {
4ea062ed
ACM
2165 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2166 nr_events += pos_hists->stats.total_non_filtered_period;
f2148330 2167 } else {
4ea062ed
ACM
2168 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2169 nr_events += pos_hists->stats.total_period;
f2148330 2170 }
717e263f
NK
2171 }
2172 }
cc686280 2173
9e207ddf
KL
2174 if (symbol_conf.show_ref_callgraph &&
2175 strstr(ev_name, "call-graph=no"))
2176 enable_ref = true;
cc686280
AR
2177 nr_samples = convert_unit(nr_samples, &unit);
2178 printed = scnprintf(bf, size,
9e207ddf
KL
2179 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2180 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
469917ce 2181
d1b4f249 2182
05e8b080 2183 if (hists->uid_filter_str)
0d37aa34 2184 printed += snprintf(bf + printed, size - printed,
05e8b080 2185 ", UID: %s", hists->uid_filter_str);
6962ccb3 2186 if (thread) {
fa82911a 2187 if (hists__has(hists, thread)) {
6962ccb3 2188 printed += scnprintf(bf + printed, size - printed,
469917ce 2189 ", Thread: %s(%d)",
b9c5143a 2190 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 2191 thread->tid);
6962ccb3
NK
2192 } else {
2193 printed += scnprintf(bf + printed, size - printed,
2194 ", Thread: %s",
2195 (thread->comm_set ? thread__comm_str(thread) : ""));
2196 }
2197 }
d1b4f249 2198 if (dso)
e7f01d1e 2199 printed += scnprintf(bf + printed, size - printed,
469917ce 2200 ", DSO: %s", dso->short_name);
84734b06 2201 if (socket_id > -1)
21394d94 2202 printed += scnprintf(bf + printed, size - printed,
84734b06 2203 ", Processor Socket: %d", socket_id);
1e378ebd
TS
2204 if (!is_report_browser(hbt)) {
2205 struct perf_top *top = hbt->arg;
2206
2207 if (top->zero)
2208 printed += scnprintf(bf + printed, size - printed, " [z]");
2209 }
2210
469917ce 2211 return printed;
d1b4f249
ACM
2212}
2213
24bff2dc
SE
2214static inline void free_popup_options(char **options, int n)
2215{
2216 int i;
2217
04662523
ACM
2218 for (i = 0; i < n; ++i)
2219 zfree(&options[i]);
24bff2dc
SE
2220}
2221
341487ab
FT
2222/*
2223 * Only runtime switching of perf data file will make "input_name" point
2224 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2225 * whether we need to call free() for current "input_name" during the switch.
2226 */
2227static bool is_input_name_malloced = false;
2228
2229static int switch_data_file(void)
2230{
2231 char *pwd, *options[32], *abs_path[32], *tmp;
2232 DIR *pwd_dir;
2233 int nr_options = 0, choice = -1, ret = -1;
2234 struct dirent *dent;
2235
2236 pwd = getenv("PWD");
2237 if (!pwd)
2238 return ret;
2239
2240 pwd_dir = opendir(pwd);
2241 if (!pwd_dir)
2242 return ret;
2243
2244 memset(options, 0, sizeof(options));
2245 memset(options, 0, sizeof(abs_path));
2246
2247 while ((dent = readdir(pwd_dir))) {
2248 char path[PATH_MAX];
2249 u64 magic;
2250 char *name = dent->d_name;
2251 FILE *file;
2252
2253 if (!(dent->d_type == DT_REG))
2254 continue;
2255
2256 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2257
2258 file = fopen(path, "r");
2259 if (!file)
2260 continue;
2261
2262 if (fread(&magic, 1, 8, file) < 8)
2263 goto close_file_and_continue;
2264
2265 if (is_perf_magic(magic)) {
2266 options[nr_options] = strdup(name);
2267 if (!options[nr_options])
2268 goto close_file_and_continue;
2269
2270 abs_path[nr_options] = strdup(path);
2271 if (!abs_path[nr_options]) {
74cf249d 2272 zfree(&options[nr_options]);
341487ab
FT
2273 ui__warning("Can't search all data files due to memory shortage.\n");
2274 fclose(file);
2275 break;
2276 }
2277
2278 nr_options++;
2279 }
2280
2281close_file_and_continue:
2282 fclose(file);
2283 if (nr_options >= 32) {
2284 ui__warning("Too many perf data files in PWD!\n"
2285 "Only the first 32 files will be listed.\n");
2286 break;
2287 }
2288 }
2289 closedir(pwd_dir);
2290
2291 if (nr_options) {
2292 choice = ui__popup_menu(nr_options, options);
2293 if (choice < nr_options && choice >= 0) {
2294 tmp = strdup(abs_path[choice]);
2295 if (tmp) {
2296 if (is_input_name_malloced)
2297 free((void *)input_name);
2298 input_name = tmp;
2299 is_input_name_malloced = true;
2300 ret = 0;
2301 } else
2302 ui__warning("Data switch failed due to memory shortage!\n");
2303 }
2304 }
2305
2306 free_popup_options(options, nr_options);
2307 free_popup_options(abs_path, nr_options);
2308 return ret;
2309}
2310
ea7cd592
NK
2311struct popup_action {
2312 struct thread *thread;
ea7cd592 2313 struct map_symbol ms;
84734b06 2314 int socket;
ea7cd592
NK
2315
2316 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2317};
2318
bc7cad42 2319static int
ea7cd592 2320do_annotate(struct hist_browser *browser, struct popup_action *act)
bc7cad42
NK
2321{
2322 struct perf_evsel *evsel;
2323 struct annotation *notes;
2324 struct hist_entry *he;
2325 int err;
2326
eebd0bfc 2327 if (!objdump_path && perf_env__lookup_objdump(browser->env))
bc7cad42
NK
2328 return 0;
2329
ea7cd592 2330 notes = symbol__annotation(act->ms.sym);
bc7cad42
NK
2331 if (!notes->src)
2332 return 0;
2333
2334 evsel = hists_to_evsel(browser->hists);
ea7cd592 2335 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
bc7cad42
NK
2336 he = hist_browser__selected_entry(browser);
2337 /*
2338 * offer option to annotate the other branch source or target
2339 * (if they exists) when returning from annotate
2340 */
2341 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2342 return 1;
2343
2344 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2345 if (err)
2346 ui_browser__handle_resize(&browser->b);
2347 return 0;
2348}
2349
2350static int
ea7cd592
NK
2351add_annotate_opt(struct hist_browser *browser __maybe_unused,
2352 struct popup_action *act, char **optstr,
2353 struct map *map, struct symbol *sym)
bc7cad42 2354{
ea7cd592
NK
2355 if (sym == NULL || map->dso->annotate_warned)
2356 return 0;
2357
2358 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2359 return 0;
2360
2361 act->ms.map = map;
2362 act->ms.sym = sym;
2363 act->fn = do_annotate;
2364 return 1;
2365}
2366
2367static int
2368do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2369{
2370 struct thread *thread = act->thread;
2371
7cecb7fe
JO
2372 if ((!hists__has(browser->hists, thread) &&
2373 !hists__has(browser->hists, comm)) || thread == NULL)
599a2f38
NK
2374 return 0;
2375
bc7cad42
NK
2376 if (browser->hists->thread_filter) {
2377 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2378 perf_hpp__set_elide(HISTC_THREAD, false);
2379 thread__zput(browser->hists->thread_filter);
2380 ui_helpline__pop();
2381 } else {
fa82911a 2382 if (hists__has(browser->hists, thread)) {
6962ccb3
NK
2383 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2384 thread->comm_set ? thread__comm_str(thread) : "",
2385 thread->tid);
2386 } else {
2387 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2388 thread->comm_set ? thread__comm_str(thread) : "");
2389 }
2390
bc7cad42
NK
2391 browser->hists->thread_filter = thread__get(thread);
2392 perf_hpp__set_elide(HISTC_THREAD, false);
2393 pstack__push(browser->pstack, &browser->hists->thread_filter);
2394 }
2395
2396 hists__filter_by_thread(browser->hists);
2397 hist_browser__reset(browser);
2398 return 0;
2399}
2400
2401static int
ea7cd592
NK
2402add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2403 char **optstr, struct thread *thread)
2404{
6962ccb3
NK
2405 int ret;
2406
7cecb7fe
JO
2407 if ((!hists__has(browser->hists, thread) &&
2408 !hists__has(browser->hists, comm)) || thread == NULL)
ea7cd592
NK
2409 return 0;
2410
fa82911a 2411 if (hists__has(browser->hists, thread)) {
6962ccb3
NK
2412 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2413 browser->hists->thread_filter ? "out of" : "into",
2414 thread->comm_set ? thread__comm_str(thread) : "",
2415 thread->tid);
2416 } else {
2417 ret = asprintf(optstr, "Zoom %s %s thread",
2418 browser->hists->thread_filter ? "out of" : "into",
2419 thread->comm_set ? thread__comm_str(thread) : "");
2420 }
2421 if (ret < 0)
ea7cd592
NK
2422 return 0;
2423
2424 act->thread = thread;
2425 act->fn = do_zoom_thread;
2426 return 1;
2427}
2428
2429static int
2430do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
bc7cad42 2431{
045b80dd 2432 struct map *map = act->ms.map;
ea7cd592 2433
69849fc5 2434 if (!hists__has(browser->hists, dso) || map == NULL)
599a2f38
NK
2435 return 0;
2436
bc7cad42
NK
2437 if (browser->hists->dso_filter) {
2438 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2439 perf_hpp__set_elide(HISTC_DSO, false);
2440 browser->hists->dso_filter = NULL;
2441 ui_helpline__pop();
2442 } else {
7727a925 2443 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
045b80dd
ACM
2444 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2445 browser->hists->dso_filter = map->dso;
bc7cad42
NK
2446 perf_hpp__set_elide(HISTC_DSO, true);
2447 pstack__push(browser->pstack, &browser->hists->dso_filter);
2448 }
2449
2450 hists__filter_by_dso(browser->hists);
2451 hist_browser__reset(browser);
2452 return 0;
2453}
2454
2455static int
ea7cd592 2456add_dso_opt(struct hist_browser *browser, struct popup_action *act,
045b80dd 2457 char **optstr, struct map *map)
bc7cad42 2458{
69849fc5 2459 if (!hists__has(browser->hists, dso) || map == NULL)
ea7cd592
NK
2460 return 0;
2461
2462 if (asprintf(optstr, "Zoom %s %s DSO",
2463 browser->hists->dso_filter ? "out of" : "into",
045b80dd 2464 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
ea7cd592
NK
2465 return 0;
2466
045b80dd 2467 act->ms.map = map;
ea7cd592
NK
2468 act->fn = do_zoom_dso;
2469 return 1;
2470}
2471
2472static int
2473do_browse_map(struct hist_browser *browser __maybe_unused,
2474 struct popup_action *act)
2475{
2476 map__browse(act->ms.map);
bc7cad42
NK
2477 return 0;
2478}
2479
ea7cd592 2480static int
69849fc5 2481add_map_opt(struct hist_browser *browser,
ea7cd592
NK
2482 struct popup_action *act, char **optstr, struct map *map)
2483{
69849fc5 2484 if (!hists__has(browser->hists, dso) || map == NULL)
ea7cd592
NK
2485 return 0;
2486
2487 if (asprintf(optstr, "Browse map details") < 0)
2488 return 0;
2489
2490 act->ms.map = map;
2491 act->fn = do_browse_map;
2492 return 1;
2493}
2494
bc7cad42
NK
2495static int
2496do_run_script(struct hist_browser *browser __maybe_unused,
ea7cd592 2497 struct popup_action *act)
bc7cad42
NK
2498{
2499 char script_opt[64];
2500 memset(script_opt, 0, sizeof(script_opt));
2501
ea7cd592 2502 if (act->thread) {
bc7cad42 2503 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
ea7cd592
NK
2504 thread__comm_str(act->thread));
2505 } else if (act->ms.sym) {
bc7cad42 2506 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
ea7cd592 2507 act->ms.sym->name);
bc7cad42
NK
2508 }
2509
2510 script_browse(script_opt);
2511 return 0;
2512}
2513
2514static int
ea7cd592
NK
2515add_script_opt(struct hist_browser *browser __maybe_unused,
2516 struct popup_action *act, char **optstr,
2517 struct thread *thread, struct symbol *sym)
2518{
2519 if (thread) {
2520 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2521 thread__comm_str(thread)) < 0)
2522 return 0;
2523 } else if (sym) {
2524 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2525 sym->name) < 0)
2526 return 0;
2527 } else {
2528 if (asprintf(optstr, "Run scripts for all samples") < 0)
2529 return 0;
2530 }
2531
2532 act->thread = thread;
2533 act->ms.sym = sym;
2534 act->fn = do_run_script;
2535 return 1;
2536}
2537
2538static int
2539do_switch_data(struct hist_browser *browser __maybe_unused,
2540 struct popup_action *act __maybe_unused)
bc7cad42
NK
2541{
2542 if (switch_data_file()) {
2543 ui__warning("Won't switch the data files due to\n"
2544 "no valid data file get selected!\n");
ea7cd592 2545 return 0;
bc7cad42
NK
2546 }
2547
2548 return K_SWITCH_INPUT_DATA;
2549}
2550
ea7cd592
NK
2551static int
2552add_switch_opt(struct hist_browser *browser,
2553 struct popup_action *act, char **optstr)
2554{
2555 if (!is_report_browser(browser->hbt))
2556 return 0;
2557
2558 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2559 return 0;
2560
2561 act->fn = do_switch_data;
2562 return 1;
2563}
2564
2565static int
2566do_exit_browser(struct hist_browser *browser __maybe_unused,
2567 struct popup_action *act __maybe_unused)
2568{
2569 return 0;
2570}
2571
2572static int
2573add_exit_opt(struct hist_browser *browser __maybe_unused,
2574 struct popup_action *act, char **optstr)
2575{
2576 if (asprintf(optstr, "Exit") < 0)
2577 return 0;
2578
2579 act->fn = do_exit_browser;
2580 return 1;
2581}
2582
84734b06
KL
2583static int
2584do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2585{
35a634f7 2586 if (!hists__has(browser->hists, socket) || act->socket < 0)
599a2f38
NK
2587 return 0;
2588
84734b06
KL
2589 if (browser->hists->socket_filter > -1) {
2590 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2591 browser->hists->socket_filter = -1;
2592 perf_hpp__set_elide(HISTC_SOCKET, false);
2593 } else {
2594 browser->hists->socket_filter = act->socket;
2595 perf_hpp__set_elide(HISTC_SOCKET, true);
2596 pstack__push(browser->pstack, &browser->hists->socket_filter);
2597 }
2598
2599 hists__filter_by_socket(browser->hists);
2600 hist_browser__reset(browser);
2601 return 0;
2602}
2603
2604static int
2605add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2606 char **optstr, int socket_id)
2607{
35a634f7 2608 if (!hists__has(browser->hists, socket) || socket_id < 0)
84734b06
KL
2609 return 0;
2610
2611 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2612 (browser->hists->socket_filter > -1) ? "out of" : "into",
2613 socket_id) < 0)
2614 return 0;
2615
2616 act->socket = socket_id;
2617 act->fn = do_zoom_socket;
2618 return 1;
2619}
2620
112f761f 2621static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
2622{
2623 u64 nr_entries = 0;
2624 struct rb_node *nd = rb_first(&hb->hists->entries);
2625
f5b763fe 2626 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
268397cb
NK
2627 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2628 return;
2629 }
2630
14135663 2631 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 2632 nr_entries++;
f5b763fe 2633 nd = rb_hierarchy_next(nd);
064f1981
NK
2634 }
2635
112f761f 2636 hb->nr_non_filtered_entries = nr_entries;
f5b763fe 2637 hb->nr_hierarchy_entries = nr_entries;
064f1981 2638}
341487ab 2639
b62e8dfc
NK
2640static void hist_browser__update_percent_limit(struct hist_browser *hb,
2641 double percent)
2642{
2643 struct hist_entry *he;
2644 struct rb_node *nd = rb_first(&hb->hists->entries);
2645 u64 total = hists__total_period(hb->hists);
2646 u64 min_callchain_hits = total * (percent / 100);
2647
2648 hb->min_pcnt = callchain_param.min_percent = percent;
2649
b62e8dfc
NK
2650 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2651 he = rb_entry(nd, struct hist_entry, rb_node);
2652
79dded87
NK
2653 if (he->has_no_entry) {
2654 he->has_no_entry = false;
2655 he->nr_rows = 0;
2656 }
2657
d0506edb
NK
2658 if (!he->leaf || !symbol_conf.use_callchain)
2659 goto next;
2660
b62e8dfc
NK
2661 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2662 total = he->stat.period;
2663
2664 if (symbol_conf.cumulate_callchain)
2665 total = he->stat_acc->period;
2666
2667 min_callchain_hits = total * (percent / 100);
2668 }
2669
2670 callchain_param.sort(&he->sorted_chain, he->callchain,
2671 min_callchain_hits, &callchain_param);
2672
d0506edb 2673next:
201fde73 2674 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
d0506edb 2675
b62e8dfc
NK
2676 /* force to re-evaluate folding state of callchains */
2677 he->init_have_children = false;
492b1010 2678 hist_entry__set_folding(he, hb, false);
b62e8dfc
NK
2679 }
2680}
2681
34958544 2682static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 2683 const char *helpline,
81cce8de 2684 bool left_exits,
68d80758 2685 struct hist_browser_timer *hbt,
064f1981 2686 float min_pcnt,
ce80d3be 2687 struct perf_env *env)
d1b4f249 2688{
4ea062ed 2689 struct hists *hists = evsel__hists(evsel);
a6ec894d 2690 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
a68c2c58 2691 struct branch_info *bi;
f2b487db
NK
2692#define MAX_OPTIONS 16
2693 char *options[MAX_OPTIONS];
ea7cd592 2694 struct popup_action actions[MAX_OPTIONS];
24bff2dc 2695 int nr_options = 0;
d1b4f249 2696 int key = -1;
938a23ae 2697 char buf[64];
9783adf7 2698 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 2699
e8e684a5
NK
2700#define HIST_BROWSER_HELP_COMMON \
2701 "h/?/F1 Show this window\n" \
2702 "UP/DOWN/PGUP\n" \
2703 "PGDN/SPACE Navigate\n" \
2704 "q/ESC/CTRL+C Exit browser\n\n" \
2705 "For multiple event sessions:\n\n" \
2706 "TAB/UNTAB Switch events\n\n" \
2707 "For symbolic views (--sort has sym):\n\n" \
7727a925
ACM
2708 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2709 "ESC Zoom out\n" \
e8e684a5
NK
2710 "a Annotate current symbol\n" \
2711 "C Collapse all callchains\n" \
2712 "d Zoom into current DSO\n" \
2713 "E Expand all callchains\n" \
105eb30f 2714 "F Toggle percentage of filtered entries\n" \
025bf7ea 2715 "H Display column headers\n" \
b62e8dfc 2716 "L Change percent limit\n" \
31eb4360 2717 "m Display context menu\n" \
84734b06 2718 "S Zoom into current Processor Socket\n" \
e8e684a5
NK
2719
2720 /* help messages are sorted by lexical order of the hotkey */
2721 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 2722 "i Show header information\n"
e8e684a5
NK
2723 "P Print histograms to perf.hist.N\n"
2724 "r Run available scripts\n"
2725 "s Switch to another data file in PWD\n"
2726 "t Zoom into current Thread\n"
2727 "V Verbose (DSO names in callchains, etc)\n"
2728 "/ Filter symbol by name";
2729 const char top_help[] = HIST_BROWSER_HELP_COMMON
2730 "P Print histograms to perf.hist.N\n"
2731 "t Zoom into current Thread\n"
2732 "V Verbose (DSO names in callchains, etc)\n"
42337a22 2733 "z Toggle zeroing of samples\n"
fbb7997e 2734 "f Enable/Disable events\n"
e8e684a5
NK
2735 "/ Filter symbol by name";
2736
d1b4f249
ACM
2737 if (browser == NULL)
2738 return -1;
2739
ed426915
NK
2740 /* reset abort key so that it can get Ctrl-C as a key */
2741 SLang_reset_tty();
2742 SLang_init_tty(0, 0, 0);
2743
03905048 2744 if (min_pcnt)
064f1981 2745 browser->min_pcnt = min_pcnt;
03905048 2746 hist_browser__update_nr_entries(browser);
064f1981 2747
84734b06 2748 browser->pstack = pstack__new(3);
01f00a1c 2749 if (browser->pstack == NULL)
d1b4f249
ACM
2750 goto out;
2751
2752 ui_helpline__push(helpline);
2753
24bff2dc 2754 memset(options, 0, sizeof(options));
ea7cd592 2755 memset(actions, 0, sizeof(actions));
24bff2dc 2756
5b591669
NK
2757 if (symbol_conf.col_width_list_str)
2758 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2759
d1b4f249 2760 while (1) {
f3b623b8 2761 struct thread *thread = NULL;
045b80dd 2762 struct map *map = NULL;
ea7cd592 2763 int choice = 0;
84734b06 2764 int socked_id = -1;
d1b4f249 2765
24bff2dc
SE
2766 nr_options = 0;
2767
5f00b0f4 2768 key = hist_browser__run(browser, helpline);
d1b4f249 2769
60098917
ACM
2770 if (browser->he_selection != NULL) {
2771 thread = hist_browser__selected_thread(browser);
045b80dd 2772 map = browser->selection->map;
84734b06 2773 socked_id = browser->he_selection->socket;
60098917 2774 }
b50e003d 2775 switch (key) {
cf958003
ACM
2776 case K_TAB:
2777 case K_UNTAB:
e4419b8e
DA
2778 if (nr_events == 1)
2779 continue;
b50e003d
ACM
2780 /*
2781 * Exit the browser, let hists__browser_tree
2782 * go to the next or previous
2783 */
2784 goto out_free_stack;
2785 case 'a':
2e0453af 2786 if (!hists__has(hists, sym)) {
7b27509f 2787 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 2788 "Annotation is only available for symbolic views, "
a68c2c58 2789 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
2790 continue;
2791 }
2792
60098917 2793 if (browser->selection == NULL ||
db9a9cbc 2794 browser->selection->sym == NULL ||
b50e003d 2795 browser->selection->map->dso->annotate_warned)
d1b4f249 2796 continue;
bc7cad42 2797
ea7cd592
NK
2798 actions->ms.map = browser->selection->map;
2799 actions->ms.sym = browser->selection->sym;
2800 do_annotate(browser, actions);
bc7cad42 2801 continue;
aff3f3f6
ACM
2802 case 'P':
2803 hist_browser__dump(browser);
2804 continue;
b50e003d 2805 case 'd':
fae00650 2806 actions->ms.map = map;
ea7cd592 2807 do_zoom_dso(browser, actions);
bc7cad42 2808 continue;
a7cb8863
ACM
2809 case 'V':
2810 browser->show_dso = !browser->show_dso;
2811 continue;
b50e003d 2812 case 't':
ea7cd592
NK
2813 actions->thread = thread;
2814 do_zoom_thread(browser, actions);
bc7cad42 2815 continue;
84734b06
KL
2816 case 'S':
2817 actions->socket = socked_id;
2818 do_zoom_socket(browser, actions);
2819 continue;
5a5626b1 2820 case '/':
938a23ae 2821 if (ui_browser__input_window("Symbol to show",
4aa8e454
ACM
2822 "Please enter the name of symbol you want to see.\n"
2823 "To remove the filter later, press / + ENTER.",
938a23ae
NK
2824 buf, "ENTER: OK, ESC: Cancel",
2825 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
2826 hists->symbol_filter_str = *buf ? buf : NULL;
2827 hists__filter_by_symbol(hists);
938a23ae
NK
2828 hist_browser__reset(browser);
2829 }
2830 continue;
cdbab7c2 2831 case 'r':
ea7cd592
NK
2832 if (is_report_browser(hbt)) {
2833 actions->thread = NULL;
2834 actions->ms.sym = NULL;
2835 do_run_script(browser, actions);
2836 }
c77d8d70 2837 continue;
341487ab 2838 case 's':
bc7cad42 2839 if (is_report_browser(hbt)) {
ea7cd592 2840 key = do_switch_data(browser, actions);
bc7cad42
NK
2841 if (key == K_SWITCH_INPUT_DATA)
2842 goto out_free_stack;
2843 }
341487ab 2844 continue;
6dd60135
NK
2845 case 'i':
2846 /* env->arch is NULL for live-mode (i.e. perf top) */
2847 if (env->arch)
2848 tui__header_window(env);
2849 continue;
105eb30f
NK
2850 case 'F':
2851 symbol_conf.filter_relative ^= 1;
2852 continue;
42337a22
NK
2853 case 'z':
2854 if (!is_report_browser(hbt)) {
2855 struct perf_top *top = hbt->arg;
2856
2857 top->zero = !top->zero;
2858 }
2859 continue;
b62e8dfc
NK
2860 case 'L':
2861 if (ui_browser__input_window("Percent Limit",
2862 "Please enter the value you want to hide entries under that percent.",
2863 buf, "ENTER: OK, ESC: Cancel",
2864 delay_secs * 2) == K_ENTER) {
2865 char *end;
2866 double new_percent = strtod(buf, &end);
2867
2868 if (new_percent < 0 || new_percent > 100) {
2869 ui_browser__warning(&browser->b, delay_secs * 2,
2870 "Invalid percent: %.2f", new_percent);
2871 continue;
2872 }
2873
2874 hist_browser__update_percent_limit(browser, new_percent);
2875 hist_browser__reset(browser);
2876 }
2877 continue;
cf958003 2878 case K_F1:
b50e003d
ACM
2879 case 'h':
2880 case '?':
4610e413 2881 ui_browser__help_window(&browser->b,
e8e684a5 2882 is_report_browser(hbt) ? report_help : top_help);
b50e003d 2883 continue;
cf958003
ACM
2884 case K_ENTER:
2885 case K_RIGHT:
31eb4360 2886 case 'm':
b50e003d
ACM
2887 /* menu */
2888 break;
63ab1749 2889 case K_ESC:
cf958003 2890 case K_LEFT: {
b50e003d 2891 const void *top;
d1b4f249 2892
01f00a1c 2893 if (pstack__empty(browser->pstack)) {
7f0030b2
ACM
2894 /*
2895 * Go back to the perf_evsel_menu__run or other user
2896 */
2897 if (left_exits)
2898 goto out_free_stack;
63ab1749
ACM
2899
2900 if (key == K_ESC &&
2901 ui_browser__dialog_yesno(&browser->b,
2902 "Do you really want to exit?"))
2903 goto out_free_stack;
2904
d1b4f249 2905 continue;
7f0030b2 2906 }
6422184b 2907 top = pstack__peek(browser->pstack);
bc7cad42 2908 if (top == &browser->hists->dso_filter) {
6422184b
NK
2909 /*
2910 * No need to set actions->dso here since
2911 * it's just to remove the current filter.
2912 * Ditto for thread below.
2913 */
2914 do_zoom_dso(browser, actions);
84734b06 2915 } else if (top == &browser->hists->thread_filter) {
6422184b 2916 do_zoom_thread(browser, actions);
84734b06
KL
2917 } else if (top == &browser->hists->socket_filter) {
2918 do_zoom_socket(browser, actions);
2919 }
b50e003d
ACM
2920 continue;
2921 }
ed7e5662
ACM
2922 case 'q':
2923 case CTRL('c'):
516e5368 2924 goto out_free_stack;
fbb7997e 2925 case 'f':
13d1e536
NK
2926 if (!is_report_browser(hbt)) {
2927 struct perf_top *top = hbt->arg;
2928
2929 perf_evlist__toggle_enable(top->evlist);
2930 /*
2931 * No need to refresh, resort/decay histogram
2932 * entries if we are not collecting samples:
2933 */
2934 if (top->evlist->enabled) {
2935 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2936 hbt->refresh = delay_secs;
2937 } else {
2938 helpline = "Press 'f' again to re-enable the events";
2939 hbt->refresh = 0;
2940 }
2941 continue;
2942 }
3e323dc0 2943 /* Fall thru */
ed7e5662 2944 default:
3e323dc0 2945 helpline = "Press '?' for help on key bindings";
ed7e5662 2946 continue;
d1b4f249
ACM
2947 }
2948
2e0453af 2949 if (!hists__has(hists, sym) || browser->selection == NULL)
0ba332f7
ACM
2950 goto skip_annotation;
2951
55369fc1 2952 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58 2953 bi = browser->he_selection->branch_info;
0ba332f7
ACM
2954
2955 if (bi == NULL)
2956 goto skip_annotation;
2957
ea7cd592
NK
2958 nr_options += add_annotate_opt(browser,
2959 &actions[nr_options],
2960 &options[nr_options],
2961 bi->from.map,
2962 bi->from.sym);
2963 if (bi->to.sym != bi->from.sym)
2964 nr_options += add_annotate_opt(browser,
2965 &actions[nr_options],
2966 &options[nr_options],
2967 bi->to.map,
2968 bi->to.sym);
a68c2c58 2969 } else {
ea7cd592
NK
2970 nr_options += add_annotate_opt(browser,
2971 &actions[nr_options],
2972 &options[nr_options],
2973 browser->selection->map,
2974 browser->selection->sym);
a68c2c58 2975 }
0ba332f7 2976skip_annotation:
ea7cd592
NK
2977 nr_options += add_thread_opt(browser, &actions[nr_options],
2978 &options[nr_options], thread);
2979 nr_options += add_dso_opt(browser, &actions[nr_options],
045b80dd 2980 &options[nr_options], map);
ea7cd592
NK
2981 nr_options += add_map_opt(browser, &actions[nr_options],
2982 &options[nr_options],
bd315aab
WN
2983 browser->selection ?
2984 browser->selection->map : NULL);
84734b06
KL
2985 nr_options += add_socket_opt(browser, &actions[nr_options],
2986 &options[nr_options],
2987 socked_id);
cdbab7c2 2988 /* perf script support */
b1baae89
NK
2989 if (!is_report_browser(hbt))
2990 goto skip_scripting;
2991
cdbab7c2 2992 if (browser->he_selection) {
fa82911a 2993 if (hists__has(hists, thread) && thread) {
2eafd410
NK
2994 nr_options += add_script_opt(browser,
2995 &actions[nr_options],
2996 &options[nr_options],
2997 thread, NULL);
2998 }
bd315aab
WN
2999 /*
3000 * Note that browser->selection != NULL
3001 * when browser->he_selection is not NULL,
3002 * so we don't need to check browser->selection
3003 * before fetching browser->selection->sym like what
3004 * we do before fetching browser->selection->map.
3005 *
3006 * See hist_browser__show_entry.
3007 */
2e0453af 3008 if (hists__has(hists, sym) && browser->selection->sym) {
c221acb0
NK
3009 nr_options += add_script_opt(browser,
3010 &actions[nr_options],
3011 &options[nr_options],
3012 NULL, browser->selection->sym);
3013 }
cdbab7c2 3014 }
ea7cd592
NK
3015 nr_options += add_script_opt(browser, &actions[nr_options],
3016 &options[nr_options], NULL, NULL);
3017 nr_options += add_switch_opt(browser, &actions[nr_options],
3018 &options[nr_options]);
b1baae89 3019skip_scripting:
ea7cd592
NK
3020 nr_options += add_exit_opt(browser, &actions[nr_options],
3021 &options[nr_options]);
d1b4f249 3022
ea7cd592
NK
3023 do {
3024 struct popup_action *act;
68d80758 3025
ea7cd592
NK
3026 choice = ui__popup_menu(nr_options, options);
3027 if (choice == -1 || choice >= nr_options)
3028 break;
a68c2c58 3029
ea7cd592
NK
3030 act = &actions[choice];
3031 key = act->fn(browser, act);
3032 } while (key == 1);
a68c2c58 3033
ea7cd592
NK
3034 if (key == K_SWITCH_INPUT_DATA)
3035 break;
d1b4f249
ACM
3036 }
3037out_free_stack:
01f00a1c 3038 pstack__delete(browser->pstack);
d1b4f249
ACM
3039out:
3040 hist_browser__delete(browser);
f2b487db 3041 free_popup_options(options, MAX_OPTIONS);
d1b4f249
ACM
3042 return key;
3043}
3044
7f0030b2
ACM
3045struct perf_evsel_menu {
3046 struct ui_browser b;
3047 struct perf_evsel *selection;
7b27509f 3048 bool lost_events, lost_events_warned;
064f1981 3049 float min_pcnt;
ce80d3be 3050 struct perf_env *env;
7f0030b2
ACM
3051};
3052
3053static void perf_evsel_menu__write(struct ui_browser *browser,
3054 void *entry, int row)
3055{
3056 struct perf_evsel_menu *menu = container_of(browser,
3057 struct perf_evsel_menu, b);
3058 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
4ea062ed 3059 struct hists *hists = evsel__hists(evsel);
7f0030b2 3060 bool current_entry = ui_browser__is_current_entry(browser, row);
4ea062ed 3061 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 3062 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 3063 char bf[256], unit;
7b27509f
ACM
3064 const char *warn = " ";
3065 size_t printed;
7f0030b2
ACM
3066
3067 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3068 HE_COLORSET_NORMAL);
3069
759ff497 3070 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
3071 struct perf_evsel *pos;
3072
3073 ev_name = perf_evsel__group_name(evsel);
3074
3075 for_each_group_member(pos, evsel) {
4ea062ed
ACM
3076 struct hists *pos_hists = evsel__hists(pos);
3077 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
717e263f
NK
3078 }
3079 }
3080
7f0030b2 3081 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 3082 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f 3083 unit, unit == ' ' ? "" : " ", ev_name);
517dfdb3 3084 ui_browser__printf(browser, "%s", bf);
7b27509f 3085
4ea062ed 3086 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
7b27509f
ACM
3087 if (nr_events != 0) {
3088 menu->lost_events = true;
3089 if (!current_entry)
3090 ui_browser__set_color(browser, HE_COLORSET_TOP);
3091 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
3092 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3093 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
3094 warn = bf;
3095 }
3096
26270a00 3097 ui_browser__write_nstring(browser, warn, browser->width - printed);
7f0030b2
ACM
3098
3099 if (current_entry)
3100 menu->selection = evsel;
3101}
3102
34958544
ACM
3103static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3104 int nr_events, const char *help,
9783adf7 3105 struct hist_browser_timer *hbt)
d1b4f249 3106{
7f0030b2 3107 struct perf_evlist *evlist = menu->b.priv;
e248de33 3108 struct perf_evsel *pos;
dd00d486 3109 const char *title = "Available samples";
9783adf7 3110 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 3111 int key;
d1b4f249 3112
7f0030b2
ACM
3113 if (ui_browser__show(&menu->b, title,
3114 "ESC: exit, ENTER|->: Browse histograms") < 0)
3115 return -1;
3116
7f0030b2 3117 while (1) {
3af6e338 3118 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
3119
3120 switch (key) {
cf958003 3121 case K_TIMER:
9783adf7 3122 hbt->timer(hbt->arg);
7b27509f
ACM
3123
3124 if (!menu->lost_events_warned && menu->lost_events) {
3125 ui_browser__warn_lost_events(&menu->b);
3126 menu->lost_events_warned = true;
3127 }
81cce8de 3128 continue;
cf958003
ACM
3129 case K_RIGHT:
3130 case K_ENTER:
7f0030b2
ACM
3131 if (!menu->selection)
3132 continue;
3133 pos = menu->selection;
3134browse_hists:
18eaf0b8
ACM
3135 perf_evlist__set_selected(evlist, pos);
3136 /*
3137 * Give the calling tool a chance to populate the non
3138 * default evsel resorted hists tree.
3139 */
9783adf7
NK
3140 if (hbt)
3141 hbt->timer(hbt->arg);
34958544 3142 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 3143 true, hbt,
064f1981 3144 menu->min_pcnt,
68d80758 3145 menu->env);
7f0030b2 3146 ui_browser__show_title(&menu->b, title);
18eaf0b8 3147 switch (key) {
cf958003 3148 case K_TAB:
18eaf0b8 3149 if (pos->node.next == &evlist->entries)
9a354cdc 3150 pos = perf_evlist__first(evlist);
18eaf0b8 3151 else
9a354cdc 3152 pos = perf_evsel__next(pos);
18eaf0b8 3153 goto browse_hists;
cf958003 3154 case K_UNTAB:
18eaf0b8 3155 if (pos->node.prev == &evlist->entries)
9a354cdc 3156 pos = perf_evlist__last(evlist);
18eaf0b8 3157 else
d87fcb4a 3158 pos = perf_evsel__prev(pos);
18eaf0b8 3159 goto browse_hists;
341487ab 3160 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
3161 case 'q':
3162 case CTRL('c'):
3163 goto out;
63ab1749 3164 case K_ESC:
18eaf0b8
ACM
3165 default:
3166 continue;
3167 }
cf958003 3168 case K_LEFT:
7f0030b2 3169 continue;
cf958003 3170 case K_ESC:
4610e413
ACM
3171 if (!ui_browser__dialog_yesno(&menu->b,
3172 "Do you really want to exit?"))
ed7e5662
ACM
3173 continue;
3174 /* Fall thru */
7f0030b2
ACM
3175 case 'q':
3176 case CTRL('c'):
3177 goto out;
d1b4f249 3178 default:
18eaf0b8 3179 continue;
d1b4f249
ACM
3180 }
3181 }
3182
7f0030b2
ACM
3183out:
3184 ui_browser__hide(&menu->b);
3185 return key;
3186}
3187
316c7136 3188static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
3189 void *entry)
3190{
3191 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3192
3193 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3194 return true;
3195
3196 return false;
3197}
3198
7f0030b2 3199static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 3200 int nr_entries, const char *help,
68d80758 3201 struct hist_browser_timer *hbt,
064f1981 3202 float min_pcnt,
ce80d3be 3203 struct perf_env *env)
7f0030b2
ACM
3204{
3205 struct perf_evsel *pos;
3206 struct perf_evsel_menu menu = {
3207 .b = {
3208 .entries = &evlist->entries,
3209 .refresh = ui_browser__list_head_refresh,
3210 .seek = ui_browser__list_head_seek,
3211 .write = perf_evsel_menu__write,
fc24d7c2
NK
3212 .filter = filter_group_entries,
3213 .nr_entries = nr_entries,
7f0030b2
ACM
3214 .priv = evlist,
3215 },
064f1981 3216 .min_pcnt = min_pcnt,
68d80758 3217 .env = env,
7f0030b2
ACM
3218 };
3219
3220 ui_helpline__push("Press ESC to exit");
3221
e5cadb93 3222 evlist__for_each_entry(evlist, pos) {
7289f83c 3223 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
3224 size_t line_len = strlen(ev_name) + 7;
3225
3226 if (menu.b.width < line_len)
3227 menu.b.width = line_len;
7f0030b2
ACM
3228 }
3229
fc24d7c2 3230 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
3231}
3232
81cce8de 3233int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 3234 struct hist_browser_timer *hbt,
064f1981 3235 float min_pcnt,
ce80d3be 3236 struct perf_env *env)
7f0030b2 3237{
fc24d7c2
NK
3238 int nr_entries = evlist->nr_entries;
3239
3240single_entry:
3241 if (nr_entries == 1) {
9a354cdc 3242 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
3243
3244 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 3245 false, hbt, min_pcnt,
064f1981 3246 env);
7f0030b2
ACM
3247 }
3248
fc24d7c2
NK
3249 if (symbol_conf.event_group) {
3250 struct perf_evsel *pos;
3251
3252 nr_entries = 0;
e5cadb93 3253 evlist__for_each_entry(evlist, pos) {
fc24d7c2
NK
3254 if (perf_evsel__is_group_leader(pos))
3255 nr_entries++;
0050f7aa 3256 }
fc24d7c2
NK
3257
3258 if (nr_entries == 1)
3259 goto single_entry;
3260 }
3261
3262 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 3263 hbt, min_pcnt, env);
d1b4f249 3264}
This page took 0.381129 seconds and 5 git commands to generate.