Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf improvements from Arnaldo Carvalho de Melo:

 * Replace event_name with perf_evsel__name, that handles the event
   modifiers and doesn't use static variables.

 * GTK browser improvements, from Namhyung Kim

 * Fix possible NULL pointer deref in the TUI annotate browser, from
   Samuel Liao

 * Add sort by source file:line number, using addr2line.

 * Allow printing histogram text snapshots at any point in top/report.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2012-06-20 13:41:42 +02:00
commit 32c46e579b
39 changed files with 1128 additions and 524 deletions

View file

@ -57,7 +57,7 @@ OPTIONS
-s::
--sort=::
Sort by key(s): pid, comm, dso, symbol, parent.
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
-p::
--parent=<regex>::

View file

@ -112,7 +112,7 @@ Default is to monitor all CPUS.
-s::
--sort::
Sort by key(s): pid, comm, dso, symbol, parent
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
-n::
--show-nr-samples::

View file

@ -503,6 +503,7 @@ else
LIB_OBJS += $(OUTPUT)ui/progress.o
LIB_OBJS += $(OUTPUT)ui/util.o
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
LIB_OBJS += $(OUTPUT)ui/tui/util.o
LIB_H += ui/browser.h
LIB_H += ui/browsers/map.h
LIB_H += ui/helpline.h
@ -522,13 +523,18 @@ else
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
else
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y)
BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR
endif
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
# Make sure that it'd be included only once.
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/util.o
endif
endif
endif

View file

@ -60,7 +60,7 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail
list_for_each_entry(pos, &session->evlist->entries, node) {
bool first = true;
printf("%s", event_name(pos));
printf("%s", perf_evsel__name(pos));
if (details->verbose || details->freq) {
comma_printf(&first, " sample_freq=%" PRIu64,

View file

@ -265,7 +265,7 @@ try_again:
if (err == ENOENT) {
ui__error("The %s event is not supported.\n",
event_name(pos));
perf_evsel__name(pos));
exit(EXIT_FAILURE);
}
@ -916,7 +916,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
usage_with_options(record_usage, record_options);
list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_header__push_event(pos->attr.config, event_name(pos)))
if (perf_header__push_event(pos->attr.config, perf_evsel__name(pos)))
goto out_free_fd;
}

View file

@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain)
&& sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread,
err = machine__resolve_callchain(machine, al->thread,
sample->callchain, &parent);
if (err)
return err;
@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
struct hist_entry *he;
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread,
err = machine__resolve_callchain(machine, al->thread,
sample->callchain, &parent);
if (err)
return err;
@ -230,7 +230,7 @@ static int process_read_event(struct perf_tool *tool,
struct perf_report *rep = container_of(tool, struct perf_report, tool);
if (rep->show_threads) {
const char *name = evsel ? event_name(evsel) : "unknown";
const char *name = evsel ? perf_evsel__name(evsel) : "unknown";
perf_read_values_add_value(&rep->show_threads_values,
event->read.pid, event->read.tid,
event->read.id,
@ -239,7 +239,7 @@ static int process_read_event(struct perf_tool *tool,
}
dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
evsel ? event_name(evsel) : "FAIL",
evsel ? perf_evsel__name(evsel) : "FAIL",
event->read.value);
return 0;
@ -314,7 +314,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists;
const char *evname = event_name(pos);
const char *evname = perf_evsel__name(pos);
hists__fprintf_nr_sample_events(hists, evname, stdout);
hists__fprintf(hists, NULL, false, true, 0, 0, stdout);

View file

@ -1601,7 +1601,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
if (thread == NULL) {
pr_debug("problem processing %s event, skipping it.\n",
evsel->name);
perf_evsel__name(evsel));
return -1;
}

View file

@ -137,10 +137,11 @@ static const char *output_field2str(enum perf_output_field field)
#define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x)
static int perf_event_attr__check_stype(struct perf_event_attr *attr,
u64 sample_type, const char *sample_msg,
enum perf_output_field field)
static int perf_evsel__check_stype(struct perf_evsel *evsel,
u64 sample_type, const char *sample_msg,
enum perf_output_field field)
{
struct perf_event_attr *attr = &evsel->attr;
int type = attr->type;
const char *evname;
@ -148,7 +149,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
return 0;
if (output[type].user_set) {
evname = __event_name(attr->type, attr->config);
evname = perf_evsel__name(evsel);
pr_err("Samples for '%s' event do not have %s attribute set. "
"Cannot print '%s' field.\n",
evname, sample_msg, output_field2str(field));
@ -157,7 +158,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
/* user did not ask for it explicitly so remove from the default list */
output[type].fields &= ~field;
evname = __event_name(attr->type, attr->config);
evname = perf_evsel__name(evsel);
pr_debug("Samples for '%s' event do not have %s attribute set. "
"Skipping '%s' field.\n",
evname, sample_msg, output_field2str(field));
@ -175,8 +176,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
return -EINVAL;
if (PRINT_FIELD(IP)) {
if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP",
PERF_OUTPUT_IP))
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
PERF_OUTPUT_IP))
return -EINVAL;
if (!no_callchain &&
@ -185,8 +186,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
}
if (PRINT_FIELD(ADDR) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR",
PERF_OUTPUT_ADDR))
perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
PERF_OUTPUT_ADDR))
return -EINVAL;
if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
@ -208,18 +209,18 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
}
if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID",
PERF_OUTPUT_TID|PERF_OUTPUT_PID))
perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID",
PERF_OUTPUT_TID|PERF_OUTPUT_PID))
return -EINVAL;
if (PRINT_FIELD(TIME) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME",
PERF_OUTPUT_TIME))
perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME",
PERF_OUTPUT_TIME))
return -EINVAL;
if (PRINT_FIELD(CPU) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU",
PERF_OUTPUT_CPU))
perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
PERF_OUTPUT_CPU))
return -EINVAL;
return 0;
@ -258,9 +259,10 @@ static int perf_session__check_output_opt(struct perf_session *session)
static void print_sample_start(struct perf_sample *sample,
struct thread *thread,
struct perf_event_attr *attr)
struct perf_evsel *evsel)
{
int type;
struct perf_event_attr *attr = &evsel->attr;
struct event_format *event;
const char *evname = NULL;
unsigned long secs;
@ -305,7 +307,7 @@ static void print_sample_start(struct perf_sample *sample,
if (event)
evname = event->name;
} else
evname = __event_name(attr->type, attr->config);
evname = perf_evsel__name(evsel);
printf("%s: ", evname ? evname : "[unknown]");
}
@ -387,7 +389,7 @@ static void print_sample_bts(union perf_event *event,
printf(" ");
else
printf("\n");
perf_event__print_ip(event, sample, machine, evsel,
perf_event__print_ip(event, sample, machine,
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET));
}
@ -412,7 +414,7 @@ static void process_event(union perf_event *event __unused,
if (output[attr->type].fields == 0)
return;
print_sample_start(sample, thread, attr);
print_sample_start(sample, thread, evsel);
if (is_bts_event(attr)) {
print_sample_bts(event, sample, evsel, machine, thread);
@ -431,7 +433,7 @@ static void process_event(union perf_event *event __unused,
printf(" ");
else
printf("\n");
perf_event__print_ip(event, sample, machine, evsel,
perf_event__print_ip(event, sample, machine,
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET));
}

View file

@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
if (verbose) {
fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
event_name(counter), count[0], count[1], count[2]);
perf_evsel__name(counter), count[0], count[1], count[2]);
}
/*
@ -496,7 +496,7 @@ static int run_perf_stat(int argc __used, const char **argv)
errno == ENXIO) {
if (verbose)
ui__warning("%s event is not supported by the kernel.\n",
event_name(counter));
perf_evsel__name(counter));
counter->supported = false;
continue;
}
@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
csv_output ? 0 : -4,
evsel_list->cpus->map[cpu], csv_sep);
fprintf(output, fmt, cpustr, msecs, csv_sep, event_name(evsel));
fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel));
if (evsel->cgrp)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@ -792,7 +792,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
else
cpu = 0;
fprintf(output, fmt, cpustr, avg, csv_sep, event_name(evsel));
fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel));
if (evsel->cgrp)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@ -908,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter)
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep,
csv_output ? 0 : -24,
event_name(counter));
perf_evsel__name(counter));
if (counter->cgrp)
fprintf(output, "%s%s", csv_sep, counter->cgrp->name);
@ -961,7 +961,7 @@ static void print_counter(struct perf_evsel *counter)
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep,
csv_output ? 0 : -24,
event_name(counter));
perf_evsel__name(counter));
if (counter->cgrp)
fprintf(output, "%s%s",

View file

@ -583,7 +583,7 @@ static int test__basic_mmap(void)
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
pr_debug("expected %d %s events, got %d\n",
expected_nr_events[evsel->idx],
event_name(evsel), nr_events[evsel->idx]);
perf_evsel__name(evsel), nr_events[evsel->idx]);
goto out_munmap;
}
}

View file

@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top)
if (notes->src == NULL)
goto out_unlock;
printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name);
printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);
printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx,
@ -408,7 +408,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries);
if (top->evlist->nr_entries > 1)
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top->sym_evsel));
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", perf_evsel__name(top->sym_evsel));
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter);
@ -503,13 +503,13 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
fprintf(stderr, "\nAvailable events:");
list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel));
fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel));
prompt_integer(&counter, "Enter details event counter");
if (counter >= top->evlist->nr_entries) {
top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel));
fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));
sleep(1);
break;
}
@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al.thread,
err = machine__resolve_callchain(machine, al.thread,
sample->callchain, &parent);
if (err)
return;
@ -960,7 +960,7 @@ try_again:
if (err == ENOENT) {
ui__error("The %s event is not supported.\n",
event_name(counter));
perf_evsel__name(counter));
goto out_err;
} else if (err == EMFILE) {
ui__error("Too many events are opened.\n"

View file

@ -78,6 +78,19 @@ int main(int argc, char *argv[])
return 0;
}
endef
define SOURCE_GTK2_INFOBAR
#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
#include <gtk/gtk.h>
#pragma GCC diagnostic error \"-Wstrict-prototypes\"
int main(void)
{
gtk_info_bar_new();
return 0;
}
endef
endif
ifndef NO_LIBPERL

View file

@ -814,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
{
struct disasm_line *pos, *n;
struct annotation *notes;
const size_t size = symbol__size(sym);
size_t size;
struct map_symbol ms = {
.map = map,
.sym = sym,
@ -834,6 +834,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
if (sym == NULL)
return -1;
size = symbol__size(sym);
if (map->dso->annotate_warned)
return -1;

View file

@ -23,6 +23,7 @@ struct hist_browser {
struct hists *hists;
struct hist_entry *he_selection;
struct map_symbol *selection;
int print_seq;
bool has_symbols;
};
@ -800,6 +801,196 @@ do_offset:
}
}
static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
struct callchain_node *chain_node,
u64 total, int level,
FILE *fp)
{
struct rb_node *node;
int offset = level * LEVEL_OFFSET_STEP;
u64 new_total, remaining;
int printed = 0;
if (callchain_param.mode == CHAIN_GRAPH_REL)
new_total = chain_node->children_hit;
else
new_total = total;
remaining = new_total;
node = rb_first(&chain_node->rb_root);
while (node) {
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
struct rb_node *next = rb_next(node);
u64 cumul = callchain_cumul_hits(child);
struct callchain_list *chain;
char folded_sign = ' ';
int first = true;
int extra_offset = 0;
remaining -= cumul;
list_for_each_entry(chain, &child->val, list) {
char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
const char *str;
bool was_first = first;
if (first)
first = false;
else
extra_offset = LEVEL_OFFSET_STEP;
folded_sign = callchain_list__folded(chain);
alloc_str = NULL;
str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
if (was_first) {
double percent = cumul * 100.0 / new_total;
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
str = "Not enough memory!";
else
str = alloc_str;
}
printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
free(alloc_str);
if (folded_sign == '+')
break;
}
if (folded_sign == '-') {
const int new_level = level + (extra_offset ? 2 : 1);
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
new_level, fp);
}
node = next;
}
return printed;
}
static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
struct callchain_node *node,
int level, FILE *fp)
{
struct callchain_list *chain;
int offset = level * LEVEL_OFFSET_STEP;
char folded_sign = ' ';
int printed = 0;
list_for_each_entry(chain, &node->val, list) {
char ipstr[BITS_PER_LONG / 4 + 1], *s;
folded_sign = callchain_list__folded(chain);
s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
}
if (folded_sign == '-')
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
browser->hists->stats.total_period,
level + 1, fp);
return printed;
}
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
struct rb_root *chain, int level, FILE *fp)
{
struct rb_node *nd;
int printed = 0;
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
}
return printed;
}
static int hist_browser__fprintf_entry(struct hist_browser *browser,
struct hist_entry *he, FILE *fp)
{
char s[8192];
double percent;
int printed = 0;
char folded_sign = ' ';
if (symbol_conf.use_callchain)
folded_sign = hist_entry__folded(he);
hist_entry__snprintf(he, s, sizeof(s), browser->hists);
percent = (he->period * 100.0) / browser->hists->stats.total_period;
if (symbol_conf.use_callchain)
printed += fprintf(fp, "%c ", folded_sign);
printed += fprintf(fp, " %5.2f%%", percent);
if (symbol_conf.show_nr_samples)
printed += fprintf(fp, " %11u", he->nr_events);
if (symbol_conf.show_total_period)
printed += fprintf(fp, " %12" PRIu64, he->period);
printed += fprintf(fp, "%s\n", rtrim(s));
if (folded_sign == '-')
printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
return printed;
}
static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
{
struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
int printed = 0;
while (nd) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
printed += hist_browser__fprintf_entry(browser, h, fp);
nd = hists__filter_entries(rb_next(nd));
}
return printed;
}
static int hist_browser__dump(struct hist_browser *browser)
{
char filename[64];
FILE *fp;
while (1) {
scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
if (access(filename, F_OK))
break;
/*
* XXX: Just an arbitrary lazy upper limit
*/
if (++browser->print_seq == 8192) {
ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
return -1;
}
}
fp = fopen(filename, "w");
if (fp == NULL) {
char bf[64];
strerror_r(errno, bf, sizeof(bf));
ui_helpline__fpush("Couldn't write to %s: %s", filename, bf);
return -1;
}
++browser->print_seq;
hist_browser__fprintf(browser, fp);
fclose(fp);
ui_helpline__fpush("%s written!", filename);
return 0;
}
static struct hist_browser *hist_browser__new(struct hists *hists)
{
struct hist_browser *browser = zalloc(sizeof(*browser));
@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
browser->selection->map->dso->annotate_warned)
continue;
goto do_annotate;
case 'P':
hist_browser__dump(browser);
continue;
case 'd':
goto zoom_dso;
case 't':
@ -969,6 +1163,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"
"P Print histograms to perf.hist.N\n"
"/ Filter symbol by name");
continue;
case K_ENTER:
@ -1172,7 +1367,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
bool current_entry = ui_browser__is_current_entry(browser, row);
unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
const char *ev_name = event_name(evsel);
const char *ev_name = perf_evsel__name(evsel);
char bf[256], unit;
const char *warn = " ";
size_t printed;
@ -1240,7 +1435,7 @@ browse_hists:
*/
if (timer)
timer(arg);
ev_name = event_name(pos);
ev_name = perf_evsel__name(pos);
key = perf_evsel__hists_browse(pos, nr_events, help,
ev_name, true, timer,
arg, delay_secs);
@ -1309,17 +1504,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
ui_helpline__push("Press ESC to exit");
list_for_each_entry(pos, &evlist->entries, node) {
const char *ev_name = event_name(pos);
const char *ev_name = perf_evsel__name(pos);
size_t line_len = strlen(ev_name) + 7;
if (menu.b.width < line_len)
menu.b.width = line_len;
/*
* Cache the evsel name, tracepoints have a _high_ cost per
* event_name() call.
*/
if (pos->name == NULL)
pos->name = strdup(ev_name);
}
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
@ -1330,11 +1519,10 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
void(*timer)(void *arg), void *arg,
int delay_secs)
{
if (evlist->nr_entries == 1) {
struct perf_evsel *first = list_entry(evlist->entries.next,
struct perf_evsel, node);
const char *ev_name = event_name(first);
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);

View file

@ -11,8 +11,8 @@
static void perf_gtk__signal(int sig)
{
perf_gtk__exit(false);
psignal(sig, "perf");
gtk_main_quit();
}
static void perf_gtk__resize_window(GtkWidget *window)
@ -122,13 +122,59 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
gtk_container_add(GTK_CONTAINER(window), view);
}
#ifdef HAVE_GTK_INFO_BAR
static GtkWidget *perf_gtk__setup_info_bar(void)
{
GtkWidget *info_bar;
GtkWidget *label;
GtkWidget *content_area;
info_bar = gtk_info_bar_new();
gtk_widget_set_no_show_all(info_bar, TRUE);
label = gtk_label_new("");
gtk_widget_show(label);
content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar));
gtk_container_add(GTK_CONTAINER(content_area), label);
gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK,
GTK_RESPONSE_OK);
g_signal_connect(info_bar, "response",
G_CALLBACK(gtk_widget_hide), NULL);
pgctx->info_bar = info_bar;
pgctx->message_label = label;
return info_bar;
}
#endif
static GtkWidget *perf_gtk__setup_statusbar(void)
{
GtkWidget *stbar;
unsigned ctxid;
stbar = gtk_statusbar_new();
ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(stbar),
"perf report");
pgctx->statbar = stbar;
pgctx->statbar_ctx_id = ctxid;
return stbar;
}
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help __used,
void (*timer) (void *arg)__used,
void *arg __used, int delay_secs __used)
{
struct perf_evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook;
GtkWidget *info_bar;
GtkWidget *statbar;
GtkWidget *window;
signal(SIGSEGV, perf_gtk__signal);
@ -143,11 +189,17 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new();
list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists;
const char *evname = event_name(pos);
const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window;
GtkWidget *tab_label;
@ -164,7 +216,16 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
}
gtk_container_add(GTK_CONTAINER(window), notebook);
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
info_bar = perf_gtk__setup_info_bar();
if (info_bar)
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(window);
@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0;
}

