aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/ui
diff options
context:
space:
mode:
authorNamhyung Kim <namhyung@gmail.com>2012-04-04 03:14:26 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2012-04-11 16:16:40 -0400
commitaca7a94d6a59d6bf2600768e752f971c6cc0ab57 (patch)
tree801b40ba5cb8aba9d3d0842a3d87c741b97f58f2 /tools/perf/ui
parenta31b7cc083b1d3d15bd475729fc4471685ebc5f6 (diff)
perf tools: Move UI bits to tools/perf/ui directory
Move those files to new directory in order to be prepared to further UI work. Makefile and header file pathes are adjusted accordingly. Signed-off-by: Namhyung Kim <namhyung.kim@lge.com> Suggested-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Acked-by: Pekka Enberg <penberg@kernel.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Namhyung Kim <namhyung.kim@lge.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Pekka Enberg <penberg@kernel.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl Link: http://lkml.kernel.org/r/1333523666-12057-1-git-send-email-namhyung.kim@lge.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/ui')
-rw-r--r--tools/perf/ui/browser.c606
-rw-r--r--tools/perf/ui/browser.h67
-rw-r--r--tools/perf/ui/browsers/annotate.c673
-rw-r--r--tools/perf/ui/browsers/hists.c1345
-rw-r--r--tools/perf/ui/browsers/map.c154
-rw-r--r--tools/perf/ui/browsers/map.h6
-rw-r--r--tools/perf/ui/helpline.c79
-rw-r--r--tools/perf/ui/helpline.h16
-rw-r--r--tools/perf/ui/keysyms.h27
-rw-r--r--tools/perf/ui/libslang.h29
-rw-r--r--tools/perf/ui/progress.c32
-rw-r--r--tools/perf/ui/progress.h8
-rw-r--r--tools/perf/ui/setup.c155
-rw-r--r--tools/perf/ui/ui.h11
-rw-r--r--tools/perf/ui/util.c250
-rw-r--r--tools/perf/ui/util.h14
16 files changed, 3472 insertions, 0 deletions
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
new file mode 100644
index 000000000000..a1b140cf75ac
--- /dev/null
+++ b/tools/perf/ui/browser.c
@@ -0,0 +1,606 @@
1#include "../util.h"
2#include "../cache.h"
3#include "../../perf.h"
4#include "libslang.h"
5#include <newt.h>
6#include "ui.h"
7#include "util.h"
8#include <linux/compiler.h>
9#include <linux/list.h>
10#include <linux/rbtree.h>
11#include <stdlib.h>
12#include <sys/ttydefaults.h>
13#include "browser.h"
14#include "helpline.h"
15#include "keysyms.h"
16#include "../color.h"
17
18static int ui_browser__percent_color(struct ui_browser *browser,
19 double percent, bool current)
20{
21 if (current && (!browser->use_navkeypressed || browser->navkeypressed))
22 return HE_COLORSET_SELECTED;
23 if (percent >= MIN_RED)
24 return HE_COLORSET_TOP;
25 if (percent >= MIN_GREEN)
26 return HE_COLORSET_MEDIUM;
27 return HE_COLORSET_NORMAL;
28}
29
30int ui_browser__set_color(struct ui_browser *browser, int color)
31{
32 int ret = browser->current_color;
33 browser->current_color = color;
34 SLsmg_set_color(color);
35 return ret;
36}
37
38void ui_browser__set_percent_color(struct ui_browser *self,
39 double percent, bool current)
40{
41 int color = ui_browser__percent_color(self, percent, current);
42 ui_browser__set_color(self, color);
43}
44
45void ui_browser__gotorc(struct ui_browser *self, int y, int x)
46{
47 SLsmg_gotorc(self->y + y, self->x + x);
48}
49
50static struct list_head *
51ui_browser__list_head_filter_entries(struct ui_browser *browser,
52 struct list_head *pos)
53{
54 do {
55 if (!browser->filter || !browser->filter(browser, pos))
56 return pos;
57 pos = pos->next;
58 } while (pos != browser->entries);
59
60 return NULL;
61}
62
63static struct list_head *
64ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
65 struct list_head *pos)
66{
67 do {
68 if (!browser->filter || !browser->filter(browser, pos))
69 return pos;
70 pos = pos->prev;
71 } while (pos != browser->entries);
72
73 return NULL;
74}
75
76void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
77{
78 struct list_head *head = self->entries;
79 struct list_head *pos;
80
81 if (self->nr_entries == 0)
82 return;
83
84 switch (whence) {
85 case SEEK_SET:
86 pos = ui_browser__list_head_filter_entries(self, head->next);
87 break;
88 case SEEK_CUR:
89 pos = self->top;
90 break;
91 case SEEK_END:
92 pos = ui_browser__list_head_filter_prev_entries(self, head->prev);
93 break;
94 default:
95 return;
96 }
97
98 assert(pos != NULL);
99
100 if (offset > 0) {
101 while (offset-- != 0)
102 pos = ui_browser__list_head_filter_entries(self, pos->next);
103 } else {
104 while (offset++ != 0)
105 pos = ui_browser__list_head_filter_prev_entries(self, pos->prev);
106 }
107
108 self->top = pos;
109}
110
111void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
112{
113 struct rb_root *root = self->entries;
114 struct rb_node *nd;
115
116 switch (whence) {
117 case SEEK_SET:
118 nd = rb_first(root);
119 break;
120 case SEEK_CUR:
121 nd = self->top;
122 break;
123 case SEEK_END:
124 nd = rb_last(root);
125 break;
126 default:
127 return;
128 }
129
130 if (offset > 0) {
131 while (offset-- != 0)
132 nd = rb_next(nd);
133 } else {
134 while (offset++ != 0)
135 nd = rb_prev(nd);
136 }
137
138 self->top = nd;
139}
140
141unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
142{
143 struct rb_node *nd;
144 int row = 0;
145
146 if (self->top == NULL)
147 self->top = rb_first(self->entries);
148
149 nd = self->top;
150
151 while (nd != NULL) {
152 ui_browser__gotorc(self, row, 0);
153 self->write(self, nd, row);
154 if (++row == self->height)
155 break;
156 nd = rb_next(nd);
157 }
158
159 return row;
160}
161
162bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
163{
164 return self->top_idx + row == self->index;
165}
166
167void ui_browser__refresh_dimensions(struct ui_browser *self)
168{
169 self->width = SLtt_Screen_Cols - 1;
170 self->height = SLtt_Screen_Rows - 2;
171 self->y = 1;
172 self->x = 0;
173}
174
175void ui_browser__handle_resize(struct ui_browser *browser)
176{
177 ui__refresh_dimensions(false);
178 ui_browser__show(browser, browser->title, ui_helpline__current);
179 ui_browser__refresh(browser);
180}
181
182int ui_browser__warning(struct ui_browser *browser, int timeout,
183 const char *format, ...)
184{
185 va_list args;
186 char *text;
187 int key = 0, err;
188
189 va_start(args, format);
190 err = vasprintf(&text, format, args);
191 va_end(args);
192
193 if (err < 0) {
194 va_start(args, format);
195 ui_helpline__vpush(format, args);
196 va_end(args);
197 } else {
198 while ((key == ui__question_window("Warning!", text,
199 "Press any key...",
200 timeout)) == K_RESIZE)
201 ui_browser__handle_resize(browser);
202 free(text);
203 }
204
205 return key;
206}
207
208int ui_browser__help_window(struct ui_browser *browser, const char *text)
209{
210 int key;
211
212 while ((key = ui__help_window(text)) == K_RESIZE)
213 ui_browser__handle_resize(browser);
214
215 return key;
216}
217
218bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
219{
220 int key;
221
222 while ((key = ui__dialog_yesno(text)) == K_RESIZE)
223 ui_browser__handle_resize(browser);
224
225 return key == K_ENTER || toupper(key) == 'Y';
226}
227
228void ui_browser__reset_index(struct ui_browser *self)
229{
230 self->index = self->top_idx = 0;
231 self->seek(self, 0, SEEK_SET);
232}
233
234void __ui_browser__show_title(struct ui_browser *browser, const char *title)
235{
236 SLsmg_gotorc(0, 0);
237 ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
238 slsmg_write_nstring(title, browser->width + 1);
239}
240
241void ui_browser__show_title(struct ui_browser *browser, const char *title)
242{
243 pthread_mutex_lock(&ui__lock);
244 __ui_browser__show_title(browser, title);
245 pthread_mutex_unlock(&ui__lock);
246}
247
248int ui_browser__show(struct ui_browser *self, const char *title,
249 const char *helpline, ...)
250{
251 int err;
252 va_list ap;
253
254 ui_browser__refresh_dimensions(self);
255
256 pthread_mutex_lock(&ui__lock);
257 __ui_browser__show_title(self, title);
258
259 self->title = title;
260 free(self->helpline);
261 self->helpline = NULL;
262
263 va_start(ap, helpline);
264 err = vasprintf(&self->helpline, helpline, ap);
265 va_end(ap);
266 if (err > 0)
267 ui_helpline__push(self->helpline);
268 pthread_mutex_unlock(&ui__lock);
269 return err ? 0 : -1;
270}
271
272void ui_browser__hide(struct ui_browser *browser __used)
273{
274 pthread_mutex_lock(&ui__lock);
275 ui_helpline__pop();
276 pthread_mutex_unlock(&ui__lock);
277}
278
279static void ui_browser__scrollbar_set(struct ui_browser *browser)
280{
281 int height = browser->height, h = 0, pct = 0,
282 col = browser->width,
283 row = browser->y - 1;
284
285 if (browser->nr_entries > 1) {
286 pct = ((browser->index * (browser->height - 1)) /
287 (browser->nr_entries - 1));
288 }
289
290 SLsmg_set_char_set(1);
291
292 while (h < height) {
293 ui_browser__gotorc(browser, row++, col);
294 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
295 ++h;
296 }
297
298 SLsmg_set_char_set(0);
299}
300
301static int __ui_browser__refresh(struct ui_browser *browser)
302{
303 int row;
304 int width = browser->width;
305
306 row = browser->refresh(browser);
307 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
308
309 if (!browser->use_navkeypressed || browser->navkeypressed)
310 ui_browser__scrollbar_set(browser);
311 else
312 width += 1;
313
314 SLsmg_fill_region(browser->y + row, browser->x,
315 browser->height - row, width, ' ');
316
317 return 0;
318}
319
320int ui_browser__refresh(struct ui_browser *browser)
321{
322 pthread_mutex_lock(&ui__lock);
323 __ui_browser__refresh(browser);
324 pthread_mutex_unlock(&ui__lock);
325
326 return 0;
327}
328
329/*
330 * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
331 * forget about any reference to any entry in the underlying data structure,
332 * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
333 * after an output_resort and hist decay.
334 */
335void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
336{
337 off_t offset = nr_entries - browser->nr_entries;
338
339 browser->nr_entries = nr_entries;
340
341 if (offset < 0) {
342 if (browser->top_idx < (u64)-offset)
343 offset = -browser->top_idx;
344
345 browser->index += offset;
346 browser->top_idx += offset;
347 }
348
349 browser->top = NULL;
350 browser->seek(browser, browser->top_idx, SEEK_SET);
351}
352
353int ui_browser__run(struct ui_browser *self, int delay_secs)
354{
355 int err, key;
356
357 while (1) {
358 off_t offset;
359
360 pthread_mutex_lock(&ui__lock);
361 err = __ui_browser__refresh(self);
362 SLsmg_refresh();
363 pthread_mutex_unlock(&ui__lock);
364 if (err < 0)
365 break;
366
367 key = ui__getch(delay_secs);
368
369 if (key == K_RESIZE) {
370 ui__refresh_dimensions(false);
371 ui_browser__refresh_dimensions(self);
372 __ui_browser__show_title(self, self->title);
373 ui_helpline__puts(self->helpline);
374 continue;
375 }
376
377 if (self->use_navkeypressed && !self->navkeypressed) {
378 if (key == K_DOWN || key == K_UP ||
379 key == K_PGDN || key == K_PGUP ||
380 key == K_HOME || key == K_END ||
381 key == ' ') {
382 self->navkeypressed = true;
383 continue;
384 } else
385 return key;
386 }
387
388 switch (key) {
389 case K_DOWN:
390 if (self->index == self->nr_entries - 1)
391 break;
392 ++self->index;
393 if (self->index == self->top_idx + self->height) {
394 ++self->top_idx;
395 self->seek(self, +1, SEEK_CUR);
396 }
397 break;
398 case K_UP:
399 if (self->index == 0)
400 break;
401 --self->index;
402 if (self->index < self->top_idx) {
403 --self->top_idx;
404 self->seek(self, -1, SEEK_CUR);
405 }
406 break;
407 case K_PGDN:
408 case ' ':
409 if (self->top_idx + self->height > self->nr_entries - 1)
410 break;
411
412 offset = self->height;
413 if (self->index + offset > self->nr_entries - 1)
414 offset = self->nr_entries - 1 - self->index;
415 self->index += offset;
416 self->top_idx += offset;
417 self->seek(self, +offset, SEEK_CUR);
418 break;
419 case K_PGUP:
420 if (self->top_idx == 0)
421 break;
422
423 if (self->top_idx < self->height)
424 offset = self->top_idx;
425 else
426 offset = self->height;
427
428 self->index -= offset;
429 self->top_idx -= offset;
430 self->seek(self, -offset, SEEK_CUR);
431 break;
432 case K_HOME:
433 ui_browser__reset_index(self);
434 break;
435 case K_END:
436 offset = self->height - 1;
437 if (offset >= self->nr_entries)
438 offset = self->nr_entries - 1;
439
440 self->index = self->nr_entries - 1;
441 self->top_idx = self->index - offset;
442 self->seek(self, -offset, SEEK_END);
443 break;
444 default:
445 return key;
446 }
447 }
448 return -1;
449}
450
451unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
452{
453 struct list_head *pos;
454 struct list_head *head = self->entries;
455 int row = 0;
456
457 if (self->top == NULL || self->top == self->entries)
458 self->top = ui_browser__list_head_filter_entries(self, head->next);
459
460 pos = self->top;
461
462 list_for_each_from(pos, head) {
463 if (!self->filter || !self->filter(self, pos)) {
464 ui_browser__gotorc(self, row, 0);
465 self->write(self, pos, row);
466 if (++row == self->height)
467 break;
468 }
469 }
470
471 return row;
472}
473
474static struct ui_browser__colorset {
475 const char *name, *fg, *bg;
476 int colorset;
477} ui_browser__colorsets[] = {
478 {
479 .colorset = HE_COLORSET_TOP,
480 .name = "top",
481 .fg = "red",
482 .bg = "default",
483 },
484 {
485 .colorset = HE_COLORSET_MEDIUM,
486 .name = "medium",
487 .fg = "green",
488 .bg = "default",
489 },
490 {
491 .colorset = HE_COLORSET_NORMAL,
492 .name = "normal",
493 .fg = "default",
494 .bg = "default",
495 },
496 {
497 .colorset = HE_COLORSET_SELECTED,
498 .name = "selected",
499 .fg = "black",
500 .bg = "lightgray",
501 },
502 {
503 .colorset = HE_COLORSET_CODE,
504 .name = "code",
505 .fg = "blue",
506 .bg = "default",
507 },
508 {
509 .colorset = HE_COLORSET_ADDR,
510 .name = "addr",
511 .fg = "magenta",
512 .bg = "default",
513 },
514 {
515 .name = NULL,
516 }
517};
518
519
520static int ui_browser__color_config(const char *var, const char *value,
521 void *data __used)
522{
523 char *fg = NULL, *bg;
524 int i;
525
526 /* same dir for all commands */
527 if (prefixcmp(var, "colors.") != 0)
528 return 0;
529
530 for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
531 const char *name = var + 7;
532
533 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
534 continue;
535
536 fg = strdup(value);
537 if (fg == NULL)
538 break;
539
540 bg = strchr(fg, ',');
541 if (bg == NULL)
542 break;
543
544 *bg = '\0';
545 while (isspace(*++bg));
546 ui_browser__colorsets[i].bg = bg;
547 ui_browser__colorsets[i].fg = fg;
548 return 0;
549 }
550
551 free(fg);
552 return -1;
553}
554
555void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
556{
557 switch (whence) {
558 case SEEK_SET:
559 browser->top = browser->entries;
560 break;
561 case SEEK_CUR:
562 browser->top = browser->top + browser->top_idx + offset;
563 break;
564 case SEEK_END:
565 browser->top = browser->top + browser->nr_entries + offset;
566 break;
567 default:
568 return;
569 }
570}
571
572unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
573{
574 unsigned int row = 0, idx = browser->top_idx;
575 char **pos;
576
577 if (browser->top == NULL)
578 browser->top = browser->entries;
579
580 pos = (char **)browser->top;
581 while (idx < browser->nr_entries) {
582 if (!browser->filter || !browser->filter(browser, *pos)) {
583 ui_browser__gotorc(browser, row, 0);
584 browser->write(browser, pos, row);
585 if (++row == browser->height)
586 break;
587 }
588
589 ++idx;
590 ++pos;
591 }
592
593 return row;
594}
595
596void ui_browser__init(void)
597{
598 int i = 0;
599
600 perf_config(ui_browser__color_config, NULL);
601
602 while (ui_browser__colorsets[i].name) {
603 struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
604 sltt_set_color(c->colorset, c->name, c->fg, c->bg);
605 }
606}
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h
new file mode 100644
index 000000000000..2550277db9f9
--- /dev/null
+++ b/tools/perf/ui/browser.h
@@ -0,0 +1,67 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <sys/types.h>
6#include "../types.h"
7
8#define HE_COLORSET_TOP 50
9#define HE_COLORSET_MEDIUM 51
10#define HE_COLORSET_NORMAL 52
11#define HE_COLORSET_SELECTED 53
12#define HE_COLORSET_CODE 54
13#define HE_COLORSET_ADDR 55
14
15struct ui_browser {
16 u64 index, top_idx;
17 void *top, *entries;
18 u16 y, x, width, height;
19 int current_color;
20 void *priv;
21 const char *title;
22 char *helpline;
23 unsigned int (*refresh)(struct ui_browser *self);
24 void (*write)(struct ui_browser *self, void *entry, int row);
25 void (*seek)(struct ui_browser *self, off_t offset, int whence);
26 bool (*filter)(struct ui_browser *self, void *entry);
27 u32 nr_entries;
28 bool navkeypressed;
29 bool use_navkeypressed;
30};
31
32int ui_browser__set_color(struct ui_browser *browser, int color);
33void ui_browser__set_percent_color(struct ui_browser *self,
34 double percent, bool current);
35bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
36void ui_browser__refresh_dimensions(struct ui_browser *self);
37void ui_browser__reset_index(struct ui_browser *self);
38
39void ui_browser__gotorc(struct ui_browser *self, int y, int x);
40void __ui_browser__show_title(struct ui_browser *browser, const char *title);
41void ui_browser__show_title(struct ui_browser *browser, const char *title);
42int ui_browser__show(struct ui_browser *self, const char *title,
43 const char *helpline, ...);
44void ui_browser__hide(struct ui_browser *self);
45int ui_browser__refresh(struct ui_browser *self);
46int ui_browser__run(struct ui_browser *browser, int delay_secs);
47void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);
48void ui_browser__handle_resize(struct ui_browser *browser);
49
50int ui_browser__warning(struct ui_browser *browser, int timeout,
51 const char *format, ...);
52int ui_browser__help_window(struct ui_browser *browser, const char *text);
53bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
54int ui_browser__input_window(const char *title, const char *text, char *input,
55 const char *exit_msg, int delay_sec);
56
57void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
58unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
59
60void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
61unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
62
63void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
64unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
65
66void ui_browser__init(void);
67#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
new file mode 100644
index 000000000000..4db5186472b5
--- /dev/null
+++ b/tools/perf/ui/browsers/annotate.c
@@ -0,0 +1,673 @@
1#include "../../util/util.h"
2#include "../browser.h"
3#include "../helpline.h"
4#include "../libslang.h"
5#include "../ui.h"
6#include "../util.h"
7#include "../../util/annotate.h"
8#include "../../util/hist.h"
9#include "../../util/sort.h"
10#include "../../util/symbol.h"
11#include <pthread.h>
12#include <newt.h>
13
14struct annotate_browser {
15 struct ui_browser b;
16 struct rb_root entries;
17 struct rb_node *curr_hot;
18 struct objdump_line *selection;
19 u64 start;
20 int nr_asm_entries;
21 int nr_entries;
22 bool hide_src_code;
23 bool use_offset;
24 bool searching_backwards;
25 char search_bf[128];
26};
27
28struct objdump_line_rb_node {
29 struct rb_node rb_node;
30 double percent;
31 u32 idx;
32 int idx_asm;
33};
34
35static inline
36struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
37{
38 return (struct objdump_line_rb_node *)(self + 1);
39}
40
41static bool objdump_line__filter(struct ui_browser *browser, void *entry)
42{
43 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
44
45 if (ab->hide_src_code) {
46 struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
47 return ol->offset == -1;
48 }
49
50 return false;
51}
52
53static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
54{
55 struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
56 struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
57 bool current_entry = ui_browser__is_current_entry(self, row);
58 bool change_color = (!ab->hide_src_code &&
59 (!current_entry || (self->use_navkeypressed &&
60 !self->navkeypressed)));
61 int width = self->width;
62
63 if (ol->offset != -1) {
64 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
65 ui_browser__set_percent_color(self, olrb->percent, current_entry);
66 slsmg_printf(" %7.2f ", olrb->percent);
67 } else {
68 ui_browser__set_percent_color(self, 0, current_entry);
69 slsmg_write_nstring(" ", 9);
70 }
71
72 SLsmg_write_char(':');
73 slsmg_write_nstring(" ", 8);
74
75 /* The scroll bar isn't being used */
76 if (!self->navkeypressed)
77 width += 1;
78
79 if (ol->offset != -1 && change_color)
80 ui_browser__set_color(self, HE_COLORSET_CODE);
81
82 if (!*ol->line)
83 slsmg_write_nstring(" ", width - 18);
84 else if (ol->offset == -1)
85 slsmg_write_nstring(ol->line, width - 18);
86 else {
87 char bf[64];
88 u64 addr = ol->offset;
89 int printed, color = -1;
90
91 if (!ab->use_offset)
92 addr += ab->start;
93
94 printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
95 if (change_color)
96 color = ui_browser__set_color(self, HE_COLORSET_ADDR);
97 slsmg_write_nstring(bf, printed);
98 if (change_color)
99 ui_browser__set_color(self, color);
100 slsmg_write_nstring(ol->line, width - 18 - printed);
101 }
102
103 if (current_entry)
104 ab->selection = ol;
105}
106
107static double objdump_line__calc_percent(struct objdump_line *self,
108 struct symbol *sym, int evidx)
109{
110 double percent = 0.0;
111
112 if (self->offset != -1) {
113 int len = sym->end - sym->start;
114 unsigned int hits = 0;
115 struct annotation *notes = symbol__annotation(sym);
116 struct source_line *src_line = notes->src->lines;
117 struct sym_hist *h = annotation__histogram(notes, evidx);
118 s64 offset = self->offset;
119 struct objdump_line *next;
120
121 next = objdump__get_next_ip_line(&notes->src->source, self);
122 while (offset < (s64)len &&
123 (next == NULL || offset < next->offset)) {
124 if (src_line) {
125 percent += src_line[offset].percent;
126 } else
127 hits += h->addr[offset];
128
129 ++offset;
130 }
131 /*
132 * If the percentage wasn't already calculated in
133 * symbol__get_source_line, do it now:
134 */
135 if (src_line == NULL && h->sum)
136 percent = 100.0 * hits / h->sum;
137 }
138
139 return percent;
140}
141
142static void objdump__insert_line(struct rb_root *self,
143 struct objdump_line_rb_node *line)
144{
145 struct rb_node **p = &self->rb_node;
146 struct rb_node *parent = NULL;
147 struct objdump_line_rb_node *l;
148
149 while (*p != NULL) {
150 parent = *p;
151 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
152 if (line->percent < l->percent)
153 p = &(*p)->rb_left;
154 else
155 p = &(*p)->rb_right;
156 }
157 rb_link_node(&line->rb_node, parent, p);
158 rb_insert_color(&line->rb_node, self);
159}
160
161static void annotate_browser__set_top(struct annotate_browser *self,
162 struct objdump_line *pos, u32 idx)
163{
164 unsigned back;
165
166 ui_browser__refresh_dimensions(&self->b);
167 back = self->b.height / 2;
168 self->b.top_idx = self->b.index = idx;
169
170 while (self->b.top_idx != 0 && back != 0) {
171 pos = list_entry(pos->node.prev, struct objdump_line, node);
172
173 if (objdump_line__filter(&self->b, &pos->node))
174 continue;
175
176 --self->b.top_idx;
177 --back;
178 }
179
180 self->b.top = pos;
181 self->b.navkeypressed = true;
182}
183
184static void annotate_browser__set_rb_top(struct annotate_browser *browser,
185 struct rb_node *nd)
186{
187 struct objdump_line_rb_node *rbpos;
188 struct objdump_line *pos;
189
190 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
191 pos = ((struct objdump_line *)rbpos) - 1;
192 annotate_browser__set_top(browser, pos, rbpos->idx);
193 browser->curr_hot = nd;
194}
195
196static void annotate_browser__calc_percent(struct annotate_browser *browser,
197 int evidx)
198{
199 struct map_symbol *ms = browser->b.priv;
200 struct symbol *sym = ms->sym;
201 struct annotation *notes = symbol__annotation(sym);
202 struct objdump_line *pos;
203
204 browser->entries = RB_ROOT;
205
206 pthread_mutex_lock(&notes->lock);
207
208 list_for_each_entry(pos, &notes->src->source, node) {
209 struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
210 rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
211 if (rbpos->percent < 0.01) {
212 RB_CLEAR_NODE(&rbpos->rb_node);
213 continue;
214 }
215 objdump__insert_line(&browser->entries, rbpos);
216 }
217 pthread_mutex_unlock(&notes->lock);
218
219 browser->curr_hot = rb_last(&browser->entries);
220}
221
222static bool annotate_browser__toggle_source(struct annotate_browser *browser)
223{
224 struct objdump_line *ol;
225 struct objdump_line_rb_node *olrb;
226 off_t offset = browser->b.index - browser->b.top_idx;
227
228 browser->b.seek(&browser->b, offset, SEEK_CUR);
229 ol = list_entry(browser->b.top, struct objdump_line, node);
230 olrb = objdump_line__rb(ol);
231
232 if (browser->hide_src_code) {
233 if (olrb->idx_asm < offset)
234 offset = olrb->idx;
235
236 browser->b.nr_entries = browser->nr_entries;
237 browser->hide_src_code = false;
238 browser->b.seek(&browser->b, -offset, SEEK_CUR);
239 browser->b.top_idx = olrb->idx - offset;
240 browser->b.index = olrb->idx;
241 } else {
242 if (olrb->idx_asm < 0) {
243 ui_helpline__puts("Only available for assembly lines.");
244 browser->b.seek(&browser->b, -offset, SEEK_CUR);
245 return false;
246 }
247
248 if (olrb->idx_asm < offset)
249 offset = olrb->idx_asm;
250
251 browser->b.nr_entries = browser->nr_asm_entries;
252 browser->hide_src_code = true;
253 browser->b.seek(&browser->b, -offset, SEEK_CUR);
254 browser->b.top_idx = olrb->idx_asm - offset;
255 browser->b.index = olrb->idx_asm;
256 }
257
258 return true;
259}
260
261static bool annotate_browser__callq(struct annotate_browser *browser,
262 int evidx, void (*timer)(void *arg),
263 void *arg, int delay_secs)
264{
265 struct map_symbol *ms = browser->b.priv;
266 struct symbol *sym = ms->sym;
267 struct annotation *notes;
268 struct symbol *target;
269 char *s = strstr(browser->selection->line, "callq ");
270 u64 ip;
271
272 if (s == NULL)
273 return false;
274
275 s = strchr(s, ' ');
276 if (s++ == NULL) {
277 ui_helpline__puts("Invallid callq instruction.");
278 return true;
279 }
280
281 ip = strtoull(s, NULL, 16);
282 ip = ms->map->map_ip(ms->map, ip);
283 target = map__find_symbol(ms->map, ip, NULL);
284 if (target == NULL) {
285 ui_helpline__puts("The called function was not found.");
286 return true;
287 }
288
289 notes = symbol__annotation(target);
290 pthread_mutex_lock(&notes->lock);
291
292 if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
293 pthread_mutex_unlock(&notes->lock);
294 ui__warning("Not enough memory for annotating '%s' symbol!\n",
295 target->name);
296 return true;
297 }
298
299 pthread_mutex_unlock(&notes->lock);
300 symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
301 ui_browser__show_title(&browser->b, sym->name);
302 return true;
303}
304
305static struct objdump_line *
306 annotate_browser__find_offset(struct annotate_browser *browser,
307 s64 offset, s64 *idx)
308{
309 struct map_symbol *ms = browser->b.priv;
310 struct symbol *sym = ms->sym;
311 struct annotation *notes = symbol__annotation(sym);
312 struct objdump_line *pos;
313
314 *idx = 0;
315 list_for_each_entry(pos, &notes->src->source, node) {
316 if (pos->offset == offset)
317 return pos;
318 if (!objdump_line__filter(&browser->b, &pos->node))
319 ++*idx;
320 }
321
322 return NULL;
323}
324
325static bool annotate_browser__jump(struct annotate_browser *browser)
326{
327 const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
328 struct objdump_line *line;
329 s64 idx, offset;
330 char *s = NULL;
331 int i = 0;
332
333 while (jumps[i]) {
334 s = strstr(browser->selection->line, jumps[i++]);
335 if (s)
336 break;
337 }
338
339 if (s == NULL)
340 return false;
341
342 s = strchr(s, '+');
343 if (s++ == NULL) {
344 ui_helpline__puts("Invallid jump instruction.");
345 return true;
346 }
347
348 offset = strtoll(s, NULL, 16);
349 line = annotate_browser__find_offset(browser, offset, &idx);
350 if (line == NULL) {
351 ui_helpline__puts("Invallid jump offset");
352 return true;
353 }
354
355 annotate_browser__set_top(browser, line, idx);
356
357 return true;
358}
359
360static struct objdump_line *
361 annotate_browser__find_string(struct annotate_browser *browser,
362 char *s, s64 *idx)
363{
364 struct map_symbol *ms = browser->b.priv;
365 struct symbol *sym = ms->sym;
366 struct annotation *notes = symbol__annotation(sym);
367 struct objdump_line *pos = browser->selection;
368
369 *idx = browser->b.index;
370 list_for_each_entry_continue(pos, &notes->src->source, node) {
371 if (objdump_line__filter(&browser->b, &pos->node))
372 continue;
373
374 ++*idx;
375
376 if (pos->line && strstr(pos->line, s) != NULL)
377 return pos;
378 }
379
380 return NULL;
381}
382
383static bool __annotate_browser__search(struct annotate_browser *browser)
384{
385 struct objdump_line *line;
386 s64 idx;
387
388 line = annotate_browser__find_string(browser, browser->search_bf, &idx);
389 if (line == NULL) {
390 ui_helpline__puts("String not found!");
391 return false;
392 }
393
394 annotate_browser__set_top(browser, line, idx);
395 browser->searching_backwards = false;
396 return true;
397}
398
399static struct objdump_line *
400 annotate_browser__find_string_reverse(struct annotate_browser *browser,
401 char *s, s64 *idx)
402{
403 struct map_symbol *ms = browser->b.priv;
404 struct symbol *sym = ms->sym;
405 struct annotation *notes = symbol__annotation(sym);
406 struct objdump_line *pos = browser->selection;
407
408 *idx = browser->b.index;
409 list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
410 if (objdump_line__filter(&browser->b, &pos->node))
411 continue;
412
413 --*idx;
414
415 if (pos->line && strstr(pos->line, s) != NULL)
416 return pos;
417 }
418
419 return NULL;
420}
421
422static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
423{
424 struct objdump_line *line;
425 s64 idx;
426
427 line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
428 if (line == NULL) {
429 ui_helpline__puts("String not found!");
430 return false;
431 }
432
433 annotate_browser__set_top(browser, line, idx);
434 browser->searching_backwards = true;
435 return true;
436}
437
438static bool annotate_browser__search_window(struct annotate_browser *browser,
439 int delay_secs)
440{
441 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
442 "ENTER: OK, ESC: Cancel",
443 delay_secs * 2) != K_ENTER ||
444 !*browser->search_bf)
445 return false;
446
447 return true;
448}
449
450static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
451{
452 if (annotate_browser__search_window(browser, delay_secs))
453 return __annotate_browser__search(browser);
454
455 return false;
456}
457
458static bool annotate_browser__continue_search(struct annotate_browser *browser,
459 int delay_secs)
460{
461 if (!*browser->search_bf)
462 return annotate_browser__search(browser, delay_secs);
463
464 return __annotate_browser__search(browser);
465}
466
467static bool annotate_browser__search_reverse(struct annotate_browser *browser,
468 int delay_secs)
469{
470 if (annotate_browser__search_window(browser, delay_secs))
471 return __annotate_browser__search_reverse(browser);
472
473 return false;
474}
475
476static
477bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
478 int delay_secs)
479{
480 if (!*browser->search_bf)
481 return annotate_browser__search_reverse(browser, delay_secs);
482
483 return __annotate_browser__search_reverse(browser);
484}
485
486static int annotate_browser__run(struct annotate_browser *self, int evidx,
487 void(*timer)(void *arg),
488 void *arg, int delay_secs)
489{
490 struct rb_node *nd = NULL;
491 struct map_symbol *ms = self->b.priv;
492 struct symbol *sym = ms->sym;
493 const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
494 "H: Go to hottest line, ->/ENTER: Line action, "
495 "O: Toggle offset view, "
496 "S: Toggle source code view";
497 int key;
498
499 if (ui_browser__show(&self->b, sym->name, help) < 0)
500 return -1;
501
502 annotate_browser__calc_percent(self, evidx);
503
504 if (self->curr_hot) {
505 annotate_browser__set_rb_top(self, self->curr_hot);
506 self->b.navkeypressed = false;
507 }
508
509 nd = self->curr_hot;
510
511 while (1) {
512 key = ui_browser__run(&self->b, delay_secs);
513
514 if (delay_secs != 0) {
515 annotate_browser__calc_percent(self, evidx);
516 /*
517 * Current line focus got out of the list of most active
518 * lines, NULL it so that if TAB|UNTAB is pressed, we
519 * move to curr_hot (current hottest line).
520 */
521 if (nd != NULL && RB_EMPTY_NODE(nd))
522 nd = NULL;
523 }
524
525 switch (key) {
526 case K_TIMER:
527 if (timer != NULL)
528 timer(arg);
529
530 if (delay_secs != 0)
531 symbol__annotate_decay_histogram(sym, evidx);
532 continue;
533 case K_TAB:
534 if (nd != NULL) {
535 nd = rb_prev(nd);
536 if (nd == NULL)
537 nd = rb_last(&self->entries);
538 } else
539 nd = self->curr_hot;
540 break;
541 case K_UNTAB:
542 if (nd != NULL)
543 nd = rb_next(nd);
544 if (nd == NULL)
545 nd = rb_first(&self->entries);
546 else
547 nd = self->curr_hot;
548 break;
549 case 'H':
550 case 'h':
551 nd = self->curr_hot;
552 break;
553 case 'S':
554 case 's':
555 if (annotate_browser__toggle_source(self))
556 ui_helpline__puts(help);
557 continue;
558 case 'O':
559 case 'o':
560 self->use_offset = !self->use_offset;
561 continue;
562 case '/':
563 if (annotate_browser__search(self, delay_secs)) {
564show_help:
565 ui_helpline__puts(help);
566 }
567 continue;
568 case 'n':
569 if (self->searching_backwards ?
570 annotate_browser__continue_search_reverse(self, delay_secs) :
571 annotate_browser__continue_search(self, delay_secs))
572 goto show_help;
573 continue;
574 case '?':
575 if (annotate_browser__search_reverse(self, delay_secs))
576 goto show_help;
577 continue;
578 case K_ENTER:
579 case K_RIGHT:
580 if (self->selection == NULL)
581 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
582 else if (self->selection->offset == -1)
583 ui_helpline__puts("Actions are only available for assembly lines.");
584 else if (!(annotate_browser__jump(self) ||
585 annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
586 ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
587 continue;
588 case K_LEFT:
589 case K_ESC:
590 case 'q':
591 case CTRL('c'):
592 goto out;
593 default:
594 continue;
595 }
596
597 if (nd != NULL)
598 annotate_browser__set_rb_top(self, nd);
599 }
600out:
601 ui_browser__hide(&self->b);
602 return key;
603}
604
605int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
606 void(*timer)(void *arg), void *arg, int delay_secs)
607{
608 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
609 timer, arg, delay_secs);
610}
611
612int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
613 void(*timer)(void *arg), void *arg,
614 int delay_secs)
615{
616 struct objdump_line *pos, *n;
617 struct annotation *notes;
618 struct map_symbol ms = {
619 .map = map,
620 .sym = sym,
621 };
622 struct annotate_browser browser = {
623 .b = {
624 .refresh = ui_browser__list_head_refresh,
625 .seek = ui_browser__list_head_seek,
626 .write = annotate_browser__write,
627 .filter = objdump_line__filter,
628 .priv = &ms,
629 .use_navkeypressed = true,
630 },
631 };
632 int ret;
633
634 if (sym == NULL)
635 return -1;
636
637 if (map->dso->annotate_warned)
638 return -1;
639
640 if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
641 ui__error("%s", ui_helpline__last_msg);
642 return -1;
643 }
644
645 ui_helpline__push("Press <- or ESC to exit");
646
647 notes = symbol__annotation(sym);
648 browser.start = map__rip_2objdump(map, sym->start);
649
650 list_for_each_entry(pos, &notes->src->source, node) {
651 struct objdump_line_rb_node *rbpos;
652 size_t line_len = strlen(pos->line);
653
654 if (browser.b.width < line_len)
655 browser.b.width = line_len;
656 rbpos = objdump_line__rb(pos);
657 rbpos->idx = browser.nr_entries++;
658 if (pos->offset != -1)
659 rbpos->idx_asm = browser.nr_asm_entries++;
660 else
661 rbpos->idx_asm = -1;
662 }
663
664 browser.b.nr_entries = browser.nr_entries;
665 browser.b.entries = &notes->src->source,
666 browser.b.width += 18; /* Percentage */
667 ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
668 list_for_each_entry_safe(pos, n, &notes->src->source, node) {
669 list_del(&pos->node);
670 objdump_line__free(pos);
671 }
672 return ret;
673}
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
new file mode 100644
index 000000000000..466827e91b87
--- /dev/null
+++ b/tools/perf/ui/browsers/hists.c
@@ -0,0 +1,1345 @@
1#include <stdio.h>
2#include "../libslang.h"
3#include <stdlib.h>
4#include <string.h>
5#include <newt.h>
6#include <linux/rbtree.h>
7
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"
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
18#include "../ui.h"
19#include "map.h"
20
21struct hist_browser {
22 struct ui_browser b;
23 struct hists *hists;
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
26 bool has_symbols;
27};
28
29static int hists__browser_title(struct hists *self, char *bf, size_t size,
30 const char *ev_name);
31
32static void hist_browser__refresh_dimensions(struct hist_browser *self)
33{
34 /* 3 == +/- toggle symbol before actual hist_entry rendering */
35 self->b.width = 3 + (hists__sort_list_width(self->hists) +
36 sizeof("[k]"));
37}
38
39static void hist_browser__reset(struct hist_browser *self)
40{
41 self->b.nr_entries = self->hists->nr_entries;
42 hist_browser__refresh_dimensions(self);
43 ui_browser__reset_index(&self->b);
44}
45
46static char tree__folded_sign(bool unfolded)
47{
48 return unfolded ? '-' : '+';
49}
50
51static char map_symbol__folded(const struct map_symbol *self)
52{
53 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
54}
55
56static char hist_entry__folded(const struct hist_entry *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
61static char callchain_list__folded(const struct callchain_list *self)
62{
63 return map_symbol__folded(&self->ms);
64}
65
66static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
67{
68 self->unfolded = unfold ? self->has_children : false;
69}
70
71static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
72{
73 int n = 0;
74 struct rb_node *nd;
75
76 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
77 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
78 struct callchain_list *chain;
79 char folded_sign = ' '; /* No children */
80
81 list_for_each_entry(chain, &child->val, list) {
82 ++n;
83 /* We need this because we may not have children */
84 folded_sign = callchain_list__folded(chain);
85 if (folded_sign == '+')
86 break;
87 }
88
89 if (folded_sign == '-') /* Have children and they're unfolded */
90 n += callchain_node__count_rows_rb_tree(child);
91 }
92
93 return n;
94}
95
96static int callchain_node__count_rows(struct callchain_node *node)
97{
98 struct callchain_list *chain;
99 bool unfolded = false;
100 int n = 0;
101
102 list_for_each_entry(chain, &node->val, list) {
103 ++n;
104 unfolded = chain->ms.unfolded;
105 }
106
107 if (unfolded)
108 n += callchain_node__count_rows_rb_tree(node);
109
110 return n;
111}
112
113static int callchain__count_rows(struct rb_root *chain)
114{
115 struct rb_node *nd;
116 int n = 0;
117
118 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
119 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
120 n += callchain_node__count_rows(node);
121 }
122
123 return n;
124}
125
126static bool map_symbol__toggle_fold(struct map_symbol *self)
127{
128 if (!self)
129 return false;
130
131 if (!self->has_children)
132 return false;
133
134 self->unfolded = !self->unfolded;
135 return true;
136}
137
138static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
139{
140 struct rb_node *nd = rb_first(&self->rb_root);
141
142 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
143 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
144 struct callchain_list *chain;
145 bool first = true;
146
147 list_for_each_entry(chain, &child->val, list) {
148 if (first) {
149 first = false;
150 chain->ms.has_children = chain->list.next != &child->val ||
151 !RB_EMPTY_ROOT(&child->rb_root);
152 } else
153 chain->ms.has_children = chain->list.next == &child->val &&
154 !RB_EMPTY_ROOT(&child->rb_root);
155 }
156
157 callchain_node__init_have_children_rb_tree(child);
158 }
159}
160
161static void callchain_node__init_have_children(struct callchain_node *self)
162{
163 struct callchain_list *chain;
164
165 list_for_each_entry(chain, &self->val, list)
166 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
167
168 callchain_node__init_have_children_rb_tree(self);
169}
170
171static void callchain__init_have_children(struct rb_root *self)
172{
173 struct rb_node *nd;
174
175 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
176 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
177 callchain_node__init_have_children(node);
178 }
179}
180
181static void hist_entry__init_have_children(struct hist_entry *self)
182{
183 if (!self->init_have_children) {
184 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
185 callchain__init_have_children(&self->sorted_chain);
186 self->init_have_children = true;
187 }
188}
189
190static bool hist_browser__toggle_fold(struct hist_browser *self)
191{
192 if (map_symbol__toggle_fold(self->selection)) {
193 struct hist_entry *he = self->he_selection;
194
195 hist_entry__init_have_children(he);
196 self->hists->nr_entries -= he->nr_rows;
197
198 if (he->ms.unfolded)
199 he->nr_rows = callchain__count_rows(&he->sorted_chain);
200 else
201 he->nr_rows = 0;
202 self->hists->nr_entries += he->nr_rows;
203 self->b.nr_entries = self->hists->nr_entries;
204
205 return true;
206 }
207
208 /* If it doesn't have children, no toggling performed */
209 return false;
210}
211
212static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
213{
214 int n = 0;
215 struct rb_node *nd;
216
217 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
218 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
219 struct callchain_list *chain;
220 bool has_children = false;
221
222 list_for_each_entry(chain, &child->val, list) {
223 ++n;
224 map_symbol__set_folding(&chain->ms, unfold);
225 has_children = chain->ms.has_children;
226 }
227
228 if (has_children)
229 n += callchain_node__set_folding_rb_tree(child, unfold);
230 }
231
232 return n;
233}
234
235static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
236{
237 struct callchain_list *chain;
238 bool has_children = false;
239 int n = 0;
240
241 list_for_each_entry(chain, &node->val, list) {
242 ++n;
243 map_symbol__set_folding(&chain->ms, unfold);
244 has_children = chain->ms.has_children;
245 }
246
247 if (has_children)
248 n += callchain_node__set_folding_rb_tree(node, unfold);
249
250 return n;
251}
252
253static int callchain__set_folding(struct rb_root *chain, bool unfold)
254{
255 struct rb_node *nd;
256 int n = 0;
257
258 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
259 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
260 n += callchain_node__set_folding(node, unfold);
261 }
262
263 return n;
264}
265
266static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
267{
268 hist_entry__init_have_children(self);
269 map_symbol__set_folding(&self->ms, unfold);
270
271 if (self->ms.has_children) {
272 int n = callchain__set_folding(&self->sorted_chain, unfold);
273 self->nr_rows = unfold ? n : 0;
274 } else
275 self->nr_rows = 0;
276}
277
278static void hists__set_folding(struct hists *self, bool unfold)
279{
280 struct rb_node *nd;
281
282 self->nr_entries = 0;
283
284 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
285 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
286 hist_entry__set_folding(he, unfold);
287 self->nr_entries += 1 + he->nr_rows;
288 }
289}
290
291static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
292{
293 hists__set_folding(self->hists, unfold);
294 self->b.nr_entries = self->hists->nr_entries;
295 /* Go to the start, we may be way after valid entries after a collapse */
296 ui_browser__reset_index(&self->b);
297}
298
299static void ui_browser__warn_lost_events(struct ui_browser *browser)
300{
301 ui_browser__warning(browser, 4,
302 "Events are being lost, check IO/CPU overload!\n\n"
303 "You may want to run 'perf' using a RT scheduler policy:\n\n"
304 " perf top -r 80\n\n"
305 "Or reduce the sampling frequency.");
306}
307
308static int hist_browser__run(struct hist_browser *self, const char *ev_name,
309 void(*timer)(void *arg), void *arg, int delay_secs)
310{
311 int key;
312 char title[160];
313
314 self->b.entries = &self->hists->entries;
315 self->b.nr_entries = self->hists->nr_entries;
316
317 hist_browser__refresh_dimensions(self);
318 hists__browser_title(self->hists, title, sizeof(title), ev_name);
319
320 if (ui_browser__show(&self->b, title,
321 "Press '?' for help on key bindings") < 0)
322 return -1;
323
324 while (1) {
325 key = ui_browser__run(&self->b, delay_secs);
326
327 switch (key) {
328 case K_TIMER:
329 timer(arg);
330 ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
331
332 if (self->hists->stats.nr_lost_warned !=
333 self->hists->stats.nr_events[PERF_RECORD_LOST]) {
334 self->hists->stats.nr_lost_warned =
335 self->hists->stats.nr_events[PERF_RECORD_LOST];
336 ui_browser__warn_lost_events(&self->b);
337 }
338
339 hists__browser_title(self->hists, title, sizeof(title), ev_name);
340 ui_browser__show_title(&self->b, title);
341 continue;
342 case 'D': { /* Debug */
343 static int seq;
344 struct hist_entry *h = rb_entry(self->b.top,
345 struct hist_entry, rb_node);
346 ui_helpline__pop();
347 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
348 seq++, self->b.nr_entries,
349 self->hists->nr_entries,
350 self->b.height,
351 self->b.index,
352 self->b.top_idx,
353 h->row_offset, h->nr_rows);
354 }
355 break;
356 case 'C':
357 /* Collapse the whole world. */
358 hist_browser__set_folding(self, false);
359 break;
360 case 'E':
361 /* Expand the whole world. */
362 hist_browser__set_folding(self, true);
363 break;
364 case K_ENTER:
365 if (hist_browser__toggle_fold(self))
366 break;
367 /* fall thru */
368 default:
369 goto out;
370 }
371 }
372out:
373 ui_browser__hide(&self->b);
374 return key;
375}
376
377static char *callchain_list__sym_name(struct callchain_list *self,
378 char *bf, size_t bfsize)
379{
380 if (self->ms.sym)
381 return self->ms.sym->name;
382
383 snprintf(bf, bfsize, "%#" PRIx64, self->ip);
384 return bf;
385}
386
387#define LEVEL_OFFSET_STEP 3
388
389static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
390 struct callchain_node *chain_node,
391 u64 total, int level,
392 unsigned short row,
393 off_t *row_offset,
394 bool *is_current_entry)
395{
396 struct rb_node *node;
397 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
398 u64 new_total, remaining;
399
400 if (callchain_param.mode == CHAIN_GRAPH_REL)
401 new_total = chain_node->children_hit;
402 else
403 new_total = total;
404
405 remaining = new_total;
406 node = rb_first(&chain_node->rb_root);
407 while (node) {
408 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
409 struct rb_node *next = rb_next(node);
410 u64 cumul = callchain_cumul_hits(child);
411 struct callchain_list *chain;
412 char folded_sign = ' ';
413 int first = true;
414 int extra_offset = 0;
415
416 remaining -= cumul;
417
418 list_for_each_entry(chain, &child->val, list) {
419 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
420 const char *str;
421 int color;
422 bool was_first = first;
423
424 if (first)
425 first = false;
426 else
427 extra_offset = LEVEL_OFFSET_STEP;
428
429 folded_sign = callchain_list__folded(chain);
430 if (*row_offset != 0) {
431 --*row_offset;
432 goto do_next;
433 }
434
435 alloc_str = NULL;
436 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
437 if (was_first) {
438 double percent = cumul * 100.0 / new_total;
439
440 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
441 str = "Not enough memory!";
442 else
443 str = alloc_str;
444 }
445
446 color = HE_COLORSET_NORMAL;
447 width = self->b.width - (offset + extra_offset + 2);
448 if (ui_browser__is_current_entry(&self->b, row)) {
449 self->selection = &chain->ms;
450 color = HE_COLORSET_SELECTED;
451 *is_current_entry = true;
452 }
453
454 ui_browser__set_color(&self->b, color);
455 ui_browser__gotorc(&self->b, row, 0);
456 slsmg_write_nstring(" ", offset + extra_offset);
457 slsmg_printf("%c ", folded_sign);
458 slsmg_write_nstring(str, width);
459 free(alloc_str);
460
461 if (++row == self->b.height)
462 goto out;
463do_next:
464 if (folded_sign == '+')
465 break;
466 }
467
468 if (folded_sign == '-') {
469 const int new_level = level + (extra_offset ? 2 : 1);
470 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
471 new_level, row, row_offset,
472 is_current_entry);
473 }
474 if (row == self->b.height)
475 goto out;
476 node = next;
477 }
478out:
479 return row - first_row;
480}
481
482static int hist_browser__show_callchain_node(struct hist_browser *self,
483 struct callchain_node *node,
484 int level, unsigned short row,
485 off_t *row_offset,
486 bool *is_current_entry)
487{
488 struct callchain_list *chain;
489 int first_row = row,
490 offset = level * LEVEL_OFFSET_STEP,
491 width = self->b.width - offset;
492 char folded_sign = ' ';
493
494 list_for_each_entry(chain, &node->val, list) {
495 char ipstr[BITS_PER_LONG / 4 + 1], *s;
496 int color;
497
498 folded_sign = callchain_list__folded(chain);
499
500 if (*row_offset != 0) {
501 --*row_offset;
502 continue;
503 }
504
505 color = HE_COLORSET_NORMAL;
506 if (ui_browser__is_current_entry(&self->b, row)) {
507 self->selection = &chain->ms;
508 color = HE_COLORSET_SELECTED;
509 *is_current_entry = true;
510 }
511
512 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
513 ui_browser__gotorc(&self->b, row, 0);
514 ui_browser__set_color(&self->b, color);
515 slsmg_write_nstring(" ", offset);
516 slsmg_printf("%c ", folded_sign);
517 slsmg_write_nstring(s, width - 2);
518
519 if (++row == self->b.height)
520 goto out;
521 }
522
523 if (folded_sign == '-')
524 row += hist_browser__show_callchain_node_rb_tree(self, node,
525 self->hists->stats.total_period,
526 level + 1, row,
527 row_offset,
528 is_current_entry);
529out:
530 return row - first_row;
531}
532
533static int hist_browser__show_callchain(struct hist_browser *self,
534 struct rb_root *chain,
535 int level, unsigned short row,
536 off_t *row_offset,
537 bool *is_current_entry)
538{
539 struct rb_node *nd;
540 int first_row = row;
541
542 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
543 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
544
545 row += hist_browser__show_callchain_node(self, node, level,
546 row, row_offset,
547 is_current_entry);
548 if (row == self->b.height)
549 break;
550 }
551
552 return row - first_row;
553}
554
555static int hist_browser__show_entry(struct hist_browser *self,
556 struct hist_entry *entry,
557 unsigned short row)
558{
559 char s[256];
560 double percent;
561 int printed = 0;
562 int width = self->b.width - 6; /* The percentage */
563 char folded_sign = ' ';
564 bool current_entry = ui_browser__is_current_entry(&self->b, row);
565 off_t row_offset = entry->row_offset;
566
567 if (current_entry) {
568 self->he_selection = entry;
569 self->selection = &entry->ms;
570 }
571
572 if (symbol_conf.use_callchain) {
573 hist_entry__init_have_children(entry);
574 folded_sign = hist_entry__folded(entry);
575 }
576
577 if (row_offset == 0) {
578 hist_entry__snprintf(entry, s, sizeof(s), self->hists);
579 percent = (entry->period * 100.0) / self->hists->stats.total_period;
580
581 ui_browser__set_percent_color(&self->b, percent, current_entry);
582 ui_browser__gotorc(&self->b, row, 0);
583 if (symbol_conf.use_callchain) {
584 slsmg_printf("%c ", folded_sign);
585 width -= 2;
586 }
587
588 slsmg_printf(" %5.2f%%", percent);
589
590 /* The scroll bar isn't being used */
591 if (!self->b.navkeypressed)
592 width += 1;
593
594 if (!current_entry || !self->b.navkeypressed)
595 ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
596
597 if (symbol_conf.show_nr_samples) {
598 slsmg_printf(" %11u", entry->nr_events);
599 width -= 12;
600 }
601
602 if (symbol_conf.show_total_period) {
603 slsmg_printf(" %12" PRIu64, entry->period);
604 width -= 13;
605 }
606
607 slsmg_write_nstring(s, width);
608 ++row;
609 ++printed;
610 } else
611 --row_offset;
612
613 if (folded_sign == '-' && row != self->b.height) {
614 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
615 1, row, &row_offset,
616 &current_entry);
617 if (current_entry)
618 self->he_selection = entry;
619 }
620
621 return printed;
622}
623
624static void ui_browser__hists_init_top(struct ui_browser *browser)
625{
626 if (browser->top == NULL) {
627 struct hist_browser *hb;
628
629 hb = container_of(browser, struct hist_browser, b);
630 browser->top = rb_first(&hb->hists->entries);
631 }
632}
633
634static unsigned int hist_browser__refresh(struct ui_browser *self)
635{
636 unsigned row = 0;
637 struct rb_node *nd;
638 struct hist_browser *hb = container_of(self, struct hist_browser, b);
639
640 ui_browser__hists_init_top(self);
641
642 for (nd = self->top; nd; nd = rb_next(nd)) {
643 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
644
645 if (h->filtered)
646 continue;
647
648 row += hist_browser__show_entry(hb, h, row);
649 if (row == self->height)
650 break;
651 }
652
653 return row;
654}
655
656static struct rb_node *hists__filter_entries(struct rb_node *nd)
657{
658 while (nd != NULL) {
659 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
660 if (!h->filtered)
661 return nd;
662
663 nd = rb_next(nd);
664 }
665
666 return NULL;
667}
668
669static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
670{
671 while (nd != NULL) {
672 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
673 if (!h->filtered)
674 return nd;
675
676 nd = rb_prev(nd);
677 }
678
679 return NULL;
680}
681
682static void ui_browser__hists_seek(struct ui_browser *self,
683 off_t offset, int whence)
684{
685 struct hist_entry *h;
686 struct rb_node *nd;
687 bool first = true;
688
689 if (self->nr_entries == 0)
690 return;
691
692 ui_browser__hists_init_top(self);
693
694 switch (whence) {
695 case SEEK_SET:
696 nd = hists__filter_entries(rb_first(self->entries));
697 break;
698 case SEEK_CUR:
699 nd = self->top;
700 goto do_offset;
701 case SEEK_END:
702 nd = hists__filter_prev_entries(rb_last(self->entries));
703 first = false;
704 break;
705 default:
706 return;
707 }
708
709 /*
710 * Moves not relative to the first visible entry invalidates its
711 * row_offset:
712 */
713 h = rb_entry(self->top, struct hist_entry, rb_node);
714 h->row_offset = 0;
715
716 /*
717 * Here we have to check if nd is expanded (+), if it is we can't go
718 * the next top level hist_entry, instead we must compute an offset of
719 * what _not_ to show and not change the first visible entry.
720 *
721 * This offset increments when we are going from top to bottom and
722 * decreases when we're going from bottom to top.
723 *
724 * As we don't have backpointers to the top level in the callchains
725 * structure, we need to always print the whole hist_entry callchain,
726 * skipping the first ones that are before the first visible entry
727 * and stop when we printed enough lines to fill the screen.
728 */
729do_offset:
730 if (offset > 0) {
731 do {
732 h = rb_entry(nd, struct hist_entry, rb_node);
733 if (h->ms.unfolded) {
734 u16 remaining = h->nr_rows - h->row_offset;
735 if (offset > remaining) {
736 offset -= remaining;
737 h->row_offset = 0;
738 } else {
739 h->row_offset += offset;
740 offset = 0;
741 self->top = nd;
742 break;
743 }
744 }
745 nd = hists__filter_entries(rb_next(nd));
746 if (nd == NULL)
747 break;
748 --offset;
749 self->top = nd;
750 } while (offset != 0);
751 } else if (offset < 0) {
752 while (1) {
753 h = rb_entry(nd, struct hist_entry, rb_node);
754 if (h->ms.unfolded) {
755 if (first) {
756 if (-offset > h->row_offset) {
757 offset += h->row_offset;
758 h->row_offset = 0;
759 } else {
760 h->row_offset += offset;
761 offset = 0;
762 self->top = nd;
763 break;
764 }
765 } else {
766 if (-offset > h->nr_rows) {
767 offset += h->nr_rows;
768 h->row_offset = 0;
769 } else {
770 h->row_offset = h->nr_rows + offset;
771 offset = 0;
772 self->top = nd;
773 break;
774 }
775 }
776 }
777
778 nd = hists__filter_prev_entries(rb_prev(nd));
779 if (nd == NULL)
780 break;
781 ++offset;
782 self->top = nd;
783 if (offset == 0) {
784 /*
785 * Last unfiltered hist_entry, check if it is
786 * unfolded, if it is then we should have
787 * row_offset at its last entry.
788 */
789 h = rb_entry(nd, struct hist_entry, rb_node);
790 if (h->ms.unfolded)
791 h->row_offset = h->nr_rows;
792 break;
793 }
794 first = false;
795 }
796 } else {
797 self->top = nd;
798 h = rb_entry(nd, struct hist_entry, rb_node);
799 h->row_offset = 0;
800 }
801}
802
803static struct hist_browser *hist_browser__new(struct hists *hists)
804{
805 struct hist_browser *self = zalloc(sizeof(*self));
806
807 if (self) {
808 self->hists = hists;
809 self->b.refresh = hist_browser__refresh;
810 self->b.seek = ui_browser__hists_seek;
811 self->b.use_navkeypressed = true;
812 if (sort__branch_mode == 1)
813 self->has_symbols = sort_sym_from.list.next != NULL;
814 else
815 self->has_symbols = sort_sym.list.next != NULL;
816 }
817
818 return self;
819}
820
821static void hist_browser__delete(struct hist_browser *self)
822{
823 free(self);
824}
825
826static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
827{
828 return self->he_selection;
829}
830
831static struct thread *hist_browser__selected_thread(struct hist_browser *self)
832{
833 return self->he_selection->thread;
834}
835
836static int hists__browser_title(struct hists *self, char *bf, size_t size,
837 const char *ev_name)
838{
839 char unit;
840 int printed;
841 const struct dso *dso = self->dso_filter;
842 const struct thread *thread = self->thread_filter;
843 unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
844 u64 nr_events = self->stats.total_period;
845
846 nr_samples = convert_unit(nr_samples, &unit);
847 printed = scnprintf(bf, size,
848 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
849 nr_samples, unit, ev_name, nr_events);
850
851
852 if (self->uid_filter_str)
853 printed += snprintf(bf + printed, size - printed,
854 ", UID: %s", self->uid_filter_str);
855 if (thread)
856 printed += scnprintf(bf + printed, size - printed,
857 ", Thread: %s(%d)",
858 (thread->comm_set ? thread->comm : ""),
859 thread->pid);
860 if (dso)
861 printed += scnprintf(bf + printed, size - printed,
862 ", DSO: %s", dso->short_name);
863 return printed;
864}
865
866static inline void free_popup_options(char **options, int n)
867{
868 int i;
869
870 for (i = 0; i < n; ++i) {
871 free(options[i]);
872 options[i] = NULL;
873 }
874}
875
876static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
877 const char *helpline, const char *ev_name,
878 bool left_exits,
879 void(*timer)(void *arg), void *arg,
880 int delay_secs)
881{
882 struct hists *self = &evsel->hists;
883 struct hist_browser *browser = hist_browser__new(self);
884 struct branch_info *bi;
885 struct pstack *fstack;
886 char *options[16];
887 int nr_options = 0;
888 int key = -1;
889 char buf[64];
890
891 if (browser == NULL)
892 return -1;
893
894 fstack = pstack__new(2);
895 if (fstack == NULL)
896 goto out;
897
898 ui_helpline__push(helpline);
899
900 memset(options, 0, sizeof(options));
901
902 while (1) {
903 const struct thread *thread = NULL;
904 const struct dso *dso = NULL;
905 int choice = 0,
906 annotate = -2, zoom_dso = -2, zoom_thread = -2,
907 annotate_f = -2, annotate_t = -2, browse_map = -2;
908
909 nr_options = 0;
910
911 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
912
913 if (browser->he_selection != NULL) {
914 thread = hist_browser__selected_thread(browser);
915 dso = browser->selection->map ? browser->selection->map->dso : NULL;
916 }
917 switch (key) {
918 case K_TAB:
919 case K_UNTAB:
920 if (nr_events == 1)
921 continue;
922 /*
923 * Exit the browser, let hists__browser_tree
924 * go to the next or previous
925 */
926 goto out_free_stack;
927 case 'a':
928 if (!browser->has_symbols) {
929 ui_browser__warning(&browser->b, delay_secs * 2,
930 "Annotation is only available for symbolic views, "
931 "include \"sym*\" in --sort to use it.");
932 continue;
933 }
934
935 if (browser->selection == NULL ||
936 browser->selection->sym == NULL ||
937 browser->selection->map->dso->annotate_warned)
938 continue;
939 goto do_annotate;
940 case 'd':
941 goto zoom_dso;
942 case 't':
943 goto zoom_thread;
944 case 's':
945 if (ui_browser__input_window("Symbol to show",
946 "Please enter the name of symbol you want to see",
947 buf, "ENTER: OK, ESC: Cancel",
948 delay_secs * 2) == K_ENTER) {
949 self->symbol_filter_str = *buf ? buf : NULL;
950 hists__filter_by_symbol(self);
951 hist_browser__reset(browser);
952 }
953 continue;
954 case K_F1:
955 case 'h':
956 case '?':
957 ui_browser__help_window(&browser->b,
958 "h/?/F1 Show this window\n"
959 "UP/DOWN/PGUP\n"
960 "PGDN/SPACE Navigate\n"
961 "q/ESC/CTRL+C Exit browser\n\n"
962 "For multiple event sessions:\n\n"
963 "TAB/UNTAB Switch events\n\n"
964 "For symbolic views (--sort has sym):\n\n"
965 "-> Zoom into DSO/Threads & Annotate current symbol\n"
966 "<- Zoom out\n"
967 "a Annotate current symbol\n"
968 "C Collapse all callchains\n"
969 "E Expand all callchains\n"
970 "d Zoom into current DSO\n"
971 "t Zoom into current Thread\n"
972 "s Filter symbol by name");
973 continue;
974 case K_ENTER:
975 case K_RIGHT:
976 /* menu */
977 break;
978 case K_LEFT: {
979 const void *top;
980
981 if (pstack__empty(fstack)) {
982 /*
983 * Go back to the perf_evsel_menu__run or other user
984 */
985 if (left_exits)
986 goto out_free_stack;
987 continue;
988 }
989 top = pstack__pop(fstack);
990 if (top == &browser->hists->dso_filter)
991 goto zoom_out_dso;
992 if (top == &browser->hists->thread_filter)
993 goto zoom_out_thread;
994 continue;
995 }
996 case K_ESC:
997 if (!left_exits &&
998 !ui_browser__dialog_yesno(&browser->b,
999 "Do you really want to exit?"))
1000 continue;
1001 /* Fall thru */
1002 case 'q':
1003 case CTRL('c'):
1004 goto out_free_stack;
1005 default:
1006 continue;
1007 }
1008
1009 if (!browser->has_symbols)
1010 goto add_exit_option;
1011
1012 if (sort__branch_mode == 1) {
1013 bi = browser->he_selection->branch_info;
1014 if (browser->selection != NULL &&
1015 bi &&
1016 bi->from.sym != NULL &&
1017 !bi->from.map->dso->annotate_warned &&
1018 asprintf(&options[nr_options], "Annotate %s",
1019 bi->from.sym->name) > 0)
1020 annotate_f = nr_options++;
1021
1022 if (browser->selection != NULL &&
1023 bi &&
1024 bi->to.sym != NULL &&
1025 !bi->to.map->dso->annotate_warned &&
1026 (bi->to.sym != bi->from.sym ||
1027 bi->to.map->dso != bi->from.map->dso) &&
1028 asprintf(&options[nr_options], "Annotate %s",
1029 bi->to.sym->name) > 0)
1030 annotate_t = nr_options++;
1031 } else {
1032
1033 if (browser->selection != NULL &&
1034 browser->selection->sym != NULL &&
1035 !browser->selection->map->dso->annotate_warned &&
1036 asprintf(&options[nr_options], "Annotate %s",
1037 browser->selection->sym->name) > 0)
1038 annotate = nr_options++;
1039 }
1040
1041 if (thread != NULL &&
1042 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1043 (browser->hists->thread_filter ? "out of" : "into"),
1044 (thread->comm_set ? thread->comm : ""),
1045 thread->pid) > 0)
1046 zoom_thread = nr_options++;
1047
1048 if (dso != NULL &&
1049 asprintf(&options[nr_options], "Zoom %s %s DSO",
1050 (browser->hists->dso_filter ? "out of" : "into"),
1051 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1052 zoom_dso = nr_options++;
1053
1054 if (browser->selection != NULL &&
1055 browser->selection->map != NULL &&
1056 asprintf(&options[nr_options], "Browse map details") > 0)
1057 browse_map = nr_options++;
1058add_exit_option:
1059 options[nr_options++] = (char *)"Exit";
1060retry_popup_menu:
1061 choice = ui__popup_menu(nr_options, options);
1062
1063 if (choice == nr_options - 1)
1064 break;
1065
1066 if (choice == -1) {
1067 free_popup_options(options, nr_options - 1);
1068 continue;
1069 }
1070
1071 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1072 struct hist_entry *he;
1073 int err;
1074do_annotate:
1075 he = hist_browser__selected_entry(browser);
1076 if (he == NULL)
1077 continue;
1078
1079 /*
1080 * we stash the branch_info symbol + map into the
1081 * the ms so we don't have to rewrite all the annotation
1082 * code to use branch_info.
1083 * in branch mode, the ms struct is not used
1084 */
1085 if (choice == annotate_f) {
1086 he->ms.sym = he->branch_info->from.sym;
1087 he->ms.map = he->branch_info->from.map;
1088 } else if (choice == annotate_t) {
1089 he->ms.sym = he->branch_info->to.sym;
1090 he->ms.map = he->branch_info->to.map;
1091 }
1092
1093 /*
1094 * Don't let this be freed, say, by hists__decay_entry.
1095 */
1096 he->used = true;
1097 err = hist_entry__tui_annotate(he, evsel->idx,
1098 timer, arg, delay_secs);
1099 he->used = false;
1100 /*
1101 * offer option to annotate the other branch source or target
1102 * (if they exists) when returning from annotate
1103 */
1104 if ((err == 'q' || err == CTRL('c'))
1105 && annotate_t != -2 && annotate_f != -2)
1106 goto retry_popup_menu;
1107
1108 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1109 if (err)
1110 ui_browser__handle_resize(&browser->b);
1111
1112 } else if (choice == browse_map)
1113 map__browse(browser->selection->map);
1114 else if (choice == zoom_dso) {
1115zoom_dso:
1116 if (browser->hists->dso_filter) {
1117 pstack__remove(fstack, &browser->hists->dso_filter);
1118zoom_out_dso:
1119 ui_helpline__pop();
1120 browser->hists->dso_filter = NULL;
1121 sort_dso.elide = false;
1122 } else {
1123 if (dso == NULL)
1124 continue;
1125 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1126 dso->kernel ? "the Kernel" : dso->short_name);
1127 browser->hists->dso_filter = dso;
1128 sort_dso.elide = true;
1129 pstack__push(fstack, &browser->hists->dso_filter);
1130 }
1131 hists__filter_by_dso(self);
1132 hist_browser__reset(browser);
1133 } else if (choice == zoom_thread) {
1134zoom_thread:
1135 if (browser->hists->thread_filter) {
1136 pstack__remove(fstack, &browser->hists->thread_filter);
1137zoom_out_thread:
1138 ui_helpline__pop();
1139 browser->hists->thread_filter = NULL;
1140 sort_thread.elide = false;
1141 } else {
1142 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1143 thread->comm_set ? thread->comm : "",
1144 thread->pid);
1145 browser->hists->thread_filter = thread;
1146 sort_thread.elide = true;
1147 pstack__push(fstack, &browser->hists->thread_filter);
1148 }
1149 hists__filter_by_thread(self);
1150 hist_browser__reset(browser);
1151 }
1152 }
1153out_free_stack:
1154 pstack__delete(fstack);
1155out:
1156 hist_browser__delete(browser);
1157 free_popup_options(options, nr_options - 1);
1158 return key;
1159}
1160
1161struct perf_evsel_menu {
1162 struct ui_browser b;
1163 struct perf_evsel *selection;
1164 bool lost_events, lost_events_warned;
1165};
1166
1167static void perf_evsel_menu__write(struct ui_browser *browser,
1168 void *entry, int row)
1169{
1170 struct perf_evsel_menu *menu = container_of(browser,
1171 struct perf_evsel_menu, b);
1172 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1173 bool current_entry = ui_browser__is_current_entry(browser, row);
1174 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1175 const char *ev_name = event_name(evsel);
1176 char bf[256], unit;
1177 const char *warn = " ";
1178 size_t printed;
1179
1180 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1181 HE_COLORSET_NORMAL);
1182
1183 nr_events = convert_unit(nr_events, &unit);
1184 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1185 unit, unit == ' ' ? "" : " ", ev_name);
1186 slsmg_printf("%s", bf);
1187
1188 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1189 if (nr_events != 0) {
1190 menu->lost_events = true;
1191 if (!current_entry)
1192 ui_browser__set_color(browser, HE_COLORSET_TOP);
1193 nr_events = convert_unit(nr_events, &unit);
1194 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1195 nr_events, unit, unit == ' ' ? "" : " ");
1196 warn = bf;
1197 }
1198
1199 slsmg_write_nstring(warn, browser->width - printed);
1200
1201 if (current_entry)
1202 menu->selection = evsel;
1203}
1204
1205static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1206 int nr_events, const char *help,
1207 void(*timer)(void *arg), void *arg, int delay_secs)
1208{
1209 struct perf_evlist *evlist = menu->b.priv;
1210 struct perf_evsel *pos;
1211 const char *ev_name, *title = "Available samples";
1212 int key;
1213
1214 if (ui_browser__show(&menu->b, title,
1215 "ESC: exit, ENTER|->: Browse histograms") < 0)
1216 return -1;
1217
1218 while (1) {
1219 key = ui_browser__run(&menu->b, delay_secs);
1220
1221 switch (key) {
1222 case K_TIMER:
1223 timer(arg);
1224
1225 if (!menu->lost_events_warned && menu->lost_events) {
1226 ui_browser__warn_lost_events(&menu->b);
1227 menu->lost_events_warned = true;
1228 }
1229 continue;
1230 case K_RIGHT:
1231 case K_ENTER:
1232 if (!menu->selection)
1233 continue;
1234 pos = menu->selection;
1235browse_hists:
1236 perf_evlist__set_selected(evlist, pos);
1237 /*
1238 * Give the calling tool a chance to populate the non
1239 * default evsel resorted hists tree.
1240 */
1241 if (timer)
1242 timer(arg);
1243 ev_name = event_name(pos);
1244 key = perf_evsel__hists_browse(pos, nr_events, help,
1245 ev_name, true, timer,
1246 arg, delay_secs);
1247 ui_browser__show_title(&menu->b, title);
1248 switch (key) {
1249 case K_TAB:
1250 if (pos->node.next == &evlist->entries)
1251 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1252 else
1253 pos = list_entry(pos->node.next, struct perf_evsel, node);
1254 goto browse_hists;
1255 case K_UNTAB:
1256 if (pos->node.prev == &evlist->entries)
1257 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1258 else
1259 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1260 goto browse_hists;
1261 case K_ESC:
1262 if (!ui_browser__dialog_yesno(&menu->b,
1263 "Do you really want to exit?"))
1264 continue;
1265 /* Fall thru */
1266 case 'q':
1267 case CTRL('c'):
1268 goto out;
1269 default:
1270 continue;
1271 }
1272 case K_LEFT:
1273 continue;
1274 case K_ESC:
1275 if (!ui_browser__dialog_yesno(&menu->b,
1276 "Do you really want to exit?"))
1277 continue;
1278 /* Fall thru */
1279 case 'q':
1280 case CTRL('c'):
1281 goto out;
1282 default:
1283 continue;
1284 }
1285 }
1286
1287out:
1288 ui_browser__hide(&menu->b);
1289 return key;
1290}
1291
1292static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1293 const char *help,
1294 void(*timer)(void *arg), void *arg,
1295 int delay_secs)
1296{
1297 struct perf_evsel *pos;
1298 struct perf_evsel_menu menu = {
1299 .b = {
1300 .entries = &evlist->entries,
1301 .refresh = ui_browser__list_head_refresh,
1302 .seek = ui_browser__list_head_seek,
1303 .write = perf_evsel_menu__write,
1304 .nr_entries = evlist->nr_entries,
1305 .priv = evlist,
1306 },
1307 };
1308
1309 ui_helpline__push("Press ESC to exit");
1310
1311 list_for_each_entry(pos, &evlist->entries, node) {
1312 const char *ev_name = event_name(pos);
1313 size_t line_len = strlen(ev_name) + 7;
1314
1315 if (menu.b.width < line_len)
1316 menu.b.width = line_len;
1317 /*
1318 * Cache the evsel name, tracepoints have a _high_ cost per
1319 * event_name() call.
1320 */
1321 if (pos->name == NULL)
1322 pos->name = strdup(ev_name);
1323 }
1324
1325 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1326 arg, delay_secs);
1327}
1328
1329int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1330 void(*timer)(void *arg), void *arg,
1331 int delay_secs)
1332{
1333
1334 if (evlist->nr_entries == 1) {
1335 struct perf_evsel *first = list_entry(evlist->entries.next,
1336 struct perf_evsel, node);
1337 const char *ev_name = event_name(first);
1338 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1339 ev_name, false, timer, arg,
1340 delay_secs);
1341 }
1342
1343 return __perf_evlist__tui_browse_hists(evlist, help,
1344 timer, arg, delay_secs);
1345}
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
new file mode 100644
index 000000000000..98851d55a53e
--- /dev/null
+++ b/tools/perf/ui/browsers/map.c
@@ -0,0 +1,154 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <newt.h>
4#include <inttypes.h>
5#include <sys/ttydefaults.h>
6#include <string.h>
7#include <linux/bitops.h>
8#include "../../util/util.h"
9#include "../../util/debug.h"
10#include "../../util/symbol.h"
11#include "../browser.h"
12#include "../helpline.h"
13#include "map.h"
14
15static int ui_entry__read(const char *title, char *bf, size_t size, int width)
16{
17 struct newtExitStruct es;
18 newtComponent form, entry;
19 const char *result;
20 int err = -1;
21
22 newtCenteredWindow(width, 1, title);
23 form = newtForm(NULL, NULL, 0);
24 if (form == NULL)
25 return -1;
26
27 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
28 if (entry == NULL)
29 goto out_free_form;
30
31 newtFormAddComponent(form, entry);
32 newtFormAddHotKey(form, NEWT_KEY_ENTER);
33 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
34 newtFormAddHotKey(form, NEWT_KEY_LEFT);
35 newtFormAddHotKey(form, CTRL('c'));
36 newtFormRun(form, &es);
37
38 if (result != NULL) {
39 strncpy(bf, result, size);
40 err = 0;
41 }
42out_free_form:
43 newtPopWindow();
44 newtFormDestroy(form);
45 return err;
46}
47
48struct map_browser {
49 struct ui_browser b;
50 struct map *map;
51 u8 addrlen;
52};
53
54static void map_browser__write(struct ui_browser *self, void *nd, int row)
55{
56 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
57 struct map_browser *mb = container_of(self, struct map_browser, b);
58 bool current_entry = ui_browser__is_current_entry(self, row);
59 int width;
60
61 ui_browser__set_percent_color(self, 0, current_entry);
62 slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
63 mb->addrlen, sym->start, mb->addrlen, sym->end,
64 sym->binding == STB_GLOBAL ? 'g' :
65 sym->binding == STB_LOCAL ? 'l' : 'w');
66 width = self->width - ((mb->addrlen * 2) + 4);
67 if (width > 0)
68 slsmg_write_nstring(sym->name, width);
69}
70
71/* FIXME uber-kludgy, see comment on cmd_report... */
72static u32 *symbol__browser_index(struct symbol *self)
73{
74 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
75}
76
77static int map_browser__search(struct map_browser *self)
78{
79 char target[512];
80 struct symbol *sym;
81 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
82
83 if (err)
84 return err;
85
86 if (target[0] == '0' && tolower(target[1]) == 'x') {
87 u64 addr = strtoull(target, NULL, 16);
88 sym = map__find_symbol(self->map, addr, NULL);
89 } else
90 sym = map__find_symbol_by_name(self->map, target, NULL);
91
92 if (sym != NULL) {
93 u32 *idx = symbol__browser_index(sym);
94
95 self->b.top = &sym->rb_node;
96 self->b.index = self->b.top_idx = *idx;
97 } else
98 ui_helpline__fpush("%s not found!", target);
99
100 return 0;
101}
102
103static int map_browser__run(struct map_browser *self)
104{
105 int key;
106
107 if (ui_browser__show(&self->b, self->map->dso->long_name,
108 "Press <- or ESC to exit, %s / to search",
109 verbose ? "" : "restart with -v to use") < 0)
110 return -1;
111
112 while (1) {
113 key = ui_browser__run(&self->b, 0);
114
115 if (verbose && key == '/')
116 map_browser__search(self);
117 else
118 break;
119 }
120
121 ui_browser__hide(&self->b);
122 return key;
123}
124
125int map__browse(struct map *self)
126{
127 struct map_browser mb = {
128 .b = {
129 .entries = &self->dso->symbols[self->type],
130 .refresh = ui_browser__rb_tree_refresh,
131 .seek = ui_browser__rb_tree_seek,
132 .write = map_browser__write,
133 },
134 .map = self,
135 };
136 struct rb_node *nd;
137 char tmp[BITS_PER_LONG / 4];
138 u64 maxaddr = 0;
139
140 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
141 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
142
143 if (maxaddr < pos->end)
144 maxaddr = pos->end;
145 if (verbose) {
146 u32 *idx = symbol__browser_index(pos);
147 *idx = mb.b.nr_entries;
148 }
149 ++mb.b.nr_entries;
150 }
151
152 mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
153 return map_browser__run(&mb);
154}
diff --git a/tools/perf/ui/browsers/map.h b/tools/perf/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c
new file mode 100644
index 000000000000..2f950c2641c8
--- /dev/null
+++ b/tools/perf/ui/helpline.c
@@ -0,0 +1,79 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "../debug.h"
6#include "helpline.h"
7#include "ui.h"
8#include "libslang.h"
9
10void ui_helpline__pop(void)
11{
12}
13
14char ui_helpline__current[512];
15
16void ui_helpline__push(const char *msg)
17{
18 const size_t sz = sizeof(ui_helpline__current);
19
20 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
21 SLsmg_set_color(0);
22 SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
23 SLsmg_refresh();
24 strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
25}
26
27void ui_helpline__vpush(const char *fmt, va_list ap)
28{
29 char *s;
30
31 if (vasprintf(&s, fmt, ap) < 0)
32 vfprintf(stderr, fmt, ap);
33 else {
34 ui_helpline__push(s);
35 free(s);
36 }
37}
38
39void ui_helpline__fpush(const char *fmt, ...)
40{
41 va_list ap;
42
43 va_start(ap, fmt);
44 ui_helpline__vpush(fmt, ap);
45 va_end(ap);
46}
47
48void ui_helpline__puts(const char *msg)
49{
50 ui_helpline__pop();
51 ui_helpline__push(msg);
52}
53
54void ui_helpline__init(void)
55{
56 ui_helpline__puts(" ");
57}
58
59char ui_helpline__last_msg[1024];
60
61int ui_helpline__show_help(const char *format, va_list ap)
62{
63 int ret;
64 static int backlog;
65
66 pthread_mutex_lock(&ui__lock);
67 ret = vscnprintf(ui_helpline__last_msg + backlog,
68 sizeof(ui_helpline__last_msg) - backlog, format, ap);
69 backlog += ret;
70
71 if (ui_helpline__last_msg[backlog - 1] == '\n') {
72 ui_helpline__puts(ui_helpline__last_msg);
73 SLsmg_refresh();
74 backlog = 0;
75 }
76 pthread_mutex_unlock(&ui__lock);
77
78 return ret;
79}
diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h
new file mode 100644
index 000000000000..7bab6b34e35e
--- /dev/null
+++ b/tools/perf/ui/helpline.h
@@ -0,0 +1,16 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4#include <stdio.h>
5#include <stdarg.h>
6
7void ui_helpline__init(void);
8void ui_helpline__pop(void);
9void ui_helpline__push(const char *msg);
10void ui_helpline__vpush(const char *fmt, va_list ap);
11void ui_helpline__fpush(const char *fmt, ...);
12void ui_helpline__puts(const char *msg);
13
14extern char ui_helpline__current[];
15
16#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h
new file mode 100644
index 000000000000..809eca5707fa
--- /dev/null
+++ b/tools/perf/ui/keysyms.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_KEYSYMS_H_
2#define _PERF_KEYSYMS_H_ 1
3
4#include "libslang.h"
5
6#define K_DOWN SL_KEY_DOWN
7#define K_END SL_KEY_END
8#define K_ENTER '\r'
9#define K_ESC 033
10#define K_F1 SL_KEY_F(1)
11#define K_HOME SL_KEY_HOME
12#define K_LEFT SL_KEY_LEFT
13#define K_PGDN SL_KEY_NPAGE
14#define K_PGUP SL_KEY_PPAGE
15#define K_RIGHT SL_KEY_RIGHT
16#define K_TAB '\t'
17#define K_UNTAB SL_KEY_UNTAB
18#define K_UP SL_KEY_UP
19#define K_BKSPC 0x7f
20#define K_DEL SL_KEY_DELETE
21
22/* Not really keys */
23#define K_TIMER -1
24#define K_ERROR -2
25#define K_RESIZE -3
26
27#endif /* _PERF_KEYSYMS_H_ */
diff --git a/tools/perf/ui/libslang.h b/tools/perf/ui/libslang.h
new file mode 100644
index 000000000000..4d54b6450f5b
--- /dev/null
+++ b/tools/perf/ui/libslang.h
@@ -0,0 +1,29 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)(msg), ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)(msg), len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg))
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#define SL_KEY_UNTAB 0x1000
28
29#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c
new file mode 100644
index 000000000000..13aa64e50e11
--- /dev/null
+++ b/tools/perf/ui/progress.c
@@ -0,0 +1,32 @@
1#include "../cache.h"
2#include "progress.h"
3#include "libslang.h"
4#include "ui.h"
5#include "browser.h"
6
7void ui_progress__update(u64 curr, u64 total, const char *title)
8{
9 int bar, y;
10 /*
11 * FIXME: We should have a per UI backend way of showing progress,
12 * stdio will just show a percentage as NN%, etc.
13 */
14 if (use_browser <= 0)
15 return;
16
17 if (total == 0)
18 return;
19
20 ui__refresh_dimensions(true);
21 pthread_mutex_lock(&ui__lock);
22 y = SLtt_Screen_Rows / 2 - 2;
23 SLsmg_set_color(0);
24 SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
25 SLsmg_gotorc(y++, 1);
26 SLsmg_write_string((char *)title);
27 SLsmg_set_color(HE_COLORSET_SELECTED);
28 bar = ((SLtt_Screen_Cols - 2) * curr) / total;
29 SLsmg_fill_region(y, 1, 1, bar, ' ');
30 SLsmg_refresh();
31 pthread_mutex_unlock(&ui__lock);
32}
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h
new file mode 100644
index 000000000000..d9c205b59aa1
--- /dev/null
+++ b/tools/perf/ui/progress.h
@@ -0,0 +1,8 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4#include <../types.h>
5
6void ui_progress__update(u64 curr, u64 total, const char *title);
7
8#endif
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
new file mode 100644
index 000000000000..85a69faa09aa
--- /dev/null
+++ b/tools/perf/ui/setup.c
@@ -0,0 +1,155 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9#include "ui.h"
10#include "util.h"
11#include "libslang.h"
12#include "keysyms.h"
13
14pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
15
16static volatile int ui__need_resize;
17
18void ui__refresh_dimensions(bool force)
19{
20 if (force || ui__need_resize) {
21 ui__need_resize = 0;
22 pthread_mutex_lock(&ui__lock);
23 SLtt_get_screen_size();
24 SLsmg_reinit_smg();
25 pthread_mutex_unlock(&ui__lock);
26 }
27}
28
29static void ui__sigwinch(int sig __used)
30{
31 ui__need_resize = 1;
32}
33
34static void ui__setup_sigwinch(void)
35{
36 static bool done;
37
38 if (done)
39 return;
40
41 done = true;
42 pthread__unblock_sigwinch();
43 signal(SIGWINCH, ui__sigwinch);
44}
45
46int ui__getch(int delay_secs)
47{
48 struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
49 fd_set read_set;
50 int err, key;
51
52 ui__setup_sigwinch();
53
54 FD_ZERO(&read_set);
55 FD_SET(0, &read_set);
56
57 if (delay_secs) {
58 timeout.tv_sec = delay_secs;
59 timeout.tv_usec = 0;
60 }
61
62 err = select(1, &read_set, NULL, NULL, ptimeout);
63
64 if (err == 0)
65 return K_TIMER;
66
67 if (err == -1) {
68 if (errno == EINTR)
69 return K_RESIZE;
70 return K_ERROR;
71 }
72
73 key = SLang_getkey();
74 if (key != K_ESC)
75 return key;
76
77 FD_ZERO(&read_set);
78 FD_SET(0, &read_set);
79 timeout.tv_sec = 0;
80 timeout.tv_usec = 20;
81 err = select(1, &read_set, NULL, NULL, &timeout);
82 if (err == 0)
83 return K_ESC;
84
85 SLang_ungetkey(key);
86 return SLkp_getkey();
87}
88
89static void newt_suspend(void *d __used)
90{
91 newtSuspend();
92 raise(SIGTSTP);
93 newtResume();
94}
95
96static int ui__init(void)
97{
98 int err = SLkp_init();
99
100 if (err < 0)
101 goto out;
102
103 SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
104out:
105 return err;
106}
107
108static void ui__exit(void)
109{
110 SLtt_set_cursor_visibility(1);
111 SLsmg_refresh();
112 SLsmg_reset_smg();
113 SLang_reset_tty();
114}
115
116static void ui__signal(int sig)
117{
118 ui__exit();
119 psignal(sig, "perf");
120 exit(0);
121}
122
123void setup_browser(bool fallback_to_pager)
124{
125 if (!isatty(1) || !use_browser || dump_trace) {
126 use_browser = 0;
127 if (fallback_to_pager)
128 setup_pager();
129 return;
130 }
131
132 use_browser = 1;
133 newtInit();
134 ui__init();
135 newtSetSuspendCallback(newt_suspend, NULL);
136 ui_helpline__init();
137 ui_browser__init();
138
139 signal(SIGSEGV, ui__signal);
140 signal(SIGFPE, ui__signal);
141 signal(SIGINT, ui__signal);
142 signal(SIGQUIT, ui__signal);
143 signal(SIGTERM, ui__signal);
144}
145
146void exit_browser(bool wait_for_ok)
147{
148 if (use_browser > 0) {
149 if (wait_for_ok)
150 ui__question_window("Fatal Error",
151 ui_helpline__last_msg,
152 "Press any key...", 0);
153 ui__exit();
154 }
155}
diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h
new file mode 100644
index 000000000000..7b67045479f6
--- /dev/null
+++ b/tools/perf/ui/ui.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_H_
2#define _PERF_UI_H_ 1
3
4#include <pthread.h>
5#include <stdbool.h>
6
7extern pthread_mutex_t ui__lock;
8
9void ui__refresh_dimensions(bool force);
10
11#endif /* _PERF_UI_H_ */
diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c
new file mode 100644
index 000000000000..ad4374a16bb0
--- /dev/null
+++ b/tools/perf/ui/util.c
@@ -0,0 +1,250 @@
1#include "../util.h"
2#include <signal.h>
3#include <stdbool.h>
4#include <string.h>
5#include <sys/ttydefaults.h>
6
7#include "../cache.h"
8#include "../debug.h"
9#include "browser.h"
10#include "keysyms.h"
11#include "helpline.h"
12#include "ui.h"
13#include "util.h"
14#include "libslang.h"
15
16static void ui_browser__argv_write(struct ui_browser *browser,
17 void *entry, int row)
18{
19 char **arg = entry;
20 bool current_entry = ui_browser__is_current_entry(browser, row);
21
22 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
23 HE_COLORSET_NORMAL);
24 slsmg_write_nstring(*arg, browser->width);
25}
26
27static int popup_menu__run(struct ui_browser *menu)
28{
29 int key;
30
31 if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
32 return -1;
33
34 while (1) {
35 key = ui_browser__run(menu, 0);
36
37 switch (key) {
38 case K_RIGHT:
39 case K_ENTER:
40 key = menu->index;
41 break;
42 case K_LEFT:
43 case K_ESC:
44 case 'q':
45 case CTRL('c'):
46 key = -1;
47 break;
48 default:
49 continue;
50 }
51
52 break;
53 }
54
55 ui_browser__hide(menu);
56 return key;
57}
58
59int ui__popup_menu(int argc, char * const argv[])
60{
61 struct ui_browser menu = {
62 .entries = (void *)argv,
63 .refresh = ui_browser__argv_refresh,
64 .seek = ui_browser__argv_seek,
65 .write = ui_browser__argv_write,
66 .nr_entries = argc,
67 };
68
69 return popup_menu__run(&menu);
70}
71
72int ui_browser__input_window(const char *title, const char *text, char *input,
73 const char *exit_msg, int delay_secs)
74{
75 int x, y, len, key;
76 int max_len = 60, nr_lines = 0;
77 static char buf[50];
78 const char *t;
79
80 t = text;
81 while (1) {
82 const char *sep = strchr(t, '\n');
83
84 if (sep == NULL)
85 sep = strchr(t, '\0');
86 len = sep - t;
87 if (max_len < len)
88 max_len = len;
89 ++nr_lines;
90 if (*sep == '\0')
91 break;
92 t = sep + 1;
93 }
94
95 max_len += 2;
96 nr_lines += 8;
97 y = SLtt_Screen_Rows / 2 - nr_lines / 2;
98 x = SLtt_Screen_Cols / 2 - max_len / 2;
99
100 SLsmg_set_color(0);
101 SLsmg_draw_box(y, x++, nr_lines, max_len);
102 if (title) {
103 SLsmg_gotorc(y, x + 1);
104 SLsmg_write_string((char *)title);
105 }
106 SLsmg_gotorc(++y, x);
107 nr_lines -= 7;
108 max_len -= 2;
109 SLsmg_write_wrapped_string((unsigned char *)text, y, x,
110 nr_lines, max_len, 1);
111 y += nr_lines;
112 len = 5;
113 while (len--) {
114 SLsmg_gotorc(y + len - 1, x);
115 SLsmg_write_nstring((char *)" ", max_len);
116 }
117 SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
118
119 SLsmg_gotorc(y + 3, x);
120 SLsmg_write_nstring((char *)exit_msg, max_len);
121 SLsmg_refresh();
122
123 x += 2;
124 len = 0;
125 key = ui__getch(delay_secs);
126 while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
127 if (key == K_BKSPC) {
128 if (len == 0)
129 goto next_key;
130 SLsmg_gotorc(y, x + --len);
131 SLsmg_write_char(' ');
132 } else {
133 buf[len] = key;
134 SLsmg_gotorc(y, x + len++);
135 SLsmg_write_char(key);
136 }
137 SLsmg_refresh();
138
139 /* XXX more graceful overflow handling needed */
140 if (len == sizeof(buf) - 1) {
141 ui_helpline__push("maximum size of symbol name reached!");
142 key = K_ENTER;
143 break;
144 }
145next_key:
146 key = ui__getch(delay_secs);
147 }
148
149 buf[len] = '\0';
150 strncpy(input, buf, len+1);
151 return key;
152}
153
154int ui__question_window(const char *title, const char *text,
155 const char *exit_msg, int delay_secs)
156{
157 int x, y;
158 int max_len = 0, nr_lines = 0;
159 const char *t;
160
161 t = text;
162 while (1) {
163 const char *sep = strchr(t, '\n');
164 int len;
165
166 if (sep == NULL)
167 sep = strchr(t, '\0');
168 len = sep - t;
169 if (max_len < len)
170 max_len = len;
171 ++nr_lines;
172 if (*sep == '\0')
173 break;
174 t = sep + 1;
175 }
176
177 max_len += 2;
178 nr_lines += 4;
179 y = SLtt_Screen_Rows / 2 - nr_lines / 2,
180 x = SLtt_Screen_Cols / 2 - max_len / 2;
181
182 SLsmg_set_color(0);
183 SLsmg_draw_box(y, x++, nr_lines, max_len);
184 if (title) {
185 SLsmg_gotorc(y, x + 1);
186 SLsmg_write_string((char *)title);
187 }
188 SLsmg_gotorc(++y, x);
189 nr_lines -= 2;
190 max_len -= 2;
191 SLsmg_write_wrapped_string((unsigned char *)text, y, x,
192 nr_lines, max_len, 1);
193 SLsmg_gotorc(y + nr_lines - 2, x);
194 SLsmg_write_nstring((char *)" ", max_len);
195 SLsmg_gotorc(y + nr_lines - 1, x);
196 SLsmg_write_nstring((char *)exit_msg, max_len);
197 SLsmg_refresh();
198 return ui__getch(delay_secs);
199}
200
201int ui__help_window(const char *text)
202{
203 return ui__question_window("Help", text, "Press any key...", 0);
204}
205
206int ui__dialog_yesno(const char *msg)
207{
208 return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
209}
210
211int __ui__warning(const char *title, const char *format, va_list args)
212{
213 char *s;
214
215 if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
216 int key;
217
218 pthread_mutex_lock(&ui__lock);
219 key = ui__question_window(title, s, "Press any key...", 0);
220 pthread_mutex_unlock(&ui__lock);
221 free(s);
222 return key;
223 }
224
225 fprintf(stderr, "%s:\n", title);
226 vfprintf(stderr, format, args);
227 return K_ESC;
228}
229
230int ui__warning(const char *format, ...)
231{
232 int key;
233 va_list args;
234
235 va_start(args, format);
236 key = __ui__warning("Warning", format, args);
237 va_end(args);
238 return key;
239}
240
241int ui__error(const char *format, ...)
242{
243 int key;
244 va_list args;
245
246 va_start(args, format);
247 key = __ui__warning("Error", format, args);
248 va_end(args);
249 return key;
250}
diff --git a/tools/perf/ui/util.h b/tools/perf/ui/util.h
new file mode 100644
index 000000000000..2d1738bd71c8
--- /dev/null
+++ b/tools/perf/ui/util.h
@@ -0,0 +1,14 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdarg.h>
5
6int ui__getch(int delay_secs);
7int ui__popup_menu(int argc, char * const argv[]);
8int ui__help_window(const char *text);
9int ui__dialog_yesno(const char *msg);
10int ui__question_window(const char *title, const char *text,
11 const char *exit_msg, int delay_secs);
12int __ui__warning(const char *title, const char *format, va_list args);
13
14#endif /* _PERF_UI_UTIL_H_ */