perf hists browser: Display columns header text on 'H' press
[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;
656 int ret;
657 va_list args;
658 double percent;
371d8c40 659
2f6d9009
NK
660 va_start(args, fmt);
661 percent = va_arg(args, double);
662 va_end(args);
371d8c40 663
2f6d9009 664 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
371d8c40 665
2f6d9009
NK
666 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
667 slsmg_printf("%s", hpp->buf);
5aed9d24 668
2f6d9009 669 advance_hpp(hpp, ret);
5aed9d24
NK
670 return ret;
671}
672
fb821c9e 673#define __HPP_COLOR_PERCENT_FN(_type, _field) \
5aed9d24
NK
674static u64 __hpp_get_##_field(struct hist_entry *he) \
675{ \
676 return he->stat._field; \
677} \
678 \
2c5d4b4a
JO
679static int \
680hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
681 struct perf_hpp *hpp, \
682 struct hist_entry *he) \
f5951d56 683{ \
fb821c9e 684 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
2f6d9009 685 __hpp__slsmg_color_printf, true); \
f5951d56
NK
686}
687
0434ddd2
NK
688#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
689static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
690{ \
691 return he->stat_acc->_field; \
692} \
693 \
694static int \
695hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
696 struct perf_hpp *hpp, \
697 struct hist_entry *he) \
698{ \
699 if (!symbol_conf.cumulate_callchain) { \
700 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
701 slsmg_printf("%s", hpp->buf); \
702 \
703 return ret; \
704 } \
705 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
706 __hpp__slsmg_color_printf, true); \
707}
708
fb821c9e
NK
709__HPP_COLOR_PERCENT_FN(overhead, period)
710__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
711__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
712__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
713__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 714__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 715
5aed9d24 716#undef __HPP_COLOR_PERCENT_FN
0434ddd2 717#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
718
719void hist_browser__init_hpp(void)
720{
f5951d56
NK
721 perf_hpp__format[PERF_HPP__OVERHEAD].color =
722 hist_browser__hpp_color_overhead;
723 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
724 hist_browser__hpp_color_overhead_sys;
725 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
726 hist_browser__hpp_color_overhead_us;
727 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
728 hist_browser__hpp_color_overhead_guest_sys;
729 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
730 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
731 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
732 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
733}
734
05e8b080 735static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
736 struct hist_entry *entry,
737 unsigned short row)
738{
739 char s[256];
1240005e 740 int printed = 0;
67d25916 741 int width = browser->b.width;
d1b4f249 742 char folded_sign = ' ';
05e8b080 743 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 744 off_t row_offset = entry->row_offset;
63a1a3d8 745 bool first = true;
1240005e 746 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
747
748 if (current_entry) {
05e8b080
ACM
749 browser->he_selection = entry;
750 browser->selection = &entry->ms;
d1b4f249
ACM
751 }
752
753 if (symbol_conf.use_callchain) {
163caed9 754 hist_entry__init_have_children(entry);
d1b4f249
ACM
755 folded_sign = hist_entry__folded(entry);
756 }
757
758 if (row_offset == 0) {
89701460 759 struct hpp_arg arg = {
fb821c9e 760 .b = &browser->b,
89701460
NK
761 .folded_sign = folded_sign,
762 .current_entry = current_entry,
763 };
f5951d56
NK
764 struct perf_hpp hpp = {
765 .buf = s,
766 .size = sizeof(s),
89701460 767 .ptr = &arg,
f5951d56 768 };
d1b4f249 769
ca3ff33b 770 hist_browser__gotorc(browser, row, 0);
c172f742 771
1240005e 772 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
773 if (perf_hpp__should_skip(fmt))
774 continue;
775
fb821c9e
NK
776 if (current_entry && browser->b.navkeypressed) {
777 ui_browser__set_color(&browser->b,
778 HE_COLORSET_SELECTED);
779 } else {
780 ui_browser__set_color(&browser->b,
781 HE_COLORSET_NORMAL);
782 }
783
784 if (first) {
785 if (symbol_conf.use_callchain) {
786 slsmg_printf("%c ", folded_sign);
787 width -= 2;
788 }
789 first = false;
790 } else {
f5951d56
NK
791 slsmg_printf(" ");
792 width -= 2;
793 }
c172f742 794
1240005e 795 if (fmt->color) {
2c5d4b4a 796 width -= fmt->color(fmt, &hpp, entry);
f5951d56 797 } else {
2c5d4b4a 798 width -= fmt->entry(fmt, &hpp, entry);
f5951d56
NK
799 slsmg_printf("%s", s);
800 }
2cf9cebf
ACM
801 }
802
f5951d56
NK
803 /* The scroll bar isn't being used */
804 if (!browser->b.navkeypressed)
805 width += 1;
806
26d8b338
NK
807 slsmg_write_nstring("", width);
808
d1b4f249
ACM
809 ++row;
810 ++printed;
811 } else
812 --row_offset;
813
62c95ae3 814 if (folded_sign == '-' && row != browser->b.rows) {
05e8b080 815 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
d1b4f249
ACM
816 1, row, &row_offset,
817 &current_entry);
818 if (current_entry)
05e8b080 819 browser->he_selection = entry;
d1b4f249
ACM
820 }
821
822 return printed;
823}
824
81a888fe
JO
825static int advance_hpp_check(struct perf_hpp *hpp, int inc)
826{
827 advance_hpp(hpp, inc);
828 return hpp->size <= 0;
829}
830
831static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
832{
833 struct perf_hpp dummy_hpp = {
834 .buf = buf,
835 .size = size,
836 };
837 struct perf_hpp_fmt *fmt;
838 size_t ret = 0;
839
840 if (symbol_conf.use_callchain) {
841 ret = scnprintf(buf, size, " ");
842 if (advance_hpp_check(&dummy_hpp, ret))
843 return ret;
844 }
845
846 perf_hpp__for_each_format(fmt) {
847 if (perf_hpp__should_skip(fmt))
848 continue;
849
850 /* We need to add the length of the columns header. */
851 perf_hpp__reset_width(fmt, hists);
852
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;
1084 u64 new_total, remaining;
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
1092 remaining = new_total;
1093 node = rb_first(&chain_node->rb_root);
1094 while (node) {
1095 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1096 struct rb_node *next = rb_next(node);
1097 u64 cumul = callchain_cumul_hits(child);
1098 struct callchain_list *chain;
1099 char folded_sign = ' ';
1100 int first = true;
1101 int extra_offset = 0;
1102
1103 remaining -= cumul;
1104
1105 list_for_each_entry(chain, &child->val, list) {
a7cb8863 1106 char bf[1024], *alloc_str;
aff3f3f6
ACM
1107 const char *str;
1108 bool was_first = first;
1109
1110 if (first)
1111 first = false;
1112 else
1113 extra_offset = LEVEL_OFFSET_STEP;
1114
1115 folded_sign = callchain_list__folded(chain);
1116
1117 alloc_str = NULL;
a7cb8863
ACM
1118 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1119 browser->show_dso);
aff3f3f6
ACM
1120 if (was_first) {
1121 double percent = cumul * 100.0 / new_total;
1122
1123 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1124 str = "Not enough memory!";
1125 else
1126 str = alloc_str;
1127 }
1128
1129 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1130 free(alloc_str);
1131 if (folded_sign == '+')
1132 break;
1133 }
1134
1135 if (folded_sign == '-') {
1136 const int new_level = level + (extra_offset ? 2 : 1);
1137 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1138 new_level, fp);
1139 }
1140
1141 node = next;
1142 }
1143
1144 return printed;
1145}
1146
1147static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1148 struct callchain_node *node,
1149 int level, FILE *fp)
1150{
1151 struct callchain_list *chain;
1152 int offset = level * LEVEL_OFFSET_STEP;
1153 char folded_sign = ' ';
1154 int printed = 0;
1155
1156 list_for_each_entry(chain, &node->val, list) {
a7cb8863 1157 char bf[1024], *s;
aff3f3f6
ACM
1158
1159 folded_sign = callchain_list__folded(chain);
a7cb8863 1160 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
aff3f3f6
ACM
1161 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1162 }
1163
1164 if (folded_sign == '-')
1165 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1166 browser->hists->stats.total_period,
1167 level + 1, fp);
1168 return printed;
1169}
1170
1171static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1172 struct rb_root *chain, int level, FILE *fp)
1173{
1174 struct rb_node *nd;
1175 int printed = 0;
1176
1177 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1178 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1179
1180 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1181 }
1182
1183 return printed;
1184}
1185
1186static int hist_browser__fprintf_entry(struct hist_browser *browser,
1187 struct hist_entry *he, FILE *fp)
1188{
1189 char s[8192];
aff3f3f6
ACM
1190 int printed = 0;
1191 char folded_sign = ' ';
26d8b338
NK
1192 struct perf_hpp hpp = {
1193 .buf = s,
1194 .size = sizeof(s),
1195 };
1196 struct perf_hpp_fmt *fmt;
1197 bool first = true;
1198 int ret;
aff3f3f6
ACM
1199
1200 if (symbol_conf.use_callchain)
1201 folded_sign = hist_entry__folded(he);
1202
aff3f3f6
ACM
1203 if (symbol_conf.use_callchain)
1204 printed += fprintf(fp, "%c ", folded_sign);
1205
26d8b338 1206 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
1207 if (perf_hpp__should_skip(fmt))
1208 continue;
1209
26d8b338
NK
1210 if (!first) {
1211 ret = scnprintf(hpp.buf, hpp.size, " ");
1212 advance_hpp(&hpp, ret);
1213 } else
1214 first = false;
aff3f3f6 1215
26d8b338
NK
1216 ret = fmt->entry(fmt, &hpp, he);
1217 advance_hpp(&hpp, ret);
1218 }
aff3f3f6
ACM
1219 printed += fprintf(fp, "%s\n", rtrim(s));
1220
1221 if (folded_sign == '-')
1222 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1223
1224 return printed;
1225}
1226
1227static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1228{
064f1981 1229 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 1230 browser->min_pcnt);
aff3f3f6
ACM
1231 int printed = 0;
1232
1233 while (nd) {
1234 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1235
1236 printed += hist_browser__fprintf_entry(browser, h, fp);
14135663 1237 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
aff3f3f6
ACM
1238 }
1239
1240 return printed;
1241}
1242
1243static int hist_browser__dump(struct hist_browser *browser)
1244{
1245 char filename[64];
1246 FILE *fp;
1247
1248 while (1) {
1249 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1250 if (access(filename, F_OK))
1251 break;
1252 /*
1253 * XXX: Just an arbitrary lazy upper limit
1254 */
1255 if (++browser->print_seq == 8192) {
1256 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1257 return -1;
1258 }
1259 }
1260
1261 fp = fopen(filename, "w");
1262 if (fp == NULL) {
1263 char bf[64];
4cc49d4d
KS
1264 const char *err = strerror_r(errno, bf, sizeof(bf));
1265 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1266 return -1;
1267 }
1268
1269 ++browser->print_seq;
1270 hist_browser__fprintf(browser, fp);
1271 fclose(fp);
1272 ui_helpline__fpush("%s written!", filename);
1273
1274 return 0;
1275}
1276
d1b4f249
ACM
1277static struct hist_browser *hist_browser__new(struct hists *hists)
1278{
05e8b080 1279 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1280
05e8b080
ACM
1281 if (browser) {
1282 browser->hists = hists;
1283 browser->b.refresh = hist_browser__refresh;
357cfff1 1284 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
05e8b080
ACM
1285 browser->b.seek = ui_browser__hists_seek;
1286 browser->b.use_navkeypressed = true;
81a888fe 1287 browser->show_headers = true;
d1b4f249
ACM
1288 }
1289
05e8b080 1290 return browser;
d1b4f249
ACM
1291}
1292
05e8b080 1293static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1294{
05e8b080 1295 free(browser);
d1b4f249
ACM
1296}
1297
05e8b080 1298static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1299{
05e8b080 1300 return browser->he_selection;
d1b4f249
ACM
1301}
1302
05e8b080 1303static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1304{
05e8b080 1305 return browser->he_selection->thread;
d1b4f249
ACM
1306}
1307
dd00d486 1308static int hists__browser_title(struct hists *hists, char *bf, size_t size)
d1b4f249 1309{
469917ce
ACM
1310 char unit;
1311 int printed;
05e8b080
ACM
1312 const struct dso *dso = hists->dso_filter;
1313 const struct thread *thread = hists->thread_filter;
1314 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1315 u64 nr_events = hists->stats.total_period;
717e263f 1316 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 1317 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
1318 char buf[512];
1319 size_t buflen = sizeof(buf);
1320
f2148330
NK
1321 if (symbol_conf.filter_relative) {
1322 nr_samples = hists->stats.nr_non_filtered_samples;
1323 nr_events = hists->stats.total_non_filtered_period;
1324 }
1325
759ff497 1326 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1327 struct perf_evsel *pos;
1328
1329 perf_evsel__group_desc(evsel, buf, buflen);
1330 ev_name = buf;
1331
1332 for_each_group_member(pos, evsel) {
f2148330
NK
1333 if (symbol_conf.filter_relative) {
1334 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1335 nr_events += pos->hists.stats.total_non_filtered_period;
1336 } else {
1337 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1338 nr_events += pos->hists.stats.total_period;
1339 }
717e263f
NK
1340 }
1341 }
cc686280
AR
1342
1343 nr_samples = convert_unit(nr_samples, &unit);
1344 printed = scnprintf(bf, size,
1345 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1346 nr_samples, unit, ev_name, nr_events);
469917ce 1347
d1b4f249 1348
05e8b080 1349 if (hists->uid_filter_str)
0d37aa34 1350 printed += snprintf(bf + printed, size - printed,
05e8b080 1351 ", UID: %s", hists->uid_filter_str);
d1b4f249 1352 if (thread)
e7f01d1e 1353 printed += scnprintf(bf + printed, size - printed,
469917ce 1354 ", Thread: %s(%d)",
b9c5143a 1355 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1356 thread->tid);
d1b4f249 1357 if (dso)
e7f01d1e 1358 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1359 ", DSO: %s", dso->short_name);
1360 return printed;
d1b4f249
ACM
1361}
1362
24bff2dc
SE
1363static inline void free_popup_options(char **options, int n)
1364{
1365 int i;
1366
04662523
ACM
1367 for (i = 0; i < n; ++i)
1368 zfree(&options[i]);
24bff2dc
SE
1369}
1370
c77d8d70
FT
1371/* Check whether the browser is for 'top' or 'report' */
1372static inline bool is_report_browser(void *timer)
1373{
1374 return timer == NULL;
1375}
1376
341487ab
FT
1377/*
1378 * Only runtime switching of perf data file will make "input_name" point
1379 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1380 * whether we need to call free() for current "input_name" during the switch.
1381 */
1382static bool is_input_name_malloced = false;
1383
1384static int switch_data_file(void)
1385{
1386 char *pwd, *options[32], *abs_path[32], *tmp;
1387 DIR *pwd_dir;
1388 int nr_options = 0, choice = -1, ret = -1;
1389 struct dirent *dent;
1390
1391 pwd = getenv("PWD");
1392 if (!pwd)
1393 return ret;
1394
1395 pwd_dir = opendir(pwd);
1396 if (!pwd_dir)
1397 return ret;
1398
1399 memset(options, 0, sizeof(options));
1400 memset(options, 0, sizeof(abs_path));
1401
1402 while ((dent = readdir(pwd_dir))) {
1403 char path[PATH_MAX];
1404 u64 magic;
1405 char *name = dent->d_name;
1406 FILE *file;
1407
1408 if (!(dent->d_type == DT_REG))
1409 continue;
1410
1411 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1412
1413 file = fopen(path, "r");
1414 if (!file)
1415 continue;
1416
1417 if (fread(&magic, 1, 8, file) < 8)
1418 goto close_file_and_continue;
1419
1420 if (is_perf_magic(magic)) {
1421 options[nr_options] = strdup(name);
1422 if (!options[nr_options])
1423 goto close_file_and_continue;
1424
1425 abs_path[nr_options] = strdup(path);
1426 if (!abs_path[nr_options]) {
74cf249d 1427 zfree(&options[nr_options]);
341487ab
FT
1428 ui__warning("Can't search all data files due to memory shortage.\n");
1429 fclose(file);
1430 break;
1431 }
1432
1433 nr_options++;
1434 }
1435
1436close_file_and_continue:
1437 fclose(file);
1438 if (nr_options >= 32) {
1439 ui__warning("Too many perf data files in PWD!\n"
1440 "Only the first 32 files will be listed.\n");
1441 break;
1442 }
1443 }
1444 closedir(pwd_dir);
1445
1446 if (nr_options) {
1447 choice = ui__popup_menu(nr_options, options);
1448 if (choice < nr_options && choice >= 0) {
1449 tmp = strdup(abs_path[choice]);
1450 if (tmp) {
1451 if (is_input_name_malloced)
1452 free((void *)input_name);
1453 input_name = tmp;
1454 is_input_name_malloced = true;
1455 ret = 0;
1456 } else
1457 ui__warning("Data switch failed due to memory shortage!\n");
1458 }
1459 }
1460
1461 free_popup_options(options, nr_options);
1462 free_popup_options(abs_path, nr_options);
1463 return ret;
1464}
1465
112f761f 1466static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
1467{
1468 u64 nr_entries = 0;
1469 struct rb_node *nd = rb_first(&hb->hists->entries);
1470
268397cb
NK
1471 if (hb->min_pcnt == 0) {
1472 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1473 return;
1474 }
1475
14135663 1476 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 1477 nr_entries++;
c481f930 1478 nd = rb_next(nd);
064f1981
NK
1479 }
1480
112f761f 1481 hb->nr_non_filtered_entries = nr_entries;
064f1981 1482}
341487ab 1483
34958544 1484static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 1485 const char *helpline,
81cce8de 1486 bool left_exits,
68d80758 1487 struct hist_browser_timer *hbt,
064f1981 1488 float min_pcnt,
68d80758 1489 struct perf_session_env *env)
d1b4f249 1490{
05e8b080
ACM
1491 struct hists *hists = &evsel->hists;
1492 struct hist_browser *browser = hist_browser__new(hists);
a68c2c58 1493 struct branch_info *bi;
d1b4f249 1494 struct pstack *fstack;
24bff2dc
SE
1495 char *options[16];
1496 int nr_options = 0;
d1b4f249 1497 int key = -1;
938a23ae 1498 char buf[64];
cdbab7c2 1499 char script_opt[64];
9783adf7 1500 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 1501
e8e684a5
NK
1502#define HIST_BROWSER_HELP_COMMON \
1503 "h/?/F1 Show this window\n" \
1504 "UP/DOWN/PGUP\n" \
1505 "PGDN/SPACE Navigate\n" \
1506 "q/ESC/CTRL+C Exit browser\n\n" \
1507 "For multiple event sessions:\n\n" \
1508 "TAB/UNTAB Switch events\n\n" \
1509 "For symbolic views (--sort has sym):\n\n" \
1510 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1511 "<- Zoom out\n" \
1512 "a Annotate current symbol\n" \
1513 "C Collapse all callchains\n" \
1514 "d Zoom into current DSO\n" \
1515 "E Expand all callchains\n" \
105eb30f 1516 "F Toggle percentage of filtered entries\n" \
025bf7ea 1517 "H Display column headers\n" \
e8e684a5
NK
1518
1519 /* help messages are sorted by lexical order of the hotkey */
1520 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 1521 "i Show header information\n"
e8e684a5
NK
1522 "P Print histograms to perf.hist.N\n"
1523 "r Run available scripts\n"
1524 "s Switch to another data file in PWD\n"
1525 "t Zoom into current Thread\n"
1526 "V Verbose (DSO names in callchains, etc)\n"
1527 "/ Filter symbol by name";
1528 const char top_help[] = HIST_BROWSER_HELP_COMMON
1529 "P Print histograms to perf.hist.N\n"
1530 "t Zoom into current Thread\n"
1531 "V Verbose (DSO names in callchains, etc)\n"
1532 "/ Filter symbol by name";
1533
d1b4f249
ACM
1534 if (browser == NULL)
1535 return -1;
1536
064f1981
NK
1537 if (min_pcnt) {
1538 browser->min_pcnt = min_pcnt;
112f761f 1539 hist_browser__update_nr_entries(browser);
064f1981
NK
1540 }
1541
d1b4f249
ACM
1542 fstack = pstack__new(2);
1543 if (fstack == NULL)
1544 goto out;
1545
1546 ui_helpline__push(helpline);
1547
24bff2dc
SE
1548 memset(options, 0, sizeof(options));
1549
d1b4f249 1550 while (1) {
60098917
ACM
1551 const struct thread *thread = NULL;
1552 const struct dso *dso = NULL;
24bff2dc 1553 int choice = 0,
d1b4f249 1554 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 1555 annotate_f = -2, annotate_t = -2, browse_map = -2;
341487ab
FT
1556 int scripts_comm = -2, scripts_symbol = -2,
1557 scripts_all = -2, switch_data = -2;
d1b4f249 1558
24bff2dc
SE
1559 nr_options = 0;
1560
dd00d486 1561 key = hist_browser__run(browser, hbt);
d1b4f249 1562
60098917
ACM
1563 if (browser->he_selection != NULL) {
1564 thread = hist_browser__selected_thread(browser);
1565 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1566 }
b50e003d 1567 switch (key) {
cf958003
ACM
1568 case K_TAB:
1569 case K_UNTAB:
e4419b8e
DA
1570 if (nr_events == 1)
1571 continue;
b50e003d
ACM
1572 /*
1573 * Exit the browser, let hists__browser_tree
1574 * go to the next or previous
1575 */
1576 goto out_free_stack;
1577 case 'a':
9c796ec8 1578 if (!sort__has_sym) {
7b27509f 1579 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1580 "Annotation is only available for symbolic views, "
a68c2c58 1581 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1582 continue;
1583 }
1584
60098917 1585 if (browser->selection == NULL ||
db9a9cbc 1586 browser->selection->sym == NULL ||
b50e003d 1587 browser->selection->map->dso->annotate_warned)
d1b4f249 1588 continue;
b50e003d 1589 goto do_annotate;
aff3f3f6
ACM
1590 case 'P':
1591 hist_browser__dump(browser);
1592 continue;
b50e003d
ACM
1593 case 'd':
1594 goto zoom_dso;
a7cb8863
ACM
1595 case 'V':
1596 browser->show_dso = !browser->show_dso;
1597 continue;
b50e003d
ACM
1598 case 't':
1599 goto zoom_thread;
5a5626b1 1600 case '/':
938a23ae
NK
1601 if (ui_browser__input_window("Symbol to show",
1602 "Please enter the name of symbol you want to see",
1603 buf, "ENTER: OK, ESC: Cancel",
1604 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1605 hists->symbol_filter_str = *buf ? buf : NULL;
1606 hists__filter_by_symbol(hists);
938a23ae
NK
1607 hist_browser__reset(browser);
1608 }
1609 continue;
cdbab7c2 1610 case 'r':
9783adf7 1611 if (is_report_browser(hbt))
c77d8d70
FT
1612 goto do_scripts;
1613 continue;
341487ab
FT
1614 case 's':
1615 if (is_report_browser(hbt))
1616 goto do_data_switch;
1617 continue;
6dd60135
NK
1618 case 'i':
1619 /* env->arch is NULL for live-mode (i.e. perf top) */
1620 if (env->arch)
1621 tui__header_window(env);
1622 continue;
105eb30f
NK
1623 case 'F':
1624 symbol_conf.filter_relative ^= 1;
1625 continue;
cf958003 1626 case K_F1:
b50e003d
ACM
1627 case 'h':
1628 case '?':
4610e413 1629 ui_browser__help_window(&browser->b,
e8e684a5 1630 is_report_browser(hbt) ? report_help : top_help);
b50e003d 1631 continue;
cf958003
ACM
1632 case K_ENTER:
1633 case K_RIGHT:
b50e003d
ACM
1634 /* menu */
1635 break;
cf958003 1636 case K_LEFT: {
b50e003d 1637 const void *top;
d1b4f249 1638
7f0030b2
ACM
1639 if (pstack__empty(fstack)) {
1640 /*
1641 * Go back to the perf_evsel_menu__run or other user
1642 */
1643 if (left_exits)
1644 goto out_free_stack;
d1b4f249 1645 continue;
7f0030b2 1646 }
b50e003d 1647 top = pstack__pop(fstack);
d7b76f09 1648 if (top == &browser->hists->dso_filter)
b50e003d 1649 goto zoom_out_dso;
d7b76f09 1650 if (top == &browser->hists->thread_filter)
b50e003d
ACM
1651 goto zoom_out_thread;
1652 continue;
1653 }
cf958003 1654 case K_ESC:
7f0030b2 1655 if (!left_exits &&
4610e413
ACM
1656 !ui_browser__dialog_yesno(&browser->b,
1657 "Do you really want to exit?"))
b50e003d
ACM
1658 continue;
1659 /* Fall thru */
ed7e5662
ACM
1660 case 'q':
1661 case CTRL('c'):
b50e003d 1662 goto out_free_stack;
ed7e5662
ACM
1663 default:
1664 continue;
d1b4f249
ACM
1665 }
1666
9c796ec8 1667 if (!sort__has_sym)
724c9c9f
ACM
1668 goto add_exit_option;
1669
55369fc1 1670 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58
SE
1671 bi = browser->he_selection->branch_info;
1672 if (browser->selection != NULL &&
1673 bi &&
1674 bi->from.sym != NULL &&
1675 !bi->from.map->dso->annotate_warned &&
1676 asprintf(&options[nr_options], "Annotate %s",
1677 bi->from.sym->name) > 0)
1678 annotate_f = nr_options++;
1679
1680 if (browser->selection != NULL &&
1681 bi &&
1682 bi->to.sym != NULL &&
1683 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1684 (bi->to.sym != bi->from.sym ||
1685 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1686 asprintf(&options[nr_options], "Annotate %s",
1687 bi->to.sym->name) > 0)
1688 annotate_t = nr_options++;
1689 } else {
a68c2c58
SE
1690 if (browser->selection != NULL &&
1691 browser->selection->sym != NULL &&
d755330c
JO
1692 !browser->selection->map->dso->annotate_warned) {
1693 struct annotation *notes;
1694
1695 notes = symbol__annotation(browser->selection->sym);
1696
1697 if (notes->src &&
1698 asprintf(&options[nr_options], "Annotate %s",
1699 browser->selection->sym->name) > 0)
1700 annotate = nr_options++;
1701 }
a68c2c58 1702 }
d1b4f249
ACM
1703
1704 if (thread != NULL &&
1705 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1706 (browser->hists->thread_filter ? "out of" : "into"),
b9c5143a 1707 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1708 thread->tid) > 0)
d1b4f249
ACM
1709 zoom_thread = nr_options++;
1710
1711 if (dso != NULL &&
1712 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1713 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1714 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1715 zoom_dso = nr_options++;
1716
60098917
ACM
1717 if (browser->selection != NULL &&
1718 browser->selection->map != NULL &&
d1b4f249
ACM
1719 asprintf(&options[nr_options], "Browse map details") > 0)
1720 browse_map = nr_options++;
cdbab7c2
FT
1721
1722 /* perf script support */
1723 if (browser->he_selection) {
1724 struct symbol *sym;
1725
1726 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
b9c5143a 1727 thread__comm_str(browser->he_selection->thread)) > 0)
cdbab7c2
FT
1728 scripts_comm = nr_options++;
1729
1730 sym = browser->he_selection->ms.sym;
1731 if (sym && sym->namelen &&
1732 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1733 sym->name) > 0)
1734 scripts_symbol = nr_options++;
1735 }
1736
1737 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1738 scripts_all = nr_options++;
1739
341487ab
FT
1740 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1741 "Switch to another data file in PWD") > 0)
1742 switch_data = nr_options++;
724c9c9f 1743add_exit_option:
d1b4f249 1744 options[nr_options++] = (char *)"Exit";
24bff2dc 1745retry_popup_menu:
1e6dd077 1746 choice = ui__popup_menu(nr_options, options);
d1b4f249 1747
d1b4f249
ACM
1748 if (choice == nr_options - 1)
1749 break;
1750
24bff2dc
SE
1751 if (choice == -1) {
1752 free_popup_options(options, nr_options - 1);
d1b4f249 1753 continue;
24bff2dc 1754 }
d1b4f249 1755
a68c2c58 1756 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1757 struct hist_entry *he;
d755330c 1758 struct annotation *notes;
4610e413 1759 int err;
d1b4f249 1760do_annotate:
68d80758
NK
1761 if (!objdump_path && perf_session_env__lookup_objdump(env))
1762 continue;
1763
d1b4f249
ACM
1764 he = hist_browser__selected_entry(browser);
1765 if (he == NULL)
1766 continue;
a68c2c58
SE
1767
1768 /*
1769 * we stash the branch_info symbol + map into the
1770 * the ms so we don't have to rewrite all the annotation
1771 * code to use branch_info.
1772 * in branch mode, the ms struct is not used
1773 */
1774 if (choice == annotate_f) {
1775 he->ms.sym = he->branch_info->from.sym;
1776 he->ms.map = he->branch_info->from.map;
1777 } else if (choice == annotate_t) {
1778 he->ms.sym = he->branch_info->to.sym;
1779 he->ms.map = he->branch_info->to.map;
1780 }
1781
d755330c
JO
1782 notes = symbol__annotation(he->ms.sym);
1783 if (!notes->src)
1784 continue;
1785
df71d95f
ACM
1786 /*
1787 * Don't let this be freed, say, by hists__decay_entry.
1788 */
1789 he->used = true;
db8fd07a 1790 err = hist_entry__tui_annotate(he, evsel, hbt);
df71d95f 1791 he->used = false;
24bff2dc
SE
1792 /*
1793 * offer option to annotate the other branch source or target
1794 * (if they exists) when returning from annotate
1795 */
1796 if ((err == 'q' || err == CTRL('c'))
1797 && annotate_t != -2 && annotate_f != -2)
1798 goto retry_popup_menu;
1799
900e14a8 1800 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1801 if (err)
1802 ui_browser__handle_resize(&browser->b);
24bff2dc 1803
d1b4f249
ACM
1804 } else if (choice == browse_map)
1805 map__browse(browser->selection->map);
1806 else if (choice == zoom_dso) {
1807zoom_dso:
d7b76f09
ACM
1808 if (browser->hists->dso_filter) {
1809 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1810zoom_out_dso:
1811 ui_helpline__pop();
d7b76f09 1812 browser->hists->dso_filter = NULL;
f2998422 1813 perf_hpp__set_elide(HISTC_DSO, false);
d1b4f249
ACM
1814 } else {
1815 if (dso == NULL)
1816 continue;
1817 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1818 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1819 browser->hists->dso_filter = dso;
f2998422 1820 perf_hpp__set_elide(HISTC_DSO, true);
d7b76f09 1821 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1822 }
05e8b080 1823 hists__filter_by_dso(hists);
d1b4f249
ACM
1824 hist_browser__reset(browser);
1825 } else if (choice == zoom_thread) {
1826zoom_thread:
d7b76f09
ACM
1827 if (browser->hists->thread_filter) {
1828 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1829zoom_out_thread:
1830 ui_helpline__pop();
d7b76f09 1831 browser->hists->thread_filter = NULL;
f2998422 1832 perf_hpp__set_elide(HISTC_THREAD, false);
d1b4f249
ACM
1833 } else {
1834 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
b9c5143a 1835 thread->comm_set ? thread__comm_str(thread) : "",
38051234 1836 thread->tid);
d7b76f09 1837 browser->hists->thread_filter = thread;
f2998422 1838 perf_hpp__set_elide(HISTC_THREAD, false);
d7b76f09 1839 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1840 }
05e8b080 1841 hists__filter_by_thread(hists);
d1b4f249
ACM
1842 hist_browser__reset(browser);
1843 }
cdbab7c2
FT
1844 /* perf scripts support */
1845 else if (choice == scripts_all || choice == scripts_comm ||
1846 choice == scripts_symbol) {
1847do_scripts:
1848 memset(script_opt, 0, 64);
1849
1850 if (choice == scripts_comm)
b9c5143a 1851 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
cdbab7c2
FT
1852
1853 if (choice == scripts_symbol)
1854 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1855
1856 script_browse(script_opt);
1857 }
341487ab
FT
1858 /* Switch to another data file */
1859 else if (choice == switch_data) {
1860do_data_switch:
1861 if (!switch_data_file()) {
1862 key = K_SWITCH_INPUT_DATA;
1863 break;
1864 } else
1865 ui__warning("Won't switch the data files due to\n"
1866 "no valid data file get selected!\n");
1867 }
d1b4f249
ACM
1868 }
1869out_free_stack:
1870 pstack__delete(fstack);
1871out:
1872 hist_browser__delete(browser);
24bff2dc 1873 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1874 return key;
1875}
1876
7f0030b2
ACM
1877struct perf_evsel_menu {
1878 struct ui_browser b;
1879 struct perf_evsel *selection;
7b27509f 1880 bool lost_events, lost_events_warned;
064f1981 1881 float min_pcnt;
68d80758 1882 struct perf_session_env *env;
7f0030b2
ACM
1883};
1884
1885static void perf_evsel_menu__write(struct ui_browser *browser,
1886 void *entry, int row)
1887{
1888 struct perf_evsel_menu *menu = container_of(browser,
1889 struct perf_evsel_menu, b);
1890 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1891 bool current_entry = ui_browser__is_current_entry(browser, row);
1892 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 1893 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 1894 char bf[256], unit;
7b27509f
ACM
1895 const char *warn = " ";
1896 size_t printed;
7f0030b2
ACM
1897
1898 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1899 HE_COLORSET_NORMAL);
1900
759ff497 1901 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1902 struct perf_evsel *pos;
1903
1904 ev_name = perf_evsel__group_name(evsel);
1905
1906 for_each_group_member(pos, evsel) {
1907 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1908 }
1909 }
1910
7f0030b2 1911 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1912 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1913 unit, unit == ' ' ? "" : " ", ev_name);
1914 slsmg_printf("%s", bf);
1915
1916 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1917 if (nr_events != 0) {
1918 menu->lost_events = true;
1919 if (!current_entry)
1920 ui_browser__set_color(browser, HE_COLORSET_TOP);
1921 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1922 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1923 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1924 warn = bf;
1925 }
1926
1927 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1928
1929 if (current_entry)
1930 menu->selection = evsel;
1931}
1932
34958544
ACM
1933static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1934 int nr_events, const char *help,
9783adf7 1935 struct hist_browser_timer *hbt)
d1b4f249 1936{
7f0030b2 1937 struct perf_evlist *evlist = menu->b.priv;
e248de33 1938 struct perf_evsel *pos;
dd00d486 1939 const char *title = "Available samples";
9783adf7 1940 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 1941 int key;
d1b4f249 1942
7f0030b2
ACM
1943 if (ui_browser__show(&menu->b, title,
1944 "ESC: exit, ENTER|->: Browse histograms") < 0)
1945 return -1;
1946
7f0030b2 1947 while (1) {
3af6e338 1948 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1949
1950 switch (key) {
cf958003 1951 case K_TIMER:
9783adf7 1952 hbt->timer(hbt->arg);
7b27509f
ACM
1953
1954 if (!menu->lost_events_warned && menu->lost_events) {
1955 ui_browser__warn_lost_events(&menu->b);
1956 menu->lost_events_warned = true;
1957 }
81cce8de 1958 continue;
cf958003
ACM
1959 case K_RIGHT:
1960 case K_ENTER:
7f0030b2
ACM
1961 if (!menu->selection)
1962 continue;
1963 pos = menu->selection;
1964browse_hists:
18eaf0b8
ACM
1965 perf_evlist__set_selected(evlist, pos);
1966 /*
1967 * Give the calling tool a chance to populate the non
1968 * default evsel resorted hists tree.
1969 */
9783adf7
NK
1970 if (hbt)
1971 hbt->timer(hbt->arg);
34958544 1972 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 1973 true, hbt,
064f1981 1974 menu->min_pcnt,
68d80758 1975 menu->env);
7f0030b2 1976 ui_browser__show_title(&menu->b, title);
18eaf0b8 1977 switch (key) {
cf958003 1978 case K_TAB:
18eaf0b8 1979 if (pos->node.next == &evlist->entries)
9a354cdc 1980 pos = perf_evlist__first(evlist);
18eaf0b8 1981 else
9a354cdc 1982 pos = perf_evsel__next(pos);
18eaf0b8 1983 goto browse_hists;
cf958003 1984 case K_UNTAB:
18eaf0b8 1985 if (pos->node.prev == &evlist->entries)
9a354cdc 1986 pos = perf_evlist__last(evlist);
18eaf0b8 1987 else
d87fcb4a 1988 pos = perf_evsel__prev(pos);
18eaf0b8 1989 goto browse_hists;
cf958003 1990 case K_ESC:
4610e413
ACM
1991 if (!ui_browser__dialog_yesno(&menu->b,
1992 "Do you really want to exit?"))
18eaf0b8
ACM
1993 continue;
1994 /* Fall thru */
341487ab 1995 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
1996 case 'q':
1997 case CTRL('c'):
1998 goto out;
1999 default:
2000 continue;
2001 }
cf958003 2002 case K_LEFT:
7f0030b2 2003 continue;
cf958003 2004 case K_ESC:
4610e413
ACM
2005 if (!ui_browser__dialog_yesno(&menu->b,
2006 "Do you really want to exit?"))
ed7e5662
ACM
2007 continue;
2008 /* Fall thru */
7f0030b2
ACM
2009 case 'q':
2010 case CTRL('c'):
2011 goto out;
d1b4f249 2012 default:
18eaf0b8 2013 continue;
d1b4f249
ACM
2014 }
2015 }
2016
7f0030b2
ACM
2017out:
2018 ui_browser__hide(&menu->b);
2019 return key;
2020}
2021
316c7136 2022static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
2023 void *entry)
2024{
2025 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2026
2027 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2028 return true;
2029
2030 return false;
2031}
2032
7f0030b2 2033static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 2034 int nr_entries, const char *help,
68d80758 2035 struct hist_browser_timer *hbt,
064f1981 2036 float min_pcnt,
68d80758 2037 struct perf_session_env *env)
7f0030b2
ACM
2038{
2039 struct perf_evsel *pos;
2040 struct perf_evsel_menu menu = {
2041 .b = {
2042 .entries = &evlist->entries,
2043 .refresh = ui_browser__list_head_refresh,
2044 .seek = ui_browser__list_head_seek,
2045 .write = perf_evsel_menu__write,
fc24d7c2
NK
2046 .filter = filter_group_entries,
2047 .nr_entries = nr_entries,
7f0030b2
ACM
2048 .priv = evlist,
2049 },
064f1981 2050 .min_pcnt = min_pcnt,
68d80758 2051 .env = env,
7f0030b2
ACM
2052 };
2053
2054 ui_helpline__push("Press ESC to exit");
2055
0050f7aa 2056 evlist__for_each(evlist, pos) {
7289f83c 2057 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
2058 size_t line_len = strlen(ev_name) + 7;
2059
2060 if (menu.b.width < line_len)
2061 menu.b.width = line_len;
7f0030b2
ACM
2062 }
2063
fc24d7c2 2064 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
2065}
2066
81cce8de 2067int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 2068 struct hist_browser_timer *hbt,
064f1981 2069 float min_pcnt,
68d80758 2070 struct perf_session_env *env)
7f0030b2 2071{
fc24d7c2
NK
2072 int nr_entries = evlist->nr_entries;
2073
2074single_entry:
2075 if (nr_entries == 1) {
9a354cdc 2076 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
2077
2078 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 2079 false, hbt, min_pcnt,
064f1981 2080 env);
7f0030b2
ACM
2081 }
2082
fc24d7c2
NK
2083 if (symbol_conf.event_group) {
2084 struct perf_evsel *pos;
2085
2086 nr_entries = 0;
0050f7aa 2087 evlist__for_each(evlist, pos) {
fc24d7c2
NK
2088 if (perf_evsel__is_group_leader(pos))
2089 nr_entries++;
0050f7aa 2090 }
fc24d7c2
NK
2091
2092 if (nr_entries == 1)
2093 goto single_entry;
2094 }
2095
2096 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 2097 hbt, min_pcnt, env);
d1b4f249 2098}
This page took 0.252315 seconds and 5 git commands to generate.