perf tools: Save column length in perf_hpp_fmt
[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
05e8b080
ACM
231 list_for_each_entry(chain, &node->val, list)
232 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
d1b4f249 233
05e8b080 234 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
235}
236
05e8b080 237static void callchain__init_have_children(struct rb_root *root)
d1b4f249
ACM
238{
239 struct rb_node *nd;
240
05e8b080 241 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
242 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
243 callchain_node__init_have_children(node);
244 }
245}
246
05e8b080 247static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 248{
05e8b080
ACM
249 if (!he->init_have_children) {
250 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
251 callchain__init_have_children(&he->sorted_chain);
252 he->init_have_children = true;
d1b4f249
ACM
253 }
254}
255
05e8b080 256static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 257{
05e8b080
ACM
258 if (map_symbol__toggle_fold(browser->selection)) {
259 struct hist_entry *he = browser->he_selection;
d1b4f249
ACM
260
261 hist_entry__init_have_children(he);
c3b78952
NK
262 browser->b.nr_entries -= he->nr_rows;
263 browser->nr_callchain_rows -= he->nr_rows;
d1b4f249
ACM
264
265 if (he->ms.unfolded)
266 he->nr_rows = callchain__count_rows(&he->sorted_chain);
267 else
268 he->nr_rows = 0;
c3b78952
NK
269
270 browser->b.nr_entries += he->nr_rows;
271 browser->nr_callchain_rows += he->nr_rows;
d1b4f249
ACM
272
273 return true;
274 }
275
276 /* If it doesn't have children, no toggling performed */
277 return false;
278}
279
05e8b080 280static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
281{
282 int n = 0;
283 struct rb_node *nd;
284
05e8b080 285 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
286 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
287 struct callchain_list *chain;
288 bool has_children = false;
289
290 list_for_each_entry(chain, &child->val, list) {
291 ++n;
292 map_symbol__set_folding(&chain->ms, unfold);
293 has_children = chain->ms.has_children;
294 }
295
296 if (has_children)
297 n += callchain_node__set_folding_rb_tree(child, unfold);
298 }
299
300 return n;
301}
302
303static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
304{
305 struct callchain_list *chain;
306 bool has_children = false;
307 int n = 0;
308
309 list_for_each_entry(chain, &node->val, list) {
310 ++n;
311 map_symbol__set_folding(&chain->ms, unfold);
312 has_children = chain->ms.has_children;
313 }
314
315 if (has_children)
316 n += callchain_node__set_folding_rb_tree(node, unfold);
317
318 return n;
319}
320
321static int callchain__set_folding(struct rb_root *chain, bool unfold)
322{
323 struct rb_node *nd;
324 int n = 0;
325
326 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
327 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
328 n += callchain_node__set_folding(node, unfold);
329 }
330
331 return n;
332}
333
05e8b080 334static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
3c916cc2 335{
05e8b080
ACM
336 hist_entry__init_have_children(he);
337 map_symbol__set_folding(&he->ms, unfold);
3c916cc2 338
05e8b080
ACM
339 if (he->ms.has_children) {
340 int n = callchain__set_folding(&he->sorted_chain, unfold);
341 he->nr_rows = unfold ? n : 0;
3c916cc2 342 } else
05e8b080 343 he->nr_rows = 0;
3c916cc2
ACM
344}
345
c3b78952
NK
346static void
347__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2
ACM
348{
349 struct rb_node *nd;
c3b78952 350 struct hists *hists = browser->hists;
3c916cc2 351
c3b78952 352 for (nd = rb_first(&hists->entries);
14135663 353 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
c3b78952 354 nd = rb_next(nd)) {
3c916cc2
ACM
355 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
356 hist_entry__set_folding(he, unfold);
c3b78952 357 browser->nr_callchain_rows += he->nr_rows;
3c916cc2
ACM
358 }
359}
360
05e8b080 361static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 362{
c3b78952
NK
363 browser->nr_callchain_rows = 0;
364 __hist_browser__set_folding(browser, unfold);
365
366 browser->b.nr_entries = hist_browser__nr_entries(browser);
3c916cc2 367 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 368 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
369}
370
7b27509f
ACM
371static void ui_browser__warn_lost_events(struct ui_browser *browser)
372{
373 ui_browser__warning(browser, 4,
374 "Events are being lost, check IO/CPU overload!\n\n"
375 "You may want to run 'perf' using a RT scheduler policy:\n\n"
376 " perf top -r 80\n\n"
377 "Or reduce the sampling frequency.");
378}
379
dd00d486 380static int hist_browser__run(struct hist_browser *browser,
9783adf7 381 struct hist_browser_timer *hbt)
d1b4f249 382{
b50e003d 383 int key;
81cce8de 384 char title[160];
9783adf7 385 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 386
05e8b080 387 browser->b.entries = &browser->hists->entries;
c3b78952 388 browser->b.nr_entries = hist_browser__nr_entries(browser);
d1b4f249 389
dd00d486 390 hists__browser_title(browser->hists, title, sizeof(title));
d1b4f249 391
05e8b080 392 if (ui_browser__show(&browser->b, title,
59e8fe32 393 "Press '?' for help on key bindings") < 0)
d1b4f249
ACM
394 return -1;
395
d1b4f249 396 while (1) {
05e8b080 397 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 398
b50e003d 399 switch (key) {
fa5df943
NK
400 case K_TIMER: {
401 u64 nr_entries;
9783adf7 402 hbt->timer(hbt->arg);
fa5df943 403
c3b78952 404 if (hist_browser__has_filter(browser))
112f761f 405 hist_browser__update_nr_entries(browser);
fa5df943 406
c3b78952 407 nr_entries = hist_browser__nr_entries(browser);
fa5df943 408 ui_browser__update_nr_entries(&browser->b, nr_entries);
7b27509f 409
05e8b080
ACM
410 if (browser->hists->stats.nr_lost_warned !=
411 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
412 browser->hists->stats.nr_lost_warned =
413 browser->hists->stats.nr_events[PERF_RECORD_LOST];
414 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
415 }
416
dd00d486 417 hists__browser_title(browser->hists, title, sizeof(title));
05e8b080 418 ui_browser__show_title(&browser->b, title);
81cce8de 419 continue;
fa5df943 420 }
4694153c 421 case 'D': { /* Debug */
d1b4f249 422 static int seq;
05e8b080 423 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
424 struct hist_entry, rb_node);
425 ui_helpline__pop();
62c95ae3 426 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
427 seq++, browser->b.nr_entries,
428 browser->hists->nr_entries,
62c95ae3 429 browser->b.rows,
05e8b080
ACM
430 browser->b.index,
431 browser->b.top_idx,
d1b4f249
ACM
432 h->row_offset, h->nr_rows);
433 }
3c916cc2
ACM
434 break;
435 case 'C':
436 /* Collapse the whole world. */
05e8b080 437 hist_browser__set_folding(browser, false);
3c916cc2
ACM
438 break;
439 case 'E':
440 /* Expand the whole world. */
05e8b080 441 hist_browser__set_folding(browser, true);
3c916cc2 442 break;
025bf7ea
ACM
443 case 'H':
444 browser->show_headers = !browser->show_headers;
445 hist_browser__update_rows(browser);
446 break;
cf958003 447 case K_ENTER:
05e8b080 448 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
449 break;
450 /* fall thru */
451 default:
b50e003d 452 goto out;
d1b4f249
ACM
453 }
454 }
b50e003d 455out:
05e8b080 456 ui_browser__hide(&browser->b);
b50e003d 457 return key;
d1b4f249
ACM
458}
459
05e8b080 460static char *callchain_list__sym_name(struct callchain_list *cl,
a7cb8863 461 char *bf, size_t bfsize, bool show_dso)
d1b4f249 462{
a7cb8863
ACM
463 int printed;
464
05e8b080 465 if (cl->ms.sym)
a7cb8863
ACM
466 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
467 else
468 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
469
470 if (show_dso)
471 scnprintf(bf + printed, bfsize - printed, " %s",
472 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
d1b4f249 473
d1b4f249
ACM
474 return bf;
475}
476
477#define LEVEL_OFFSET_STEP 3
478
05e8b080 479static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
d1b4f249
ACM
480 struct callchain_node *chain_node,
481 u64 total, int level,
482 unsigned short row,
483 off_t *row_offset,
484 bool *is_current_entry)
485{
486 struct rb_node *node;
487 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
488 u64 new_total, remaining;
489
490 if (callchain_param.mode == CHAIN_GRAPH_REL)
491 new_total = chain_node->children_hit;
492 else
493 new_total = total;
494
495 remaining = new_total;
496 node = rb_first(&chain_node->rb_root);
497 while (node) {
498 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
499 struct rb_node *next = rb_next(node);
f08c3154 500 u64 cumul = callchain_cumul_hits(child);
d1b4f249
ACM
501 struct callchain_list *chain;
502 char folded_sign = ' ';
503 int first = true;
504 int extra_offset = 0;
505
506 remaining -= cumul;
507
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
JO
680static int \
681hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
682 struct perf_hpp *hpp, \
683 struct hist_entry *he) \
f5951d56 684{ \
d675107c 685 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %*.2f%%", 6, \
2f6d9009 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 \
696hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
697 struct perf_hpp *hpp, \
698 struct hist_entry *he) \
699{ \
700 if (!symbol_conf.cumulate_callchain) { \
d675107c
NK
701 int ret = scnprintf(hpp->buf, hpp->size, \
702 "%*s", 8, "N/A"); \
0434ddd2
NK
703 slsmg_printf("%s", hpp->buf); \
704 \
705 return ret; \
706 } \
d675107c
NK
707 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %*.2f%%", \
708 6, __hpp__slsmg_color_printf, true); \
0434ddd2
NK
709}
710
fb821c9e
NK
711__HPP_COLOR_PERCENT_FN(overhead, period)
712__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
713__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
714__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
715__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 716__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 717
5aed9d24 718#undef __HPP_COLOR_PERCENT_FN
0434ddd2 719#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
720
721void hist_browser__init_hpp(void)
722{
f5951d56
NK
723 perf_hpp__format[PERF_HPP__OVERHEAD].color =
724 hist_browser__hpp_color_overhead;
725 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
726 hist_browser__hpp_color_overhead_sys;
727 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
728 hist_browser__hpp_color_overhead_us;
729 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
730 hist_browser__hpp_color_overhead_guest_sys;
731 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
732 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
733 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
734 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
735}
736
05e8b080 737static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
738 struct hist_entry *entry,
739 unsigned short row)
740{
741 char s[256];
1240005e 742 int printed = 0;
67d25916 743 int width = browser->b.width;
d1b4f249 744 char folded_sign = ' ';
05e8b080 745 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 746 off_t row_offset = entry->row_offset;
63a1a3d8 747 bool first = true;
1240005e 748 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
749
750 if (current_entry) {
05e8b080
ACM
751 browser->he_selection = entry;
752 browser->selection = &entry->ms;
d1b4f249
ACM
753 }
754
755 if (symbol_conf.use_callchain) {
163caed9 756 hist_entry__init_have_children(entry);
d1b4f249
ACM
757 folded_sign = hist_entry__folded(entry);
758 }
759
760 if (row_offset == 0) {
89701460 761 struct hpp_arg arg = {
fb821c9e 762 .b = &browser->b,
89701460
NK
763 .folded_sign = folded_sign,
764 .current_entry = current_entry,
765 };
f5951d56
NK
766 struct perf_hpp hpp = {
767 .buf = s,
768 .size = sizeof(s),
89701460 769 .ptr = &arg,
f5951d56 770 };
d1b4f249 771
ca3ff33b 772 hist_browser__gotorc(browser, row, 0);
c172f742 773
1240005e 774 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
775 if (perf_hpp__should_skip(fmt))
776 continue;
777
fb821c9e
NK
778 if (current_entry && browser->b.navkeypressed) {
779 ui_browser__set_color(&browser->b,
780 HE_COLORSET_SELECTED);
781 } else {
782 ui_browser__set_color(&browser->b,
783 HE_COLORSET_NORMAL);
784 }
785
786 if (first) {
787 if (symbol_conf.use_callchain) {
788 slsmg_printf("%c ", folded_sign);
789 width -= 2;
790 }
791 first = false;
792 } else {
f5951d56
NK
793 slsmg_printf(" ");
794 width -= 2;
795 }
c172f742 796
1240005e 797 if (fmt->color) {
2c5d4b4a 798 width -= fmt->color(fmt, &hpp, entry);
f5951d56 799 } else {
2c5d4b4a 800 width -= fmt->entry(fmt, &hpp, entry);
f5951d56
NK
801 slsmg_printf("%s", s);
802 }
2cf9cebf
ACM
803 }
804
f5951d56
NK
805 /* The scroll bar isn't being used */
806 if (!browser->b.navkeypressed)
807 width += 1;
808
26d8b338
NK
809 slsmg_write_nstring("", width);
810
d1b4f249
ACM
811 ++row;
812 ++printed;
813 } else
814 --row_offset;
815
62c95ae3 816 if (folded_sign == '-' && row != browser->b.rows) {
05e8b080 817 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
d1b4f249
ACM
818 1, row, &row_offset,
819 &current_entry);
820 if (current_entry)
05e8b080 821 browser->he_selection = entry;
d1b4f249
ACM
822 }
823
824 return printed;
825}
826
81a888fe
JO
827static int advance_hpp_check(struct perf_hpp *hpp, int inc)
828{
829 advance_hpp(hpp, inc);
830 return hpp->size <= 0;
831}
832
833static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
834{
835 struct perf_hpp dummy_hpp = {
836 .buf = buf,
837 .size = size,
838 };
839 struct perf_hpp_fmt *fmt;
840 size_t ret = 0;
841
842 if (symbol_conf.use_callchain) {
843 ret = scnprintf(buf, size, " ");
844 if (advance_hpp_check(&dummy_hpp, ret))
845 return ret;
846 }
847
848 perf_hpp__for_each_format(fmt) {
849 if (perf_hpp__should_skip(fmt))
850 continue;
851
e0d66c74 852 /* We need to ensure length of the columns header. */
81a888fe
JO
853 perf_hpp__reset_width(fmt, hists);
854
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;
d1b4f249 1503
e8e684a5
NK
1504#define HIST_BROWSER_HELP_COMMON \
1505 "h/?/F1 Show this window\n" \
1506 "UP/DOWN/PGUP\n" \
1507 "PGDN/SPACE Navigate\n" \
1508 "q/ESC/CTRL+C Exit browser\n\n" \
1509 "For multiple event sessions:\n\n" \
1510 "TAB/UNTAB Switch events\n\n" \
1511 "For symbolic views (--sort has sym):\n\n" \
1512 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1513 "<- Zoom out\n" \
1514 "a Annotate current symbol\n" \
1515 "C Collapse all callchains\n" \
1516 "d Zoom into current DSO\n" \
1517 "E Expand all callchains\n" \
105eb30f 1518 "F Toggle percentage of filtered entries\n" \
025bf7ea 1519 "H Display column headers\n" \
e8e684a5
NK
1520
1521 /* help messages are sorted by lexical order of the hotkey */
1522 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 1523 "i Show header information\n"
e8e684a5
NK
1524 "P Print histograms to perf.hist.N\n"
1525 "r Run available scripts\n"
1526 "s Switch to another data file in PWD\n"
1527 "t Zoom into current Thread\n"
1528 "V Verbose (DSO names in callchains, etc)\n"
1529 "/ Filter symbol by name";
1530 const char top_help[] = HIST_BROWSER_HELP_COMMON
1531 "P Print histograms to perf.hist.N\n"
1532 "t Zoom into current Thread\n"
1533 "V Verbose (DSO names in callchains, etc)\n"
1534 "/ Filter symbol by name";
1535
d1b4f249
ACM
1536 if (browser == NULL)
1537 return -1;
1538
064f1981
NK
1539 if (min_pcnt) {
1540 browser->min_pcnt = min_pcnt;
112f761f 1541 hist_browser__update_nr_entries(browser);
064f1981
NK
1542 }
1543
d1b4f249
ACM
1544 fstack = pstack__new(2);
1545 if (fstack == NULL)
1546 goto out;
1547
1548 ui_helpline__push(helpline);
1549
24bff2dc
SE
1550 memset(options, 0, sizeof(options));
1551
d1b4f249 1552 while (1) {
60098917
ACM
1553 const struct thread *thread = NULL;
1554 const struct dso *dso = NULL;
24bff2dc 1555 int choice = 0,
d1b4f249 1556 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 1557 annotate_f = -2, annotate_t = -2, browse_map = -2;
341487ab
FT
1558 int scripts_comm = -2, scripts_symbol = -2,
1559 scripts_all = -2, switch_data = -2;
d1b4f249 1560
24bff2dc
SE
1561 nr_options = 0;
1562
dd00d486 1563 key = hist_browser__run(browser, hbt);
d1b4f249 1564
60098917
ACM
1565 if (browser->he_selection != NULL) {
1566 thread = hist_browser__selected_thread(browser);
1567 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1568 }
b50e003d 1569 switch (key) {
cf958003
ACM
1570 case K_TAB:
1571 case K_UNTAB:
e4419b8e
DA
1572 if (nr_events == 1)
1573 continue;
b50e003d
ACM
1574 /*
1575 * Exit the browser, let hists__browser_tree
1576 * go to the next or previous
1577 */
1578 goto out_free_stack;
1579 case 'a':
9c796ec8 1580 if (!sort__has_sym) {
7b27509f 1581 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1582 "Annotation is only available for symbolic views, "
a68c2c58 1583 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1584 continue;
1585 }
1586
60098917 1587 if (browser->selection == NULL ||
db9a9cbc 1588 browser->selection->sym == NULL ||
b50e003d 1589 browser->selection->map->dso->annotate_warned)
d1b4f249 1590 continue;
b50e003d 1591 goto do_annotate;
aff3f3f6
ACM
1592 case 'P':
1593 hist_browser__dump(browser);
1594 continue;
b50e003d
ACM
1595 case 'd':
1596 goto zoom_dso;
a7cb8863
ACM
1597 case 'V':
1598 browser->show_dso = !browser->show_dso;
1599 continue;
b50e003d
ACM
1600 case 't':
1601 goto zoom_thread;
5a5626b1 1602 case '/':
938a23ae
NK
1603 if (ui_browser__input_window("Symbol to show",
1604 "Please enter the name of symbol you want to see",
1605 buf, "ENTER: OK, ESC: Cancel",
1606 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1607 hists->symbol_filter_str = *buf ? buf : NULL;
1608 hists__filter_by_symbol(hists);
938a23ae
NK
1609 hist_browser__reset(browser);
1610 }
1611 continue;
cdbab7c2 1612 case 'r':
9783adf7 1613 if (is_report_browser(hbt))
c77d8d70
FT
1614 goto do_scripts;
1615 continue;
341487ab
FT
1616 case 's':
1617 if (is_report_browser(hbt))
1618 goto do_data_switch;
1619 continue;
6dd60135
NK
1620 case 'i':
1621 /* env->arch is NULL for live-mode (i.e. perf top) */
1622 if (env->arch)
1623 tui__header_window(env);
1624 continue;
105eb30f
NK
1625 case 'F':
1626 symbol_conf.filter_relative ^= 1;
1627 continue;
cf958003 1628 case K_F1:
b50e003d
ACM
1629 case 'h':
1630 case '?':
4610e413 1631 ui_browser__help_window(&browser->b,
e8e684a5 1632 is_report_browser(hbt) ? report_help : top_help);
b50e003d 1633 continue;
cf958003
ACM
1634 case K_ENTER:
1635 case K_RIGHT:
b50e003d
ACM
1636 /* menu */
1637 break;
cf958003 1638 case K_LEFT: {
b50e003d 1639 const void *top;
d1b4f249 1640
7f0030b2
ACM
1641 if (pstack__empty(fstack)) {
1642 /*
1643 * Go back to the perf_evsel_menu__run or other user
1644 */
1645 if (left_exits)
1646 goto out_free_stack;
d1b4f249 1647 continue;
7f0030b2 1648 }
b50e003d 1649 top = pstack__pop(fstack);
d7b76f09 1650 if (top == &browser->hists->dso_filter)
b50e003d 1651 goto zoom_out_dso;
d7b76f09 1652 if (top == &browser->hists->thread_filter)
b50e003d
ACM
1653 goto zoom_out_thread;
1654 continue;
1655 }
cf958003 1656 case K_ESC:
7f0030b2 1657 if (!left_exits &&
4610e413
ACM
1658 !ui_browser__dialog_yesno(&browser->b,
1659 "Do you really want to exit?"))
b50e003d
ACM
1660 continue;
1661 /* Fall thru */
ed7e5662
ACM
1662 case 'q':
1663 case CTRL('c'):
b50e003d 1664 goto out_free_stack;
ed7e5662
ACM
1665 default:
1666 continue;
d1b4f249
ACM
1667 }
1668
9c796ec8 1669 if (!sort__has_sym)
724c9c9f
ACM
1670 goto add_exit_option;
1671
55369fc1 1672 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58
SE
1673 bi = browser->he_selection->branch_info;
1674 if (browser->selection != NULL &&
1675 bi &&
1676 bi->from.sym != NULL &&
1677 !bi->from.map->dso->annotate_warned &&
1678 asprintf(&options[nr_options], "Annotate %s",
1679 bi->from.sym->name) > 0)
1680 annotate_f = nr_options++;
1681
1682 if (browser->selection != NULL &&
1683 bi &&
1684 bi->to.sym != NULL &&
1685 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1686 (bi->to.sym != bi->from.sym ||
1687 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1688 asprintf(&options[nr_options], "Annotate %s",
1689 bi->to.sym->name) > 0)
1690 annotate_t = nr_options++;
1691 } else {
a68c2c58
SE
1692 if (browser->selection != NULL &&
1693 browser->selection->sym != NULL &&
d755330c
JO
1694 !browser->selection->map->dso->annotate_warned) {
1695 struct annotation *notes;
1696
1697 notes = symbol__annotation(browser->selection->sym);
1698
1699 if (notes->src &&
1700 asprintf(&options[nr_options], "Annotate %s",
1701 browser->selection->sym->name) > 0)
1702 annotate = nr_options++;
1703 }
a68c2c58 1704 }
d1b4f249
ACM
1705
1706 if (thread != NULL &&
1707 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1708 (browser->hists->thread_filter ? "out of" : "into"),
b9c5143a 1709 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1710 thread->tid) > 0)
d1b4f249
ACM
1711 zoom_thread = nr_options++;
1712
1713 if (dso != NULL &&
1714 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1715 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1716 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1717 zoom_dso = nr_options++;
1718
60098917
ACM
1719 if (browser->selection != NULL &&
1720 browser->selection->map != NULL &&
d1b4f249
ACM
1721 asprintf(&options[nr_options], "Browse map details") > 0)
1722 browse_map = nr_options++;
cdbab7c2
FT
1723
1724 /* perf script support */
1725 if (browser->he_selection) {
1726 struct symbol *sym;
1727
1728 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
b9c5143a 1729 thread__comm_str(browser->he_selection->thread)) > 0)
cdbab7c2
FT
1730 scripts_comm = nr_options++;
1731
1732 sym = browser->he_selection->ms.sym;
1733 if (sym && sym->namelen &&
1734 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1735 sym->name) > 0)
1736 scripts_symbol = nr_options++;
1737 }
1738
1739 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1740 scripts_all = nr_options++;
1741
341487ab
FT
1742 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1743 "Switch to another data file in PWD") > 0)
1744 switch_data = nr_options++;
724c9c9f 1745add_exit_option:
d1b4f249 1746 options[nr_options++] = (char *)"Exit";
24bff2dc 1747retry_popup_menu:
1e6dd077 1748 choice = ui__popup_menu(nr_options, options);
d1b4f249 1749
d1b4f249
ACM
1750 if (choice == nr_options - 1)
1751 break;
1752
24bff2dc
SE
1753 if (choice == -1) {
1754 free_popup_options(options, nr_options - 1);
d1b4f249 1755 continue;
24bff2dc 1756 }
d1b4f249 1757
a68c2c58 1758 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1759 struct hist_entry *he;
d755330c 1760 struct annotation *notes;
4610e413 1761 int err;
d1b4f249 1762do_annotate:
68d80758
NK
1763 if (!objdump_path && perf_session_env__lookup_objdump(env))
1764 continue;
1765
d1b4f249
ACM
1766 he = hist_browser__selected_entry(browser);
1767 if (he == NULL)
1768 continue;
a68c2c58
SE
1769
1770 /*
1771 * we stash the branch_info symbol + map into the
1772 * the ms so we don't have to rewrite all the annotation
1773 * code to use branch_info.
1774 * in branch mode, the ms struct is not used
1775 */
1776 if (choice == annotate_f) {
1777 he->ms.sym = he->branch_info->from.sym;
1778 he->ms.map = he->branch_info->from.map;
1779 } else if (choice == annotate_t) {
1780 he->ms.sym = he->branch_info->to.sym;
1781 he->ms.map = he->branch_info->to.map;
1782 }
1783
d755330c
JO
1784 notes = symbol__annotation(he->ms.sym);
1785 if (!notes->src)
1786 continue;
1787
df71d95f
ACM
1788 /*
1789 * Don't let this be freed, say, by hists__decay_entry.
1790 */
1791 he->used = true;
db8fd07a 1792 err = hist_entry__tui_annotate(he, evsel, hbt);
df71d95f 1793 he->used = false;
24bff2dc
SE
1794 /*
1795 * offer option to annotate the other branch source or target
1796 * (if they exists) when returning from annotate
1797 */
1798 if ((err == 'q' || err == CTRL('c'))
1799 && annotate_t != -2 && annotate_f != -2)
1800 goto retry_popup_menu;
1801
900e14a8 1802 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1803 if (err)
1804 ui_browser__handle_resize(&browser->b);
24bff2dc 1805
d1b4f249
ACM
1806 } else if (choice == browse_map)
1807 map__browse(browser->selection->map);
1808 else if (choice == zoom_dso) {
1809zoom_dso:
d7b76f09
ACM
1810 if (browser->hists->dso_filter) {
1811 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1812zoom_out_dso:
1813 ui_helpline__pop();
d7b76f09 1814 browser->hists->dso_filter = NULL;
f2998422 1815 perf_hpp__set_elide(HISTC_DSO, false);
d1b4f249
ACM
1816 } else {
1817 if (dso == NULL)
1818 continue;
1819 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1820 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1821 browser->hists->dso_filter = dso;
f2998422 1822 perf_hpp__set_elide(HISTC_DSO, true);
d7b76f09 1823 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1824 }
05e8b080 1825 hists__filter_by_dso(hists);
d1b4f249
ACM
1826 hist_browser__reset(browser);
1827 } else if (choice == zoom_thread) {
1828zoom_thread:
d7b76f09
ACM
1829 if (browser->hists->thread_filter) {
1830 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1831zoom_out_thread:
1832 ui_helpline__pop();
d7b76f09 1833 browser->hists->thread_filter = NULL;
f2998422 1834 perf_hpp__set_elide(HISTC_THREAD, false);
d1b4f249
ACM
1835 } else {
1836 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
b9c5143a 1837 thread->comm_set ? thread__comm_str(thread) : "",
38051234 1838 thread->tid);
d7b76f09 1839 browser->hists->thread_filter = thread;
f2998422 1840 perf_hpp__set_elide(HISTC_THREAD, false);
d7b76f09 1841 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1842 }
05e8b080 1843 hists__filter_by_thread(hists);
d1b4f249
ACM
1844 hist_browser__reset(browser);
1845 }
cdbab7c2
FT
1846 /* perf scripts support */
1847 else if (choice == scripts_all || choice == scripts_comm ||
1848 choice == scripts_symbol) {
1849do_scripts:
1850 memset(script_opt, 0, 64);
1851
1852 if (choice == scripts_comm)
b9c5143a 1853 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
cdbab7c2
FT
1854
1855 if (choice == scripts_symbol)
1856 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1857
1858 script_browse(script_opt);
1859 }
341487ab
FT
1860 /* Switch to another data file */
1861 else if (choice == switch_data) {
1862do_data_switch:
1863 if (!switch_data_file()) {
1864 key = K_SWITCH_INPUT_DATA;
1865 break;
1866 } else
1867 ui__warning("Won't switch the data files due to\n"
1868 "no valid data file get selected!\n");
1869 }
d1b4f249
ACM
1870 }
1871out_free_stack:
1872 pstack__delete(fstack);
1873out:
1874 hist_browser__delete(browser);
24bff2dc 1875 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1876 return key;
1877}
1878
7f0030b2
ACM
1879struct perf_evsel_menu {
1880 struct ui_browser b;
1881 struct perf_evsel *selection;
7b27509f 1882 bool lost_events, lost_events_warned;
064f1981 1883 float min_pcnt;
68d80758 1884 struct perf_session_env *env;
7f0030b2
ACM
1885};
1886
1887static void perf_evsel_menu__write(struct ui_browser *browser,
1888 void *entry, int row)
1889{
1890 struct perf_evsel_menu *menu = container_of(browser,
1891 struct perf_evsel_menu, b);
1892 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1893 bool current_entry = ui_browser__is_current_entry(browser, row);
1894 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 1895 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 1896 char bf[256], unit;
7b27509f
ACM
1897 const char *warn = " ";
1898 size_t printed;
7f0030b2
ACM
1899
1900 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1901 HE_COLORSET_NORMAL);
1902
759ff497 1903 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1904 struct perf_evsel *pos;
1905
1906 ev_name = perf_evsel__group_name(evsel);
1907
1908 for_each_group_member(pos, evsel) {
1909 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1910 }
1911 }
1912
7f0030b2 1913 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1914 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1915 unit, unit == ' ' ? "" : " ", ev_name);
1916 slsmg_printf("%s", bf);
1917
1918 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1919 if (nr_events != 0) {
1920 menu->lost_events = true;
1921 if (!current_entry)
1922 ui_browser__set_color(browser, HE_COLORSET_TOP);
1923 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1924 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1925 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1926 warn = bf;
1927 }
1928
1929 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1930
1931 if (current_entry)
1932 menu->selection = evsel;
1933}
1934
34958544
ACM
1935static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1936 int nr_events, const char *help,
9783adf7 1937 struct hist_browser_timer *hbt)
d1b4f249 1938{
7f0030b2 1939 struct perf_evlist *evlist = menu->b.priv;
e248de33 1940 struct perf_evsel *pos;
dd00d486 1941 const char *title = "Available samples";
9783adf7 1942 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 1943 int key;
d1b4f249 1944
7f0030b2
ACM
1945 if (ui_browser__show(&menu->b, title,
1946 "ESC: exit, ENTER|->: Browse histograms") < 0)
1947 return -1;
1948
7f0030b2 1949 while (1) {
3af6e338 1950 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1951
1952 switch (key) {
cf958003 1953 case K_TIMER:
9783adf7 1954 hbt->timer(hbt->arg);
7b27509f
ACM
1955
1956 if (!menu->lost_events_warned && menu->lost_events) {
1957 ui_browser__warn_lost_events(&menu->b);
1958 menu->lost_events_warned = true;
1959 }
81cce8de 1960 continue;
cf958003
ACM
1961 case K_RIGHT:
1962 case K_ENTER:
7f0030b2
ACM
1963 if (!menu->selection)
1964 continue;
1965 pos = menu->selection;
1966browse_hists:
18eaf0b8
ACM
1967 perf_evlist__set_selected(evlist, pos);
1968 /*
1969 * Give the calling tool a chance to populate the non
1970 * default evsel resorted hists tree.
1971 */
9783adf7
NK
1972 if (hbt)
1973 hbt->timer(hbt->arg);
34958544 1974 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 1975 true, hbt,
064f1981 1976 menu->min_pcnt,
68d80758 1977 menu->env);
7f0030b2 1978 ui_browser__show_title(&menu->b, title);
18eaf0b8 1979 switch (key) {
cf958003 1980 case K_TAB:
18eaf0b8 1981 if (pos->node.next == &evlist->entries)
9a354cdc 1982 pos = perf_evlist__first(evlist);
18eaf0b8 1983 else
9a354cdc 1984 pos = perf_evsel__next(pos);
18eaf0b8 1985 goto browse_hists;
cf958003 1986 case K_UNTAB:
18eaf0b8 1987 if (pos->node.prev == &evlist->entries)
9a354cdc 1988 pos = perf_evlist__last(evlist);
18eaf0b8 1989 else
d87fcb4a 1990 pos = perf_evsel__prev(pos);
18eaf0b8 1991 goto browse_hists;
cf958003 1992 case K_ESC:
4610e413
ACM
1993 if (!ui_browser__dialog_yesno(&menu->b,
1994 "Do you really want to exit?"))
18eaf0b8
ACM
1995 continue;
1996 /* Fall thru */
341487ab 1997 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
1998 case 'q':
1999 case CTRL('c'):
2000 goto out;
2001 default:
2002 continue;
2003 }
cf958003 2004 case K_LEFT:
7f0030b2 2005 continue;
cf958003 2006 case K_ESC:
4610e413
ACM
2007 if (!ui_browser__dialog_yesno(&menu->b,
2008 "Do you really want to exit?"))
ed7e5662
ACM
2009 continue;
2010 /* Fall thru */
7f0030b2
ACM
2011 case 'q':
2012 case CTRL('c'):
2013 goto out;
d1b4f249 2014 default:
18eaf0b8 2015 continue;
d1b4f249
ACM
2016 }
2017 }
2018
7f0030b2
ACM
2019out:
2020 ui_browser__hide(&menu->b);
2021 return key;
2022}
2023
316c7136 2024static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
2025 void *entry)
2026{
2027 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2028
2029 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2030 return true;
2031
2032 return false;
2033}
2034
7f0030b2 2035static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 2036 int nr_entries, const char *help,
68d80758 2037 struct hist_browser_timer *hbt,
064f1981 2038 float min_pcnt,
68d80758 2039 struct perf_session_env *env)
7f0030b2
ACM
2040{
2041 struct perf_evsel *pos;
2042 struct perf_evsel_menu menu = {
2043 .b = {
2044 .entries = &evlist->entries,
2045 .refresh = ui_browser__list_head_refresh,
2046 .seek = ui_browser__list_head_seek,
2047 .write = perf_evsel_menu__write,
fc24d7c2
NK
2048 .filter = filter_group_entries,
2049 .nr_entries = nr_entries,
7f0030b2
ACM
2050 .priv = evlist,
2051 },
064f1981 2052 .min_pcnt = min_pcnt,
68d80758 2053 .env = env,
7f0030b2
ACM
2054 };
2055
2056 ui_helpline__push("Press ESC to exit");
2057
0050f7aa 2058 evlist__for_each(evlist, pos) {
7289f83c 2059 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
2060 size_t line_len = strlen(ev_name) + 7;
2061
2062 if (menu.b.width < line_len)
2063 menu.b.width = line_len;
7f0030b2
ACM
2064 }
2065
fc24d7c2 2066 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
2067}
2068
81cce8de 2069int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 2070 struct hist_browser_timer *hbt,
064f1981 2071 float min_pcnt,
68d80758 2072 struct perf_session_env *env)
7f0030b2 2073{
fc24d7c2
NK
2074 int nr_entries = evlist->nr_entries;
2075
2076single_entry:
2077 if (nr_entries == 1) {
9a354cdc 2078 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
2079
2080 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 2081 false, hbt, min_pcnt,
064f1981 2082 env);
7f0030b2
ACM
2083 }
2084
fc24d7c2
NK
2085 if (symbol_conf.event_group) {
2086 struct perf_evsel *pos;
2087
2088 nr_entries = 0;
0050f7aa 2089 evlist__for_each(evlist, pos) {
fc24d7c2
NK
2090 if (perf_evsel__is_group_leader(pos))
2091 nr_entries++;
0050f7aa 2092 }
fc24d7c2
NK
2093
2094 if (nr_entries == 1)
2095 goto single_entry;
2096 }
2097
2098 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 2099 hbt, min_pcnt, env);
d1b4f249 2100}
This page took 0.391107 seconds and 5 git commands to generate.