View file

@ -1,8 +1,39 @@
#ifndef _PERF_GTK_H_
#define _PERF_GTK_H_ 1
#include <stdbool.h>
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <gtk/gtk.h>
#pragma GCC diagnostic error "-Wstrict-prototypes"
struct perf_gtk_context {
GtkWidget *main_window;
#ifdef HAVE_GTK_INFO_BAR
GtkWidget *info_bar;
GtkWidget *message_label;
#endif
GtkWidget *statbar;
guint statbar_ctx_id;
};
extern struct perf_gtk_context *pgctx;
static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx)
{
return ctx && ctx->main_window;
}
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
#ifndef HAVE_GTK_INFO_BAR
static inline GtkWidget *perf_gtk__setup_info_bar(void)
{
return NULL;
}
#endif
#endif /* _PERF_GTK_H_ */

View file

@ -1,12 +1,17 @@
#include "gtk.h"
#include "../../util/cache.h"
#include "../../util/debug.h"
extern struct perf_error_ops perf_gtk_eops;
int perf_gtk__init(void)
{
perf_error__register(&perf_gtk_eops);
return gtk_init_check(NULL, NULL) ? 0 : -1;
}
void perf_gtk__exit(bool wait_for_ok __used)
{
perf_error__unregister(&perf_gtk_eops);
gtk_main_quit();
}

129
tools/perf/ui/gtk/util.c Normal file
View file

@ -0,0 +1,129 @@
#include "../util.h"
#include "../../util/debug.h"
#include "gtk.h"
#include <string.h>
struct perf_gtk_context *pgctx;
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window)
{
struct perf_gtk_context *ctx;
ctx = malloc(sizeof(*pgctx));
if (ctx)
ctx->main_window = window;
return ctx;
}
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)
{
if (!perf_gtk__is_active_context(*ctx))
return -1;
free(*ctx);
*ctx = NULL;
return 0;
}
static int perf_gtk__error(const char *format, va_list args)
{
char *msg;
GtkWidget *dialog;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Error:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"<b>Error</b>\n\n%s", msg);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
free(msg);
return 0;
}
#ifdef HAVE_GTK_INFO_BAR
static int perf_gtk__warning_info_bar(const char *format, va_list args)
{
char *msg;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
gtk_label_set_text(GTK_LABEL(pgctx->message_label), msg);
gtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx->info_bar),
GTK_MESSAGE_WARNING);
gtk_widget_show(pgctx->info_bar);
free(msg);
return 0;
}
#else
static int perf_gtk__warning_statusbar(const char *format, va_list args)
{
char *msg, *p;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id);
/* Only first line can be displayed */
p = strchr(msg, '\n');
if (p)
*p = '\0';
gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id, msg);
free(msg);
return 0;
}
#endif
struct perf_error_ops perf_gtk_eops = {
.error = perf_gtk__error,
#ifdef HAVE_GTK_INFO_BAR
.warning = perf_gtk__warning_info_bar,
#else
.warning = perf_gtk__warning_statusbar,
#endif
};
/*
* FIXME: Functions below should be implemented properly.
* For now, just add stubs for NO_NEWT=1 build.
*/
#ifdef NO_NEWT_SUPPORT
int ui_helpline__show_help(const char *format __used, va_list ap __used)
{
return 0;
}
void ui_progress__update(u64 curr __used, u64 total __used,
const char *title __used)
{
}
#endif

View file

@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
static volatile int ui__need_resize;
extern struct perf_error_ops perf_tui_eops;
void ui__refresh_dimensions(bool force)
{
if (force || ui__need_resize) {
@ -122,6 +124,8 @@ int ui__init(void)
signal(SIGINT, ui__signal);
signal(SIGQUIT, ui__signal);
signal(SIGTERM, ui__signal);
perf_error__register(&perf_tui_eops);
out:
return err;
}
@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok)
SLsmg_refresh();
SLsmg_reset_smg();
SLang_reset_tty();
perf_error__unregister(&perf_tui_eops);
}

243
tools/perf/ui/tui/util.c Normal file
View file

@ -0,0 +1,243 @@
#include "../../util/util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ttydefaults.h>
#include "../../util/cache.h"
#include "../../util/debug.h"
#include "../browser.h"
#include "../keysyms.h"
#include "../helpline.h"
#include "../ui.h"
#include "../util.h"
#include "../libslang.h"
static void ui_browser__argv_write(struct ui_browser *browser,
void *entry, int row)
{
char **arg = entry;
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(*arg, browser->width);
}
static int popup_menu__run(struct ui_browser *menu)
{
int key;
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return -1;
while (1) {
key = ui_browser__run(menu, 0);
switch (key) {
case K_RIGHT:
case K_ENTER:
key = menu->index;
break;
case K_LEFT:
case K_ESC:
case 'q':
case CTRL('c'):
key = -1;
break;
default:
continue;
}
break;
}
ui_browser__hide(menu);
return key;
}
int ui__popup_menu(int argc, char * const argv[])
{
struct ui_browser menu = {
.entries = (void *)argv,
.refresh = ui_browser__argv_refresh,
.seek = ui_browser__argv_seek,
.write = ui_browser__argv_write,
.nr_entries = argc,
};
return popup_menu__run(&menu);
}
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_secs)
{
int x, y, len, key;
int max_len = 60, nr_lines = 0;
static char buf[50];
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 8;
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 7;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
y += nr_lines;
len = 5;
while (len--) {
SLsmg_gotorc(y + len - 1, x);
SLsmg_write_nstring((char *)" ", max_len);
}
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
SLsmg_gotorc(y + 3, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
x += 2;
len = 0;
key = ui__getch(delay_secs);
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
if (key == K_BKSPC) {
if (len == 0)
goto next_key;
SLsmg_gotorc(y, x + --len);
SLsmg_write_char(' ');
} else {
buf[len] = key;
SLsmg_gotorc(y, x + len++);
SLsmg_write_char(key);
}
SLsmg_refresh();
/* XXX more graceful overflow handling needed */
if (len == sizeof(buf) - 1) {
ui_helpline__push("maximum size of symbol name reached!");
key = K_ENTER;
break;
}
next_key:
key = ui__getch(delay_secs);
}
buf[len] = '\0';
strncpy(input, buf, len+1);
return key;
}
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs)
{
int x, y;
int max_len = 0, nr_lines = 0;
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
int len;
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 4;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
SLsmg_gotorc(y + nr_lines - 2, x);
SLsmg_write_nstring((char *)" ", max_len);
SLsmg_gotorc(y + nr_lines - 1, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
return ui__getch(delay_secs);
}
int ui__help_window(const char *text)
{
return ui__question_window("Help", text, "Press any key...", 0);
}
int ui__dialog_yesno(const char *msg)
{
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
}
static int __ui__warning(const char *title, const char *format, va_list args)
{
char *s;
if (vasprintf(&s, format, args) > 0) {
int key;
pthread_mutex_lock(&ui__lock);
key = ui__question_window(title, s, "Press any key...", 0);
pthread_mutex_unlock(&ui__lock);
free(s);
return key;
}
fprintf(stderr, "%s\n", title);
vfprintf(stderr, format, args);
return K_ESC;
}
static int perf_tui__error(const char *format, va_list args)
{
return __ui__warning("Error:", format, args);
}
static int perf_tui__warning(const char *format, va_list args)
{
return __ui__warning("Warning:", format, args);
}
struct perf_error_ops perf_tui_eops = {
.error = perf_tui__error,
.warning = perf_tui__warning,
};

View file

@ -1,250 +1,85 @@
#include "../util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ttydefaults.h>
#include "../cache.h"
#include "../debug.h"
#include "browser.h"
#include "keysyms.h"
#include "helpline.h"
#include "ui.h"
#include "util.h"
#include "libslang.h"
#include "../debug.h"
static void ui_browser__argv_write(struct ui_browser *browser,
void *entry, int row)
/*
* Default error logging functions
*/
static int perf_stdio__error(const char *format, va_list args)
{
char **arg = entry;
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(*arg, browser->width);
}
static int popup_menu__run(struct ui_browser *menu)
{
int key;
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return -1;
while (1) {
key = ui_browser__run(menu, 0);
switch (key) {
case K_RIGHT:
case K_ENTER:
key = menu->index;
break;
case K_LEFT:
case K_ESC:
case 'q':
case CTRL('c'):
key = -1;
break;
default:
continue;
}
break;
}
ui_browser__hide(menu);
return key;
}
int ui__popup_menu(int argc, char * const argv[])
{
struct ui_browser menu = {
.entries = (void *)argv,
.refresh = ui_browser__argv_refresh,
.seek = ui_browser__argv_seek,
.write = ui_browser__argv_write,
.nr_entries = argc,
};
return popup_menu__run(&menu);
}
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_secs)
{
int x, y, len, key;
int max_len = 60, nr_lines = 0;
static char buf[50];
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 8;
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 7;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
y += nr_lines;
len = 5;
while (len--) {
SLsmg_gotorc(y + len - 1, x);
SLsmg_write_nstring((char *)" ", max_len);
}
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
SLsmg_gotorc(y + 3, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
x += 2;
len = 0;
key = ui__getch(delay_secs);
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
if (key == K_BKSPC) {
if (len == 0)
goto next_key;
SLsmg_gotorc(y, x + --len);
SLsmg_write_char(' ');
} else {
buf[len] = key;
SLsmg_gotorc(y, x + len++);
SLsmg_write_char(key);
}
SLsmg_refresh();
/* XXX more graceful overflow handling needed */
if (len == sizeof(buf) - 1) {
ui_helpline__push("maximum size of symbol name reached!");
key = K_ENTER;
break;
}
next_key:
key = ui__getch(delay_secs);
}
buf[len] = '\0';
strncpy(input, buf, len+1);
return key;
}
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs)
{
int x, y;
int max_len = 0, nr_lines = 0;
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
int len;
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 4;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
SLsmg_gotorc(y + nr_lines - 2, x);
SLsmg_write_nstring((char *)" ", max_len);
SLsmg_gotorc(y + nr_lines - 1, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
return ui__getch(delay_secs);
}
int ui__help_window(const char *text)
{
return ui__question_window("Help", text, "Press any key...", 0);
}
int ui__dialog_yesno(const char *msg)
{
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
}
int __ui__warning(const char *title, const char *format, va_list args)
{
char *s;
if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
int key;
pthread_mutex_lock(&ui__lock);
key = ui__question_window(title, s, "Press any key...", 0);
pthread_mutex_unlock(&ui__lock);
free(s);
return key;
}
fprintf(stderr, "%s:\n", title);
fprintf(stderr, "Error:\n");
vfprintf(stderr, format, args);
return K_ESC;
return 0;
}
static int perf_stdio__warning(const char *format, va_list args)
{
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
return 0;
}
static struct perf_error_ops default_eops =
{
.error = perf_stdio__error,
.warning = perf_stdio__warning,
};
static struct perf_error_ops *perf_eops = &default_eops;
int ui__error(const char *format, ...)
{
int ret;
va_list args;
va_start(args, format);
ret = perf_eops->error(format, args);
va_end(args);
return ret;
}
int ui__warning(const char *format, ...)
{
int key;
int ret;
va_list args;
va_start(args, format);
key = __ui__warning("Warning", format, args);
ret = perf_eops->warning(format, args);
va_end(args);
return key;
return ret;
}
int ui__error(const char *format, ...)
/**
* perf_error__register - Register error logging functions
* @eops: The pointer to error logging function struct
*
* Register UI-specific error logging functions. Before calling this,
* other logging functions should be unregistered, if any.
*/
int perf_error__register(struct perf_error_ops *eops)
{
int key;
va_list args;
if (perf_eops != &default_eops)
return -1;
va_start(args, format);
key = __ui__warning("Error", format, args);
va_end(args);
return key;
perf_eops = eops;
return 0;
}
/**
* perf_error__unregister - Unregister error logging functions
* @eops: The pointer to error logging function struct
*
* Unregister already registered error logging functions.
*/
int perf_error__unregister(struct perf_error_ops *eops)
{
if (perf_eops != eops)
return -1;
perf_eops = &default_eops;
return 0;
}

View file

@ -9,6 +9,13 @@ int ui__help_window(const char *text);
int ui__dialog_yesno(const char *msg);
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs);
int __ui__warning(const char *title, const char *format, va_list args);
struct perf_error_ops {
int (*error)(const char *format, va_list args);
int (*warning)(const char *format, va_list args);
};
int perf_error__register(struct perf_error_ops *eops);
int perf_error__unregister(struct perf_error_ops *eops);
#endif /* _PERF_UI_UTIL_H_ */

View file

