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