mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-27 01:30:55 +07:00
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::
|
||||
--sort=::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent.
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
|
||||
|
||||
-p::
|
||||
--parent=<regex>::
|
||||
|
@ -112,7 +112,7 @@ Default is to monitor all CPUS.
|
||||
|
||||
-s::
|
||||
--sort::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
|
||||
|
||||
-n::
|
||||
--show-nr-samples::
|
||||
|
@ -503,6 +503,7 @@ else
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/util.o
|
||||
LIB_H += ui/browser.h
|
||||
LIB_H += ui/browsers/map.h
|
||||
LIB_H += ui/helpline.h
|
||||
@ -522,13 +523,18 @@ else
|
||||
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
|
||||
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
|
||||
else
|
||||
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y)
|
||||
BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR
|
||||
endif
|
||||
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
|
||||
EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
# Make sure that it'd be included only once.
|
||||
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
@ -60,7 +60,7 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail
|
||||
list_for_each_entry(pos, &session->evlist->entries, node) {
|
||||
bool first = true;
|
||||
|
||||
printf("%s", event_name(pos));
|
||||
printf("%s", perf_evsel__name(pos));
|
||||
|
||||
if (details->verbose || details->freq) {
|
||||
comma_printf(&first, " sample_freq=%" PRIu64,
|
||||
|
@ -265,7 +265,7 @@ static void perf_record__open(struct perf_record *rec)
|
||||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
event_name(pos));
|
||||
perf_evsel__name(pos));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -916,7 +916,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
usage_with_options(record_usage, record_options);
|
||||
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_header__push_event(pos->attr.config, event_name(pos)))
|
||||
if (perf_header__push_event(pos->attr.config, perf_evsel__name(pos)))
|
||||
goto out_free_fd;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
|
||||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain)
|
||||
&& sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
err = machine__resolve_callchain(machine, al->thread,
|
||||
sample->callchain, &parent);
|
||||
if (err)
|
||||
return err;
|
||||
@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
||||
struct hist_entry *he;
|
||||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
err = machine__resolve_callchain(machine, al->thread,
|
||||
sample->callchain, &parent);
|
||||
if (err)
|
||||
return err;
|
||||
@ -230,7 +230,7 @@ static int process_read_event(struct perf_tool *tool,
|
||||
struct perf_report *rep = container_of(tool, struct perf_report, tool);
|
||||
|
||||
if (rep->show_threads) {
|
||||
const char *name = evsel ? event_name(evsel) : "unknown";
|
||||
const char *name = evsel ? perf_evsel__name(evsel) : "unknown";
|
||||
perf_read_values_add_value(&rep->show_threads_values,
|
||||
event->read.pid, event->read.tid,
|
||||
event->read.id,
|
||||
@ -239,7 +239,7 @@ static int process_read_event(struct perf_tool *tool,
|
||||
}
|
||||
|
||||
dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
|
||||
evsel ? event_name(evsel) : "FAIL",
|
||||
evsel ? perf_evsel__name(evsel) : "FAIL",
|
||||
event->read.value);
|
||||
|
||||
return 0;
|
||||
@ -314,7 +314,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = event_name(pos);
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
|
||||
hists__fprintf_nr_sample_events(hists, evname, stdout);
|
||||
hists__fprintf(hists, NULL, false, true, 0, 0, stdout);
|
||||
|
@ -1601,7 +1601,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %s event, skipping it.\n",
|
||||
evsel->name);
|
||||
perf_evsel__name(evsel));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -137,10 +137,11 @@ static const char *output_field2str(enum perf_output_field field)
|
||||
|
||||
#define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x)
|
||||
|
||||
static int perf_event_attr__check_stype(struct perf_event_attr *attr,
|
||||
u64 sample_type, const char *sample_msg,
|
||||
enum perf_output_field field)
|
||||
static int perf_evsel__check_stype(struct perf_evsel *evsel,
|
||||
u64 sample_type, const char *sample_msg,
|
||||
enum perf_output_field field)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int type = attr->type;
|
||||
const char *evname;
|
||||
|
||||
@ -148,7 +149,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
|
||||
return 0;
|
||||
|
||||
if (output[type].user_set) {
|
||||
evname = __event_name(attr->type, attr->config);
|
||||
evname = perf_evsel__name(evsel);
|
||||
pr_err("Samples for '%s' event do not have %s attribute set. "
|
||||
"Cannot print '%s' field.\n",
|
||||
evname, sample_msg, output_field2str(field));
|
||||
@ -157,7 +158,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
|
||||
|
||||
/* user did not ask for it explicitly so remove from the default list */
|
||||
output[type].fields &= ~field;
|
||||
evname = __event_name(attr->type, attr->config);
|
||||
evname = perf_evsel__name(evsel);
|
||||
pr_debug("Samples for '%s' event do not have %s attribute set. "
|
||||
"Skipping '%s' field.\n",
|
||||
evname, sample_msg, output_field2str(field));
|
||||
@ -175,8 +176,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
||||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(IP)) {
|
||||
if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP",
|
||||
PERF_OUTPUT_IP))
|
||||
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
|
||||
PERF_OUTPUT_IP))
|
||||
return -EINVAL;
|
||||
|
||||
if (!no_callchain &&
|
||||
@ -185,8 +186,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(ADDR) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR",
|
||||
PERF_OUTPUT_ADDR))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
|
||||
PERF_OUTPUT_ADDR))
|
||||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
|
||||
@ -208,18 +209,18 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
||||
}
|
||||
|
||||
if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID",
|
||||
PERF_OUTPUT_TID|PERF_OUTPUT_PID))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID",
|
||||
PERF_OUTPUT_TID|PERF_OUTPUT_PID))
|
||||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(TIME) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME",
|
||||
PERF_OUTPUT_TIME))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME",
|
||||
PERF_OUTPUT_TIME))
|
||||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(CPU) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU",
|
||||
PERF_OUTPUT_CPU))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
|
||||
PERF_OUTPUT_CPU))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
@ -258,9 +259,10 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
|
||||
static void print_sample_start(struct perf_sample *sample,
|
||||
struct thread *thread,
|
||||
struct perf_event_attr *attr)
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int type;
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
struct event_format *event;
|
||||
const char *evname = NULL;
|
||||
unsigned long secs;
|
||||
@ -305,7 +307,7 @@ static void print_sample_start(struct perf_sample *sample,
|
||||
if (event)
|
||||
evname = event->name;
|
||||
} else
|
||||
evname = __event_name(attr->type, attr->config);
|
||||
evname = perf_evsel__name(evsel);
|
||||
|
||||
printf("%s: ", evname ? evname : "[unknown]");
|
||||
}
|
||||
@ -387,7 +389,7 @@ static void print_sample_bts(union perf_event *event,
|
||||
printf(" ");
|
||||
else
|
||||
printf("\n");
|
||||
perf_event__print_ip(event, sample, machine, evsel,
|
||||
perf_event__print_ip(event, sample, machine,
|
||||
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
|
||||
PRINT_FIELD(SYMOFFSET));
|
||||
}
|
||||
@ -412,7 +414,7 @@ static void process_event(union perf_event *event __unused,
|
||||
if (output[attr->type].fields == 0)
|
||||
return;
|
||||
|
||||
print_sample_start(sample, thread, attr);
|
||||
print_sample_start(sample, thread, evsel);
|
||||
|
||||
if (is_bts_event(attr)) {
|
||||
print_sample_bts(event, sample, evsel, machine, thread);
|
||||
@ -431,7 +433,7 @@ static void process_event(union perf_event *event __unused,
|
||||
printf(" ");
|
||||
else
|
||||
printf("\n");
|
||||
perf_event__print_ip(event, sample, machine, evsel,
|
||||
perf_event__print_ip(event, sample, machine,
|
||||
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
|
||||
PRINT_FIELD(SYMOFFSET));
|
||||
}
|
||||
|
@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
|
||||
|
||||
if (verbose) {
|
||||
fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
|
||||
event_name(counter), count[0], count[1], count[2]);
|
||||
perf_evsel__name(counter), count[0], count[1], count[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -496,7 +496,7 @@ static int run_perf_stat(int argc __used, const char **argv)
|
||||
errno == ENXIO) {
|
||||
if (verbose)
|
||||
ui__warning("%s event is not supported by the kernel.\n",
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
counter->supported = false;
|
||||
continue;
|
||||
}
|
||||
@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
csv_output ? 0 : -4,
|
||||
evsel_list->cpus->map[cpu], csv_sep);
|
||||
|
||||
fprintf(output, fmt, cpustr, msecs, csv_sep, event_name(evsel));
|
||||
fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel));
|
||||
|
||||
if (evsel->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
||||
@ -792,7 +792,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
else
|
||||
cpu = 0;
|
||||
|
||||
fprintf(output, fmt, cpustr, avg, csv_sep, event_name(evsel));
|
||||
fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel));
|
||||
|
||||
if (evsel->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
||||
@ -908,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter)
|
||||
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
|
||||
csv_sep,
|
||||
csv_output ? 0 : -24,
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
|
||||
if (counter->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, counter->cgrp->name);
|
||||
@ -961,7 +961,7 @@ static void print_counter(struct perf_evsel *counter)
|
||||
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
|
||||
csv_sep,
|
||||
csv_output ? 0 : -24,
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
|
||||
if (counter->cgrp)
|
||||
fprintf(output, "%s%s",
|
||||
|
@ -583,7 +583,7 @@ static int test__basic_mmap(void)
|
||||
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
|
||||
pr_debug("expected %d %s events, got %d\n",
|
||||
expected_nr_events[evsel->idx],
|
||||
event_name(evsel), nr_events[evsel->idx]);
|
||||
perf_evsel__name(evsel), nr_events[evsel->idx]);
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top)
|
||||
if (notes->src == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name);
|
||||
printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);
|
||||
printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
|
||||
|
||||
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx,
|
||||
@ -408,7 +408,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)
|
||||
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries);
|
||||
|
||||
if (top->evlist->nr_entries > 1)
|
||||
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top->sym_evsel));
|
||||
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", perf_evsel__name(top->sym_evsel));
|
||||
|
||||
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter);
|
||||
|
||||
@ -503,13 +503,13 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
fprintf(stderr, "\nAvailable events:");
|
||||
|
||||
list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
|
||||
fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel));
|
||||
fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel));
|
||||
|
||||
prompt_integer(&counter, "Enter details event counter");
|
||||
|
||||
if (counter >= top->evlist->nr_entries) {
|
||||
top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
|
||||
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel));
|
||||
fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));
|
||||
sleep(1);
|
||||
break;
|
||||
}
|
||||
@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain) &&
|
||||
sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al.thread,
|
||||
err = machine__resolve_callchain(machine, al.thread,
|
||||
sample->callchain, &parent);
|
||||
if (err)
|
||||
return;
|
||||
@ -960,7 +960,7 @@ static void perf_top__start_counters(struct perf_top *top)
|
||||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
goto out_err;
|
||||
} else if (err == EMFILE) {
|
||||
ui__error("Too many events are opened.\n"
|
||||
|
@ -78,6 +78,19 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
endef
|
||||
|
||||
define SOURCE_GTK2_INFOBAR
|
||||
#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
|
||||
#include <gtk/gtk.h>
|
||||
#pragma GCC diagnostic error \"-Wstrict-prototypes\"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
gtk_info_bar_new();
|
||||
|
||||
return 0;
|
||||
}
|
||||
endef
|
||||
endif
|
||||
|
||||
ifndef NO_LIBPERL
|
||||
|
@ -814,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
{
|
||||
struct disasm_line *pos, *n;
|
||||
struct annotation *notes;
|
||||
const size_t size = symbol__size(sym);
|
||||
size_t size;
|
||||
struct map_symbol ms = {
|
||||
.map = map,
|
||||
.sym = sym,
|
||||
@ -834,6 +834,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
if (sym == NULL)
|
||||
return -1;
|
||||
|
||||
size = symbol__size(sym);
|
||||
|
||||
if (map->dso->annotate_warned)
|
||||
return -1;
|
||||
|
||||
|
@ -23,6 +23,7 @@ struct hist_browser {
|
||||
struct hists *hists;
|
||||
struct hist_entry *he_selection;
|
||||
struct map_symbol *selection;
|
||||
int print_seq;
|
||||
bool has_symbols;
|
||||
};
|
||||
|
||||
@ -800,6 +801,196 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
|
||||
}
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
|
||||
struct callchain_node *chain_node,
|
||||
u64 total, int level,
|
||||
FILE *fp)
|
||||
{
|
||||
struct rb_node *node;
|
||||
int offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total, remaining;
|
||||
int printed = 0;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = chain_node->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
remaining = new_total;
|
||||
node = rb_first(&chain_node->rb_root);
|
||||
while (node) {
|
||||
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
||||
struct rb_node *next = rb_next(node);
|
||||
u64 cumul = callchain_cumul_hits(child);
|
||||
struct callchain_list *chain;
|
||||
char folded_sign = ' ';
|
||||
int first = true;
|
||||
int extra_offset = 0;
|
||||
|
||||
remaining -= cumul;
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
|
||||
const char *str;
|
||||
bool was_first = first;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
extra_offset = LEVEL_OFFSET_STEP;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
|
||||
alloc_str = NULL;
|
||||
str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
|
||||
if (was_first) {
|
||||
double percent = cumul * 100.0 / new_total;
|
||||
|
||||
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
|
||||
str = "Not enough memory!";
|
||||
else
|
||||
str = alloc_str;
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
|
||||
free(alloc_str);
|
||||
if (folded_sign == '+')
|
||||
break;
|
||||
}
|
||||
|
||||
if (folded_sign == '-') {
|
||||
const int new_level = level + (extra_offset ? 2 : 1);
|
||||
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
|
||||
new_level, fp);
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
|
||||
struct callchain_node *node,
|
||||
int level, FILE *fp)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
int offset = level * LEVEL_OFFSET_STEP;
|
||||
char folded_sign = ' ';
|
||||
int printed = 0;
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char ipstr[BITS_PER_LONG / 4 + 1], *s;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
|
||||
printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
|
||||
}
|
||||
|
||||
if (folded_sign == '-')
|
||||
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
|
||||
browser->hists->stats.total_period,
|
||||
level + 1, fp);
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
|
||||
struct rb_root *chain, int level, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
int printed = 0;
|
||||
|
||||
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
|
||||
printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
struct hist_entry *he, FILE *fp)
|
||||
{
|
||||
char s[8192];
|
||||
double percent;
|
||||
int printed = 0;
|
||||
char folded_sign = ' ';
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
folded_sign = hist_entry__folded(he);
|
||||
|
||||
hist_entry__snprintf(he, s, sizeof(s), browser->hists);
|
||||
percent = (he->period * 100.0) / browser->hists->stats.total_period;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
printed += fprintf(fp, "%c ", folded_sign);
|
||||
|
||||
printed += fprintf(fp, " %5.2f%%", percent);
|
||||
|
||||
if (symbol_conf.show_nr_samples)
|
||||
printed += fprintf(fp, " %11u", he->nr_events);
|
||||
|
||||
if (symbol_conf.show_total_period)
|
||||
printed += fprintf(fp, " %12" PRIu64, he->period);
|
||||
|
||||
printed += fprintf(fp, "%s\n", rtrim(s));
|
||||
|
||||
if (folded_sign == '-')
|
||||
printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
|
||||
int printed = 0;
|
||||
|
||||
while (nd) {
|
||||
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
||||
|
||||
printed += hist_browser__fprintf_entry(browser, h, fp);
|
||||
nd = hists__filter_entries(rb_next(nd));
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__dump(struct hist_browser *browser)
|
||||
{
|
||||
char filename[64];
|
||||
FILE *fp;
|
||||
|
||||
while (1) {
|
||||
scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
|
||||
if (access(filename, F_OK))
|
||||
break;
|
||||
/*
|
||||
* XXX: Just an arbitrary lazy upper limit
|
||||
*/
|
||||
if (++browser->print_seq == 8192) {
|
||||
ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fp = fopen(filename, "w");
|
||||
if (fp == NULL) {
|
||||
char bf[64];
|
||||
strerror_r(errno, bf, sizeof(bf));
|
||||
ui_helpline__fpush("Couldn't write to %s: %s", filename, bf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
++browser->print_seq;
|
||||
hist_browser__fprintf(browser, fp);
|
||||
fclose(fp);
|
||||
ui_helpline__fpush("%s written!", filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hist_browser *hist_browser__new(struct hists *hists)
|
||||
{
|
||||
struct hist_browser *browser = zalloc(sizeof(*browser));
|
||||
@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
browser->selection->map->dso->annotate_warned)
|
||||
continue;
|
||||
goto do_annotate;
|
||||
case 'P':
|
||||
hist_browser__dump(browser);
|
||||
continue;
|
||||
case 'd':
|
||||
goto zoom_dso;
|
||||
case 't':
|
||||
@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"/ Filter symbol by name");
|
||||
continue;
|
||||
case K_ENTER:
|
||||
@ -1172,7 +1367,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
|
||||
struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
const char *ev_name = event_name(evsel);
|
||||
const char *ev_name = perf_evsel__name(evsel);
|
||||
char bf[256], unit;
|
||||
const char *warn = " ";
|
||||
size_t printed;
|
||||
@ -1240,7 +1435,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
|
||||
*/
|
||||
if (timer)
|
||||
timer(arg);
|
||||
ev_name = event_name(pos);
|
||||
ev_name = perf_evsel__name(pos);
|
||||
key = perf_evsel__hists_browse(pos, nr_events, help,
|
||||
ev_name, true, timer,
|
||||
arg, delay_secs);
|
||||
@ -1309,17 +1504,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
ui_helpline__push("Press ESC to exit");
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
const char *ev_name = event_name(pos);
|
||||
const char *ev_name = perf_evsel__name(pos);
|
||||
size_t line_len = strlen(ev_name) + 7;
|
||||
|
||||
if (menu.b.width < line_len)
|
||||
menu.b.width = line_len;
|
||||
/*
|
||||
* Cache the evsel name, tracepoints have a _high_ cost per
|
||||
* event_name() call.
|
||||
*/
|
||||
if (pos->name == NULL)
|
||||
pos->name = strdup(ev_name);
|
||||
}
|
||||
|
||||
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
|
||||
@ -1330,11 +1519,10 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
{
|
||||
|
||||
if (evlist->nr_entries == 1) {
|
||||
struct perf_evsel *first = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
const char *ev_name = event_name(first);
|
||||
const char *ev_name = perf_evsel__name(first);
|
||||
return perf_evsel__hists_browse(first, evlist->nr_entries, help,
|
||||
ev_name, false, timer, arg,
|
||||
delay_secs);
|
||||
|
@ -11,8 +11,8 @@
|
||||
|
||||
static void perf_gtk__signal(int sig)
|
||||
{
|
||||
perf_gtk__exit(false);
|
||||
psignal(sig, "perf");
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static void perf_gtk__resize_window(GtkWidget *window)
|
||||
@ -122,13 +122,59 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
|
||||
gtk_container_add(GTK_CONTAINER(window), view);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
static GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
{
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *label;
|
||||
GtkWidget *content_area;
|
||||
|
||||
info_bar = gtk_info_bar_new();
|
||||
gtk_widget_set_no_show_all(info_bar, TRUE);
|
||||
|
||||
label = gtk_label_new("");
|
||||
gtk_widget_show(label);
|
||||
|
||||
content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar));
|
||||
gtk_container_add(GTK_CONTAINER(content_area), label);
|
||||
|
||||
gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK,
|
||||
GTK_RESPONSE_OK);
|
||||
g_signal_connect(info_bar, "response",
|
||||
G_CALLBACK(gtk_widget_hide), NULL);
|
||||
|
||||
pgctx->info_bar = info_bar;
|
||||
pgctx->message_label = label;
|
||||
|
||||
return info_bar;
|
||||
}
|
||||
#endif
|
||||
|
||||
static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
{
|
||||
GtkWidget *stbar;
|
||||
unsigned ctxid;
|
||||
|
||||
stbar = gtk_statusbar_new();
|
||||
|
||||
ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(stbar),
|
||||
"perf report");
|
||||
pgctx->statbar = stbar;
|
||||
pgctx->statbar_ctx_id = ctxid;
|
||||
|
||||
return stbar;
|
||||
}
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help __used,
|
||||
void (*timer) (void *arg)__used,
|
||||
void *arg __used, int delay_secs __used)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *notebook;
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *statbar;
|
||||
GtkWidget *window;
|
||||
|
||||
signal(SIGSEGV, perf_gtk__signal);
|
||||
@ -143,11 +189,17 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
|
||||
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
|
||||
|
||||
pgctx = perf_gtk__activate_context(window);
|
||||
if (!pgctx)
|
||||
return -1;
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
|
||||
notebook = gtk_notebook_new();
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = event_name(pos);
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *tab_label;
|
||||
|
||||
@ -164,7 +216,16 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), notebook);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
|
||||
|
||||
info_bar = perf_gtk__setup_info_bar();
|
||||
if (info_bar)
|
||||
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
|
||||
|
||||
statbar = perf_gtk__setup_statusbar();
|
||||
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
|
||||
gtk_main();
|
||||
|
||||
perf_gtk__deactivate_context(&pgctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,8 +1,39 @@
|
||||
#ifndef _PERF_GTK_H_
|
||||
#define _PERF_GTK_H_ 1
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#include <gtk/gtk.h>
|
||||
#pragma GCC diagnostic error "-Wstrict-prototypes"
|
||||
|
||||
|
||||
struct perf_gtk_context {
|
||||
GtkWidget *main_window;
|
||||
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *message_label;
|
||||
#endif
|
||||
GtkWidget *statbar;
|
||||
guint statbar_ctx_id;
|
||||
};
|
||||
|
||||
extern struct perf_gtk_context *pgctx;
|
||||
|
||||
static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx)
|
||||
{
|
||||
return ctx && ctx->main_window;
|
||||
}
|
||||
|
||||
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
|
||||
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
|
||||
|
||||
#ifndef HAVE_GTK_INFO_BAR
|
||||
static inline GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PERF_GTK_H_ */
|
||||
|
@ -1,12 +1,17 @@
|
||||
#include "gtk.h"
|
||||
#include "../../util/cache.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
extern struct perf_error_ops perf_gtk_eops;
|
||||
|
||||
int perf_gtk__init(void)
|
||||
{
|
||||
perf_error__register(&perf_gtk_eops);
|
||||
return gtk_init_check(NULL, NULL) ? 0 : -1;
|
||||
}
|
||||
|
||||
void perf_gtk__exit(bool wait_for_ok __used)
|
||||
{
|
||||
perf_error__unregister(&perf_gtk_eops);
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
129
tools/perf/ui/gtk/util.c
Normal file
129
tools/perf/ui/gtk/util.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include "../util.h"
|
||||
#include "../../util/debug.h"
|
||||
#include "gtk.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
struct perf_gtk_context *pgctx;
|
||||
|
||||
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window)
|
||||
{
|
||||
struct perf_gtk_context *ctx;
|
||||
|
||||
ctx = malloc(sizeof(*pgctx));
|
||||
if (ctx)
|
||||
ctx->main_window = window;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)
|
||||
{
|
||||
if (!perf_gtk__is_active_context(*ctx))
|
||||
return -1;
|
||||
|
||||
free(*ctx);
|
||||
*ctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_gtk__error(const char *format, va_list args)
|
||||
{
|
||||
char *msg;
|
||||
GtkWidget *dialog;
|
||||
|
||||
if (!perf_gtk__is_active_context(pgctx) ||
|
||||
vasprintf(&msg, format, args) < 0) {
|
||||
fprintf(stderr, "Error:\n");
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"<b>Error</b>\n\n%s", msg);
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
static int perf_gtk__warning_info_bar(const char *format, va_list args)
|
||||
{
|
||||
char *msg;
|
||||
|
||||
if (!perf_gtk__is_active_context(pgctx) ||
|
||||
vasprintf(&msg, format, args) < 0) {
|
||||
fprintf(stderr, "Warning:\n");
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gtk_label_set_text(GTK_LABEL(pgctx->message_label), msg);
|
||||
gtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx->info_bar),
|
||||
GTK_MESSAGE_WARNING);
|
||||
gtk_widget_show(pgctx->info_bar);
|
||||
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int perf_gtk__warning_statusbar(const char *format, va_list args)
|
||||
{
|
||||
char *msg, *p;
|
||||
|
||||
if (!perf_gtk__is_active_context(pgctx) ||
|
||||
vasprintf(&msg, format, args) < 0) {
|
||||
fprintf(stderr, "Warning:\n");
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
|
||||
pgctx->statbar_ctx_id);
|
||||
|
||||
/* Only first line can be displayed */
|
||||
p = strchr(msg, '\n');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
|
||||
pgctx->statbar_ctx_id, msg);
|
||||
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct perf_error_ops perf_gtk_eops = {
|
||||
.error = perf_gtk__error,
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
.warning = perf_gtk__warning_info_bar,
|
||||
#else
|
||||
.warning = perf_gtk__warning_statusbar,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* FIXME: Functions below should be implemented properly.
|
||||
* For now, just add stubs for NO_NEWT=1 build.
|
||||
*/
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
int ui_helpline__show_help(const char *format __used, va_list ap __used)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ui_progress__update(u64 curr __used, u64 total __used,
|
||||
const char *title __used)
|
||||
{
|
||||
}
|
||||
#endif
|
@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static volatile int ui__need_resize;
|
||||
|
||||
extern struct perf_error_ops perf_tui_eops;
|
||||
|
||||
void ui__refresh_dimensions(bool force)
|
||||
{
|
||||
if (force || ui__need_resize) {
|
||||
@ -122,6 +124,8 @@ int ui__init(void)
|
||||
signal(SIGINT, ui__signal);
|
||||
signal(SIGQUIT, ui__signal);
|
||||
signal(SIGTERM, ui__signal);
|
||||
|
||||
perf_error__register(&perf_tui_eops);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok)
|
||||
SLsmg_refresh();
|
||||
SLsmg_reset_smg();
|
||||
SLang_reset_tty();
|
||||
|
||||
perf_error__unregister(&perf_tui_eops);
|
||||
}
|
||||
|
243
tools/perf/ui/tui/util.c
Normal file
243
tools/perf/ui/tui/util.c
Normal file
@ -0,0 +1,243 @@
|
||||
#include "../../util/util.h"
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
|
||||
#include "../../util/cache.h"
|
||||
#include "../../util/debug.h"
|
||||
#include "../browser.h"
|
||||
#include "../keysyms.h"
|
||||
#include "../helpline.h"
|
||||
#include "../ui.h"
|
||||
#include "../util.h"
|
||||
#include "../libslang.h"
|
||||
|
||||
static void ui_browser__argv_write(struct ui_browser *browser,
|
||||
void *entry, int row)
|
||||
{
|
||||
char **arg = entry;
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
|
||||
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
|
||||
HE_COLORSET_NORMAL);
|
||||
slsmg_write_nstring(*arg, browser->width);
|
||||
}
|
||||
|
||||
static int popup_menu__run(struct ui_browser *menu)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
key = ui_browser__run(menu, 0);
|
||||
|
||||
switch (key) {
|
||||
case K_RIGHT:
|
||||
case K_ENTER:
|
||||
key = menu->index;
|
||||
break;
|
||||
case K_LEFT:
|
||||
case K_ESC:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
key = -1;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(menu);
|
||||
return key;
|
||||
}
|
||||
|
||||
int ui__popup_menu(int argc, char * const argv[])
|
||||
{
|
||||
struct ui_browser menu = {
|
||||
.entries = (void *)argv,
|
||||
.refresh = ui_browser__argv_refresh,
|
||||
.seek = ui_browser__argv_seek,
|
||||
.write = ui_browser__argv_write,
|
||||
.nr_entries = argc,
|
||||
};
|
||||
|
||||
return popup_menu__run(&menu);
|
||||
}
|
||||
|
||||
int ui_browser__input_window(const char *title, const char *text, char *input,
|
||||
const char *exit_msg, int delay_secs)
|
||||
{
|
||||
int x, y, len, key;
|
||||
int max_len = 60, nr_lines = 0;
|
||||
static char buf[50];
|
||||
const char *t;
|
||||
|
||||
t = text;
|
||||
while (1) {
|
||||
const char *sep = strchr(t, '\n');
|
||||
|
||||
if (sep == NULL)
|
||||
sep = strchr(t, '\0');
|
||||
len = sep - t;
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
++nr_lines;
|
||||
if (*sep == '\0')
|
||||
break;
|
||||
t = sep + 1;
|
||||
}
|
||||
|
||||
max_len += 2;
|
||||
nr_lines += 8;
|
||||
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
|
||||
x = SLtt_Screen_Cols / 2 - max_len / 2;
|
||||
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, x++, nr_lines, max_len);
|
||||
if (title) {
|
||||
SLsmg_gotorc(y, x + 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
}
|
||||
SLsmg_gotorc(++y, x);
|
||||
nr_lines -= 7;
|
||||
max_len -= 2;
|
||||
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
|
||||
nr_lines, max_len, 1);
|
||||
y += nr_lines;
|
||||
len = 5;
|
||||
while (len--) {
|
||||
SLsmg_gotorc(y + len - 1, x);
|
||||
SLsmg_write_nstring((char *)" ", max_len);
|
||||
}
|
||||
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
|
||||
|
||||
SLsmg_gotorc(y + 3, x);
|
||||
SLsmg_write_nstring((char *)exit_msg, max_len);
|
||||
SLsmg_refresh();
|
||||
|
||||
x += 2;
|
||||
len = 0;
|
||||
key = ui__getch(delay_secs);
|
||||
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
|
||||
if (key == K_BKSPC) {
|
||||
if (len == 0)
|
||||
goto next_key;
|
||||
SLsmg_gotorc(y, x + --len);
|
||||
SLsmg_write_char(' ');
|
||||
} else {
|
||||
buf[len] = key;
|
||||
SLsmg_gotorc(y, x + len++);
|
||||
SLsmg_write_char(key);
|
||||
}
|
||||
SLsmg_refresh();
|
||||
|
||||
/* XXX more graceful overflow handling needed */
|
||||
if (len == sizeof(buf) - 1) {
|
||||
ui_helpline__push("maximum size of symbol name reached!");
|
||||
key = K_ENTER;
|
||||
break;
|
||||
}
|
||||
next_key:
|
||||
key = ui__getch(delay_secs);
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
strncpy(input, buf, len+1);
|
||||
return key;
|
||||
}
|
||||
|
||||
int ui__question_window(const char *title, const char *text,
|
||||
const char *exit_msg, int delay_secs)
|
||||
{
|
||||
int x, y;
|
||||
int max_len = 0, nr_lines = 0;
|
||||
const char *t;
|
||||
|
||||
t = text;
|
||||
while (1) {
|
||||
const char *sep = strchr(t, '\n');
|
||||
int len;
|
||||
|
||||
if (sep == NULL)
|
||||
sep = strchr(t, '\0');
|
||||
len = sep - t;
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
++nr_lines;
|
||||
if (*sep == '\0')
|
||||
break;
|
||||
t = sep + 1;
|
||||
}
|
||||
|
||||
max_len += 2;
|
||||
nr_lines += 4;
|
||||
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
|
||||
x = SLtt_Screen_Cols / 2 - max_len / 2;
|
||||
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, x++, nr_lines, max_len);
|
||||
if (title) {
|
||||
SLsmg_gotorc(y, x + 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
}
|
||||
SLsmg_gotorc(++y, x);
|
||||
nr_lines -= 2;
|
||||
max_len -= 2;
|
||||
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
|
||||
nr_lines, max_len, 1);
|
||||
SLsmg_gotorc(y + nr_lines - 2, x);
|
||||
SLsmg_write_nstring((char *)" ", max_len);
|
||||
SLsmg_gotorc(y + nr_lines - 1, x);
|
||||
SLsmg_write_nstring((char *)exit_msg, max_len);
|
||||
SLsmg_refresh();
|
||||
return ui__getch(delay_secs);
|
||||
}
|
||||
|
||||
int ui__help_window(const char *text)
|
||||
{
|
||||
return ui__question_window("Help", text, "Press any key...", 0);
|
||||
}
|
||||
|
||||
int ui__dialog_yesno(const char *msg)
|
||||
{
|
||||
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
|
||||
}
|
||||
|
||||
static int __ui__warning(const char *title, const char *format, va_list args)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (vasprintf(&s, format, args) > 0) {
|
||||
int key;
|
||||
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
key = ui__question_window(title, s, "Press any key...", 0);
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
free(s);
|
||||
return key;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s\n", title);
|
||||
vfprintf(stderr, format, args);
|
||||
return K_ESC;
|
||||
}
|
||||
|
||||
static int perf_tui__error(const char *format, va_list args)
|
||||
{
|
||||
return __ui__warning("Error:", format, args);
|
||||
}
|
||||
|
||||
static int perf_tui__warning(const char *format, va_list args)
|
||||
{
|
||||
return __ui__warning("Warning:", format, args);
|
||||
}
|
||||
|
||||
struct perf_error_ops perf_tui_eops = {
|
||||
.error = perf_tui__error,
|
||||
.warning = perf_tui__warning,
|
||||
};
|
@ -1,250 +1,85 @@
|
||||
#include "../util.h"
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
|
||||
#include "../cache.h"
|
||||
#include "../debug.h"
|
||||
#include "browser.h"
|
||||
#include "keysyms.h"
|
||||
#include "helpline.h"
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
#include "libslang.h"
|
||||
#include "../debug.h"
|
||||
|
||||
static void ui_browser__argv_write(struct ui_browser *browser,
|
||||
void *entry, int row)
|
||||
|
||||
/*
|
||||
* Default error logging functions
|
||||
*/
|
||||
static int perf_stdio__error(const char *format, va_list args)
|
||||
{
|
||||
char **arg = entry;
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
|
||||
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
|
||||
HE_COLORSET_NORMAL);
|
||||
slsmg_write_nstring(*arg, browser->width);
|
||||
}
|
||||
|
||||
static int popup_menu__run(struct ui_browser *menu)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
key = ui_browser__run(menu, 0);
|
||||
|
||||
switch (key) {
|
||||
case K_RIGHT:
|
||||
case K_ENTER:
|
||||
key = menu->index;
|
||||
break;
|
||||
case K_LEFT:
|
||||
case K_ESC:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
key = -1;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(menu);
|
||||
return key;
|
||||
}
|
||||
|
||||
int ui__popup_menu(int argc, char * const argv[])
|
||||
{
|
||||
struct ui_browser menu = {
|
||||
.entries = (void *)argv,
|
||||
.refresh = ui_browser__argv_refresh,
|
||||
.seek = ui_browser__argv_seek,
|
||||
.write = ui_browser__argv_write,
|
||||
.nr_entries = argc,
|
||||
};
|
||||
|
||||
return popup_menu__run(&menu);
|
||||
}
|
||||
|
||||
int ui_browser__input_window(const char *title, const char *text, char *input,
|
||||
const char *exit_msg, int delay_secs)
|
||||
{
|
||||
int x, y, len, key;
|
||||
int max_len = 60, nr_lines = 0;
|
||||
static char buf[50];
|
||||
const char *t;
|
||||
|
||||
t = text;
|
||||
while (1) {
|
||||
const char *sep = strchr(t, '\n');
|
||||
|
||||
if (sep == NULL)
|
||||
sep = strchr(t, '\0');
|
||||
len = sep - t;
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
++nr_lines;
|
||||
if (*sep == '\0')
|
||||
break;
|
||||
t = sep + 1;
|
||||
}
|
||||
|
||||
max_len += 2;
|
||||
nr_lines += 8;
|
||||
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
|
||||
x = SLtt_Screen_Cols / 2 - max_len / 2;
|
||||
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, x++, nr_lines, max_len);
|
||||
if (title) {
|
||||
SLsmg_gotorc(y, x + 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
}
|
||||
SLsmg_gotorc(++y, x);
|
||||
nr_lines -= 7;
|
||||
max_len -= 2;
|
||||
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
|
||||
nr_lines, max_len, 1);
|
||||
y += nr_lines;
|
||||
len = 5;
|
||||
while (len--) {
|
||||
SLsmg_gotorc(y + len - 1, x);
|
||||
SLsmg_write_nstring((char *)" ", max_len);
|
||||
}
|
||||
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
|
||||
|
||||
SLsmg_gotorc(y + 3, x);
|
||||
SLsmg_write_nstring((char *)exit_msg, max_len);
|
||||
SLsmg_refresh();
|
||||
|
||||
x += 2;
|
||||
len = 0;
|
||||
key = ui__getch(delay_secs);
|
||||
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
|
||||
if (key == K_BKSPC) {
|
||||
if (len == 0)
|
||||
goto next_key;
|
||||
SLsmg_gotorc(y, x + --len);
|
||||
SLsmg_write_char(' ');
|
||||
} else {
|
||||
buf[len] = key;
|
||||
SLsmg_gotorc(y, x + len++);
|
||||
SLsmg_write_char(key);
|
||||
}
|
||||
SLsmg_refresh();
|
||||
|
||||
/* XXX more graceful overflow handling needed */
|
||||
if (len == sizeof(buf) - 1) {
|
||||
ui_helpline__push("maximum size of symbol name reached!");
|
||||
key = K_ENTER;
|
||||
break;
|
||||
}
|
||||
next_key:
|
||||
key = ui__getch(delay_secs);
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
strncpy(input, buf, len+1);
|
||||
return key;
|
||||
}
|
||||
|
||||
int ui__question_window(const char *title, const char *text,
|
||||
const char *exit_msg, int delay_secs)
|
||||
{
|
||||
int x, y;
|
||||
int max_len = 0, nr_lines = 0;
|
||||
const char *t;
|
||||
|
||||
t = text;
|
||||
while (1) {
|
||||
const char *sep = strchr(t, '\n');
|
||||
int len;
|
||||
|
||||
if (sep == NULL)
|
||||
sep = strchr(t, '\0');
|
||||
len = sep - t;
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
++nr_lines;
|
||||
if (*sep == '\0')
|
||||
break;
|
||||
t = sep + 1;
|
||||
}
|
||||
|
||||
max_len += 2;
|
||||
nr_lines += 4;
|
||||
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
|
||||
x = SLtt_Screen_Cols / 2 - max_len / 2;
|
||||
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, x++, nr_lines, max_len);
|
||||
if (title) {
|
||||
SLsmg_gotorc(y, x + 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
}
|
||||
SLsmg_gotorc(++y, x);
|
||||
nr_lines -= 2;
|
||||
max_len -= 2;
|
||||
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
|
||||
nr_lines, max_len, 1);
|
||||
SLsmg_gotorc(y + nr_lines - 2, x);
|
||||
SLsmg_write_nstring((char *)" ", max_len);
|
||||
SLsmg_gotorc(y + nr_lines - 1, x);
|
||||
SLsmg_write_nstring((char *)exit_msg, max_len);
|
||||
SLsmg_refresh();
|
||||
return ui__getch(delay_secs);
|
||||
}
|
||||
|
||||
int ui__help_window(const char *text)
|
||||
{
|
||||
return ui__question_window("Help", text, "Press any key...", 0);
|
||||
}
|
||||
|
||||
int ui__dialog_yesno(const char *msg)
|
||||
{
|
||||
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
|
||||
}
|
||||
|
||||
int __ui__warning(const char *title, const char *format, va_list args)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
|
||||
int key;
|
||||
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
key = ui__question_window(title, s, "Press any key...", 0);
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
free(s);
|
||||
return key;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s:\n", title);
|
||||
fprintf(stderr, "Error:\n");
|
||||
vfprintf(stderr, format, args);
|
||||
return K_ESC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_stdio__warning(const char *format, va_list args)
|
||||
{
|
||||
fprintf(stderr, "Warning:\n");
|
||||
vfprintf(stderr, format, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_error_ops default_eops =
|
||||
{
|
||||
.error = perf_stdio__error,
|
||||
.warning = perf_stdio__warning,
|
||||
};
|
||||
|
||||
static struct perf_error_ops *perf_eops = &default_eops;
|
||||
|
||||
|
||||
int ui__error(const char *format, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
ret = perf_eops->error(format, args);
|
||||
va_end(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ui__warning(const char *format, ...)
|
||||
{
|
||||
int key;
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
key = __ui__warning("Warning", format, args);
|
||||
ret = perf_eops->warning(format, args);
|
||||
va_end(args);
|
||||
return key;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ui__error(const char *format, ...)
|
||||
|
||||
/**
|
||||
* perf_error__register - Register error logging functions
|
||||
* @eops: The pointer to error logging function struct
|
||||
*
|
||||
* Register UI-specific error logging functions. Before calling this,
|
||||
* other logging functions should be unregistered, if any.
|
||||
*/
|
||||
int perf_error__register(struct perf_error_ops *eops)
|
||||
{
|
||||
int key;
|
||||
va_list args;
|
||||
if (perf_eops != &default_eops)
|
||||
return -1;
|
||||
|
||||
va_start(args, format);
|
||||
key = __ui__warning("Error", format, args);
|
||||
va_end(args);
|
||||
return key;
|
||||
perf_eops = eops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* perf_error__unregister - Unregister error logging functions
|
||||
* @eops: The pointer to error logging function struct
|
||||
*
|
||||
* Unregister already registered error logging functions.
|
||||
*/
|
||||
int perf_error__unregister(struct perf_error_ops *eops)
|
||||
{
|
||||
if (perf_eops != eops)
|
||||
return -1;
|
||||
|
||||
perf_eops = &default_eops;
|
||||
return 0;
|
||||
}
|
||||
|
@ -9,6 +9,13 @@ int ui__help_window(const char *text);
|
||||
int ui__dialog_yesno(const char *msg);
|
||||
int ui__question_window(const char *title, const char *text,
|
||||
const char *exit_msg, int delay_secs);
|
||||
int __ui__warning(const char *title, const char *format, va_list args);
|
||||
|
||||
struct perf_error_ops {
|
||||
int (*error)(const char *format, va_list args);
|
||||
int (*warning)(const char *format, va_list args);
|
||||
};
|
||||
|
||||
int perf_error__register(struct perf_error_ops *eops);
|
||||
int perf_error__unregister(struct perf_error_ops *eops);
|
||||
|
||||
#endif /* _PERF_UI_UTIL_H_ */
|
||||
|
@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
|
||||
int ui__warning(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void trace_event(union perf_event *event);
|
||||
|
||||
struct ui_progress;
|
||||
struct perf_error_ops;
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
|
||||
static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
|
||||
{
|
||||
return 0;
|
||||
@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
|
||||
const char *title __used) {}
|
||||
|
||||
#define ui__error(format, arg...) ui__warning(format, ##arg)
|
||||
#else
|
||||
|
||||
static inline int
|
||||
perf_error__register(struct perf_error_ops *eops __used)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
perf_error__unregister(struct perf_error_ops *eops __used)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
|
||||
|
||||
extern char ui_helpline__last_msg[];
|
||||
int ui_helpline__show_help(const char *format, va_list ap);
|
||||
#include "../ui/progress.h"
|
||||
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
#endif
|
||||
#include "../ui/util.h"
|
||||
|
||||
#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
|
||||
|
||||
int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
int ui__error_paranoid(void);
|
||||
|
@ -78,7 +78,7 @@ static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
|
||||
"ref-cycles",
|
||||
};
|
||||
|
||||
const char *__perf_evsel__hw_name(u64 config)
|
||||
static const char *__perf_evsel__hw_name(u64 config)
|
||||
{
|
||||
if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
|
||||
return perf_evsel__hw_names[config];
|
||||
@ -86,16 +86,15 @@ const char *__perf_evsel__hw_name(u64 config)
|
||||
return "unknown-hardware";
|
||||
}
|
||||
|
||||
static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
static int perf_evsel__add_modifiers(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int colon = 0;
|
||||
int colon = 0, r = 0;
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(attr->config));
|
||||
bool exclude_guest_default = false;
|
||||
|
||||
#define MOD_PRINT(context, mod) do { \
|
||||
if (!attr->exclude_##context) { \
|
||||
if (!colon) colon = r++; \
|
||||
if (!colon) colon = ++r; \
|
||||
r += scnprintf(bf + r, size - r, "%c", mod); \
|
||||
} } while(0)
|
||||
|
||||
@ -108,7 +107,7 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
|
||||
if (attr->precise_ip) {
|
||||
if (!colon)
|
||||
colon = r++;
|
||||
colon = ++r;
|
||||
r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp");
|
||||
exclude_guest_default = true;
|
||||
}
|
||||
@ -119,39 +118,182 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
}
|
||||
#undef MOD_PRINT
|
||||
if (colon)
|
||||
bf[colon] = ':';
|
||||
bf[colon - 1] = ':';
|
||||
return r;
|
||||
}
|
||||
|
||||
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->attr.config));
|
||||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
|
||||
"cpu-clock",
|
||||
"task-clock",
|
||||
"page-faults",
|
||||
"context-switches",
|
||||
"CPU-migrations",
|
||||
"minor-faults",
|
||||
"major-faults",
|
||||
"alignment-faults",
|
||||
"emulation-faults",
|
||||
};
|
||||
|
||||
static const char *__perf_evsel__sw_name(u64 config)
|
||||
{
|
||||
if (config < PERF_COUNT_SW_MAX && perf_evsel__sw_names[config])
|
||||
return perf_evsel__sw_names[config];
|
||||
return "unknown-software";
|
||||
}
|
||||
|
||||
static int perf_evsel__sw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->attr.config));
|
||||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
|
||||
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
|
||||
{ "LLC", "L2", },
|
||||
{ "dTLB", "d-tlb", "Data-TLB", },
|
||||
{ "iTLB", "i-tlb", "Instruction-TLB", },
|
||||
{ "branch", "branches", "bpu", "btb", "bpc", },
|
||||
{ "node", },
|
||||
};
|
||||
|
||||
const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
{ "load", "loads", "read", },
|
||||
{ "store", "stores", "write", },
|
||||
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
|
||||
};
|
||||
|
||||
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
{ "refs", "Reference", "ops", "access", },
|
||||
{ "misses", "miss", },
|
||||
};
|
||||
|
||||
#define C(x) PERF_COUNT_HW_CACHE_##x
|
||||
#define CACHE_READ (1 << C(OP_READ))
|
||||
#define CACHE_WRITE (1 << C(OP_WRITE))
|
||||
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
|
||||
#define COP(x) (1 << x)
|
||||
|
||||
/*
|
||||
* cache operartion stat
|
||||
* L1I : Read and prefetch only
|
||||
* ITLB and BPU : Read-only
|
||||
*/
|
||||
static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = {
|
||||
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
|
||||
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(ITLB)] = (CACHE_READ),
|
||||
[C(BPU)] = (CACHE_READ),
|
||||
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
};
|
||||
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op)
|
||||
{
|
||||
if (perf_evsel__hw_cache_stat[type] & COP(op))
|
||||
return true; /* valid */
|
||||
else
|
||||
return false; /* invalid */
|
||||
}
|
||||
|
||||
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
|
||||
char *bf, size_t size)
|
||||
{
|
||||
if (result) {
|
||||
return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0],
|
||||
perf_evsel__hw_cache_op[op][0],
|
||||
perf_evsel__hw_cache_result[result][0]);
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0],
|
||||
perf_evsel__hw_cache_op[op][1]);
|
||||
}
|
||||
|
||||
static int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size)
|
||||
{
|
||||
u8 op, result, type = (config >> 0) & 0xff;
|
||||
const char *err = "unknown-ext-hardware-cache-type";
|
||||
|
||||
if (type > PERF_COUNT_HW_CACHE_MAX)
|
||||
goto out_err;
|
||||
|
||||
op = (config >> 8) & 0xff;
|
||||
err = "unknown-ext-hardware-cache-op";
|
||||
if (op > PERF_COUNT_HW_CACHE_OP_MAX)
|
||||
goto out_err;
|
||||
|
||||
result = (config >> 16) & 0xff;
|
||||
err = "unknown-ext-hardware-cache-result";
|
||||
if (result > PERF_COUNT_HW_CACHE_RESULT_MAX)
|
||||
goto out_err;
|
||||
|
||||
err = "invalid-cache";
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
goto out_err;
|
||||
|
||||
return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size);
|
||||
out_err:
|
||||
return scnprintf(bf, size, "%s", err);
|
||||
}
|
||||
|
||||
static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size);
|
||||
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
|
||||
}
|
||||
|
||||
static int perf_evsel__raw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
|
||||
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
|
||||
}
|
||||
|
||||
const char *perf_evsel__name(struct perf_evsel *evsel)
|
||||
{
|
||||
char bf[128];
|
||||
|
||||
if (evsel->name)
|
||||
return evsel->name;
|
||||
|
||||
switch (evsel->attr.type) {
|
||||
case PERF_TYPE_RAW:
|
||||
ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
|
||||
perf_evsel__raw_name(evsel, bf, sizeof(bf));
|
||||
break;
|
||||
|
||||
case PERF_TYPE_HARDWARE:
|
||||
ret = perf_evsel__hw_name(evsel, bf, size);
|
||||
perf_evsel__hw_name(evsel, bf, sizeof(bf));
|
||||
break;
|
||||
|
||||
case PERF_TYPE_HW_CACHE:
|
||||
perf_evsel__hw_cache_name(evsel, bf, sizeof(bf));
|
||||
break;
|
||||
|
||||
case PERF_TYPE_SOFTWARE:
|
||||
perf_evsel__sw_name(evsel, bf, sizeof(bf));
|
||||
break;
|
||||
|
||||
case PERF_TYPE_TRACEPOINT:
|
||||
scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint");
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* This is the minimal perf_evsel__name so that we can
|
||||
* reconstruct event names taking into account event modifiers.
|
||||
*
|
||||
* The old event_name uses it now for raw anr hw events, so that
|
||||
* we don't drag all the parsing stuff into the python binding.
|
||||
*
|
||||
* On the next devel cycle the rest of the event naming will be
|
||||
* brought here.
|
||||
*/
|
||||
return 0;
|
||||
scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
evsel->name = strdup(bf);
|
||||
|
||||
return evsel->name ?: "unknown";
|
||||
}
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
|
||||
|
@ -83,8 +83,19 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_record_opts *opts,
|
||||
struct perf_evsel *first);
|
||||
|
||||
const char* __perf_evsel__hw_name(u64 config);
|
||||
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
|
||||
|
||||
#define PERF_EVSEL__MAX_ALIASES 8
|
||||
|
||||
extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
|
||||
char *bf, size_t size);
|
||||
const char *perf_evsel__name(struct perf_evsel *evsel);
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
|
@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
|
||||
/*
|
||||
* write event string as passed on cmdline
|
||||
*/
|
||||
ret = do_write_string(fd, event_name(attr));
|
||||
ret = do_write_string(fd, perf_evsel__name(attr));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
|
@ -47,6 +47,7 @@ enum hist_column {
|
||||
HISTC_SYMBOL_TO,
|
||||
HISTC_DSO_FROM,
|
||||
HISTC_DSO_TO,
|
||||
HISTC_SRCLINE,
|
||||
HISTC_NR_COLS, /* Last entry */
|
||||
};
|
||||
|
||||
|
@ -157,7 +157,7 @@ void machine__exit(struct machine *self);
|
||||
void machine__delete(struct machine *self);
|
||||
|
||||
int machine__resolve_callchain(struct machine *machine,
|
||||
struct perf_evsel *evsel, struct thread *thread,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,
|
||||
|
@ -418,14 +418,14 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "krava"));
|
||||
TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava"));
|
||||
|
||||
/* cpu/config=2/" */
|
||||
evsel = list_entry(evsel->node.next, struct perf_evsel, node);
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "raw 0x2"));
|
||||
TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "raw 0x2"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -64,63 +64,6 @@ static struct event_symbol event_symbols[] = {
|
||||
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
|
||||
#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
|
||||
|
||||
static const char *sw_event_names[PERF_COUNT_SW_MAX] = {
|
||||
"cpu-clock",
|
||||
"task-clock",
|
||||
"page-faults",
|
||||
"context-switches",
|
||||
"CPU-migrations",
|
||||
"minor-faults",
|
||||
"major-faults",
|
||||
"alignment-faults",
|
||||
"emulation-faults",
|
||||
};
|
||||
|
||||
#define MAX_ALIASES 8
|
||||
|
||||
static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = {
|
||||
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
|
||||
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
|
||||
{ "LLC", "L2", },
|
||||
{ "dTLB", "d-tlb", "Data-TLB", },
|
||||
{ "iTLB", "i-tlb", "Instruction-TLB", },
|
||||
{ "branch", "branches", "bpu", "btb", "bpc", },
|
||||
{ "node", },
|
||||
};
|
||||
|
||||
static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = {
|
||||
{ "load", "loads", "read", },
|
||||
{ "store", "stores", "write", },
|
||||
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
|
||||
};
|
||||
|
||||
static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[MAX_ALIASES] = {
|
||||
{ "refs", "Reference", "ops", "access", },
|
||||
{ "misses", "miss", },
|
||||
};
|
||||
|
||||
#define C(x) PERF_COUNT_HW_CACHE_##x
|
||||
#define CACHE_READ (1 << C(OP_READ))
|
||||
#define CACHE_WRITE (1 << C(OP_WRITE))
|
||||
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
|
||||
#define COP(x) (1 << x)
|
||||
|
||||
/*
|
||||
* cache operartion stat
|
||||
* L1I : Read and prefetch only
|
||||
* ITLB and BPU : Read-only
|
||||
*/
|
||||
static unsigned long hw_cache_stat[C(MAX)] = {
|
||||
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
|
||||
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(ITLB)] = (CACHE_READ),
|
||||
[C(BPU)] = (CACHE_READ),
|
||||
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
};
|
||||
|
||||
#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
|
||||
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
|
||||
if (sys_dirent.d_type == DT_DIR && \
|
||||
@ -220,48 +163,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
|
||||
static const char *tracepoint_id_to_name(u64 config)
|
||||
{
|
||||
static char buf[TP_PATH_LEN];
|
||||
struct tracepoint_path *path;
|
||||
|
||||
path = tracepoint_id_to_path(config);
|
||||
if (path) {
|
||||
snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
|
||||
free(path->name);
|
||||
free(path->system);
|
||||
free(path);
|
||||
} else
|
||||
snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int is_cache_op_valid(u8 cache_type, u8 cache_op)
|
||||
{
|
||||
if (hw_cache_stat[cache_type] & COP(cache_op))
|
||||
return 1; /* valid */
|
||||
else
|
||||
return 0; /* invalid */
|
||||
}
|
||||
|
||||
static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
|
||||
{
|
||||
static char name[50];
|
||||
|
||||
if (cache_result) {
|
||||
sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
|
||||
hw_cache_op[cache_op][0],
|
||||
hw_cache_result[cache_result][0]);
|
||||
} else {
|
||||
sprintf(name, "%s-%s", hw_cache[cache_type][0],
|
||||
hw_cache_op[cache_op][1]);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *event_type(int type)
|
||||
{
|
||||
switch (type) {
|
||||
@ -284,76 +185,6 @@ const char *event_type(int type)
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *event_name(struct perf_evsel *evsel)
|
||||
{
|
||||
u64 config = evsel->attr.config;
|
||||
int type = evsel->attr.type;
|
||||
|
||||
if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE) {
|
||||
/*
|
||||
* XXX minimal fix, see comment on perf_evsen__name, this static buffer
|
||||
* will go away together with event_name in the next devel cycle.
|
||||
*/
|
||||
static char bf[128];
|
||||
perf_evsel__name(evsel, bf, sizeof(bf));
|
||||
return bf;
|
||||
}
|
||||
|
||||
if (evsel->name)
|
||||
return evsel->name;
|
||||
|
||||
return __event_name(type, config);
|
||||
}
|
||||
|
||||
const char *__event_name(int type, u64 config)
|
||||
{
|
||||
static char buf[32];
|
||||
|
||||
if (type == PERF_TYPE_RAW) {
|
||||
sprintf(buf, "raw 0x%" PRIx64, config);
|
||||
return buf;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case PERF_TYPE_HARDWARE:
|
||||
return __perf_evsel__hw_name(config);
|
||||
|
||||
case PERF_TYPE_HW_CACHE: {
|
||||
u8 cache_type, cache_op, cache_result;
|
||||
|
||||
cache_type = (config >> 0) & 0xff;
|
||||
if (cache_type > PERF_COUNT_HW_CACHE_MAX)
|
||||
return "unknown-ext-hardware-cache-type";
|
||||
|
||||
cache_op = (config >> 8) & 0xff;
|
||||
if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
|
||||
return "unknown-ext-hardware-cache-op";
|
||||
|
||||
cache_result = (config >> 16) & 0xff;
|
||||
if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
|
||||
return "unknown-ext-hardware-cache-result";
|
||||
|
||||
if (!is_cache_op_valid(cache_type, cache_op))
|
||||
return "invalid-cache";
|
||||
|
||||
return event_cache_name(cache_type, cache_op, cache_result);
|
||||
}
|
||||
|
||||
case PERF_TYPE_SOFTWARE:
|
||||
if (config < PERF_COUNT_SW_MAX && sw_event_names[config])
|
||||
return sw_event_names[config];
|
||||
return "unknown-software";
|
||||
|
||||
case PERF_TYPE_TRACEPOINT:
|
||||
return tracepoint_id_to_name(config);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static int add_event(struct list_head **_list, int *idx,
|
||||
struct perf_event_attr *attr, char *name)
|
||||
{
|
||||
@ -375,19 +206,20 @@ static int add_event(struct list_head **_list, int *idx,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
evsel->name = strdup(name);
|
||||
if (name)
|
||||
evsel->name = strdup(name);
|
||||
list_add_tail(&evsel->node, list);
|
||||
*_list = list;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
|
||||
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
|
||||
{
|
||||
int i, j;
|
||||
int n, longest = -1;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
|
||||
for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) {
|
||||
n = strlen(names[i][j]);
|
||||
if (n > longest && !strncasecmp(str, names[i][j], n))
|
||||
longest = n;
|
||||
@ -412,7 +244,7 @@ int parse_events_add_cache(struct list_head **list, int *idx,
|
||||
* No fallback - if we cannot get a clear cache type
|
||||
* then bail out:
|
||||
*/
|
||||
cache_type = parse_aliases(type, hw_cache,
|
||||
cache_type = parse_aliases(type, perf_evsel__hw_cache,
|
||||
PERF_COUNT_HW_CACHE_MAX);
|
||||
if (cache_type == -1)
|
||||
return -EINVAL;
|
||||
@ -425,18 +257,18 @@ int parse_events_add_cache(struct list_head **list, int *idx,
|
||||
snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
|
||||
|
||||
if (cache_op == -1) {
|
||||
cache_op = parse_aliases(str, hw_cache_op,
|
||||
cache_op = parse_aliases(str, perf_evsel__hw_cache_op,
|
||||
PERF_COUNT_HW_CACHE_OP_MAX);
|
||||
if (cache_op >= 0) {
|
||||
if (!is_cache_op_valid(cache_type, cache_op))
|
||||
if (!perf_evsel__is_cache_op_valid(cache_type, cache_op))
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache_result == -1) {
|
||||
cache_result = parse_aliases(str, hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
cache_result = parse_aliases(str, perf_evsel__hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
if (cache_result >= 0)
|
||||
continue;
|
||||
}
|
||||
@ -668,8 +500,7 @@ int parse_events_add_numeric(struct list_head **list, int *idx,
|
||||
config_attr(&attr, head_config, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return add_event(list, idx, &attr,
|
||||
(char *) __event_name(type, config));
|
||||
return add_event(list, idx, &attr, NULL);
|
||||
}
|
||||
|
||||
static int parse_events__is_name_term(struct parse_events__term *term)
|
||||
@ -677,8 +508,7 @@ static int parse_events__is_name_term(struct parse_events__term *term)
|
||||
return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
|
||||
}
|
||||
|
||||
static char *pmu_event_name(struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
static char *pmu_event_name(struct list_head *head_terms)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
@ -686,7 +516,7 @@ static char *pmu_event_name(struct perf_event_attr *attr,
|
||||
if (parse_events__is_name_term(term))
|
||||
return term->val.str;
|
||||
|
||||
return (char *) __event_name(PERF_TYPE_RAW, attr->config);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int parse_events_add_pmu(struct list_head **list, int *idx,
|
||||
@ -714,7 +544,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
|
||||
return -EINVAL;
|
||||
|
||||
return add_event(list, idx, &attr,
|
||||
pmu_event_name(&attr, head_config));
|
||||
pmu_event_name(head_config));
|
||||
}
|
||||
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
@ -1010,16 +840,17 @@ void print_events_type(u8 type)
|
||||
int print_hwcache_events(const char *event_glob)
|
||||
{
|
||||
unsigned int type, op, i, printed = 0;
|
||||
char name[64];
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
/* skip invalid cache type */
|
||||
if (!is_cache_op_valid(type, op))
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
char *name = event_cache_name(type, op, i);
|
||||
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
if (event_glob != NULL && !strglobmatch(name, event_glob))
|
||||
continue;
|
||||
|
||||
|
@ -26,8 +26,6 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
|
||||
extern bool have_tracepoints(struct list_head *evlist);
|
||||
|
||||
const char *event_type(int type);
|
||||
const char *event_name(struct perf_evsel *event);
|
||||
extern const char *__event_name(int type, u64 config);
|
||||
|
||||
extern int parse_events_option(const struct option *opt, const char *str,
|
||||
int unset);
|
||||
|
@ -289,7 +289,6 @@ struct branch_info *machine__resolve_bstack(struct machine *self,
|
||||
}
|
||||
|
||||
int machine__resolve_callchain(struct machine *self,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent)
|
||||
@ -1449,7 +1448,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
|
||||
ret += hists__fprintf_nr_events(&session->hists, fp);
|
||||
|
||||
list_for_each_entry(pos, &session->evlist->entries, node) {
|
||||
ret += fprintf(fp, "%s stats:\n", event_name(pos));
|
||||
ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
|
||||
ret += hists__fprintf_nr_events(&pos->hists, fp);
|
||||
}
|
||||
|
||||
@ -1490,8 +1489,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
}
|
||||
|
||||
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
|
||||
struct machine *machine, struct perf_evsel *evsel,
|
||||
int print_sym, int print_dso, int print_symoffset)
|
||||
struct machine *machine, int print_sym,
|
||||
int print_dso, int print_symoffset)
|
||||
{
|
||||
struct addr_location al;
|
||||
struct callchain_cursor_node *node;
|
||||
@ -1505,7 +1504,7 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
|
||||
|
||||
if (symbol_conf.use_callchain && sample->callchain) {
|
||||
|
||||
if (machine__resolve_callchain(machine, evsel, al.thread,
|
||||
if (machine__resolve_callchain(machine, al.thread,
|
||||
sample->callchain, NULL) != 0) {
|
||||
if (verbose)
|
||||
error("Failed to resolve callchain. Skipping\n");
|
||||
|
@ -151,8 +151,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
unsigned int type);
|
||||
|
||||
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
|
||||
struct machine *machine, struct perf_evsel *evsel,
|
||||
int print_sym, int print_dso, int print_symoffset);
|
||||
struct machine *machine, int print_sym,
|
||||
int print_dso, int print_symoffset);
|
||||
|
||||
int perf_session__cpu_bitmap(struct perf_session *session,
|
||||
const char *cpu_list, unsigned long *cpu_bitmap);
|
||||
|
@ -241,6 +241,54 @@ struct sort_entry sort_sym = {
|
||||
.se_width_idx = HISTC_SYMBOL,
|
||||
};
|
||||
|
||||
/* --sort srcline */
|
||||
|
||||
static int64_t
|
||||
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return (int64_t)(right->ip - left->ip);
|
||||
}
|
||||
|
||||
static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width __used)
|
||||
{
|
||||
FILE *fp;
|
||||
char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
|
||||
size_t line_len;
|
||||
|
||||
if (path != NULL)
|
||||
goto out_path;
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
|
||||
self->ms.map->dso->long_name, self->ip);
|
||||
fp = popen(cmd, "r");
|
||||
if (!fp)
|
||||
goto out_ip;
|
||||
|
||||
if (getline(&path, &line_len, fp) < 0 || !line_len)
|
||||
goto out_ip;
|
||||
fclose(fp);
|
||||
self->srcline = strdup(path);
|
||||
if (self->srcline == NULL)
|
||||
goto out_ip;
|
||||
|
||||
nl = strchr(self->srcline, '\n');
|
||||
if (nl != NULL)
|
||||
*nl = '\0';
|
||||
path = self->srcline;
|
||||
out_path:
|
||||
return repsep_snprintf(bf, size, "%s", path);
|
||||
out_ip:
|
||||
return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
|
||||
}
|
||||
|
||||
struct sort_entry sort_srcline = {
|
||||
.se_header = "Source:Line",
|
||||
.se_cmp = sort__srcline_cmp,
|
||||
.se_snprintf = hist_entry__srcline_snprintf,
|
||||
.se_width_idx = HISTC_SRCLINE,
|
||||
};
|
||||
|
||||
/* --sort parent */
|
||||
|
||||
static int64_t
|
||||
@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = {
|
||||
DIM(SORT_PARENT, "parent", sort_parent),
|
||||
DIM(SORT_CPU, "cpu", sort_cpu),
|
||||
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
|
||||
DIM(SORT_SRCLINE, "srcline", sort_srcline),
|
||||
};
|
||||
|
||||
int sort_dimension__add(const char *tok)
|
||||
|
@ -71,6 +71,7 @@ struct hist_entry {
|
||||
char level;
|
||||
bool used;
|
||||
u8 filtered;
|
||||
char *srcline;
|
||||
struct symbol *parent;
|
||||
union {
|
||||
unsigned long position;
|
||||
@ -93,6 +94,7 @@ enum sort_type {
|
||||
SORT_SYM_FROM,
|
||||
SORT_SYM_TO,
|
||||
SORT_MISPREDICT,
|
||||
SORT_SRCLINE,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -313,3 +313,25 @@ int strtailcmp(const char *s1, const char *s2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rtrim - Removes trailing whitespace from @s.
|
||||
* @s: The string to be stripped.
|
||||
*
|
||||
* Note that the first trailing whitespace is replaced with a %NUL-terminator
|
||||
* in the given string @s. Returns @s.
|
||||
*/
|
||||
char *rtrim(char *s)
|
||||
{
|
||||
size_t size = strlen(s);
|
||||
char *end;
|
||||
|
||||
if (!size)
|
||||
return s;
|
||||
|
||||
end = s + size - 1;
|
||||
while (end >= s && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
|
||||
top->freq ? "Hz" : "");
|
||||
}
|
||||
|
||||
ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel));
|
||||
ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel));
|
||||
|
||||
ret += SNPRINTF(bf + ret, size - ret, "], ");
|
||||
|
||||
|
@ -264,4 +264,6 @@ bool is_power_of_2(unsigned long n)
|
||||
|
||||
size_t hex_width(u64 v);
|
||||
|
||||
char *rtrim(char *s);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user