perf hists: Separate out hist print functions
[linux-2.6-block.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>
5#include <newt.h>
6#include <linux/rbtree.h>
7
aca7a94d
NK
8#include "../../util/evsel.h"
9#include "../../util/evlist.h"
10#include "../../util/hist.h"
11#include "../../util/pstack.h"
12#include "../../util/sort.h"
13#include "../../util/util.h"
d1b4f249
ACM
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
4610e413 18#include "../ui.h"
d1b4f249
ACM
19#include "map.h"
20
d1b4f249
ACM
21struct hist_browser {
22 struct ui_browser b;
23 struct hists *hists;
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
aff3f3f6 26 int print_seq;
a7cb8863 27 bool show_dso;
724c9c9f 28 bool has_symbols;
d1b4f249
ACM
29};
30
05e8b080 31static int hists__browser_title(struct hists *hists, char *bf, size_t size,
d7b76f09 32 const char *ev_name);
81cce8de 33
05e8b080 34static void hist_browser__refresh_dimensions(struct hist_browser *browser)
d1b4f249
ACM
35{
36 /* 3 == +/- toggle symbol before actual hist_entry rendering */
05e8b080 37 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
d1b4f249
ACM
38 sizeof("[k]"));
39}
40
05e8b080 41static void hist_browser__reset(struct hist_browser *browser)
d1b4f249 42{
05e8b080
ACM
43 browser->b.nr_entries = browser->hists->nr_entries;
44 hist_browser__refresh_dimensions(browser);
45 ui_browser__reset_index(&browser->b);
d1b4f249
ACM
46}
47
48static char tree__folded_sign(bool unfolded)
49{
50 return unfolded ? '-' : '+';
51}
52
05e8b080 53static char map_symbol__folded(const struct map_symbol *ms)
d1b4f249 54{
05e8b080 55 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
d1b4f249
ACM
56}
57
05e8b080 58static char hist_entry__folded(const struct hist_entry *he)
d1b4f249 59{
05e8b080 60 return map_symbol__folded(&he->ms);
d1b4f249
ACM
61}
62
05e8b080 63static char callchain_list__folded(const struct callchain_list *cl)
d1b4f249 64{
05e8b080 65 return map_symbol__folded(&cl->ms);
d1b4f249
ACM
66}
67
05e8b080 68static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
3c916cc2 69{
05e8b080 70 ms->unfolded = unfold ? ms->has_children : false;
3c916cc2
ACM
71}
72
05e8b080 73static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
d1b4f249
ACM
74{
75 int n = 0;
76 struct rb_node *nd;
77
05e8b080 78 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
79 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
80 struct callchain_list *chain;
81 char folded_sign = ' '; /* No children */
82
83 list_for_each_entry(chain, &child->val, list) {
84 ++n;
85 /* We need this because we may not have children */
86 folded_sign = callchain_list__folded(chain);
87 if (folded_sign == '+')
88 break;
89 }
90
91 if (folded_sign == '-') /* Have children and they're unfolded */
92 n += callchain_node__count_rows_rb_tree(child);
93 }
94
95 return n;
96}
97
98static int callchain_node__count_rows(struct callchain_node *node)
99{
100 struct callchain_list *chain;
101 bool unfolded = false;
102 int n = 0;
103
104 list_for_each_entry(chain, &node->val, list) {
105 ++n;
106 unfolded = chain->ms.unfolded;
107 }
108
109 if (unfolded)
110 n += callchain_node__count_rows_rb_tree(node);
111
112 return n;
113}
114
115static int callchain__count_rows(struct rb_root *chain)
116{
117 struct rb_node *nd;
118 int n = 0;
119
120 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
121 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
122 n += callchain_node__count_rows(node);
123 }
124
125 return n;
126}
127
05e8b080 128static bool map_symbol__toggle_fold(struct map_symbol *ms)
d1b4f249 129{
05e8b080 130 if (!ms)
8493fe1d
JO
131 return false;
132
05e8b080 133 if (!ms->has_children)
d1b4f249
ACM
134 return false;
135
05e8b080 136 ms->unfolded = !ms->unfolded;
d1b4f249
ACM
137 return true;
138}
139
05e8b080 140static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
d1b4f249 141{
05e8b080 142 struct rb_node *nd = rb_first(&node->rb_root);
d1b4f249 143
05e8b080 144 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
145 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
146 struct callchain_list *chain;
293db47f 147 bool first = true;
d1b4f249
ACM
148
149 list_for_each_entry(chain, &child->val, list) {
150 if (first) {
151 first = false;
152 chain->ms.has_children = chain->list.next != &child->val ||
293db47f 153 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
154 } else
155 chain->ms.has_children = chain->list.next == &child->val &&
293db47f 156 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
157 }
158
159 callchain_node__init_have_children_rb_tree(child);
160 }
161}
162
05e8b080 163static void callchain_node__init_have_children(struct callchain_node *node)
d1b4f249
ACM
164{
165 struct callchain_list *chain;
166
05e8b080
ACM
167 list_for_each_entry(chain, &node->val, list)
168 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
d1b4f249 169
05e8b080 170 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
171}
172
05e8b080 173static void callchain__init_have_children(struct rb_root *root)
d1b4f249
ACM
174{
175 struct rb_node *nd;
176
05e8b080 177 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
178 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
179 callchain_node__init_have_children(node);
180 }
181}
182
05e8b080 183static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 184{
05e8b080
ACM
185 if (!he->init_have_children) {
186 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
187 callchain__init_have_children(&he->sorted_chain);
188 he->init_have_children = true;
d1b4f249
ACM
189 }
190}
191
05e8b080 192static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 193{
05e8b080
ACM
194 if (map_symbol__toggle_fold(browser->selection)) {
195 struct hist_entry *he = browser->he_selection;
d1b4f249
ACM
196
197 hist_entry__init_have_children(he);
05e8b080 198 browser->hists->nr_entries -= he->nr_rows;
d1b4f249
ACM
199
200 if (he->ms.unfolded)
201 he->nr_rows = callchain__count_rows(&he->sorted_chain);
202 else
203 he->nr_rows = 0;
05e8b080
ACM
204 browser->hists->nr_entries += he->nr_rows;
205 browser->b.nr_entries = browser->hists->nr_entries;
d1b4f249
ACM
206
207 return true;
208 }
209
210 /* If it doesn't have children, no toggling performed */
211 return false;
212}
213
05e8b080 214static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
215{
216 int n = 0;
217 struct rb_node *nd;
218
05e8b080 219 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
220 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
221 struct callchain_list *chain;
222 bool has_children = false;
223
224 list_for_each_entry(chain, &child->val, list) {
225 ++n;
226 map_symbol__set_folding(&chain->ms, unfold);
227 has_children = chain->ms.has_children;
228 }
229
230 if (has_children)
231 n += callchain_node__set_folding_rb_tree(child, unfold);
232 }
233
234 return n;
235}
236
237static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
238{
239 struct callchain_list *chain;
240 bool has_children = false;
241 int n = 0;
242
243 list_for_each_entry(chain, &node->val, list) {
244 ++n;
245 map_symbol__set_folding(&chain->ms, unfold);
246 has_children = chain->ms.has_children;
247 }
248
249 if (has_children)
250 n += callchain_node__set_folding_rb_tree(node, unfold);
251
252 return n;
253}
254
255static int callchain__set_folding(struct rb_root *chain, bool unfold)
256{
257 struct rb_node *nd;
258 int n = 0;
259
260 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
261 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
262 n += callchain_node__set_folding(node, unfold);
263 }
264
265 return n;
266}
267
05e8b080 268static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
3c916cc2 269{
05e8b080
ACM
270 hist_entry__init_have_children(he);
271 map_symbol__set_folding(&he->ms, unfold);
3c916cc2 272
05e8b080
ACM
273 if (he->ms.has_children) {
274 int n = callchain__set_folding(&he->sorted_chain, unfold);
275 he->nr_rows = unfold ? n : 0;
3c916cc2 276 } else
05e8b080 277 he->nr_rows = 0;
3c916cc2
ACM
278}
279
05e8b080 280static void hists__set_folding(struct hists *hists, bool unfold)
3c916cc2
ACM
281{
282 struct rb_node *nd;
283
05e8b080 284 hists->nr_entries = 0;
3c916cc2 285
05e8b080 286 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
3c916cc2
ACM
287 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
288 hist_entry__set_folding(he, unfold);
05e8b080 289 hists->nr_entries += 1 + he->nr_rows;
3c916cc2
ACM
290 }
291}
292
05e8b080 293static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 294{
05e8b080
ACM
295 hists__set_folding(browser->hists, unfold);
296 browser->b.nr_entries = browser->hists->nr_entries;
3c916cc2 297 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 298 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
299}
300
7b27509f
ACM
301static void ui_browser__warn_lost_events(struct ui_browser *browser)
302{
303 ui_browser__warning(browser, 4,
304 "Events are being lost, check IO/CPU overload!\n\n"
305 "You may want to run 'perf' using a RT scheduler policy:\n\n"
306 " perf top -r 80\n\n"
307 "Or reduce the sampling frequency.");
308}
309
05e8b080 310static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
81cce8de 311 void(*timer)(void *arg), void *arg, int delay_secs)
d1b4f249 312{
b50e003d 313 int key;
81cce8de 314 char title[160];
d1b4f249 315
05e8b080
ACM
316 browser->b.entries = &browser->hists->entries;
317 browser->b.nr_entries = browser->hists->nr_entries;
d1b4f249 318
05e8b080
ACM
319 hist_browser__refresh_dimensions(browser);
320 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
d1b4f249 321
05e8b080 322 if (ui_browser__show(&browser->b, title,
59e8fe32 323 "Press '?' for help on key bindings") < 0)
d1b4f249
ACM
324 return -1;
325
d1b4f249 326 while (1) {
05e8b080 327 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 328
b50e003d 329 switch (key) {
13d8f96c 330 case K_TIMER:
81cce8de 331 timer(arg);
05e8b080 332 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
7b27509f 333
05e8b080
ACM
334 if (browser->hists->stats.nr_lost_warned !=
335 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
336 browser->hists->stats.nr_lost_warned =
337 browser->hists->stats.nr_events[PERF_RECORD_LOST];
338 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
339 }
340
05e8b080
ACM
341 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
342 ui_browser__show_title(&browser->b, title);
81cce8de 343 continue;
4694153c 344 case 'D': { /* Debug */
d1b4f249 345 static int seq;
05e8b080 346 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
347 struct hist_entry, rb_node);
348 ui_helpline__pop();
349 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
350 seq++, browser->b.nr_entries,
351 browser->hists->nr_entries,
352 browser->b.height,
353 browser->b.index,
354 browser->b.top_idx,
d1b4f249
ACM
355 h->row_offset, h->nr_rows);
356 }
3c916cc2
ACM
357 break;
358 case 'C':
359 /* Collapse the whole world. */
05e8b080 360 hist_browser__set_folding(browser, false);
3c916cc2
ACM
361 break;
362 case 'E':
363 /* Expand the whole world. */
05e8b080 364 hist_browser__set_folding(browser, true);
3c916cc2 365 break;
cf958003 366 case K_ENTER:
05e8b080 367 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
368 break;
369 /* fall thru */
370 default:
b50e003d 371 goto out;
d1b4f249
ACM
372 }
373 }
b50e003d 374out:
05e8b080 375 ui_browser__hide(&browser->b);
b50e003d 376 return key;
d1b4f249
ACM
377}
378
05e8b080 379static char *callchain_list__sym_name(struct callchain_list *cl,
a7cb8863 380 char *bf, size_t bfsize, bool show_dso)
d1b4f249 381{
a7cb8863
ACM
382 int printed;
383
05e8b080 384 if (cl->ms.sym)
a7cb8863
ACM
385 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
386 else
387 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
388
389 if (show_dso)
390 scnprintf(bf + printed, bfsize - printed, " %s",
391 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
d1b4f249 392
d1b4f249
ACM
393 return bf;
394}
395
396#define LEVEL_OFFSET_STEP 3
397
05e8b080 398static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
d1b4f249
ACM
399 struct callchain_node *chain_node,
400 u64 total, int level,
401 unsigned short row,
402 off_t *row_offset,
403 bool *is_current_entry)
404{
405 struct rb_node *node;
406 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
407 u64 new_total, remaining;
408
409 if (callchain_param.mode == CHAIN_GRAPH_REL)
410 new_total = chain_node->children_hit;
411 else
412 new_total = total;
413
414 remaining = new_total;
415 node = rb_first(&chain_node->rb_root);
416 while (node) {
417 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
418 struct rb_node *next = rb_next(node);
f08c3154 419 u64 cumul = callchain_cumul_hits(child);
d1b4f249
ACM
420 struct callchain_list *chain;
421 char folded_sign = ' ';
422 int first = true;
423 int extra_offset = 0;
424
425 remaining -= cumul;
426
427 list_for_each_entry(chain, &child->val, list) {
a7cb8863 428 char bf[1024], *alloc_str;
d1b4f249
ACM
429 const char *str;
430 int color;
431 bool was_first = first;
432
163caed9 433 if (first)
d1b4f249 434 first = false;
163caed9 435 else
d1b4f249 436 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
437
438 folded_sign = callchain_list__folded(chain);
439 if (*row_offset != 0) {
440 --*row_offset;
441 goto do_next;
442 }
443
444 alloc_str = NULL;
a7cb8863
ACM
445 str = callchain_list__sym_name(chain, bf, sizeof(bf),
446 browser->show_dso);
d1b4f249
ACM
447 if (was_first) {
448 double percent = cumul * 100.0 / new_total;
449
450 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
451 str = "Not enough memory!";
452 else
453 str = alloc_str;
454 }
455
456 color = HE_COLORSET_NORMAL;
05e8b080
ACM
457 width = browser->b.width - (offset + extra_offset + 2);
458 if (ui_browser__is_current_entry(&browser->b, row)) {
459 browser->selection = &chain->ms;
d1b4f249
ACM
460 color = HE_COLORSET_SELECTED;
461 *is_current_entry = true;
462 }
463
05e8b080
ACM
464 ui_browser__set_color(&browser->b, color);
465 ui_browser__gotorc(&browser->b, row, 0);
d1b4f249
ACM
466 slsmg_write_nstring(" ", offset + extra_offset);
467 slsmg_printf("%c ", folded_sign);
468 slsmg_write_nstring(str, width);
469 free(alloc_str);
470
05e8b080 471 if (++row == browser->b.height)
d1b4f249
ACM
472 goto out;
473do_next:
474 if (folded_sign == '+')
475 break;
476 }
477
478 if (folded_sign == '-') {
479 const int new_level = level + (extra_offset ? 2 : 1);
05e8b080 480 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
d1b4f249
ACM
481 new_level, row, row_offset,
482 is_current_entry);
483 }
05e8b080 484 if (row == browser->b.height)
d1b4f249
ACM
485 goto out;
486 node = next;
487 }
488out:
489 return row - first_row;
490}
491
05e8b080 492static int hist_browser__show_callchain_node(struct hist_browser *browser,
d1b4f249
ACM
493 struct callchain_node *node,
494 int level, unsigned short row,
495 off_t *row_offset,
496 bool *is_current_entry)
497{
498 struct callchain_list *chain;
499 int first_row = row,
500 offset = level * LEVEL_OFFSET_STEP,
05e8b080 501 width = browser->b.width - offset;
d1b4f249
ACM
502 char folded_sign = ' ';
503
504 list_for_each_entry(chain, &node->val, list) {
a7cb8863 505 char bf[1024], *s;
d1b4f249 506 int color;
163caed9 507
d1b4f249
ACM
508 folded_sign = callchain_list__folded(chain);
509
510 if (*row_offset != 0) {
511 --*row_offset;
512 continue;
513 }
514
515 color = HE_COLORSET_NORMAL;
05e8b080
ACM
516 if (ui_browser__is_current_entry(&browser->b, row)) {
517 browser->selection = &chain->ms;
d1b4f249
ACM
518 color = HE_COLORSET_SELECTED;
519 *is_current_entry = true;
520 }
521
a7cb8863
ACM
522 s = callchain_list__sym_name(chain, bf, sizeof(bf),
523 browser->show_dso);
05e8b080
ACM
524 ui_browser__gotorc(&browser->b, row, 0);
525 ui_browser__set_color(&browser->b, color);
d1b4f249
ACM
526 slsmg_write_nstring(" ", offset);
527 slsmg_printf("%c ", folded_sign);
528 slsmg_write_nstring(s, width - 2);
529
05e8b080 530 if (++row == browser->b.height)
d1b4f249
ACM
531 goto out;
532 }
533
534 if (folded_sign == '-')
05e8b080
ACM
535 row += hist_browser__show_callchain_node_rb_tree(browser, node,
536 browser->hists->stats.total_period,
d1b4f249
ACM
537 level + 1, row,
538 row_offset,
539 is_current_entry);
540out:
541 return row - first_row;
542}
543
05e8b080 544static int hist_browser__show_callchain(struct hist_browser *browser,
d1b4f249
ACM
545 struct rb_root *chain,
546 int level, unsigned short row,
547 off_t *row_offset,
548 bool *is_current_entry)
549{
550 struct rb_node *nd;
551 int first_row = row;
552
553 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
554 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
555
05e8b080 556 row += hist_browser__show_callchain_node(browser, node, level,
d1b4f249
ACM
557 row, row_offset,
558 is_current_entry);
05e8b080 559 if (row == browser->b.height)
d1b4f249
ACM
560 break;
561 }
562
563 return row - first_row;
564}
565
05e8b080 566static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
567 struct hist_entry *entry,
568 unsigned short row)
569{
570 char s[256];
571 double percent;
572 int printed = 0;
05e8b080 573 int width = browser->b.width - 6; /* The percentage */
d1b4f249 574 char folded_sign = ' ';
05e8b080 575 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249
ACM
576 off_t row_offset = entry->row_offset;
577
578 if (current_entry) {
05e8b080
ACM
579 browser->he_selection = entry;
580 browser->selection = &entry->ms;
d1b4f249
ACM
581 }
582
583 if (symbol_conf.use_callchain) {
163caed9 584 hist_entry__init_have_children(entry);
d1b4f249
ACM
585 folded_sign = hist_entry__folded(entry);
586 }
587
588 if (row_offset == 0) {
05e8b080
ACM
589 hist_entry__snprintf(entry, s, sizeof(s), browser->hists);
590 percent = (entry->period * 100.0) / browser->hists->stats.total_period;
d1b4f249 591
05e8b080
ACM
592 ui_browser__set_percent_color(&browser->b, percent, current_entry);
593 ui_browser__gotorc(&browser->b, row, 0);
d1b4f249
ACM
594 if (symbol_conf.use_callchain) {
595 slsmg_printf("%c ", folded_sign);
596 width -= 2;
597 }
c172f742 598
f1cf602c
ACM
599 slsmg_printf(" %5.2f%%", percent);
600
c172f742 601 /* The scroll bar isn't being used */
05e8b080 602 if (!browser->b.navkeypressed)
c172f742
ACM
603 width += 1;
604
05e8b080
ACM
605 if (!current_entry || !browser->b.navkeypressed)
606 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
33f62b3f 607
2cf9cebf
ACM
608 if (symbol_conf.show_nr_samples) {
609 slsmg_printf(" %11u", entry->nr_events);
610 width -= 12;
611 }
612
613 if (symbol_conf.show_total_period) {
614 slsmg_printf(" %12" PRIu64, entry->period);
615 width -= 13;
616 }
617
d1b4f249
ACM
618 slsmg_write_nstring(s, width);
619 ++row;
620 ++printed;
621 } else
622 --row_offset;
623
05e8b080
ACM
624 if (folded_sign == '-' && row != browser->b.height) {
625 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
d1b4f249
ACM
626 1, row, &row_offset,
627 &current_entry);
628 if (current_entry)
05e8b080 629 browser->he_selection = entry;
d1b4f249
ACM
630 }
631
632 return printed;
633}
634
437cfe7a
ACM
635static void ui_browser__hists_init_top(struct ui_browser *browser)
636{
637 if (browser->top == NULL) {
638 struct hist_browser *hb;
639
640 hb = container_of(browser, struct hist_browser, b);
641 browser->top = rb_first(&hb->hists->entries);
642 }
643}
644
05e8b080 645static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
646{
647 unsigned row = 0;
648 struct rb_node *nd;
05e8b080 649 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 650
05e8b080 651 ui_browser__hists_init_top(browser);
d1b4f249 652
05e8b080 653 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249
ACM
654 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
655
656 if (h->filtered)
657 continue;
658
659 row += hist_browser__show_entry(hb, h, row);
05e8b080 660 if (row == browser->height)
d1b4f249
ACM
661 break;
662 }
663
664 return row;
665}
666
667static struct rb_node *hists__filter_entries(struct rb_node *nd)
668{
669 while (nd != NULL) {
670 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
671 if (!h->filtered)
672 return nd;
673
674 nd = rb_next(nd);
675 }
676
677 return NULL;
678}
679
680static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
681{
682 while (nd != NULL) {
683 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
684 if (!h->filtered)
685 return nd;
686
687 nd = rb_prev(nd);
688 }
689
690 return NULL;
691}
692
05e8b080 693static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
694 off_t offset, int whence)
695{
696 struct hist_entry *h;
697 struct rb_node *nd;
698 bool first = true;
699
05e8b080 700 if (browser->nr_entries == 0)
60098917
ACM
701 return;
702
05e8b080 703 ui_browser__hists_init_top(browser);
437cfe7a 704
d1b4f249
ACM
705 switch (whence) {
706 case SEEK_SET:
05e8b080 707 nd = hists__filter_entries(rb_first(browser->entries));
d1b4f249
ACM
708 break;
709 case SEEK_CUR:
05e8b080 710 nd = browser->top;
d1b4f249
ACM
711 goto do_offset;
712 case SEEK_END:
05e8b080 713 nd = hists__filter_prev_entries(rb_last(browser->entries));
d1b4f249
ACM
714 first = false;
715 break;
716 default:
717 return;
718 }
719
720 /*
721 * Moves not relative to the first visible entry invalidates its
722 * row_offset:
723 */
05e8b080 724 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
725 h->row_offset = 0;
726
727 /*
728 * Here we have to check if nd is expanded (+), if it is we can't go
729 * the next top level hist_entry, instead we must compute an offset of
730 * what _not_ to show and not change the first visible entry.
731 *
732 * This offset increments when we are going from top to bottom and
733 * decreases when we're going from bottom to top.
734 *
735 * As we don't have backpointers to the top level in the callchains
736 * structure, we need to always print the whole hist_entry callchain,
737 * skipping the first ones that are before the first visible entry
738 * and stop when we printed enough lines to fill the screen.
739 */
740do_offset:
741 if (offset > 0) {
742 do {
743 h = rb_entry(nd, struct hist_entry, rb_node);
744 if (h->ms.unfolded) {
745 u16 remaining = h->nr_rows - h->row_offset;
746 if (offset > remaining) {
747 offset -= remaining;
748 h->row_offset = 0;
749 } else {
750 h->row_offset += offset;
751 offset = 0;
05e8b080 752 browser->top = nd;
d1b4f249
ACM
753 break;
754 }
755 }
756 nd = hists__filter_entries(rb_next(nd));
757 if (nd == NULL)
758 break;
759 --offset;
05e8b080 760 browser->top = nd;
d1b4f249
ACM
761 } while (offset != 0);
762 } else if (offset < 0) {
763 while (1) {
764 h = rb_entry(nd, struct hist_entry, rb_node);
765 if (h->ms.unfolded) {
766 if (first) {
767 if (-offset > h->row_offset) {
768 offset += h->row_offset;
769 h->row_offset = 0;
770 } else {
771 h->row_offset += offset;
772 offset = 0;
05e8b080 773 browser->top = nd;
d1b4f249
ACM
774 break;
775 }
776 } else {
777 if (-offset > h->nr_rows) {
778 offset += h->nr_rows;
779 h->row_offset = 0;
780 } else {
781 h->row_offset = h->nr_rows + offset;
782 offset = 0;
05e8b080 783 browser->top = nd;
d1b4f249
ACM
784 break;
785 }
786 }
787 }
788
789 nd = hists__filter_prev_entries(rb_prev(nd));
790 if (nd == NULL)
791 break;
792 ++offset;
05e8b080 793 browser->top = nd;
d1b4f249
ACM
794 if (offset == 0) {
795 /*
796 * Last unfiltered hist_entry, check if it is
797 * unfolded, if it is then we should have
798 * row_offset at its last entry.
799 */
800 h = rb_entry(nd, struct hist_entry, rb_node);
801 if (h->ms.unfolded)
802 h->row_offset = h->nr_rows;
803 break;
804 }
805 first = false;
806 }
807 } else {
05e8b080 808 browser->top = nd;
d1b4f249
ACM
809 h = rb_entry(nd, struct hist_entry, rb_node);
810 h->row_offset = 0;
811 }
812}
813
aff3f3f6
ACM
814static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
815 struct callchain_node *chain_node,
816 u64 total, int level,
817 FILE *fp)
818{
819 struct rb_node *node;
820 int offset = level * LEVEL_OFFSET_STEP;
821 u64 new_total, remaining;
822 int printed = 0;
823
824 if (callchain_param.mode == CHAIN_GRAPH_REL)
825 new_total = chain_node->children_hit;
826 else
827 new_total = total;
828
829 remaining = new_total;
830 node = rb_first(&chain_node->rb_root);
831 while (node) {
832 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
833 struct rb_node *next = rb_next(node);
834 u64 cumul = callchain_cumul_hits(child);
835 struct callchain_list *chain;
836 char folded_sign = ' ';
837 int first = true;
838 int extra_offset = 0;
839
840 remaining -= cumul;
841
842 list_for_each_entry(chain, &child->val, list) {
a7cb8863 843 char bf[1024], *alloc_str;
aff3f3f6
ACM
844 const char *str;
845 bool was_first = first;
846
847 if (first)
848 first = false;
849 else
850 extra_offset = LEVEL_OFFSET_STEP;
851
852 folded_sign = callchain_list__folded(chain);
853
854 alloc_str = NULL;
a7cb8863
ACM
855 str = callchain_list__sym_name(chain, bf, sizeof(bf),
856 browser->show_dso);
aff3f3f6
ACM
857 if (was_first) {
858 double percent = cumul * 100.0 / new_total;
859
860 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
861 str = "Not enough memory!";
862 else
863 str = alloc_str;
864 }
865
866 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
867 free(alloc_str);
868 if (folded_sign == '+')
869 break;
870 }
871
872 if (folded_sign == '-') {
873 const int new_level = level + (extra_offset ? 2 : 1);
874 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
875 new_level, fp);
876 }
877
878 node = next;
879 }
880
881 return printed;
882}
883
884static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
885 struct callchain_node *node,
886 int level, FILE *fp)
887{
888 struct callchain_list *chain;
889 int offset = level * LEVEL_OFFSET_STEP;
890 char folded_sign = ' ';
891 int printed = 0;
892
893 list_for_each_entry(chain, &node->val, list) {
a7cb8863 894 char bf[1024], *s;
aff3f3f6
ACM
895
896 folded_sign = callchain_list__folded(chain);
a7cb8863 897 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
aff3f3f6
ACM
898 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
899 }
900
901 if (folded_sign == '-')
902 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
903 browser->hists->stats.total_period,
904 level + 1, fp);
905 return printed;
906}
907
908static int hist_browser__fprintf_callchain(struct hist_browser *browser,
909 struct rb_root *chain, int level, FILE *fp)
910{
911 struct rb_node *nd;
912 int printed = 0;
913
914 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
915 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
916
917 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
918 }
919
920 return printed;
921}
922
923static int hist_browser__fprintf_entry(struct hist_browser *browser,
924 struct hist_entry *he, FILE *fp)
925{
926 char s[8192];
927 double percent;
928 int printed = 0;
929 char folded_sign = ' ';
930
931 if (symbol_conf.use_callchain)
932 folded_sign = hist_entry__folded(he);
933
934 hist_entry__snprintf(he, s, sizeof(s), browser->hists);
935 percent = (he->period * 100.0) / browser->hists->stats.total_period;
936
937 if (symbol_conf.use_callchain)
938 printed += fprintf(fp, "%c ", folded_sign);
939
940 printed += fprintf(fp, " %5.2f%%", percent);
941
942 if (symbol_conf.show_nr_samples)
943 printed += fprintf(fp, " %11u", he->nr_events);
944
945 if (symbol_conf.show_total_period)
946 printed += fprintf(fp, " %12" PRIu64, he->period);
947
948 printed += fprintf(fp, "%s\n", rtrim(s));
949
950 if (folded_sign == '-')
951 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
952
953 return printed;
954}
955
956static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
957{
958 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
959 int printed = 0;
960
961 while (nd) {
962 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
963
964 printed += hist_browser__fprintf_entry(browser, h, fp);
965 nd = hists__filter_entries(rb_next(nd));
966 }
967
968 return printed;
969}
970
971static int hist_browser__dump(struct hist_browser *browser)
972{
973 char filename[64];
974 FILE *fp;
975
976 while (1) {
977 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
978 if (access(filename, F_OK))
979 break;
980 /*
981 * XXX: Just an arbitrary lazy upper limit
982 */
983 if (++browser->print_seq == 8192) {
984 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
985 return -1;
986 }
987 }
988
989 fp = fopen(filename, "w");
990 if (fp == NULL) {
991 char bf[64];
4cc49d4d
KS
992 const char *err = strerror_r(errno, bf, sizeof(bf));
993 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
994 return -1;
995 }
996
997 ++browser->print_seq;
998 hist_browser__fprintf(browser, fp);
999 fclose(fp);
1000 ui_helpline__fpush("%s written!", filename);
1001
1002 return 0;
1003}
1004
d1b4f249
ACM
1005static struct hist_browser *hist_browser__new(struct hists *hists)
1006{
05e8b080 1007 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1008
05e8b080
ACM
1009 if (browser) {
1010 browser->hists = hists;
1011 browser->b.refresh = hist_browser__refresh;
1012 browser->b.seek = ui_browser__hists_seek;
1013 browser->b.use_navkeypressed = true;
a68c2c58 1014 if (sort__branch_mode == 1)
05e8b080 1015 browser->has_symbols = sort_sym_from.list.next != NULL;
a68c2c58 1016 else
05e8b080 1017 browser->has_symbols = sort_sym.list.next != NULL;
d1b4f249
ACM
1018 }
1019
05e8b080 1020 return browser;
d1b4f249
ACM
1021}
1022
05e8b080 1023static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1024{
05e8b080 1025 free(browser);
d1b4f249
ACM
1026}
1027
05e8b080 1028static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1029{
05e8b080 1030 return browser->he_selection;
d1b4f249
ACM
1031}
1032
05e8b080 1033static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1034{
05e8b080 1035 return browser->he_selection->thread;
d1b4f249
ACM
1036}
1037
05e8b080 1038static int hists__browser_title(struct hists *hists, char *bf, size_t size,
d7b76f09 1039 const char *ev_name)
d1b4f249 1040{
469917ce
ACM
1041 char unit;
1042 int printed;
05e8b080
ACM
1043 const struct dso *dso = hists->dso_filter;
1044 const struct thread *thread = hists->thread_filter;
1045 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1046 u64 nr_events = hists->stats.total_period;
cc686280
AR
1047
1048 nr_samples = convert_unit(nr_samples, &unit);
1049 printed = scnprintf(bf, size,
1050 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1051 nr_samples, unit, ev_name, nr_events);
469917ce 1052
d1b4f249 1053
05e8b080 1054 if (hists->uid_filter_str)
0d37aa34 1055 printed += snprintf(bf + printed, size - printed,
05e8b080 1056 ", UID: %s", hists->uid_filter_str);
d1b4f249 1057 if (thread)
e7f01d1e 1058 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1059 ", Thread: %s(%d)",
1060 (thread->comm_set ? thread->comm : ""),
d1b4f249
ACM
1061 thread->pid);
1062 if (dso)
e7f01d1e 1063 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1064 ", DSO: %s", dso->short_name);
1065 return printed;
d1b4f249
ACM
1066}
1067
24bff2dc
SE
1068static inline void free_popup_options(char **options, int n)
1069{
1070 int i;
1071
1072 for (i = 0; i < n; ++i) {
1073 free(options[i]);
1074 options[i] = NULL;
1075 }
1076}
1077
34958544 1078static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
7f0030b2 1079 const char *helpline, const char *ev_name,
81cce8de
ACM
1080 bool left_exits,
1081 void(*timer)(void *arg), void *arg,
1082 int delay_secs)
d1b4f249 1083{
05e8b080
ACM
1084 struct hists *hists = &evsel->hists;
1085 struct hist_browser *browser = hist_browser__new(hists);
a68c2c58 1086 struct branch_info *bi;
d1b4f249 1087 struct pstack *fstack;
24bff2dc
SE
1088 char *options[16];
1089 int nr_options = 0;
d1b4f249 1090 int key = -1;
938a23ae 1091 char buf[64];
d1b4f249
ACM
1092
1093 if (browser == NULL)
1094 return -1;
1095
1096 fstack = pstack__new(2);
1097 if (fstack == NULL)
1098 goto out;
1099
1100 ui_helpline__push(helpline);
1101
24bff2dc
SE
1102 memset(options, 0, sizeof(options));
1103
d1b4f249 1104 while (1) {
60098917
ACM
1105 const struct thread *thread = NULL;
1106 const struct dso *dso = NULL;
24bff2dc 1107 int choice = 0,
d1b4f249 1108 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 1109 annotate_f = -2, annotate_t = -2, browse_map = -2;
d1b4f249 1110
24bff2dc
SE
1111 nr_options = 0;
1112
81cce8de 1113 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
d1b4f249 1114
60098917
ACM
1115 if (browser->he_selection != NULL) {
1116 thread = hist_browser__selected_thread(browser);
1117 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1118 }
b50e003d 1119 switch (key) {
cf958003
ACM
1120 case K_TAB:
1121 case K_UNTAB:
e4419b8e
DA
1122 if (nr_events == 1)
1123 continue;
b50e003d
ACM
1124 /*
1125 * Exit the browser, let hists__browser_tree
1126 * go to the next or previous
1127 */
1128 goto out_free_stack;
1129 case 'a':
a6e51f9f 1130 if (!browser->has_symbols) {
7b27509f 1131 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1132 "Annotation is only available for symbolic views, "
a68c2c58 1133 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1134 continue;
1135 }
1136
60098917 1137 if (browser->selection == NULL ||
db9a9cbc 1138 browser->selection->sym == NULL ||
b50e003d 1139 browser->selection->map->dso->annotate_warned)
d1b4f249 1140 continue;
b50e003d 1141 goto do_annotate;
aff3f3f6
ACM
1142 case 'P':
1143 hist_browser__dump(browser);
1144 continue;
b50e003d
ACM
1145 case 'd':
1146 goto zoom_dso;
a7cb8863
ACM
1147 case 'V':
1148 browser->show_dso = !browser->show_dso;
1149 continue;
b50e003d
ACM
1150 case 't':
1151 goto zoom_thread;
5a5626b1 1152 case '/':
938a23ae
NK
1153 if (ui_browser__input_window("Symbol to show",
1154 "Please enter the name of symbol you want to see",
1155 buf, "ENTER: OK, ESC: Cancel",
1156 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1157 hists->symbol_filter_str = *buf ? buf : NULL;
1158 hists__filter_by_symbol(hists);
938a23ae
NK
1159 hist_browser__reset(browser);
1160 }
1161 continue;
cf958003 1162 case K_F1:
b50e003d
ACM
1163 case 'h':
1164 case '?':
4610e413
ACM
1165 ui_browser__help_window(&browser->b,
1166 "h/?/F1 Show this window\n"
2d5646c0
ACM
1167 "UP/DOWN/PGUP\n"
1168 "PGDN/SPACE Navigate\n"
1169 "q/ESC/CTRL+C Exit browser\n\n"
1170 "For multiple event sessions:\n\n"
1171 "TAB/UNTAB Switch events\n\n"
724c9c9f 1172 "For symbolic views (--sort has sym):\n\n"
2d5646c0
ACM
1173 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1174 "<- Zoom out\n"
1175 "a Annotate current symbol\n"
1176 "C Collapse all callchains\n"
1177 "E Expand all callchains\n"
1178 "d Zoom into current DSO\n"
938a23ae 1179 "t Zoom into current Thread\n"
aff3f3f6 1180 "P Print histograms to perf.hist.N\n"
a7cb8863 1181 "V Verbose (DSO names in callchains, etc)\n"
5a5626b1 1182 "/ Filter symbol by name");
b50e003d 1183 continue;
cf958003
ACM
1184 case K_ENTER:
1185 case K_RIGHT:
b50e003d
ACM
1186 /* menu */
1187 break;
cf958003 1188 case K_LEFT: {
b50e003d 1189 const void *top;
d1b4f249 1190
7f0030b2
ACM
1191 if (pstack__empty(fstack)) {
1192 /*
1193 * Go back to the perf_evsel_menu__run or other user
1194 */
1195 if (left_exits)
1196 goto out_free_stack;
d1b4f249 1197 continue;
7f0030b2 1198 }
b50e003d 1199 top = pstack__pop(fstack);
d7b76f09 1200 if (top == &browser->hists->dso_filter)
b50e003d 1201 goto zoom_out_dso;
d7b76f09 1202 if (top == &browser->hists->thread_filter)
b50e003d
ACM
1203 goto zoom_out_thread;
1204 continue;
1205 }
cf958003 1206 case K_ESC:
7f0030b2 1207 if (!left_exits &&
4610e413
ACM
1208 !ui_browser__dialog_yesno(&browser->b,
1209 "Do you really want to exit?"))
b50e003d
ACM
1210 continue;
1211 /* Fall thru */
ed7e5662
ACM
1212 case 'q':
1213 case CTRL('c'):
b50e003d 1214 goto out_free_stack;
ed7e5662
ACM
1215 default:
1216 continue;
d1b4f249
ACM
1217 }
1218
724c9c9f
ACM
1219 if (!browser->has_symbols)
1220 goto add_exit_option;
1221
a68c2c58
SE
1222 if (sort__branch_mode == 1) {
1223 bi = browser->he_selection->branch_info;
1224 if (browser->selection != NULL &&
1225 bi &&
1226 bi->from.sym != NULL &&
1227 !bi->from.map->dso->annotate_warned &&
1228 asprintf(&options[nr_options], "Annotate %s",
1229 bi->from.sym->name) > 0)
1230 annotate_f = nr_options++;
1231
1232 if (browser->selection != NULL &&
1233 bi &&
1234 bi->to.sym != NULL &&
1235 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1236 (bi->to.sym != bi->from.sym ||
1237 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1238 asprintf(&options[nr_options], "Annotate %s",
1239 bi->to.sym->name) > 0)
1240 annotate_t = nr_options++;
1241 } else {
1242
1243 if (browser->selection != NULL &&
1244 browser->selection->sym != NULL &&
1245 !browser->selection->map->dso->annotate_warned &&
1246 asprintf(&options[nr_options], "Annotate %s",
1247 browser->selection->sym->name) > 0)
1248 annotate = nr_options++;
1249 }
d1b4f249
ACM
1250
1251 if (thread != NULL &&
1252 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1253 (browser->hists->thread_filter ? "out of" : "into"),
d1b4f249
ACM
1254 (thread->comm_set ? thread->comm : ""),
1255 thread->pid) > 0)
1256 zoom_thread = nr_options++;
1257
1258 if (dso != NULL &&
1259 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1260 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1261 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1262 zoom_dso = nr_options++;
1263
60098917
ACM
1264 if (browser->selection != NULL &&
1265 browser->selection->map != NULL &&
d1b4f249
ACM
1266 asprintf(&options[nr_options], "Browse map details") > 0)
1267 browse_map = nr_options++;
724c9c9f 1268add_exit_option:
d1b4f249 1269 options[nr_options++] = (char *)"Exit";
24bff2dc 1270retry_popup_menu:
1e6dd077 1271 choice = ui__popup_menu(nr_options, options);
d1b4f249 1272
d1b4f249
ACM
1273 if (choice == nr_options - 1)
1274 break;
1275
24bff2dc
SE
1276 if (choice == -1) {
1277 free_popup_options(options, nr_options - 1);
d1b4f249 1278 continue;
24bff2dc 1279 }
d1b4f249 1280
a68c2c58 1281 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1282 struct hist_entry *he;
4610e413 1283 int err;
d1b4f249 1284do_annotate:
d1b4f249
ACM
1285 he = hist_browser__selected_entry(browser);
1286 if (he == NULL)
1287 continue;
a68c2c58
SE
1288
1289 /*
1290 * we stash the branch_info symbol + map into the
1291 * the ms so we don't have to rewrite all the annotation
1292 * code to use branch_info.
1293 * in branch mode, the ms struct is not used
1294 */
1295 if (choice == annotate_f) {
1296 he->ms.sym = he->branch_info->from.sym;
1297 he->ms.map = he->branch_info->from.map;
1298 } else if (choice == annotate_t) {
1299 he->ms.sym = he->branch_info->to.sym;
1300 he->ms.map = he->branch_info->to.map;
1301 }
1302
df71d95f
ACM
1303 /*
1304 * Don't let this be freed, say, by hists__decay_entry.
1305 */
1306 he->used = true;
d04b35f8 1307 err = hist_entry__tui_annotate(he, evsel->idx,
4610e413 1308 timer, arg, delay_secs);
df71d95f 1309 he->used = false;
24bff2dc
SE
1310 /*
1311 * offer option to annotate the other branch source or target
1312 * (if they exists) when returning from annotate
1313 */
1314 if ((err == 'q' || err == CTRL('c'))
1315 && annotate_t != -2 && annotate_f != -2)
1316 goto retry_popup_menu;
1317
900e14a8 1318 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1319 if (err)
1320 ui_browser__handle_resize(&browser->b);
24bff2dc 1321
d1b4f249
ACM
1322 } else if (choice == browse_map)
1323 map__browse(browser->selection->map);
1324 else if (choice == zoom_dso) {
1325zoom_dso:
d7b76f09
ACM
1326 if (browser->hists->dso_filter) {
1327 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1328zoom_out_dso:
1329 ui_helpline__pop();
d7b76f09 1330 browser->hists->dso_filter = NULL;
cc02c921 1331 sort_dso.elide = false;
d1b4f249
ACM
1332 } else {
1333 if (dso == NULL)
1334 continue;
1335 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1336 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1337 browser->hists->dso_filter = dso;
cc02c921 1338 sort_dso.elide = true;
d7b76f09 1339 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1340 }
05e8b080 1341 hists__filter_by_dso(hists);
d1b4f249
ACM
1342 hist_browser__reset(browser);
1343 } else if (choice == zoom_thread) {
1344zoom_thread:
d7b76f09
ACM
1345 if (browser->hists->thread_filter) {
1346 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1347zoom_out_thread:
1348 ui_helpline__pop();
d7b76f09 1349 browser->hists->thread_filter = NULL;
cc02c921 1350 sort_thread.elide = false;
d1b4f249
ACM
1351 } else {
1352 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1353 thread->comm_set ? thread->comm : "",
1354 thread->pid);
d7b76f09 1355 browser->hists->thread_filter = thread;
cc02c921 1356 sort_thread.elide = true;
d7b76f09 1357 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1358 }
05e8b080 1359 hists__filter_by_thread(hists);
d1b4f249
ACM
1360 hist_browser__reset(browser);
1361 }
1362 }
1363out_free_stack:
1364 pstack__delete(fstack);
1365out:
1366 hist_browser__delete(browser);
24bff2dc 1367 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1368 return key;
1369}
1370
7f0030b2
ACM
1371struct perf_evsel_menu {
1372 struct ui_browser b;
1373 struct perf_evsel *selection;
7b27509f 1374 bool lost_events, lost_events_warned;
7f0030b2
ACM
1375};
1376
1377static void perf_evsel_menu__write(struct ui_browser *browser,
1378 void *entry, int row)
1379{
1380 struct perf_evsel_menu *menu = container_of(browser,
1381 struct perf_evsel_menu, b);
1382 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1383 bool current_entry = ui_browser__is_current_entry(browser, row);
1384 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 1385 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 1386 char bf[256], unit;
7b27509f
ACM
1387 const char *warn = " ";
1388 size_t printed;
7f0030b2
ACM
1389
1390 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1391 HE_COLORSET_NORMAL);
1392
1393 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1394 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1395 unit, unit == ' ' ? "" : " ", ev_name);
1396 slsmg_printf("%s", bf);
1397
1398 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1399 if (nr_events != 0) {
1400 menu->lost_events = true;
1401 if (!current_entry)
1402 ui_browser__set_color(browser, HE_COLORSET_TOP);
1403 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1404 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1405 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1406 warn = bf;
1407 }
1408
1409 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1410
1411 if (current_entry)
1412 menu->selection = evsel;
1413}
1414
34958544
ACM
1415static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1416 int nr_events, const char *help,
81cce8de 1417 void(*timer)(void *arg), void *arg, int delay_secs)
d1b4f249 1418{
7f0030b2 1419 struct perf_evlist *evlist = menu->b.priv;
e248de33 1420 struct perf_evsel *pos;
7f0030b2
ACM
1421 const char *ev_name, *title = "Available samples";
1422 int key;
d1b4f249 1423
7f0030b2
ACM
1424 if (ui_browser__show(&menu->b, title,
1425 "ESC: exit, ENTER|->: Browse histograms") < 0)
1426 return -1;
1427
7f0030b2 1428 while (1) {
3af6e338 1429 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1430
1431 switch (key) {
cf958003 1432 case K_TIMER:
81cce8de 1433 timer(arg);
7b27509f
ACM
1434
1435 if (!menu->lost_events_warned && menu->lost_events) {
1436 ui_browser__warn_lost_events(&menu->b);
1437 menu->lost_events_warned = true;
1438 }
81cce8de 1439 continue;
cf958003
ACM
1440 case K_RIGHT:
1441 case K_ENTER:
7f0030b2
ACM
1442 if (!menu->selection)
1443 continue;
1444 pos = menu->selection;
1445browse_hists:
18eaf0b8
ACM
1446 perf_evlist__set_selected(evlist, pos);
1447 /*
1448 * Give the calling tool a chance to populate the non
1449 * default evsel resorted hists tree.
1450 */
1451 if (timer)
1452 timer(arg);
7289f83c 1453 ev_name = perf_evsel__name(pos);
34958544
ACM
1454 key = perf_evsel__hists_browse(pos, nr_events, help,
1455 ev_name, true, timer,
1456 arg, delay_secs);
7f0030b2 1457 ui_browser__show_title(&menu->b, title);
18eaf0b8 1458 switch (key) {
cf958003 1459 case K_TAB:
18eaf0b8
ACM
1460 if (pos->node.next == &evlist->entries)
1461 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1462 else
1463 pos = list_entry(pos->node.next, struct perf_evsel, node);
1464 goto browse_hists;
cf958003 1465 case K_UNTAB:
18eaf0b8
ACM
1466 if (pos->node.prev == &evlist->entries)
1467 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1468 else
1469 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1470 goto browse_hists;
cf958003 1471 case K_ESC:
4610e413
ACM
1472 if (!ui_browser__dialog_yesno(&menu->b,
1473 "Do you really want to exit?"))
18eaf0b8
ACM
1474 continue;
1475 /* Fall thru */
1476 case 'q':
1477 case CTRL('c'):
1478 goto out;
1479 default:
1480 continue;
1481 }
cf958003 1482 case K_LEFT:
7f0030b2 1483 continue;
cf958003 1484 case K_ESC:
4610e413
ACM
1485 if (!ui_browser__dialog_yesno(&menu->b,
1486 "Do you really want to exit?"))
ed7e5662
ACM
1487 continue;
1488 /* Fall thru */
7f0030b2
ACM
1489 case 'q':
1490 case CTRL('c'):
1491 goto out;
d1b4f249 1492 default:
18eaf0b8 1493 continue;
d1b4f249
ACM
1494 }
1495 }
1496
7f0030b2
ACM
1497out:
1498 ui_browser__hide(&menu->b);
1499 return key;
1500}
1501
1502static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
81cce8de
ACM
1503 const char *help,
1504 void(*timer)(void *arg), void *arg,
1505 int delay_secs)
7f0030b2
ACM
1506{
1507 struct perf_evsel *pos;
1508 struct perf_evsel_menu menu = {
1509 .b = {
1510 .entries = &evlist->entries,
1511 .refresh = ui_browser__list_head_refresh,
1512 .seek = ui_browser__list_head_seek,
1513 .write = perf_evsel_menu__write,
1514 .nr_entries = evlist->nr_entries,
1515 .priv = evlist,
1516 },
1517 };
1518
1519 ui_helpline__push("Press ESC to exit");
1520
1521 list_for_each_entry(pos, &evlist->entries, node) {
7289f83c 1522 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
1523 size_t line_len = strlen(ev_name) + 7;
1524
1525 if (menu.b.width < line_len)
1526 menu.b.width = line_len;
7f0030b2
ACM
1527 }
1528
34958544
ACM
1529 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1530 arg, delay_secs);
7f0030b2
ACM
1531}
1532
81cce8de
ACM
1533int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1534 void(*timer)(void *arg), void *arg,
1535 int delay_secs)
7f0030b2 1536{
7f0030b2
ACM
1537 if (evlist->nr_entries == 1) {
1538 struct perf_evsel *first = list_entry(evlist->entries.next,
1539 struct perf_evsel, node);
7289f83c 1540 const char *ev_name = perf_evsel__name(first);
34958544
ACM
1541 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1542 ev_name, false, timer, arg,
1543 delay_secs);
7f0030b2
ACM
1544 }
1545
81cce8de
ACM
1546 return __perf_evlist__tui_browse_hists(evlist, help,
1547 timer, arg, delay_secs);
d1b4f249 1548}