diff options
Diffstat (limited to 'tools/perf/util/newt.c')
-rw-r--r-- | tools/perf/util/newt.c | 352 |
1 files changed, 306 insertions, 46 deletions
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index daa86efffce6..ba6acd04c082 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <stdio.h> | 2 | #include <stdio.h> |
3 | #undef _GNU_SOURCE | 3 | #undef _GNU_SOURCE |
4 | 4 | ||
5 | #include <slang.h> | ||
5 | #include <stdlib.h> | 6 | #include <stdlib.h> |
6 | #include <newt.h> | 7 | #include <newt.h> |
7 | #include <sys/ttydefaults.h> | 8 | #include <sys/ttydefaults.h> |
@@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg) | |||
171 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; | 172 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; |
172 | } | 173 | } |
173 | 174 | ||
175 | #define HE_COLORSET_TOP 50 | ||
176 | #define HE_COLORSET_MEDIUM 51 | ||
177 | #define HE_COLORSET_NORMAL 52 | ||
178 | #define HE_COLORSET_SELECTED 53 | ||
179 | #define HE_COLORSET_CODE 54 | ||
180 | |||
181 | static int ui_browser__percent_color(double percent, bool current) | ||
182 | { | ||
183 | if (current) | ||
184 | return HE_COLORSET_SELECTED; | ||
185 | if (percent >= MIN_RED) | ||
186 | return HE_COLORSET_TOP; | ||
187 | if (percent >= MIN_GREEN) | ||
188 | return HE_COLORSET_MEDIUM; | ||
189 | return HE_COLORSET_NORMAL; | ||
190 | } | ||
191 | |||
192 | struct ui_browser { | ||
193 | newtComponent form, sb; | ||
194 | u64 index, first_visible_entry_idx; | ||
195 | void *first_visible_entry, *entries; | ||
196 | u16 top, left, width, height; | ||
197 | void *priv; | ||
198 | u32 nr_entries; | ||
199 | }; | ||
200 | |||
201 | static void ui_browser__refresh_dimensions(struct ui_browser *self) | ||
202 | { | ||
203 | int cols, rows; | ||
204 | newtGetScreenSize(&cols, &rows); | ||
205 | |||
206 | if (self->width > cols - 4) | ||
207 | self->width = cols - 4; | ||
208 | self->height = rows - 5; | ||
209 | if (self->height > self->nr_entries) | ||
210 | self->height = self->nr_entries; | ||
211 | self->top = (rows - self->height) / 2; | ||
212 | self->left = (cols - self->width) / 2; | ||
213 | } | ||
214 | |||
215 | static void ui_browser__reset_index(struct ui_browser *self) | ||
216 | { | ||
217 | self->index = self->first_visible_entry_idx = 0; | ||
218 | self->first_visible_entry = NULL; | ||
219 | } | ||
220 | |||
221 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, | ||
222 | int width, struct hist_entry *he, int len, | ||
223 | bool current_entry) | ||
224 | { | ||
225 | if (self->offset != -1) { | ||
226 | struct symbol *sym = he->ms.sym; | ||
227 | unsigned int hits = 0; | ||
228 | double percent = 0.0; | ||
229 | int color; | ||
230 | struct sym_priv *priv = symbol__priv(sym); | ||
231 | struct sym_ext *sym_ext = priv->ext; | ||
232 | struct sym_hist *h = priv->hist; | ||
233 | s64 offset = self->offset; | ||
234 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | ||
235 | |||
236 | while (offset < (s64)len && | ||
237 | (next == NULL || offset < next->offset)) { | ||
238 | if (sym_ext) { | ||
239 | percent += sym_ext[offset].percent; | ||
240 | } else | ||
241 | hits += h->ip[offset]; | ||
242 | |||
243 | ++offset; | ||
244 | } | ||
245 | |||
246 | if (sym_ext == NULL && h->sum) | ||
247 | percent = 100.0 * hits / h->sum; | ||
248 | |||
249 | color = ui_browser__percent_color(percent, current_entry); | ||
250 | SLsmg_set_color(color); | ||
251 | SLsmg_printf(" %7.2f ", percent); | ||
252 | if (!current_entry) | ||
253 | SLsmg_set_color(HE_COLORSET_CODE); | ||
254 | } else { | ||
255 | int color = ui_browser__percent_color(0, current_entry); | ||
256 | SLsmg_set_color(color); | ||
257 | SLsmg_write_nstring(" ", 9); | ||
258 | } | ||
259 | |||
260 | SLsmg_write_char(':'); | ||
261 | SLsmg_write_nstring(" ", 8); | ||
262 | if (!*self->line) | ||
263 | SLsmg_write_nstring(" ", width - 18); | ||
264 | else | ||
265 | SLsmg_write_nstring(self->line, width - 18); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int ui_browser__refresh_entries(struct ui_browser *self) | ||
271 | { | ||
272 | struct objdump_line *pos; | ||
273 | struct list_head *head = self->entries; | ||
274 | struct hist_entry *he = self->priv; | ||
275 | int row = 0; | ||
276 | int len = he->ms.sym->end - he->ms.sym->start; | ||
277 | |||
278 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) | ||
279 | self->first_visible_entry = head->next; | ||
280 | |||
281 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); | ||
282 | |||
283 | list_for_each_entry_from(pos, head, node) { | ||
284 | bool current_entry = (self->first_visible_entry_idx + row) == self->index; | ||
285 | SLsmg_gotorc(self->top + row, self->left); | ||
286 | objdump_line__show(pos, head, self->width, | ||
287 | he, len, current_entry); | ||
288 | if (++row == self->height) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | SLsmg_set_color(HE_COLORSET_NORMAL); | ||
293 | SLsmg_fill_region(self->top + row, self->left, | ||
294 | self->height - row, self->width, ' '); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int ui_browser__run(struct ui_browser *self, const char *title, | ||
300 | struct newtExitStruct *es) | ||
301 | { | ||
302 | if (self->form) { | ||
303 | newtFormDestroy(self->form); | ||
304 | newtPopWindow(); | ||
305 | } | ||
306 | |||
307 | ui_browser__refresh_dimensions(self); | ||
308 | newtCenteredWindow(self->width + 2, self->height, title); | ||
309 | self->form = newt_form__new(); | ||
310 | if (self->form == NULL) | ||
311 | return -1; | ||
312 | |||
313 | self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, | ||
314 | HE_COLORSET_NORMAL, | ||
315 | HE_COLORSET_SELECTED); | ||
316 | if (self->sb == NULL) | ||
317 | return -1; | ||
318 | |||
319 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
320 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
321 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
322 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
323 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
324 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
325 | |||
326 | if (ui_browser__refresh_entries(self) < 0) | ||
327 | return -1; | ||
328 | newtFormAddComponent(self->form, self->sb); | ||
329 | |||
330 | while (1) { | ||
331 | unsigned int offset; | ||
332 | |||
333 | newtFormRun(self->form, es); | ||
334 | |||
335 | if (es->reason != NEWT_EXIT_HOTKEY) | ||
336 | break; | ||
337 | switch (es->u.key) { | ||
338 | case NEWT_KEY_DOWN: | ||
339 | if (self->index == self->nr_entries - 1) | ||
340 | break; | ||
341 | ++self->index; | ||
342 | if (self->index == self->first_visible_entry_idx + self->height) { | ||
343 | struct list_head *pos = self->first_visible_entry; | ||
344 | ++self->first_visible_entry_idx; | ||
345 | self->first_visible_entry = pos->next; | ||
346 | } | ||
347 | break; | ||
348 | case NEWT_KEY_UP: | ||
349 | if (self->index == 0) | ||
350 | break; | ||
351 | --self->index; | ||
352 | if (self->index < self->first_visible_entry_idx) { | ||
353 | struct list_head *pos = self->first_visible_entry; | ||
354 | --self->first_visible_entry_idx; | ||
355 | self->first_visible_entry = pos->prev; | ||
356 | } | ||
357 | break; | ||
358 | case NEWT_KEY_PGDN: | ||
359 | if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) | ||
360 | break; | ||
361 | |||
362 | offset = self->height; | ||
363 | if (self->index + offset > self->nr_entries - 1) | ||
364 | offset = self->nr_entries - 1 - self->index; | ||
365 | self->index += offset; | ||
366 | self->first_visible_entry_idx += offset; | ||
367 | |||
368 | while (offset--) { | ||
369 | struct list_head *pos = self->first_visible_entry; | ||
370 | self->first_visible_entry = pos->next; | ||
371 | } | ||
372 | |||
373 | break; | ||
374 | case NEWT_KEY_PGUP: | ||
375 | if (self->first_visible_entry_idx == 0) | ||
376 | break; | ||
377 | |||
378 | if (self->first_visible_entry_idx < self->height) | ||
379 | offset = self->first_visible_entry_idx; | ||
380 | else | ||
381 | offset = self->height; | ||
382 | |||
383 | self->index -= offset; | ||
384 | self->first_visible_entry_idx -= offset; | ||
385 | |||
386 | while (offset--) { | ||
387 | struct list_head *pos = self->first_visible_entry; | ||
388 | self->first_visible_entry = pos->prev; | ||
389 | } | ||
390 | break; | ||
391 | case NEWT_KEY_HOME: | ||
392 | ui_browser__reset_index(self); | ||
393 | break; | ||
394 | case NEWT_KEY_END: { | ||
395 | struct list_head *head = self->entries; | ||
396 | offset = self->height - 1; | ||
397 | |||
398 | if (offset > self->nr_entries) | ||
399 | offset = self->nr_entries; | ||
400 | |||
401 | self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; | ||
402 | self->first_visible_entry = head->prev; | ||
403 | while (offset-- != 0) { | ||
404 | struct list_head *pos = self->first_visible_entry; | ||
405 | self->first_visible_entry = pos->prev; | ||
406 | } | ||
407 | } | ||
408 | break; | ||
409 | case NEWT_KEY_ESCAPE: | ||
410 | case CTRL('c'): | ||
411 | case 'Q': | ||
412 | case 'q': | ||
413 | return 0; | ||
414 | default: | ||
415 | continue; | ||
416 | } | ||
417 | if (ui_browser__refresh_entries(self) < 0) | ||
418 | return -1; | ||
419 | } | ||
420 | return 0; | ||
421 | } | ||
422 | |||
174 | /* | 423 | /* |
175 | * When debugging newt problems it was useful to be able to "unroll" | 424 | * When debugging newt problems it was useful to be able to "unroll" |
176 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | 425 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate |
@@ -353,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, | |||
353 | return ret; | 602 | return ret; |
354 | } | 603 | } |
355 | 604 | ||
356 | static void map_symbol__annotate_browser(const struct map_symbol *self, | 605 | static void hist_entry__annotate_browser(struct hist_entry *self) |
357 | const char *input_name) | ||
358 | { | 606 | { |
359 | FILE *fp; | 607 | struct ui_browser browser; |
360 | int cols, rows; | ||
361 | newtComponent form, tree; | ||
362 | struct newtExitStruct es; | 608 | struct newtExitStruct es; |
363 | char *str; | 609 | struct objdump_line *pos, *n; |
364 | size_t line_len, max_line_len = 0; | 610 | LIST_HEAD(head); |
365 | size_t max_usable_width; | ||
366 | char *line = NULL; | ||
367 | 611 | ||
368 | if (self->sym == NULL) | 612 | if (self->ms.sym == NULL) |
369 | return; | 613 | return; |
370 | 614 | ||
371 | if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", | 615 | if (hist_entry__annotate(self, &head) < 0) |
372 | input_name, self->map->dso->name, self->sym->name) < 0) | ||
373 | return; | 616 | return; |
374 | 617 | ||
375 | fp = popen(str, "r"); | ||
376 | if (fp == NULL) | ||
377 | goto out_free_str; | ||
378 | |||
379 | ui_helpline__push("Press ESC to exit"); | 618 | ui_helpline__push("Press ESC to exit"); |
380 | newtGetScreenSize(&cols, &rows); | ||
381 | tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); | ||
382 | |||
383 | while (!feof(fp)) { | ||
384 | if (getline(&line, &line_len, fp) < 0 || !line_len) | ||
385 | break; | ||
386 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
387 | line[--line_len] = '\0'; | ||
388 | 619 | ||
389 | if (line_len > max_line_len) | 620 | memset(&browser, 0, sizeof(browser)); |
390 | max_line_len = line_len; | 621 | browser.entries = &head; |
391 | newtListboxAppendEntry(tree, line, NULL); | 622 | browser.priv = self; |
623 | list_for_each_entry(pos, &head, node) { | ||
624 | size_t line_len = strlen(pos->line); | ||
625 | if (browser.width < line_len) | ||
626 | browser.width = line_len; | ||
627 | ++browser.nr_entries; | ||
392 | } | 628 | } |
393 | fclose(fp); | ||
394 | free(line); | ||
395 | |||
396 | max_usable_width = cols - 22; | ||
397 | if (max_line_len > max_usable_width) | ||
398 | max_line_len = max_usable_width; | ||
399 | |||
400 | newtListboxSetWidth(tree, max_line_len); | ||
401 | 629 | ||
402 | newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); | 630 | browser.width += 18; /* Percentage */ |
403 | form = newt_form__new(); | 631 | ui_browser__run(&browser, self->ms.sym->name, &es); |
404 | newtFormAddComponent(form, tree); | 632 | newtFormDestroy(browser.form); |
405 | |||
406 | newtFormRun(form, &es); | ||
407 | newtFormDestroy(form); | ||
408 | newtPopWindow(); | 633 | newtPopWindow(); |
634 | list_for_each_entry_safe(pos, n, &head, node) { | ||
635 | list_del(&pos->node); | ||
636 | objdump_line__free(pos); | ||
637 | } | ||
409 | ui_helpline__pop(); | 638 | ui_helpline__pop(); |
410 | out_free_str: | ||
411 | free(str); | ||
412 | } | 639 | } |
413 | 640 | ||
414 | static const void *newt__symbol_tree_get_current(newtComponent self) | 641 | static const void *newt__symbol_tree_get_current(newtComponent self) |
@@ -527,7 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists | |||
527 | return 0; | 754 | return 0; |
528 | } | 755 | } |
529 | 756 | ||
530 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | 757 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) |
531 | { | 758 | { |
532 | int *indexes; | 759 | int *indexes; |
533 | 760 | ||
@@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
543 | } | 770 | } |
544 | return NULL; | 771 | return NULL; |
545 | out: | 772 | out: |
546 | return *(struct thread **)(self->selection + 1); | 773 | return container_of(self->selection, struct hist_entry, ms); |
774 | } | ||
775 | |||
776 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | ||
777 | { | ||
778 | struct hist_entry *he = hist_browser__selected_entry(self); | ||
779 | return he ? he->thread : NULL; | ||
547 | } | 780 | } |
548 | 781 | ||
549 | static int hist_browser__title(char *bf, size_t size, const char *input_name, | 782 | static int hist_browser__title(char *bf, size_t size, const char *input_name, |
@@ -637,13 +870,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na | |||
637 | continue; | 870 | continue; |
638 | do_annotate: | 871 | do_annotate: |
639 | if (choice == annotate) { | 872 | if (choice == annotate) { |
873 | struct hist_entry *he; | ||
874 | |||
640 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { | 875 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { |
641 | ui_helpline__puts("No vmlinux file found, can't " | 876 | ui_helpline__puts("No vmlinux file found, can't " |
642 | "annotate with just a " | 877 | "annotate with just a " |
643 | "kallsyms file"); | 878 | "kallsyms file"); |
644 | continue; | 879 | continue; |
645 | } | 880 | } |
646 | map_symbol__annotate_browser(browser->selection, input_name); | 881 | |
882 | he = hist_browser__selected_entry(browser); | ||
883 | if (he == NULL) | ||
884 | continue; | ||
885 | |||
886 | hist_entry__annotate_browser(he); | ||
647 | } else if (choice == zoom_dso) { | 887 | } else if (choice == zoom_dso) { |
648 | if (dso_filter) { | 888 | if (dso_filter) { |
649 | ui_helpline__pop(); | 889 | ui_helpline__pop(); |
@@ -681,8 +921,23 @@ out: | |||
681 | return err; | 921 | return err; |
682 | } | 922 | } |
683 | 923 | ||
924 | static struct newtPercentTreeColors { | ||
925 | const char *topColorFg, *topColorBg; | ||
926 | const char *mediumColorFg, *mediumColorBg; | ||
927 | const char *normalColorFg, *normalColorBg; | ||
928 | const char *selColorFg, *selColorBg; | ||
929 | const char *codeColorFg, *codeColorBg; | ||
930 | } defaultPercentTreeColors = { | ||
931 | "red", "lightgray", | ||
932 | "green", "lightgray", | ||
933 | "black", "lightgray", | ||
934 | "lightgray", "magenta", | ||
935 | "blue", "lightgray", | ||
936 | }; | ||
937 | |||
684 | void setup_browser(void) | 938 | void setup_browser(void) |
685 | { | 939 | { |
940 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | ||
686 | if (!isatty(1)) | 941 | if (!isatty(1)) |
687 | return; | 942 | return; |
688 | 943 | ||
@@ -690,6 +945,11 @@ void setup_browser(void) | |||
690 | newtInit(); | 945 | newtInit(); |
691 | newtCls(); | 946 | newtCls(); |
692 | ui_helpline__puts(" "); | 947 | ui_helpline__puts(" "); |
948 | SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | ||
949 | SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | ||
950 | SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | ||
951 | SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | ||
952 | SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | ||
693 | } | 953 | } |
694 | 954 | ||
695 | void exit_browser(bool wait_for_ok) | 955 | void exit_browser(bool wait_for_ok) |