@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...)
return ret;
}
#ifdef NO_NEWT_SUPPORT
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
int ui__warning(const char *format, ...)
{
va_list args;

View file

@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(union perf_event *event);
struct ui_progress;
struct perf_error_ops;
#ifdef NO_NEWT_SUPPORT
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
{
return 0;
@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
const char *title __used) {}
#define ui__error(format, arg...) ui__warning(format, ##arg)
#else
static inline int
perf_error__register(struct perf_error_ops *eops __used)
{
return 0;
}
static inline int
perf_error__unregister(struct perf_error_ops *eops __used)
{
return 0;
}
#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
extern char ui_helpline__last_msg[];
int ui_helpline__show_help(const char *format, va_list ap);
#include "../ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
#endif
#include "../ui/util.h"
#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
int ui__error_paranoid(void);

View file

@ -78,7 +78,7 @@ static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
"ref-cycles",
};
const char *__perf_evsel__hw_name(u64 config)
static const char *__perf_evsel__hw_name(u64 config)
{
if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
return perf_evsel__hw_names[config];
@ -86,16 +86,15 @@ const char *__perf_evsel__hw_name(u64 config)
return "unknown-hardware";
}
static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
static int perf_evsel__add_modifiers(struct perf_evsel *evsel, char *bf, size_t size)
{
int colon = 0;
int colon = 0, r = 0;
struct perf_event_attr *attr = &evsel->attr;
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(attr->config));
bool exclude_guest_default = false;
#define MOD_PRINT(context, mod) do { \
if (!attr->exclude_##context) { \
if (!colon) colon = r++; \
if (!colon) colon = ++r; \
r += scnprintf(bf + r, size - r, "%c", mod); \
} } while(0)
@ -108,7 +107,7 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
if (attr->precise_ip) {
if (!colon)
colon = r++;
colon = ++r;
r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp");
exclude_guest_default = true;
}
@ -119,39 +118,182 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
}
#undef MOD_PRINT
if (colon)
bf[colon] = ':';
bf[colon - 1] = ':';
return r;
}
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size)
static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int ret;
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->attr.config));
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
"cpu-clock",
"task-clock",
"page-faults",
"context-switches",
"CPU-migrations",
"minor-faults",
"major-faults",
"alignment-faults",
"emulation-faults",
};
static const char *__perf_evsel__sw_name(u64 config)
{
if (config < PERF_COUNT_SW_MAX && perf_evsel__sw_names[config])
return perf_evsel__sw_names[config];
return "unknown-software";
}
static int perf_evsel__sw_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->attr.config));
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
[PERF_EVSEL__MAX_ALIASES] = {
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
{ "LLC", "L2", },
{ "dTLB", "d-tlb", "Data-TLB", },
{ "iTLB", "i-tlb", "Instruction-TLB", },
{ "branch", "branches", "bpu", "btb", "bpc", },
{ "node", },
};
const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_EVSEL__MAX_ALIASES] = {
{ "load", "loads", "read", },
{ "store", "stores", "write", },
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
};
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
[PERF_EVSEL__MAX_ALIASES] = {
{ "refs", "Reference", "ops", "access", },
{ "misses", "miss", },
};
#define C(x) PERF_COUNT_HW_CACHE_##x
#define CACHE_READ (1 << C(OP_READ))
#define CACHE_WRITE (1 << C(OP_WRITE))
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
#define COP(x) (1 << x)
/*
* cache operartion stat
* L1I : Read and prefetch only
* ITLB and BPU : Read-only
*/
static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = {
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(ITLB)] = (CACHE_READ),
[C(BPU)] = (CACHE_READ),
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
};
bool perf_evsel__is_cache_op_valid(u8 type, u8 op)
{
if (perf_evsel__hw_cache_stat[type] & COP(op))
return true; /* valid */
else
return false; /* invalid */
}
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size)
{
if (result) {
return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0],
perf_evsel__hw_cache_op[op][0],
perf_evsel__hw_cache_result[result][0]);
}
return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0],
perf_evsel__hw_cache_op[op][1]);
}
static int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size)
{
u8 op, result, type = (config >> 0) & 0xff;
const char *err = "unknown-ext-hardware-cache-type";
if (type > PERF_COUNT_HW_CACHE_MAX)
goto out_err;
op = (config >> 8) & 0xff;
err = "unknown-ext-hardware-cache-op";
if (op > PERF_COUNT_HW_CACHE_OP_MAX)
goto out_err;
result = (config >> 16) & 0xff;
err = "unknown-ext-hardware-cache-result";
if (result > PERF_COUNT_HW_CACHE_RESULT_MAX)
goto out_err;
err = "invalid-cache";
if (!perf_evsel__is_cache_op_valid(type, op))
goto out_err;
return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size);
out_err:
return scnprintf(bf, size, "%s", err);
}
static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size);
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
}
static int perf_evsel__raw_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
}
const char *perf_evsel__name(struct perf_evsel *evsel)
{
char bf[128];
if (evsel->name)
return evsel->name;
switch (evsel->attr.type) {
case PERF_TYPE_RAW:
ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
perf_evsel__raw_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_HARDWARE:
ret = perf_evsel__hw_name(evsel, bf, size);
perf_evsel__hw_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_HW_CACHE:
perf_evsel__hw_cache_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_SOFTWARE:
perf_evsel__sw_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_TRACEPOINT:
scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint");
break;
default:
/*
* FIXME
*
* This is the minimal perf_evsel__name so that we can
* reconstruct event names taking into account event modifiers.
*
* The old event_name uses it now for raw anr hw events, so that
* we don't drag all the parsing stuff into the python binding.
*
* On the next devel cycle the rest of the event naming will be
* brought here.
*/
return 0;
scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
break;
}
return ret;
evsel->name = strdup(bf);
return evsel->name ?: "unknown";
}
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,

View file

@ -83,8 +83,19 @@ void perf_evsel__config(struct perf_evsel *evsel,
struct perf_record_opts *opts,
struct perf_evsel *first);
const char* __perf_evsel__hw_name(u64 config);
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
#define PERF_EVSEL__MAX_ALIASES 8
extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
[PERF_EVSEL__MAX_ALIASES];
extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_EVSEL__MAX_ALIASES];
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
[PERF_EVSEL__MAX_ALIASES];
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size);
const char *perf_evsel__name(struct perf_evsel *evsel);
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);

View file

@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
/*
* write event string as passed on cmdline
*/
ret = do_write_string(fd, event_name(attr));
ret = do_write_string(fd, perf_evsel__name(attr));
if (ret < 0)
return ret;
/*

View file

@ -47,6 +47,7 @@ enum hist_column {
HISTC_SYMBOL_TO,
HISTC_DSO_FROM,
HISTC_DSO_TO,
HISTC_SRCLINE,
HISTC_NR_COLS, /* Last entry */
};

View file

@ -157,7 +157,7 @@ void machine__exit(struct machine *self);
void machine__delete(struct machine *self);
int machine__resolve_callchain(struct machine *machine,
struct perf_evsel *evsel, struct thread *thread,
struct thread *thread,
struct ip_callchain *chain,
struct symbol **parent);
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,

View file

@ -418,14 +418,14 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "krava"));
TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava"));
/* cpu/config=2/" */
evsel = list_entry(evsel->node.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "raw 0x2"));
TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "raw 0x2"));
return 0;
}

View file

@ -64,63 +64,6 @@ static struct event_symbol event_symbols[] = {
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
static const char *sw_event_names[PERF_COUNT_SW_MAX] = {
"cpu-clock",
"task-clock",
"page-faults",
"context-switches",
"CPU-migrations",
"minor-faults",
"major-faults",
"alignment-faults",
"emulation-faults",
};
#define MAX_ALIASES 8
static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = {
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
{ "LLC", "L2", },
{ "dTLB", "d-tlb", "Data-TLB", },
{ "iTLB", "i-tlb", "Instruction-TLB", },
{ "branch", "branches", "bpu", "btb", "bpc", },
{ "node", },
};
static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = {
{ "load", "loads", "read", },
{ "store", "stores", "write", },
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
};
static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
[MAX_ALIASES] = {
{ "refs", "Reference", "ops", "access", },
{ "misses", "miss", },
};
#define C(x) PERF_COUNT_HW_CACHE_##x
#define CACHE_READ (1 << C(OP_READ))
#define CACHE_WRITE (1 << C(OP_WRITE))
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
#define COP(x) (1 << x)
/*
* cache operartion stat
* L1I : Read and prefetch only
* ITLB and BPU : Read-only
*/
static unsigned long hw_cache_stat[C(MAX)] = {
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(ITLB)] = (CACHE_READ),
[C(BPU)] = (CACHE_READ),
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
};
#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
if (sys_dirent.d_type == DT_DIR && \
@ -220,48 +163,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
return NULL;
}
#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
static const char *tracepoint_id_to_name(u64 config)
{
static char buf[TP_PATH_LEN];
struct tracepoint_path *path;
path = tracepoint_id_to_path(config);
if (path) {
snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
free(path->name);
free(path->system);
free(path);
} else
snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
return buf;
}
static int is_cache_op_valid(u8 cache_type, u8 cache_op)
{
if (hw_cache_stat[cache_type] & COP(cache_op))
return 1; /* valid */
else
return 0; /* invalid */
}
static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
{
static char name[50];
if (cache_result) {
sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
hw_cache_op[cache_op][0],
hw_cache_result[cache_result][0]);
} else {
sprintf(name, "%s-%s", hw_cache[cache_type][0],
hw_cache_op[cache_op][1]);
}
return name;
}
const char *event_type(int type)
{
switch (type) {
@ -284,76 +185,6 @@ const char *event_type(int type)
return "unknown";
}
const char *event_name(struct perf_evsel *evsel)
{
u64 config = evsel->attr.config;
int type = evsel->attr.type;
if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE) {
/*
* XXX minimal fix, see comment on perf_evsen__name, this static buffer
* will go away together with event_name in the next devel cycle.
*/
static char bf[128];
perf_evsel__name(evsel, bf, sizeof(bf));
return bf;
}
if (evsel->name)
return evsel->name;
return __event_name(type, config);
}
const char *__event_name(int type, u64 config)
{
static char buf[32];
if (type == PERF_TYPE_RAW) {
sprintf(buf, "raw 0x%" PRIx64, config);
return buf;
}
switch (type) {
case PERF_TYPE_HARDWARE:
return __perf_evsel__hw_name(config);
case PERF_TYPE_HW_CACHE: {
u8 cache_type, cache_op, cache_result;
cache_type = (config >> 0) & 0xff;
if (cache_type > PERF_COUNT_HW_CACHE_MAX)
return "unknown-ext-hardware-cache-type";
cache_op = (config >> 8) & 0xff;
if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
return "unknown-ext-hardware-cache-op";
cache_result = (config >> 16) & 0xff;
if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
return "unknown-ext-hardware-cache-result";
if (!is_cache_op_valid(cache_type, cache_op))
return "invalid-cache";
return event_cache_name(cache_type, cache_op, cache_result);
}
case PERF_TYPE_SOFTWARE:
if (config < PERF_COUNT_SW_MAX && sw_event_names[config])
return sw_event_names[config];
return "unknown-software";
case PERF_TYPE_TRACEPOINT:
return tracepoint_id_to_name(config);
default:
break;
}
return "unknown";
}
static int add_event(struct list_head **_list, int *idx,
struct perf_event_attr *attr, char *name)
{
@ -375,19 +206,20 @@ static int add_event(struct list_head **_list, int *idx,
return -ENOMEM;
}
evsel->name = strdup(name);
if (name)
evsel->name = strdup(name);
list_add_tail(&evsel->node, list);
*_list = list;
return 0;
}
static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
{
int i, j;
int n, longest = -1;
for (i = 0; i < size; i++) {
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) {
n = strlen(names[i][j]);
if (n > longest && !strncasecmp(str, names[i][j], n))
longest = n;
@ -412,7 +244,7 @@ int parse_events_add_cache(struct list_head **list, int *idx,
* No fallback - if we cannot get a clear cache type
* then bail out:
*/
cache_type = parse_aliases(type, hw_cache,
cache_type = parse_aliases(type, perf_evsel__hw_cache,
PERF_COUNT_HW_CACHE_MAX);
if (cache_type == -1)
return -EINVAL;
@ -425,18 +257,18 @@ int parse_events_add_cache(struct list_head **list, int *idx,
snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
if (cache_op == -1) {
cache_op = parse_aliases(str, hw_cache_op,
cache_op = parse_aliases(str, perf_evsel__hw_cache_op,
PERF_COUNT_HW_CACHE_OP_MAX);
if (cache_op >= 0) {
if (!is_cache_op_valid(cache_type, cache_op))
if (!perf_evsel__is_cache_op_valid(cache_type, cache_op))
return -EINVAL;
continue;
}
}
if (cache_result == -1) {
cache_result = parse_aliases(str, hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX);
cache_result = parse_aliases(str, perf_evsel__hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX);
if (cache_result >= 0)
continue;
}
@ -668,8 +500,7 @@ int parse_events_add_numeric(struct list_head **list, int *idx,
config_attr(&attr, head_config, 1))
return -EINVAL;
return add_event(list, idx, &attr,
(char *) __event_name(type, config));
return add_event(list, idx, &attr, NULL);
}
static int parse_events__is_name_term(struct parse_events__term *term)
@ -677,8 +508,7 @@ static int parse_events__is_name_term(struct parse_events__term *term)
return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
}
static char *pmu_event_name(struct perf_event_attr *attr,
struct list_head *head_terms)
static char *pmu_event_name(struct list_head *head_terms)
{
struct parse_events__term *term;
@ -686,7 +516,7 @@ static char *pmu_event_name(struct perf_event_attr *attr,
if (parse_events__is_name_term(term))
return term->val.str;
return (char *) __event_name(PERF_TYPE_RAW, attr->config);
return NULL;
}
int parse_events_add_pmu(struct list_head **list, int *idx,
@ -714,7 +544,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
return -EINVAL;
return add_event(list, idx, &attr,
pmu_event_name(&attr, head_config));
pmu_event_name(head_config));
}
void parse_events_update_lists(struct list_head *list_event,
@ -1010,16 +840,17 @@ void print_events_type(u8 type)
int print_hwcache_events(const char *event_glob)
{
unsigned int type, op, i, printed = 0;
char name[64];
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
/* skip invalid cache type */
if (!is_cache_op_valid(type, op))
if (!perf_evsel__is_cache_op_valid(type, op))
continue;
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
char *name = event_cache_name(type, op, i);
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
name, sizeof(name));
if (event_glob != NULL && !strglobmatch(name, event_glob))
continue;

View file

@ -26,8 +26,6 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
extern bool have_tracepoints(struct list_head *evlist);
const char *event_type(int type);
const char *event_name(struct perf_evsel *event);
extern const char *__event_name(int type, u64 config);
extern int parse_events_option(const struct option *opt, const char *str,
int unset);

View file

@ -289,7 +289,6 @@ struct branch_info *machine__resolve_bstack(struct machine *self,
}
int machine__resolve_callchain(struct machine *self,
struct perf_evsel *evsel __used,
struct thread *thread,
struct ip_callchain *chain,
struct symbol **parent)
@ -1449,7 +1448,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
ret += hists__fprintf_nr_events(&session->hists, fp);
list_for_each_entry(pos, &session->evlist->entries, node) {
ret += fprintf(fp, "%s stats:\n", event_name(pos));
ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
ret += hists__fprintf_nr_events(&pos->hists, fp);
}
@ -1490,8 +1489,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
}
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel,
int print_sym, int print_dso, int print_symoffset)
struct machine *machine, int print_sym,
int print_dso, int print_symoffset)
{
struct addr_location al;
struct callchain_cursor_node *node;
@ -1505,7 +1504,7 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
if (symbol_conf.use_callchain && sample->callchain) {
if (machine__resolve_callchain(machine, evsel, al.thread,
if (machine__resolve_callchain(machine, al.thread,
sample->callchain, NULL) != 0) {
if (verbose)
error("Failed to resolve callchain. Skipping\n");

View file

@ -151,8 +151,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type);
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel,
int print_sym, int print_dso, int print_symoffset);
struct machine *machine, int print_sym,
int print_dso, int print_symoffset);
int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap);

View file

@ -241,6 +241,54 @@ struct sort_entry sort_sym = {
.se_width_idx = HISTC_SYMBOL,
};
/* --sort srcline */
static int64_t
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
{
return (int64_t)(right->ip - left->ip);
}
static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width __used)
{
FILE *fp;
char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
size_t line_len;
if (path != NULL)
goto out_path;
snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
self->ms.map->dso->long_name, self->ip);
fp = popen(cmd, "r");
if (!fp)
goto out_ip;
if (getline(&path, &line_len, fp) < 0 || !line_len)
goto out_ip;
fclose(fp);
self->srcline = strdup(path);
if (self->srcline == NULL)
goto out_ip;
nl = strchr(self->srcline, '\n');
if (nl != NULL)
*nl = '\0';
path = self->srcline;
out_path:
return repsep_snprintf(bf, size, "%s", path);
out_ip:
return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
}
struct sort_entry sort_srcline = {
.se_header = "Source:Line",
.se_cmp = sort__srcline_cmp,
.se_snprintf = hist_entry__srcline_snprintf,
.se_width_idx = HISTC_SRCLINE,
};
/* --sort parent */
static int64_t
@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = {
DIM(SORT_PARENT, "parent", sort_parent),
DIM(SORT_CPU, "cpu", sort_cpu),
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
DIM(SORT_SRCLINE, "srcline", sort_srcline),
};
int sort_dimension__add(const char *tok)

View file

@ -71,6 +71,7 @@ struct hist_entry {
char level;
bool used;
u8 filtered;
char *srcline;
struct symbol *parent;
union {
unsigned long position;
@ -93,6 +94,7 @@ enum sort_type {
SORT_SYM_FROM,
SORT_SYM_TO,
SORT_MISPREDICT,
SORT_SRCLINE,
};
/*

View file

@ -313,3 +313,25 @@ int strtailcmp(const char *s1, const char *s2)
return 0;
}
/**
* rtrim - Removes trailing whitespace from @s.
* @s: The string to be stripped.
*
* Note that the first trailing whitespace is replaced with a %NUL-terminator
* in the given string @s. Returns @s.
*/
char *rtrim(char *s)
{
size_t size = strlen(s);
char *end;
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
return s;
}

View file

@ -65,7 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
top->freq ? "Hz" : "");
}
ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel));
ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel));
ret += SNPRINTF(bf + ret, size - ret, "], ");

View file

@ -264,4 +264,6 @@ bool is_power_of_2(unsigned long n)
size_t hex_width(u64 v);
char *rtrim(char *s);
#endif