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