summaryrefslogtreecommitdiff
path: root/tools/perf/ui/browsers
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/ui/browsers')
-rw-r--r--tools/perf/ui/browsers/annotate.c78
-rw-r--r--tools/perf/ui/browsers/hists.c432
-rw-r--r--tools/perf/ui/browsers/scripts.c189
3 files changed, 605 insertions, 94 deletions
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8f8cd2d73b3b..7dca1555c610 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -182,15 +182,30 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
ab->selection = dl;
}
+static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
+{
+ if (!dl || !dl->ins || !ins__is_jump(dl->ins)
+ || !disasm_line__has_offset(dl)
+ || dl->ops.target.offset >= symbol__size(sym))
+ return false;
+
+ return true;
+}
+
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
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;
- if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
- !disasm_line__has_offset(cursor))
+ /* PLT symbols contain external offsets */
+ if (strstr(sym->name, "@plt"))
+ return;
+
+ if (!disasm_line__is_valid_jump(cursor, sym))
return;
target = ab->offsets[cursor->ops.target.offset];
@@ -386,9 +401,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 +432,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
}
pthread_mutex_unlock(&notes->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 +616,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 +653,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 +690,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 +754,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,32 +777,29 @@ 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;
struct browser_disasm_line *bdlt;
- if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
- !disasm_line__has_offset(dl))
+ if (!disasm_line__is_valid_jump(dl, sym))
continue;
- if (dl->ops.target.offset >= size) {
- ui__error("jump to after symbol!\n"
- "size: %zx, jump target: %" PRIx64,
- size, dl->ops.target.offset);
- continue;
- }
-
dlt = browser->offsets[dl->ops.target.offset];
/*
* FIXME: Oops, no jump target? Buggy disassembler? Or do we
@@ -810,8 +827,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 +909,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, &notes->src->source, node) {
list_del(&pos->node);
disasm_line__free(pos);
@@ -906,11 +922,11 @@ out_free_offsets:
#define ANNOTATE_CFG(n) \
{ .name = #n, .value = &annotate_browser__opts.n, }
-
+
/*
* Keep the entries sorted, they are bsearch'ed
*/
-static struct annotate__config {
+static struct annotate_config {
const char *name;
bool *value;
} annotate__configs[] = {
@@ -924,7 +940,7 @@ static struct annotate__config {
static int annotate_config__cmp(const void *name, const void *cfgp)
{
- const struct annotate__config *cfg = cfgp;
+ const struct annotate_config *cfg = cfgp;
return strcmp(name, cfg->name);
}
@@ -932,7 +948,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp)
static int annotate__config(const char *var, const char *value,
void *data __maybe_unused)
{
- struct annotate__config *cfg;
+ struct annotate_config *cfg;
const char *name;
if (prefixcmp(var, "annotate.") != 0)
@@ -940,7 +956,7 @@ static int annotate__config(const char *var, const char *value,
name = var + 9;
cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
- sizeof(struct annotate__config), annotate_config__cmp);
+ sizeof(struct annotate_config), annotate_config__cmp);
if (cfg == NULL)
return -1;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index ef2f93ca7496..aa22704047d6 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 !=
@@ -565,26 +567,128 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
return row - first_row;
}
-#define HPP__COLOR_FN(_name, _field) \
-static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \
- struct hist_entry *he) \
+struct hpp_arg {
+ struct ui_browser *b;
+ char folded_sign;
+ bool current_entry;
+};
+
+static int __hpp__color_callchain(struct hpp_arg *arg)
+{
+ if (!symbol_conf.use_callchain)
+ return 0;
+
+ slsmg_printf("%c ", arg->folded_sign);
+ return 2;
+}
+
+static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
+ u64 (*get_field)(struct hist_entry *),
+ int (*callchain_cb)(struct hpp_arg *))
+{
+ int ret = 0;
+ double percent = 0.0;
+ struct hists *hists = he->hists;
+ struct hpp_arg *arg = hpp->ptr;
+
+ if (hists->stats.total_period)
+ percent = 100.0 * get_field(he) / hists->stats.total_period;
+
+ ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
+
+ if (callchain_cb)
+ ret += callchain_cb(arg);
+
+ ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
+ slsmg_printf("%s", hpp->buf);
+
+ if (symbol_conf.event_group) {
+ int prev_idx, idx_delta;
+ struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct hist_entry *pair;
+ int nr_members = evsel->nr_members;
+
+ if (nr_members <= 1)
+ goto out;
+
+ prev_idx = perf_evsel__group_idx(evsel);
+
+ list_for_each_entry(pair, &he->pairs.head, pairs.node) {
+ u64 period = get_field(pair);
+ u64 total = pair->hists->stats.total_period;
+
+ if (!total)
+ continue;
+
+ evsel = hists_to_evsel(pair->hists);
+ idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
+
+ while (idx_delta--) {
+ /*
+ * zero-fill group members in the middle which
+ * have no sample
+ */
+ ui_browser__set_percent_color(arg->b, 0.0,
+ arg->current_entry);
+ ret += scnprintf(hpp->buf, hpp->size,
+ " %6.2f%%", 0.0);
+ slsmg_printf("%s", hpp->buf);
+ }
+
+ percent = 100.0 * period / total;
+ ui_browser__set_percent_color(arg->b, percent,
+ arg->current_entry);
+ ret += scnprintf(hpp->buf, hpp->size,
+ " %6.2f%%", percent);
+ slsmg_printf("%s", hpp->buf);
+
+ prev_idx = perf_evsel__group_idx(evsel);
+ }
+
+ idx_delta = nr_members - prev_idx - 1;
+
+ while (idx_delta--) {
+ /*
+ * zero-fill group members at last which have no sample
+ */
+ ui_browser__set_percent_color(arg->b, 0.0,
+ arg->current_entry);
+ ret += scnprintf(hpp->buf, hpp->size,
+ " %6.2f%%", 0.0);
+ slsmg_printf("%s", hpp->buf);
+ }
+ }
+out:
+ if (!arg->current_entry || !arg->b->navkeypressed)
+ ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
+
+ return ret;
+}
+
+#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
+static u64 __hpp_get_##_field(struct hist_entry *he) \
{ \
- struct hists *hists = he->hists; \
- double percent = 100.0 * he->stat._field / hists->stats.total_period; \
- *(double *)hpp->ptr = percent; \
- return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
+ return he->stat._field; \
+} \
+ \
+static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
+ struct hist_entry *he) \
+{ \
+ return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
}
-HPP__COLOR_FN(overhead, period)
-HPP__COLOR_FN(overhead_sys, period_sys)
-HPP__COLOR_FN(overhead_us, period_us)
-HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
-HPP__COLOR_FN(overhead_guest_us, period_guest_us)
+__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
+__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
+__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
+__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
+__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
-#undef HPP__COLOR_FN
+#undef __HPP_COLOR_PERCENT_FN
void hist_browser__init_hpp(void)
{
+ perf_hpp__column_enable(PERF_HPP__OVERHEAD);
+
perf_hpp__init();
perf_hpp__format[PERF_HPP__OVERHEAD].color =
@@ -604,13 +708,13 @@ static int hist_browser__show_entry(struct hist_browser *browser,
unsigned short row)
{
char s[256];
- double percent;
- int i, printed = 0;
+ int printed = 0;
int width = browser->b.width;
char folded_sign = ' ';
bool current_entry = ui_browser__is_current_entry(&browser->b, row);
off_t row_offset = entry->row_offset;
bool first = true;
+ struct perf_hpp_fmt *fmt;
if (current_entry) {
browser->he_selection = entry;
@@ -623,41 +727,30 @@ static int hist_browser__show_entry(struct hist_browser *browser,
}
if (row_offset == 0) {
+ struct hpp_arg arg = {
+ .b = &browser->b,
+ .folded_sign = folded_sign,
+ .current_entry = current_entry,
+ };
struct perf_hpp hpp = {
.buf = s,
.size = sizeof(s),
+ .ptr = &arg,
};
ui_browser__gotorc(&browser->b, row, 0);
- for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
- if (!perf_hpp__format[i].cond)
- continue;
-
+ perf_hpp__for_each_format(fmt) {
if (!first) {
slsmg_printf(" ");
width -= 2;
}
first = false;
- if (perf_hpp__format[i].color) {
- hpp.ptr = &percent;
- /* It will set percent for us. See HPP__COLOR_FN above. */
- width -= perf_hpp__format[i].color(&hpp, entry);
-
- ui_browser__set_percent_color(&browser->b, percent, current_entry);
-
- if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) {
- slsmg_printf("%c ", folded_sign);
- width -= 2;
- }
-
- slsmg_printf("%s", s);
-
- if (!current_entry || !browser->b.navkeypressed)
- ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
+ if (fmt->color) {
+ width -= fmt->color(&hpp, entry);
} else {
- width -= perf_hpp__format[i].entry(&hpp, entry);
+ width -= fmt->entry(&hpp, entry);
slsmg_printf("%s", s);
}
}
@@ -1096,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
const struct thread *thread = hists->thread_filter;
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
u64 nr_events = hists->stats.total_period;
+ struct perf_evsel *evsel = hists_to_evsel(hists);
+ char buf[512];
+ size_t buflen = sizeof(buf);
+
+ if (symbol_conf.event_group && evsel->nr_members > 1) {
+ struct perf_evsel *pos;
+
+ perf_evsel__group_desc(evsel, buf, buflen);
+ ev_name = buf;
+
+ for_each_group_member(pos, evsel) {
+ nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+ nr_events += pos->hists.stats.total_period;
+ }
+ }
nr_samples = convert_unit(nr_samples, &unit);
printed = scnprintf(bf, size,
@@ -1127,11 +1235,107 @@ 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;
+}
+
+/*
+ * Only runtime switching of perf data file will make "input_name" point
+ * to a malloced buffer. So add "is_input_name_malloced" flag to decide
+ * whether we need to call free() for current "input_name" during the switch.
+ */
+static bool is_input_name_malloced = false;
+
+static int switch_data_file(void)
+{
+ char *pwd, *options[32], *abs_path[32], *tmp;
+ DIR *pwd_dir;
+ int nr_options = 0, choice = -1, ret = -1;
+ struct dirent *dent;
+
+ pwd = getenv("PWD");
+ if (!pwd)
+ return ret;
+
+ pwd_dir = opendir(pwd);
+ if (!pwd_dir)
+ return ret;
+
+ memset(options, 0, sizeof(options));
+ memset(options, 0, sizeof(abs_path));
+
+ while ((dent = readdir(pwd_dir))) {
+ char path[PATH_MAX];
+ u64 magic;
+ char *name = dent->d_name;
+ FILE *file;
+
+ if (!(dent->d_type == DT_REG))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", pwd, name);
+
+ file = fopen(path, "r");
+ if (!file)
+ continue;
+
+ if (fread(&magic, 1, 8, file) < 8)
+ goto close_file_and_continue;
+
+ if (is_perf_magic(magic)) {
+ options[nr_options] = strdup(name);
+ if (!options[nr_options])
+ goto close_file_and_continue;
+
+ abs_path[nr_options] = strdup(path);
+ if (!abs_path[nr_options]) {
+ free(options[nr_options]);
+ ui__warning("Can't search all data files due to memory shortage.\n");
+ fclose(file);
+ break;
+ }
+
+ nr_options++;
+ }
+
+close_file_and_continue:
+ fclose(file);
+ if (nr_options >= 32) {
+ ui__warning("Too many perf data files in PWD!\n"
+ "Only the first 32 files will be listed.\n");
+ break;
+ }
+ }
+ closedir(pwd_dir);
+
+ if (nr_options) {
+ choice = ui__popup_menu(nr_options, options);
+ if (choice < nr_options && choice >= 0) {
+ tmp = strdup(abs_path[choice]);
+ if (tmp) {
+ if (is_input_name_malloced)
+ free((void *)input_name);
+ input_name = tmp;
+ is_input_name_malloced = true;
+ ret = 0;
+ } else
+ ui__warning("Data switch failed due to memory shortage!\n");
+ }
+ }
+
+ free_popup_options(options, nr_options);
+ free_popup_options(abs_path, nr_options);
+ return ret;
+}
+
+
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 +1345,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 +1365,12 @@ 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, switch_data = -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 +1419,14 @@ 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 's':
+ if (is_report_browser(hbt))
+ goto do_data_switch;
+ continue;
case K_F1:
case 'h':
case '?':
@@ -1229,6 +1445,8 @@ 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"
+ "s Switch to another data file in PWD ('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 +1535,28 @@ 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++;
+
+ if (is_report_browser(hbt) && asprintf(&options[nr_options],
+ "Switch to another data file in PWD") > 0)
+ switch_data = nr_options++;
add_exit_option:
options[nr_options++] = (char *)"Exit";
retry_popup_menu:
@@ -1334,6 +1574,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 +1599,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 +1653,30 @@ 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);
+ }
+ /* Switch to another data file */
+ else if (choice == switch_data) {
+do_data_switch:
+ if (!switch_data_file()) {
+ key = K_SWITCH_INPUT_DATA;
+ break;
+ } else
+ ui__warning("Won't switch the data files due to\n"
+ "no valid data file get selected!\n");
+ }
}
out_free_stack:
pstack__delete(fstack);
@@ -1424,6 +1690,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,
@@ -1442,6 +1709,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL);
+ if (symbol_conf.event_group && evsel->nr_members > 1) {
+ struct perf_evsel *pos;
+
+ ev_name = perf_evsel__group_name(evsel);
+
+ for_each_group_member(pos, evsel) {
+ nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+ }
+ }
+
nr_events = convert_unit(nr_events, &unit);
printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
unit, unit == ' ' ? "" : " ", ev_name);
@@ -1466,11 +1743,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 +1760,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 +1778,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:
@@ -1525,6 +1803,7 @@ browse_hists:
"Do you really want to exit?"))
continue;
/* Fall thru */
+ case K_SWITCH_INPUT_DATA:
case 'q':
case CTRL('c'):
goto out;
@@ -1551,10 +1830,21 @@ out:
return key;
}
+static bool filter_group_entries(struct ui_browser *self __maybe_unused,
+ void *entry)
+{
+ struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+
+ if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
+ return true;
+
+ return false;
+}
+
static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
- const char *help,
- void(*timer)(void *arg), void *arg,
- int delay_secs)
+ int nr_entries, const char *help,
+ struct hist_browser_timer *hbt,
+ struct perf_session_env *env)
{
struct perf_evsel *pos;
struct perf_evsel_menu menu = {
@@ -1563,9 +1853,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
.refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek,
.write = perf_evsel_menu__write,
- .nr_entries = evlist->nr_entries,
+ .filter = filter_group_entries,
+ .nr_entries = nr_entries,
.priv = evlist,
},
+ .env = env,
};
ui_helpline__push("Press ESC to exit");
@@ -1578,23 +1870,37 @@ 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, 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) {
+ int nr_entries = evlist->nr_entries;
+
+single_entry:
+ if (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);
+
+ return perf_evsel__hists_browse(first, nr_entries, help,
+ ev_name, false, hbt, env);
+ }
+
+ if (symbol_conf.event_group) {
+ struct perf_evsel *pos;
+
+ nr_entries = 0;
+ list_for_each_entry(pos, &evlist->entries, node)
+ if (perf_evsel__is_group_leader(pos))
+ nr_entries++;
+
+ if (nr_entries == 1)
+ goto single_entry;
}
- return __perf_evlist__tui_browse_hists(evlist, help,
- timer, arg, delay_secs);
+ return __perf_evlist__tui_browse_hists(evlist, nr_entries, 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;
+}