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