Commit 32c46e57 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 2992c542 c0a58fb2
...@@ -57,7 +57,7 @@ OPTIONS ...@@ -57,7 +57,7 @@ OPTIONS
-s:: -s::
--sort=:: --sort=::
Sort by key(s): pid, comm, dso, symbol, parent. Sort by key(s): pid, comm, dso, symbol, parent, srcline.
-p:: -p::
--parent=<regex>:: --parent=<regex>::
......
...@@ -112,7 +112,7 @@ Default is to monitor all CPUS. ...@@ -112,7 +112,7 @@ Default is to monitor all CPUS.
-s:: -s::
--sort:: --sort::
Sort by key(s): pid, comm, dso, symbol, parent Sort by key(s): pid, comm, dso, symbol, parent, srcline.
-n:: -n::
--show-nr-samples:: --show-nr-samples::
......
...@@ -503,6 +503,7 @@ else ...@@ -503,6 +503,7 @@ else
LIB_OBJS += $(OUTPUT)ui/progress.o LIB_OBJS += $(OUTPUT)ui/progress.o
LIB_OBJS += $(OUTPUT)ui/util.o LIB_OBJS += $(OUTPUT)ui/util.o
LIB_OBJS += $(OUTPUT)ui/tui/setup.o LIB_OBJS += $(OUTPUT)ui/tui/setup.o
LIB_OBJS += $(OUTPUT)ui/tui/util.o
LIB_H += ui/browser.h LIB_H += ui/browser.h
LIB_H += ui/browsers/map.h LIB_H += ui/browsers/map.h
LIB_H += ui/helpline.h LIB_H += ui/helpline.h
...@@ -522,13 +523,18 @@ else ...@@ -522,13 +523,18 @@ else
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
BASIC_CFLAGS += -DNO_GTK2_SUPPORT BASIC_CFLAGS += -DNO_GTK2_SUPPORT
else 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) BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
EXTLIBS += $(shell pkg-config --libs gtk+-2.0) EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
# Make sure that it'd be included only once. # Make sure that it'd be included only once.
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),) ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/util.o
endif endif
endif endif
endif endif
......
...@@ -60,7 +60,7 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail ...@@ -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) { list_for_each_entry(pos, &session->evlist->entries, node) {
bool first = true; bool first = true;
printf("%s", event_name(pos)); printf("%s", perf_evsel__name(pos));
if (details->verbose || details->freq) { if (details->verbose || details->freq) {
comma_printf(&first, " sample_freq=%" PRIu64, comma_printf(&first, " sample_freq=%" PRIu64,
......
...@@ -265,7 +265,7 @@ static void perf_record__open(struct perf_record *rec) ...@@ -265,7 +265,7 @@ static void perf_record__open(struct perf_record *rec)
if (err == ENOENT) { if (err == ENOENT) {
ui__error("The %s event is not supported.\n", ui__error("The %s event is not supported.\n",
event_name(pos)); perf_evsel__name(pos));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -916,7 +916,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -916,7 +916,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
usage_with_options(record_usage, record_options); usage_with_options(record_usage, record_options);
list_for_each_entry(pos, &evsel_list->entries, node) { 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; goto out_free_fd;
} }
......
...@@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, ...@@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) if ((sort__has_parent || symbol_conf.use_callchain)
&& sample->callchain) { && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread, err = machine__resolve_callchain(machine, al->thread,
sample->callchain, &parent); sample->callchain, &parent);
if (err) if (err)
return err; return err;
...@@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, ...@@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
struct hist_entry *he; struct hist_entry *he;
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { 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); sample->callchain, &parent);
if (err) if (err)
return err; return err;
...@@ -230,7 +230,7 @@ static int process_read_event(struct perf_tool *tool, ...@@ -230,7 +230,7 @@ static int process_read_event(struct perf_tool *tool,
struct perf_report *rep = container_of(tool, struct perf_report, tool); struct perf_report *rep = container_of(tool, struct perf_report, tool);
if (rep->show_threads) { 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, perf_read_values_add_value(&rep->show_threads_values,
event->read.pid, event->read.tid, event->read.pid, event->read.tid,
event->read.id, event->read.id,
...@@ -239,7 +239,7 @@ static int process_read_event(struct perf_tool *tool, ...@@ -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, 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); event->read.value);
return 0; return 0;
...@@ -314,7 +314,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, ...@@ -314,7 +314,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists; 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_nr_sample_events(hists, evname, stdout);
hists__fprintf(hists, NULL, false, true, 0, 0, stdout); hists__fprintf(hists, NULL, false, true, 0, 0, stdout);
......
...@@ -1601,7 +1601,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool, ...@@ -1601,7 +1601,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %s event, skipping it.\n", pr_debug("problem processing %s event, skipping it.\n",
evsel->name); perf_evsel__name(evsel));
return -1; return -1;
} }
......
...@@ -137,10 +137,11 @@ static const char *output_field2str(enum perf_output_field field) ...@@ -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) #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x)
static int perf_event_attr__check_stype(struct perf_event_attr *attr, static int perf_evsel__check_stype(struct perf_evsel *evsel,
u64 sample_type, const char *sample_msg, u64 sample_type, const char *sample_msg,
enum perf_output_field field) enum perf_output_field field)
{ {
struct perf_event_attr *attr = &evsel->attr;
int type = attr->type; int type = attr->type;
const char *evname; const char *evname;
...@@ -148,7 +149,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr, ...@@ -148,7 +149,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
return 0; return 0;
if (output[type].user_set) { 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. " pr_err("Samples for '%s' event do not have %s attribute set. "
"Cannot print '%s' field.\n", "Cannot print '%s' field.\n",
evname, sample_msg, output_field2str(field)); evname, sample_msg, output_field2str(field));
...@@ -157,7 +158,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr, ...@@ -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 */ /* user did not ask for it explicitly so remove from the default list */
output[type].fields &= ~field; 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. " pr_debug("Samples for '%s' event do not have %s attribute set. "
"Skipping '%s' field.\n", "Skipping '%s' field.\n",
evname, sample_msg, output_field2str(field)); evname, sample_msg, output_field2str(field));
...@@ -175,8 +176,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, ...@@ -175,8 +176,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(IP)) { if (PRINT_FIELD(IP)) {
if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
PERF_OUTPUT_IP)) PERF_OUTPUT_IP))
return -EINVAL; return -EINVAL;
if (!no_callchain && if (!no_callchain &&
...@@ -185,8 +186,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, ...@@ -185,8 +186,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
} }
if (PRINT_FIELD(ADDR) && if (PRINT_FIELD(ADDR) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR", perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
PERF_OUTPUT_ADDR)) PERF_OUTPUT_ADDR))
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
...@@ -208,18 +209,18 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, ...@@ -208,18 +209,18 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
} }
if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID",
PERF_OUTPUT_TID|PERF_OUTPUT_PID)) PERF_OUTPUT_TID|PERF_OUTPUT_PID))
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(TIME) && if (PRINT_FIELD(TIME) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME", perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME",
PERF_OUTPUT_TIME)) PERF_OUTPUT_TIME))
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(CPU) && if (PRINT_FIELD(CPU) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU", perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
PERF_OUTPUT_CPU)) PERF_OUTPUT_CPU))
return -EINVAL; return -EINVAL;
return 0; return 0;
...@@ -258,9 +259,10 @@ static int perf_session__check_output_opt(struct perf_session *session) ...@@ -258,9 +259,10 @@ static int perf_session__check_output_opt(struct perf_session *session)
static void print_sample_start(struct perf_sample *sample, static void print_sample_start(struct perf_sample *sample,
struct thread *thread, struct thread *thread,
struct perf_event_attr *attr) struct perf_evsel *evsel)
{ {
int type; int type;
struct perf_event_attr *attr = &evsel->attr;
struct event_format *event; struct event_format *event;
const char *evname = NULL; const char *evname = NULL;
unsigned long secs; unsigned long secs;
...@@ -305,7 +307,7 @@ static void print_sample_start(struct perf_sample *sample, ...@@ -305,7 +307,7 @@ static void print_sample_start(struct perf_sample *sample,
if (event) if (event)
evname = event->name; evname = event->name;
} else } else
evname = __event_name(attr->type, attr->config); evname = perf_evsel__name(evsel);
printf("%s: ", evname ? evname : "[unknown]"); printf("%s: ", evname ? evname : "[unknown]");
} }
...@@ -387,7 +389,7 @@ static void print_sample_bts(union perf_event *event, ...@@ -387,7 +389,7 @@ static void print_sample_bts(union perf_event *event,
printf(" "); printf(" ");
else else
printf("\n"); 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(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET)); PRINT_FIELD(SYMOFFSET));
} }
...@@ -412,7 +414,7 @@ static void process_event(union perf_event *event __unused, ...@@ -412,7 +414,7 @@ static void process_event(union perf_event *event __unused,
if (output[attr->type].fields == 0) if (output[attr->type].fields == 0)
return; return;
print_sample_start(sample, thread, attr); print_sample_start(sample, thread, evsel);
if (is_bts_event(attr)) { if (is_bts_event(attr)) {
print_sample_bts(event, sample, evsel, machine, thread); print_sample_bts(event, sample, evsel, machine, thread);
...@@ -431,7 +433,7 @@ static void process_event(union perf_event *event __unused, ...@@ -431,7 +433,7 @@ static void process_event(union perf_event *event __unused,
printf(" "); printf(" ");
else else
printf("\n"); 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(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET)); PRINT_FIELD(SYMOFFSET));
} }
......
...@@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter) ...@@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
if (verbose) { if (verbose) {
fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", 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) ...@@ -496,7 +496,7 @@ static int run_perf_stat(int argc __used, const char **argv)
errno == ENXIO) { errno == ENXIO) {
if (verbose) if (verbose)
ui__warning("%s event is not supported by the kernel.\n", ui__warning("%s event is not supported by the kernel.\n",
event_name(counter)); perf_evsel__name(counter));
counter->supported = false; counter->supported = false;
continue; continue;
} }
...@@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) ...@@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
csv_output ? 0 : -4, csv_output ? 0 : -4,
evsel_list->cpus->map[cpu], csv_sep); 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) if (evsel->cgrp)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); 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) ...@@ -792,7 +792,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
else else
cpu = 0; 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) if (evsel->cgrp)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
...@@ -908,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter) ...@@ -908,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter)
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep, csv_sep,
csv_output ? 0 : -24, csv_output ? 0 : -24,
event_name(counter)); perf_evsel__name(counter));
if (counter->cgrp) if (counter->cgrp)
fprintf(output, "%s%s", csv_sep, counter->cgrp->name); fprintf(output, "%s%s", csv_sep, counter->cgrp->name);
...@@ -961,7 +961,7 @@ static void print_counter(struct perf_evsel *counter) ...@@ -961,7 +961,7 @@ static void print_counter(struct perf_evsel *counter)
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep, csv_sep,
csv_output ? 0 : -24, csv_output ? 0 : -24,
event_name(counter)); perf_evsel__name(counter));
if (counter->cgrp) if (counter->cgrp)
fprintf(output, "%s%s", fprintf(output, "%s%s",
......
...@@ -583,7 +583,7 @@ static int test__basic_mmap(void) ...@@ -583,7 +583,7 @@ static int test__basic_mmap(void)
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
pr_debug("expected %d %s events, got %d\n", pr_debug("expected %d %s events, got %d\n",
expected_nr_events[evsel->idx], expected_nr_events[evsel->idx],
event_name(evsel), nr_events[evsel->idx]); perf_evsel__name(evsel), nr_events[evsel->idx]);
goto out_munmap; goto out_munmap;
} }
} }
......
...@@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top) ...@@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top)
if (notes->src == NULL) if (notes->src == NULL)
goto out_unlock; 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); printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, 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) ...@@ -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); fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries);
if (top->evlist->nr_entries > 1) 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); 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) ...@@ -503,13 +503,13 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
fprintf(stderr, "\nAvailable events:"); fprintf(stderr, "\nAvailable events:");
list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) 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"); prompt_integer(&counter, "Enter details event counter");
if (counter >= top->evlist->nr_entries) { if (counter >= top->evlist->nr_entries) {
top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node); 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); sleep(1);
break; break;
} }
...@@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) && if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) { sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al.thread, err = machine__resolve_callchain(machine, al.thread,
sample->callchain, &parent); sample->callchain, &parent);
if (err) if (err)
return; return;
...@@ -960,7 +960,7 @@ static void perf_top__start_counters(struct perf_top *top) ...@@ -960,7 +960,7 @@ static void perf_top__start_counters(struct perf_top *top)
if (err == ENOENT) { if (err == ENOENT) {
ui__error("The %s event is not supported.\n", ui__error("The %s event is not supported.\n",
event_name(counter)); perf_evsel__name(counter));
goto out_err; goto out_err;
} else if (err == EMFILE) { } else if (err == EMFILE) {
ui__error("Too many events are opened.\n" ui__error("Too many events are opened.\n"
......
...@@ -78,6 +78,19 @@ int main(int argc, char *argv[]) ...@@ -78,6 +78,19 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
endef 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 endif
ifndef NO_LIBPERL ifndef NO_LIBPERL
......
...@@ -814,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, ...@@ -814,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
{ {
struct disasm_line *pos, *n; struct disasm_line *pos, *n;
struct annotation *notes; struct annotation *notes;
const size_t size = symbol__size(sym); size_t size;
struct map_symbol ms = { struct map_symbol ms = {
.map = map, .map = map,
.sym = sym, .sym = sym,
...@@ -834,6 +834,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, ...@@ -834,6 +834,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
if (sym == NULL) if (sym == NULL)
return -1; return -1;
size = symbol__size(sym);
if (map->dso->annotate_warned) if (map->dso->annotate_warned)
return -1; return -1;
......
...@@ -23,6 +23,7 @@ struct hist_browser { ...@@ -23,6 +23,7 @@ struct hist_browser {
struct hists *hists; struct hists *hists;
struct hist_entry *he_selection; struct hist_entry *he_selection;
struct map_symbol *selection; struct map_symbol *selection;
int print_seq;
bool has_symbols; bool has_symbols;
}; };
...@@ -800,6 +801,196 @@ static void ui_browser__hists_seek(struct ui_browser *browser, ...@@ -800,6 +801,196 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
} }
} }
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) static struct hist_browser *hist_browser__new(struct hists *hists)
{ {
struct hist_browser *browser = zalloc(sizeof(*browser)); struct hist_browser *browser = zalloc(sizeof(*browser));
...@@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, ...@@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
browser->selection->map->dso->annotate_warned) browser->selection->map->dso->annotate_warned)
continue; continue;
goto do_annotate; goto do_annotate;
case 'P':
hist_browser__dump(browser);
continue;
case 'd': case 'd':
goto zoom_dso; goto zoom_dso;
case 't': case 't':
...@@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, ...@@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"E Expand all callchains\n" "E Expand all callchains\n"
"d Zoom into current DSO\n" "d Zoom into current DSO\n"
"t Zoom into current Thread\n" "t Zoom into current Thread\n"
"P Print histograms to perf.hist.N\n"
"/ Filter symbol by name"); "/ Filter symbol by name");
continue; continue;
case K_ENTER: case K_ENTER:
...@@ -1172,7 +1367,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, ...@@ -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); struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
bool current_entry = ui_browser__is_current_entry(browser, row); bool current_entry = ui_browser__is_current_entry(browser, row);
unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 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; char bf[256], unit;
const char *warn = " "; const char *warn = " ";
size_t printed; size_t printed;
...@@ -1240,7 +1435,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, ...@@ -1240,7 +1435,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
*/ */
if (timer) if (timer)
timer(arg); timer(arg);
ev_name = event_name(pos); ev_name = perf_evsel__name(pos);
key = perf_evsel__hists_browse(pos, nr_events, help, key = perf_evsel__hists_browse(pos, nr_events, help,
ev_name, true, timer, ev_name, true, timer,
arg, delay_secs); arg, delay_secs);
...@@ -1309,17 +1504,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, ...@@ -1309,17 +1504,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
ui_helpline__push("Press ESC to exit"); ui_helpline__push("Press ESC to exit");
list_for_each_entry(pos, &evlist->entries, node) { 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; size_t line_len = strlen(ev_name) + 7;
if (menu.b.width < line_len) if (menu.b.width < line_len)
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, 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, ...@@ -1330,11 +1519,10 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
void(*timer)(void *arg), void *arg, void(*timer)(void *arg), void *arg,
int delay_secs) int delay_secs)
{ {
if (evlist->nr_entries == 1) { if (evlist->nr_entries == 1) {
struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel *first = list_entry(evlist->entries.next,
struct perf_evsel, node); 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, return perf_evsel__hists_browse(first, evlist->nr_entries, help,
ev_name, false, timer, arg, ev_name, false, timer, arg,
delay_secs); delay_secs);
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
static void perf_gtk__signal(int sig) static void perf_gtk__signal(int sig)
{ {
perf_gtk__exit(false);
psignal(sig, "perf"); psignal(sig, "perf");
gtk_main_quit();
} }
static void perf_gtk__resize_window(GtkWidget *window) static void perf_gtk__resize_window(GtkWidget *window)
...@@ -122,13 +122,59 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) ...@@ -122,13 +122,59 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
gtk_container_add(GTK_CONTAINER(window), view); 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, int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help __used, const char *help __used,
void (*timer) (void *arg)__used, void (*timer) (void *arg)__used,
void *arg __used, int delay_secs __used) void *arg __used, int delay_secs __used)
{ {
struct perf_evsel *pos; struct perf_evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook; GtkWidget *notebook;
GtkWidget *info_bar;
GtkWidget *statbar;
GtkWidget *window; GtkWidget *window;
signal(SIGSEGV, perf_gtk__signal); signal(SIGSEGV, perf_gtk__signal);
...@@ -143,11 +189,17 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, ...@@ -143,11 +189,17 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
g_signal_connect(window, "delete_event", gtk_main_quit, NULL); 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(); notebook = gtk_notebook_new();
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists; struct hists *hists = &pos->hists;
const char *evname = event_name(pos); const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window; GtkWidget *scrolled_window;
GtkWidget *tab_label; GtkWidget *tab_label;
...@@ -164,7 +216,16 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, ...@@ -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_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); gtk_widget_show_all(window);
...@@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, ...@@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_main(); gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0; return 0;
} }
#ifndef _PERF_GTK_H_ #ifndef _PERF_GTK_H_
#define _PERF_GTK_H_ 1 #define _PERF_GTK_H_ 1
#include <stdbool.h>
#pragma GCC diagnostic ignored "-Wstrict-prototypes" #pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <gtk/gtk.h> #include <gtk/gtk.h>
#pragma GCC diagnostic error "-Wstrict-prototypes" #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_ */ #endif /* _PERF_GTK_H_ */
#include "gtk.h" #include "gtk.h"
#include "../../util/cache.h" #include "../../util/cache.h"
#include "../../util/debug.h"
extern struct perf_error_ops perf_gtk_eops;
int perf_gtk__init(void) int perf_gtk__init(void)
{ {
perf_error__register(&perf_gtk_eops);
return gtk_init_check(NULL, NULL) ? 0 : -1; return gtk_init_check(NULL, NULL) ? 0 : -1;
} }
void perf_gtk__exit(bool wait_for_ok __used) void perf_gtk__exit(bool wait_for_ok __used)
{ {
perf_error__unregister(&perf_gtk_eops);
gtk_main_quit(); gtk_main_quit();
} }
#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
...@@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; ...@@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
static volatile int ui__need_resize; static volatile int ui__need_resize;
extern struct perf_error_ops perf_tui_eops;
void ui__refresh_dimensions(bool force) void ui__refresh_dimensions(bool force)
{ {
if (force || ui__need_resize) { if (force || ui__need_resize) {
...@@ -122,6 +124,8 @@ int ui__init(void) ...@@ -122,6 +124,8 @@ int ui__init(void)
signal(SIGINT, ui__signal); signal(SIGINT, ui__signal);
signal(SIGQUIT, ui__signal); signal(SIGQUIT, ui__signal);
signal(SIGTERM, ui__signal); signal(SIGTERM, ui__signal);
perf_error__register(&perf_tui_eops);
out: out:
return err; return err;
} }
...@@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok) ...@@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok)
SLsmg_refresh(); SLsmg_refresh();
SLsmg_reset_smg(); SLsmg_reset_smg();
SLang_reset_tty(); SLang_reset_tty();
perf_error__unregister(&perf_tui_eops);
} }
#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,
};
#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 "util.h"
#include "libslang.h" #include "../debug.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[]) /*
* Default error logging functions
*/
static int perf_stdio__error(const char *format, va_list args)
{ {
struct ui_browser menu = { fprintf(stderr, "Error:\n");
.entries = (void *)argv, vfprintf(stderr, format, args);
.refresh = ui_browser__argv_refresh, return 0;
.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, static int perf_stdio__warning(const char *format, va_list args)
const char *exit_msg, int delay_secs)
{ {
int x, y, len, key; fprintf(stderr, "Warning:\n");
int max_len = 60, nr_lines = 0; vfprintf(stderr, format, args);
static char buf[50]; return 0;
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, static struct perf_error_ops default_eops =
const char *exit_msg, int delay_secs)
{ {
int x, y; .error = perf_stdio__error,
int max_len = 0, nr_lines = 0; .warning = perf_stdio__warning,
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) static struct perf_error_ops *perf_eops = &default_eops;
{
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) int ui__error(const char *format, ...)
{ {
char *s; int ret;
va_list args;
if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
int key;
pthread_mutex_lock(&ui__lock); va_start(args, format);
key = ui__question_window(title, s, "Press any key...", 0); ret = perf_eops->error(format, args);
pthread_mutex_unlock(&ui__lock); va_end(args);
free(s);
return key;
}
fprintf(stderr, "%s:\n", title); return ret;
vfprintf(stderr, format, args);
return K_ESC;
} }
int ui__warning(const char *format, ...) int ui__warning(const char *format, ...)
{ {
int key; int ret;
va_list args; va_list args;
va_start(args, format); va_start(args, format);
key = __ui__warning("Warning", format, args); ret = perf_eops->warning(format, args);
va_end(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; if (perf_eops != &default_eops)
va_list args; return -1;
va_start(args, format); perf_eops = eops;
key = __ui__warning("Error", format, args); return 0;
va_end(args); }
return key;
/**
* 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;
} }
...@@ -9,6 +9,13 @@ int ui__help_window(const char *text); ...@@ -9,6 +9,13 @@ int ui__help_window(const char *text);
int ui__dialog_yesno(const char *msg); int ui__dialog_yesno(const char *msg);
int ui__question_window(const char *title, const char *text, int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs); 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_ */ #endif /* _PERF_UI_UTIL_H_ */
...@@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...) ...@@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...)
return ret; return ret;
} }
#ifdef NO_NEWT_SUPPORT #if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
int ui__warning(const char *format, ...) int ui__warning(const char *format, ...)
{ {
va_list args; va_list args;
......
...@@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); ...@@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(union perf_event *event); void trace_event(union perf_event *event);
struct ui_progress; 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) static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
{ {
return 0; return 0;
...@@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used, ...@@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
const char *title __used) {} const char *title __used) {}
#define ui__error(format, arg...) ui__warning(format, ##arg) #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[]; extern char ui_helpline__last_msg[];
int ui_helpline__show_help(const char *format, va_list ap); int ui_helpline__show_help(const char *format, va_list ap);
#include "../ui/progress.h" #include "../ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); 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__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
int ui__error_paranoid(void); int ui__error_paranoid(void);
......
...@@ -78,7 +78,7 @@ static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { ...@@ -78,7 +78,7 @@ static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
"ref-cycles", "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]) if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
return perf_evsel__hw_names[config]; return perf_evsel__hw_names[config];
...@@ -86,16 +86,15 @@ const char *__perf_evsel__hw_name(u64 config) ...@@ -86,16 +86,15 @@ const char *__perf_evsel__hw_name(u64 config)
return "unknown-hardware"; 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; struct perf_event_attr *attr = &evsel->attr;
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(attr->config));
bool exclude_guest_default = false; bool exclude_guest_default = false;
#define MOD_PRINT(context, mod) do { \ #define MOD_PRINT(context, mod) do { \
if (!attr->exclude_##context) { \ if (!attr->exclude_##context) { \
if (!colon) colon = r++; \ if (!colon) colon = ++r; \
r += scnprintf(bf + r, size - r, "%c", mod); \ r += scnprintf(bf + r, size - r, "%c", mod); \
} } while(0) } } while(0)
...@@ -108,7 +107,7 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) ...@@ -108,7 +107,7 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
if (attr->precise_ip) { if (attr->precise_ip) {
if (!colon) if (!colon)
colon = r++; colon = ++r;
r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp"); r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp");
exclude_guest_default = true; exclude_guest_default = true;
} }
...@@ -119,39 +118,182 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) ...@@ -119,39 +118,182 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
} }
#undef MOD_PRINT #undef MOD_PRINT
if (colon) if (colon)
bf[colon] = ':'; bf[colon - 1] = ':';
return r; 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 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)
{ {
int ret; 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) { switch (evsel->attr.type) {
case PERF_TYPE_RAW: case PERF_TYPE_RAW:
ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config); perf_evsel__raw_name(evsel, bf, sizeof(bf));
break; break;
case PERF_TYPE_HARDWARE: 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; break;
case PERF_TYPE_TRACEPOINT:
scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint");
break;
default: default:
/* scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
* FIXME break;
* }
* This is the minimal perf_evsel__name so that we can
* reconstruct event names taking into account event modifiers. evsel->name = strdup(bf);
*
* The old event_name uses it now for raw anr hw events, so that return evsel->name ?: "unknown";
* 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;
}
return ret;
} }
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
......
...@@ -83,8 +83,19 @@ void perf_evsel__config(struct perf_evsel *evsel, ...@@ -83,8 +83,19 @@ void perf_evsel__config(struct perf_evsel *evsel,
struct perf_record_opts *opts, struct perf_record_opts *opts,
struct perf_evsel *first); struct perf_evsel *first);
const char* __perf_evsel__hw_name(u64 config); bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
#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_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
......
...@@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used, ...@@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
/* /*
* write event string as passed on cmdline * 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) if (ret < 0)
return ret; return ret;
/* /*
......
...@@ -47,6 +47,7 @@ enum hist_column { ...@@ -47,6 +47,7 @@ enum hist_column {
HISTC_SYMBOL_TO, HISTC_SYMBOL_TO,
HISTC_DSO_FROM, HISTC_DSO_FROM,
HISTC_DSO_TO, HISTC_DSO_TO,
HISTC_SRCLINE,
HISTC_NR_COLS, /* Last entry */ HISTC_NR_COLS, /* Last entry */
}; };
......
...@@ -157,7 +157,7 @@ void machine__exit(struct machine *self); ...@@ -157,7 +157,7 @@ void machine__exit(struct machine *self);
void machine__delete(struct machine *self); void machine__delete(struct machine *self);
int machine__resolve_callchain(struct machine *machine, int machine__resolve_callchain(struct machine *machine,
struct perf_evsel *evsel, struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent); struct symbol **parent);
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,
......
...@@ -418,14 +418,14 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist) ...@@ -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 number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); 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/" */ /* cpu/config=2/" */
evsel = list_entry(evsel->node.next, struct perf_evsel, node); 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 number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); 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; return 0;
} }
......
...@@ -64,63 +64,6 @@ static struct event_symbol event_symbols[] = { ...@@ -64,63 +64,6 @@ static struct event_symbol event_symbols[] = {
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE) #define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT) #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) \ #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
if (sys_dirent.d_type == DT_DIR && \ if (sys_dirent.d_type == DT_DIR && \
...@@ -220,48 +163,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) ...@@ -220,48 +163,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
return NULL; 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) const char *event_type(int type)
{ {
switch (type) { switch (type) {
...@@ -284,76 +185,6 @@ const char *event_type(int type) ...@@ -284,76 +185,6 @@ const char *event_type(int type)
return "unknown"; 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, static int add_event(struct list_head **_list, int *idx,
struct perf_event_attr *attr, char *name) struct perf_event_attr *attr, char *name)
{ {
...@@ -375,19 +206,20 @@ static int add_event(struct list_head **_list, int *idx, ...@@ -375,19 +206,20 @@ static int add_event(struct list_head **_list, int *idx,
return -ENOMEM; return -ENOMEM;
} }
evsel->name = strdup(name); if (name)
evsel->name = strdup(name);
list_add_tail(&evsel->node, list); list_add_tail(&evsel->node, list);
*_list = list; *_list = list;
return 0; 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 i, j;
int n, longest = -1; int n, longest = -1;
for (i = 0; i < size; i++) { 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]); n = strlen(names[i][j]);
if (n > longest && !strncasecmp(str, names[i][j], n)) if (n > longest && !strncasecmp(str, names[i][j], n))
longest = n; longest = n;
...@@ -412,7 +244,7 @@ int parse_events_add_cache(struct list_head **list, int *idx, ...@@ -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 * No fallback - if we cannot get a clear cache type
* then bail out: * then bail out:
*/ */
cache_type = parse_aliases(type, hw_cache, cache_type = parse_aliases(type, perf_evsel__hw_cache,
PERF_COUNT_HW_CACHE_MAX); PERF_COUNT_HW_CACHE_MAX);
if (cache_type == -1) if (cache_type == -1)
return -EINVAL; return -EINVAL;
...@@ -425,18 +257,18 @@ int parse_events_add_cache(struct list_head **list, int *idx, ...@@ -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); snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
if (cache_op == -1) { 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); PERF_COUNT_HW_CACHE_OP_MAX);
if (cache_op >= 0) { 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; return -EINVAL;
continue; continue;
} }
} }
if (cache_result == -1) { if (cache_result == -1) {
cache_result = parse_aliases(str, hw_cache_result, cache_result = parse_aliases(str, perf_evsel__hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX); PERF_COUNT_HW_CACHE_RESULT_MAX);
if (cache_result >= 0) if (cache_result >= 0)
continue; continue;
} }
...@@ -668,8 +500,7 @@ int parse_events_add_numeric(struct list_head **list, int *idx, ...@@ -668,8 +500,7 @@ int parse_events_add_numeric(struct list_head **list, int *idx,
config_attr(&attr, head_config, 1)) config_attr(&attr, head_config, 1))
return -EINVAL; return -EINVAL;
return add_event(list, idx, &attr, return add_event(list, idx, &attr, NULL);
(char *) __event_name(type, config));
} }
static int parse_events__is_name_term(struct parse_events__term *term) 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) ...@@ -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; return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
} }
static char *pmu_event_name(struct perf_event_attr *attr, static char *pmu_event_name(struct list_head *head_terms)
struct list_head *head_terms)
{ {
struct parse_events__term *term; struct parse_events__term *term;
...@@ -686,7 +516,7 @@ static char *pmu_event_name(struct perf_event_attr *attr, ...@@ -686,7 +516,7 @@ static char *pmu_event_name(struct perf_event_attr *attr,
if (parse_events__is_name_term(term)) if (parse_events__is_name_term(term))
return term->val.str; 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, 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, ...@@ -714,7 +544,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
return -EINVAL; return -EINVAL;
return add_event(list, idx, &attr, 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, void parse_events_update_lists(struct list_head *list_event,
...@@ -1010,16 +840,17 @@ void print_events_type(u8 type) ...@@ -1010,16 +840,17 @@ void print_events_type(u8 type)
int print_hwcache_events(const char *event_glob) int print_hwcache_events(const char *event_glob)
{ {
unsigned int type, op, i, printed = 0; unsigned int type, op, i, printed = 0;
char name[64];
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
/* skip invalid cache type */ /* skip invalid cache type */
if (!is_cache_op_valid(type, op)) if (!perf_evsel__is_cache_op_valid(type, op))
continue; continue;
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { 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)) if (event_glob != NULL && !strglobmatch(name, event_glob))
continue; continue;
......
...@@ -26,8 +26,6 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config); ...@@ -26,8 +26,6 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
extern bool have_tracepoints(struct list_head *evlist); extern bool have_tracepoints(struct list_head *evlist);
const char *event_type(int type); 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, extern int parse_events_option(const struct option *opt, const char *str,
int unset); int unset);
......
...@@ -289,7 +289,6 @@ struct branch_info *machine__resolve_bstack(struct machine *self, ...@@ -289,7 +289,6 @@ struct branch_info *machine__resolve_bstack(struct machine *self,
} }
int machine__resolve_callchain(struct machine *self, int machine__resolve_callchain(struct machine *self,
struct perf_evsel *evsel __used,
struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent) struct symbol **parent)
...@@ -1449,7 +1448,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) ...@@ -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); ret += hists__fprintf_nr_events(&session->hists, fp);
list_for_each_entry(pos, &session->evlist->entries, node) { 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); ret += hists__fprintf_nr_events(&pos->hists, fp);
} }
...@@ -1490,8 +1489,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, ...@@ -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, void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel, struct machine *machine, int print_sym,
int print_sym, int print_dso, int print_symoffset) int print_dso, int print_symoffset)
{ {
struct addr_location al; struct addr_location al;
struct callchain_cursor_node *node; struct callchain_cursor_node *node;
...@@ -1505,7 +1504,7 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, ...@@ -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 (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) { sample->callchain, NULL) != 0) {
if (verbose) if (verbose)
error("Failed to resolve callchain. Skipping\n"); error("Failed to resolve callchain. Skipping\n");
......
...@@ -151,8 +151,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, ...@@ -151,8 +151,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type); unsigned int type);
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel, struct machine *machine, int print_sym,
int print_sym, int print_dso, int print_symoffset); int print_dso, int print_symoffset);
int perf_session__cpu_bitmap(struct perf_session *session, int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap); const char *cpu_list, unsigned long *cpu_bitmap);
......
...@@ -241,6 +241,54 @@ struct sort_entry sort_sym = { ...@@ -241,6 +241,54 @@ struct sort_entry sort_sym = {
.se_width_idx = HISTC_SYMBOL, .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 */ /* --sort parent */
static int64_t static int64_t
...@@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = { ...@@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = {
DIM(SORT_PARENT, "parent", sort_parent), DIM(SORT_PARENT, "parent", sort_parent),
DIM(SORT_CPU, "cpu", sort_cpu), DIM(SORT_CPU, "cpu", sort_cpu),
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
DIM(SORT_SRCLINE, "srcline", sort_srcline),
}; };
int sort_dimension__add(const char *tok) int sort_dimension__add(const char *tok)
......
...@@ -71,6 +71,7 @@ struct hist_entry { ...@@ -71,6 +71,7 @@ struct hist_entry {
char level; char level;
bool used; bool used;
u8 filtered; u8 filtered;
char *srcline;
struct symbol *parent; struct symbol *parent;
union { union {
unsigned long position; unsigned long position;
...@@ -93,6 +94,7 @@ enum sort_type { ...@@ -93,6 +94,7 @@ enum sort_type {
SORT_SYM_FROM, SORT_SYM_FROM,
SORT_SYM_TO, SORT_SYM_TO,
SORT_MISPREDICT, SORT_MISPREDICT,
SORT_SRCLINE,
}; };
/* /*
......
...@@ -313,3 +313,25 @@ int strtailcmp(const char *s1, const char *s2) ...@@ -313,3 +313,25 @@ int strtailcmp(const char *s1, const char *s2)
return 0; 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;
}
...@@ -65,7 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) ...@@ -65,7 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
top->freq ? "Hz" : ""); 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, "], "); ret += SNPRINTF(bf + ret, size - ret, "], ");
......
...@@ -264,4 +264,6 @@ bool is_power_of_2(unsigned long n) ...@@ -264,4 +264,6 @@ bool is_power_of_2(unsigned long n)
size_t hex_width(u64 v); size_t hex_width(u64 v);
char *rtrim(char *s);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment