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:
commit
32c46e579b
|
@ -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.
|
||||||
|
|
||||||
-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
|
||||||
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
|
||||||
|
|
|
@ -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 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
#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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
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"
|
||||||
|
|
|
@ -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,
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, ...)
|
||||||
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)));
|
||||||
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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
/*
|
/*
|
||||||
* 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 {
|
||||||
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);
|
||||||
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)
|
||||||
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[] = {
|
||||||
#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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
|
|
|
@ -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 = {
|
||||||
.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)
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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)
|
||||||
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)
|
||||||
|
|
||||||
size_t hex_width(u64 v);
|
size_t hex_width(u64 v);
|
||||||
|
|
||||||
|
char *rtrim(char *s);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue