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:: -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>::

View File

@ -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::

View File

@ -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
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

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) { 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,

View File

@ -265,7 +265,7 @@ try_again:
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)
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;
} }

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) 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,
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,
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,
} }
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,
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);

View File

@ -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;
} }

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) #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,
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,
/* 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,
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,
} }
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,
} }
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)
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,
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,
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,
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,
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));
} }

View File

@ -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)
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)
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)
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)
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)
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",

View File

@ -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;
} }
} }

View File

@ -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)
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)
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,
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 @@ try_again:
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"

View File

@ -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

View File

@ -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,
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;

View File

@ -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 @@ 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) 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,
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,
"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,
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 @@ browse_hists:
*/ */
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,
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,
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);

View File

@ -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)
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,
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,
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,
gtk_main(); gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0; return 0;
} }

View File

@ -1,8 +1,39 @@
#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_ */

View File

@ -1,12 +1,17 @@
#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();
} }

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; 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)
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)
SLsmg_refresh(); SLsmg_refresh();
SLsmg_reset_smg(); SLsmg_reset_smg();
SLang_reset_tty(); 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 "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; fprintf(stderr, "Error:\n");
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);
vfprintf(stderr, format, args); 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 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;
} }

View File

@ -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_ */

View File

@ -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;

View File

@ -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,
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);

View File

@ -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)
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)
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)
} }
#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 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) { 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; 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: 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.
*
* 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;
} }
return ret; evsel->name = strdup(bf);
return evsel->name ?: "unknown";
} }
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,

View File

@ -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);

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 * 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;
/* /*

View File

@ -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 */
}; };

View File

@ -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,

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 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;
} }

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_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)
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)
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,
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,
* 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,
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,
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)
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,
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,
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)
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;

View File

@ -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);

View File

@ -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)
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,
} }
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,
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");

View File

@ -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);

View File

@ -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[] = {
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)

View File

@ -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 {
SORT_SYM_FROM, SORT_SYM_FROM,
SORT_SYM_TO, SORT_SYM_TO,
SORT_MISPREDICT, SORT_MISPREDICT,
SORT_SRCLINE,
}; };
/* /*

View File

@ -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;
}

View File

@ -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, "], ");

View File

@ -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