diff options
Diffstat (limited to 'tools/perf/ui/browsers')
-rw-r--r-- | tools/perf/ui/browsers/annotate.c | 45 | ||||
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 97 | ||||
-rw-r--r-- | tools/perf/ui/browsers/scripts.c | 189 |
3 files changed, 293 insertions, 38 deletions
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8f8cd2d73b3b..5dab3ca96980 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -188,6 +188,12 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) struct disasm_line *cursor = ab->selection, *target; struct browser_disasm_line *btarget, *bcursor; unsigned int from, to; + struct map_symbol *ms = ab->b.priv; + struct symbol *sym = ms->sym; + + /* PLT symbols contain external offsets */ + if (strstr(sym->name, "@plt")) + return; if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || !disasm_line__has_offset(cursor)) @@ -386,9 +392,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) browser->b.nr_entries = browser->nr_asm_entries; } -static bool annotate_browser__callq(struct annotate_browser *browser, - int evidx, void (*timer)(void *arg), - void *arg, int delay_secs) +static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, + struct hist_browser_timer *hbt) { struct map_symbol *ms = browser->b.priv; struct disasm_line *dl = browser->selection; @@ -418,7 +423,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, } pthread_mutex_unlock(¬es->lock); - symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs); + symbol__tui_annotate(target, ms->map, evidx, hbt); ui_browser__show_title(&browser->b, sym->name); return true; } @@ -602,13 +607,13 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser } static int annotate_browser__run(struct annotate_browser *browser, int evidx, - void(*timer)(void *arg), - void *arg, int delay_secs) + struct hist_browser_timer *hbt) { struct rb_node *nd = NULL; struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; const char *help = "Press 'h' for help on key bindings"; + int delay_secs = hbt ? hbt->refresh : 0; int key; if (ui_browser__show(&browser->b, sym->name, help) < 0) @@ -639,8 +644,8 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, switch (key) { case K_TIMER: - if (timer != NULL) - timer(arg); + if (hbt) + hbt->timer(hbt->arg); if (delay_secs != 0) symbol__annotate_decay_histogram(sym, evidx); @@ -676,8 +681,14 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, "o Toggle disassembler output/simplified view\n" "s Toggle source code view\n" "/ Search string\n" + "r Run available scripts\n" "? Search previous string\n"); continue; + case 'r': + { + script_browse(NULL); + continue; + } case 'H': nd = browser->curr_hot; break; @@ -734,7 +745,7 @@ show_help: goto show_sup_ins; goto out; } else if (!(annotate_browser__jump(browser) || - annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) { + annotate_browser__callq(browser, evidx, hbt))) { show_sup_ins: ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); } @@ -757,16 +768,21 @@ out: } int hist_entry__tui_annotate(struct hist_entry *he, int evidx, - void(*timer)(void *arg), void *arg, int delay_secs) + struct hist_browser_timer *hbt) { - return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, - timer, arg, delay_secs); + return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); } static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, size_t size) { u64 offset; + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; + + /* PLT symbols contain external offsets */ + if (strstr(sym->name, "@plt")) + return; for (offset = 0; offset < size; ++offset) { struct disasm_line *dl = browser->offsets[offset], *dlt; @@ -810,8 +826,7 @@ static inline int width_jumps(int n) } int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, - void(*timer)(void *arg), void *arg, - int delay_secs) + struct hist_browser_timer *hbt) { struct disasm_line *pos, *n; struct annotation *notes; @@ -893,7 +908,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, annotate_browser__update_addr_width(&browser); - ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); + ret = annotate_browser__run(&browser, evidx, hbt); list_for_each_entry_safe(pos, n, ¬es->src->source, node) { list_del(&pos->node); disasm_line__free(pos); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ef2f93ca7496..ccc4bd161420 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -11,6 +11,7 @@ #include "../../util/pstack.h" #include "../../util/sort.h" #include "../../util/util.h" +#include "../../arch/common.h" #include "../browser.h" #include "../helpline.h" @@ -310,10 +311,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) } static int hist_browser__run(struct hist_browser *browser, const char *ev_name, - void(*timer)(void *arg), void *arg, int delay_secs) + struct hist_browser_timer *hbt) { int key; char title[160]; + int delay_secs = hbt ? hbt->refresh : 0; browser->b.entries = &browser->hists->entries; browser->b.nr_entries = browser->hists->nr_entries; @@ -330,7 +332,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, switch (key) { case K_TIMER: - timer(arg); + hbt->timer(hbt->arg); ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); if (browser->hists->stats.nr_lost_warned != @@ -1127,11 +1129,17 @@ static inline void free_popup_options(char **options, int n) } } +/* Check whether the browser is for 'top' or 'report' */ +static inline bool is_report_browser(void *timer) +{ + return timer == NULL; +} + static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, const char *helpline, const char *ev_name, bool left_exits, - void(*timer)(void *arg), void *arg, - int delay_secs) + struct hist_browser_timer *hbt, + struct perf_session_env *env) { struct hists *hists = &evsel->hists; struct hist_browser *browser = hist_browser__new(hists); @@ -1141,6 +1149,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, int nr_options = 0; int key = -1; char buf[64]; + char script_opt[64]; + int delay_secs = hbt ? hbt->refresh : 0; if (browser == NULL) return -1; @@ -1159,10 +1169,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, int choice = 0, annotate = -2, zoom_dso = -2, zoom_thread = -2, annotate_f = -2, annotate_t = -2, browse_map = -2; + int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2; nr_options = 0; - key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); + key = hist_browser__run(browser, ev_name, hbt); if (browser->he_selection != NULL) { thread = hist_browser__selected_thread(browser); @@ -1211,6 +1222,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, hist_browser__reset(browser); } continue; + case 'r': + if (is_report_browser(hbt)) + goto do_scripts; + continue; case K_F1: case 'h': case '?': @@ -1229,6 +1244,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "E Expand all callchains\n" "d Zoom into current DSO\n" "t Zoom into current Thread\n" + "r Run available scripts('perf report' only)\n" "P Print histograms to perf.hist.N\n" "V Verbose (DSO names in callchains, etc)\n" "/ Filter symbol by name"); @@ -1317,6 +1333,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, browser->selection->map != NULL && asprintf(&options[nr_options], "Browse map details") > 0) browse_map = nr_options++; + + /* perf script support */ + if (browser->he_selection) { + struct symbol *sym; + + if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", + browser->he_selection->thread->comm) > 0) + scripts_comm = nr_options++; + + sym = browser->he_selection->ms.sym; + if (sym && sym->namelen && + asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", + sym->name) > 0) + scripts_symbol = nr_options++; + } + + if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) + scripts_all = nr_options++; + add_exit_option: options[nr_options++] = (char *)"Exit"; retry_popup_menu: @@ -1334,6 +1369,9 @@ retry_popup_menu: struct hist_entry *he; int err; do_annotate: + if (!objdump_path && perf_session_env__lookup_objdump(env)) + continue; + he = hist_browser__selected_entry(browser); if (he == NULL) continue; @@ -1356,8 +1394,7 @@ do_annotate: * Don't let this be freed, say, by hists__decay_entry. */ he->used = true; - err = hist_entry__tui_annotate(he, evsel->idx, - timer, arg, delay_secs); + err = hist_entry__tui_annotate(he, evsel->idx, hbt); he->used = false; /* * offer option to annotate the other branch source or target @@ -1411,6 +1448,20 @@ zoom_out_thread: hists__filter_by_thread(hists); hist_browser__reset(browser); } + /* perf scripts support */ + else if (choice == scripts_all || choice == scripts_comm || + choice == scripts_symbol) { +do_scripts: + memset(script_opt, 0, 64); + + if (choice == scripts_comm) + sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm); + + if (choice == scripts_symbol) + sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); + + script_browse(script_opt); + } } out_free_stack: pstack__delete(fstack); @@ -1424,6 +1475,7 @@ struct perf_evsel_menu { struct ui_browser b; struct perf_evsel *selection; bool lost_events, lost_events_warned; + struct perf_session_env *env; }; static void perf_evsel_menu__write(struct ui_browser *browser, @@ -1466,11 +1518,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser, static int perf_evsel_menu__run(struct perf_evsel_menu *menu, int nr_events, const char *help, - void(*timer)(void *arg), void *arg, int delay_secs) + struct hist_browser_timer *hbt) { struct perf_evlist *evlist = menu->b.priv; struct perf_evsel *pos; const char *ev_name, *title = "Available samples"; + int delay_secs = hbt ? hbt->refresh : 0; int key; if (ui_browser__show(&menu->b, title, @@ -1482,7 +1535,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, switch (key) { case K_TIMER: - timer(arg); + hbt->timer(hbt->arg); if (!menu->lost_events_warned && menu->lost_events) { ui_browser__warn_lost_events(&menu->b); @@ -1500,12 +1553,12 @@ browse_hists: * Give the calling tool a chance to populate the non * default evsel resorted hists tree. */ - if (timer) - timer(arg); + if (hbt) + hbt->timer(hbt->arg); ev_name = perf_evsel__name(pos); key = perf_evsel__hists_browse(pos, nr_events, help, - ev_name, true, timer, - arg, delay_secs); + ev_name, true, hbt, + menu->env); ui_browser__show_title(&menu->b, title); switch (key) { case K_TAB: @@ -1553,8 +1606,8 @@ out: static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, - void(*timer)(void *arg), void *arg, - int delay_secs) + struct hist_browser_timer *hbt, + struct perf_session_env *env) { struct perf_evsel *pos; struct perf_evsel_menu menu = { @@ -1566,6 +1619,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, .nr_entries = evlist->nr_entries, .priv = evlist, }, + .env = env, }; ui_helpline__push("Press ESC to exit"); @@ -1578,23 +1632,20 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, menu.b.width = line_len; } - return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, - arg, delay_secs); + return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt); } int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, - void(*timer)(void *arg), void *arg, - int delay_secs) + struct hist_browser_timer *hbt, + struct perf_session_env *env) { if (evlist->nr_entries == 1) { struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel, node); const char *ev_name = perf_evsel__name(first); return perf_evsel__hists_browse(first, evlist->nr_entries, help, - ev_name, false, timer, arg, - delay_secs); + ev_name, false, hbt, env); } - return __perf_evlist__tui_browse_hists(evlist, help, - timer, arg, delay_secs); + return __perf_evlist__tui_browse_hists(evlist, help, hbt, env); } diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c new file mode 100644 index 000000000000..cbbd44b0d93e --- /dev/null +++ b/tools/perf/ui/browsers/scripts.c @@ -0,0 +1,189 @@ +#include <elf.h> +#include <newt.h> +#include <inttypes.h> +#include <sys/ttydefaults.h> +#include <string.h> +#include "../../util/sort.h" +#include "../../util/util.h" +#include "../../util/hist.h" +#include "../../util/debug.h" +#include "../../util/symbol.h" +#include "../browser.h" +#include "../helpline.h" +#include "../libslang.h" + +/* 2048 lines should be enough for a script output */ +#define MAX_LINES 2048 + +/* 160 bytes for one output line */ +#define AVERAGE_LINE_LEN 160 + +struct script_line { + struct list_head node; + char line[AVERAGE_LINE_LEN]; +}; + +struct perf_script_browser { + struct ui_browser b; + struct list_head entries; + const char *script_name; + int nr_lines; +}; + +#define SCRIPT_NAMELEN 128 +#define SCRIPT_MAX_NO 64 +/* + * Usually the full path for a script is: + * /home/username/libexec/perf-core/scripts/python/xxx.py + * /home/username/libexec/perf-core/scripts/perl/xxx.pl + * So 256 should be long enough to contain the full path. + */ +#define SCRIPT_FULLPATH_LEN 256 + +/* + * When success, will copy the full path of the selected script + * into the buffer pointed by script_name, and return 0. + * Return -1 on failure. + */ +static int list_scripts(char *script_name) +{ + char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; + int i, num, choice, ret = -1; + + /* Preset the script name to SCRIPT_NAMELEN */ + buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); + if (!buf) + return ret; + + for (i = 0; i < SCRIPT_MAX_NO; i++) { + names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); + paths[i] = names[i] + SCRIPT_NAMELEN; + } + + num = find_scripts(names, paths); + if (num > 0) { + choice = ui__popup_menu(num, names); + if (choice < num && choice >= 0) { + strcpy(script_name, paths[choice]); + ret = 0; + } + } + + free(buf); + return ret; +} + +static void script_browser__write(struct ui_browser *browser, + void *entry, int row) +{ + struct script_line *sline = list_entry(entry, struct script_line, node); + bool current_entry = ui_browser__is_current_entry(browser, row); + + ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : + HE_COLORSET_NORMAL); + + slsmg_write_nstring(sline->line, browser->width); +} + +static int script_browser__run(struct perf_script_browser *self) +{ + int key; + + if (ui_browser__show(&self->b, self->script_name, + "Press <- or ESC to exit") < 0) + return -1; + + while (1) { + key = ui_browser__run(&self->b, 0); + + /* We can add some special key handling here if needed */ + break; + } + + ui_browser__hide(&self->b); + return key; +} + + +int script_browse(const char *script_opt) +{ + char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN]; + char *line = NULL; + size_t len = 0; + ssize_t retlen; + int ret = -1, nr_entries = 0; + FILE *fp; + void *buf; + struct script_line *sline; + + struct perf_script_browser script = { + .b = { + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = script_browser__write, + }, + .script_name = script_name, + }; + + INIT_LIST_HEAD(&script.entries); + + /* Save each line of the output in one struct script_line object. */ + buf = zalloc((sizeof(*sline)) * MAX_LINES); + if (!buf) + return -1; + sline = buf; + + memset(script_name, 0, SCRIPT_FULLPATH_LEN); + if (list_scripts(script_name)) + goto exit; + + sprintf(cmd, "perf script -s %s ", script_name); + + if (script_opt) + strcat(cmd, script_opt); + + if (input_name) { + strcat(cmd, " -i "); + strcat(cmd, input_name); + } + + strcat(cmd, " 2>&1"); + + fp = popen(cmd, "r"); + if (!fp) + goto exit; + + while ((retlen = getline(&line, &len, fp)) != -1) { + strncpy(sline->line, line, AVERAGE_LINE_LEN); + + /* If one output line is very large, just cut it short */ + if (retlen >= AVERAGE_LINE_LEN) { + sline->line[AVERAGE_LINE_LEN - 1] = '\0'; + sline->line[AVERAGE_LINE_LEN - 2] = '\n'; + } + list_add_tail(&sline->node, &script.entries); + + if (script.b.width < retlen) + script.b.width = retlen; + + if (nr_entries++ >= MAX_LINES - 1) + break; + sline++; + } + + if (script.b.width > AVERAGE_LINE_LEN) + script.b.width = AVERAGE_LINE_LEN; + + if (line) + free(line); + pclose(fp); + + script.nr_lines = nr_entries; + script.b.nr_entries = nr_entries; + script.b.entries = &script.entries; + + ret = script_browser__run(&script); +exit: + free(buf); + return ret; +} |