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