Merge tag 'perf-core-for-mingo-20160330' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes: User visible changes: - Add support for skipping itrace instructions, useful to fast forward processor trace (Intel PT, BTS) to right after initialization code at the start of a workload (Andi Kleen) - Add support for backtraces in perl 'perf script's (Dima Kogan) - Add -U/-K (--all-user/--all-kernel) options to 'perf mem' (Jiri Olsa) - Make -f/--force option documentation consistent across tools (Jiri Olsa) Infrastructure changes: - Add 'perf test' to check for event times (Jiri Olsa) - 'perf config' cleanups (Taeung Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -940,6 +940,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)
|
||||
synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
|
||||
synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
|
||||
synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
|
||||
synth_opts->initial_skip = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1064,6 +1065,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
|
||||
synth_opts->last_branch_sz = val;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
synth_opts->initial_skip = strtoul(p, &endptr, 10);
|
||||
if (p == endptr)
|
||||
goto out_err;
|
||||
p = endptr;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
break;
|
||||
|
@@ -68,6 +68,7 @@ enum itrace_period_type {
|
||||
* @last_branch_sz: branch context size
|
||||
* @period: 'instructions' events period
|
||||
* @period_type: 'instructions' events period type
|
||||
* @initial_skip: skip N events at the beginning.
|
||||
*/
|
||||
struct itrace_synth_opts {
|
||||
bool set;
|
||||
@@ -86,6 +87,7 @@ struct itrace_synth_opts {
|
||||
unsigned int last_branch_sz;
|
||||
unsigned long long period;
|
||||
enum itrace_period_type period_type;
|
||||
unsigned long initial_skip;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -377,6 +377,21 @@ const char *perf_config_dirname(const char *name, const char *value)
|
||||
return value;
|
||||
}
|
||||
|
||||
static int perf_buildid_config(const char *var, const char *value)
|
||||
{
|
||||
/* same dir for all commands */
|
||||
if (!strcmp(var, "buildid.dir")) {
|
||||
const char *dirname = perf_config_dirname(var, value);
|
||||
|
||||
if (!dirname)
|
||||
return -1;
|
||||
strncpy(buildid_dir, dirname, MAXPATHLEN-1);
|
||||
buildid_dir[MAXPATHLEN-1] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_default_core_config(const char *var __maybe_unused,
|
||||
const char *value __maybe_unused)
|
||||
{
|
||||
@@ -412,6 +427,9 @@ int perf_default_config(const char *var, const char *value,
|
||||
if (!prefixcmp(var, "llvm."))
|
||||
return perf_llvm_config(var, value);
|
||||
|
||||
if (!prefixcmp(var, "buildid."))
|
||||
return perf_buildid_config(var, value);
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
@@ -515,49 +533,18 @@ int config_error_nonbool(const char *var)
|
||||
return error("Missing value for '%s'", var);
|
||||
}
|
||||
|
||||
struct buildid_dir_config {
|
||||
char *dir;
|
||||
};
|
||||
|
||||
static int buildid_dir_command_config(const char *var, const char *value,
|
||||
void *data)
|
||||
{
|
||||
struct buildid_dir_config *c = data;
|
||||
const char *v;
|
||||
|
||||
/* same dir for all commands */
|
||||
if (!strcmp(var, "buildid.dir")) {
|
||||
v = perf_config_dirname(var, value);
|
||||
if (!v)
|
||||
return -1;
|
||||
strncpy(c->dir, v, MAXPATHLEN-1);
|
||||
c->dir[MAXPATHLEN-1] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_buildid_dir_config(void)
|
||||
{
|
||||
struct buildid_dir_config c;
|
||||
c.dir = buildid_dir;
|
||||
perf_config(buildid_dir_command_config, &c);
|
||||
}
|
||||
|
||||
void set_buildid_dir(const char *dir)
|
||||
{
|
||||
if (dir)
|
||||
scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir);
|
||||
|
||||
/* try config file */
|
||||
if (buildid_dir[0] == '\0')
|
||||
check_buildid_dir_config();
|
||||
|
||||
/* default to $HOME/.debug */
|
||||
if (buildid_dir[0] == '\0') {
|
||||
char *v = getenv("HOME");
|
||||
if (v) {
|
||||
char *home = getenv("HOME");
|
||||
|
||||
if (home) {
|
||||
snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
|
||||
v, DEBUG_CACHE_DIR);
|
||||
home, DEBUG_CACHE_DIR);
|
||||
} else {
|
||||
strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
|
||||
}
|
||||
|
@@ -1295,8 +1295,9 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,
|
||||
struct hist_entry *he)
|
||||
static int hists__collapse_insert_entry(struct hists *hists,
|
||||
struct rb_root *root,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
@@ -199,8 +199,6 @@ int hists__init(void);
|
||||
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list);
|
||||
|
||||
struct rb_root *hists__get_rotate_entries_in(struct hists *hists);
|
||||
int hists__collapse_insert_entry(struct hists *hists,
|
||||
struct rb_root *root, struct hist_entry *he);
|
||||
|
||||
struct perf_hpp {
|
||||
char *buf;
|
||||
|
@@ -66,6 +66,7 @@ struct intel_bts {
|
||||
u64 branches_id;
|
||||
size_t branches_event_size;
|
||||
bool synth_needs_swap;
|
||||
unsigned long num_events;
|
||||
};
|
||||
|
||||
struct intel_bts_queue {
|
||||
@@ -275,6 +276,10 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq,
|
||||
union perf_event event;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
if (bts->synth_opts.initial_skip &&
|
||||
bts->num_events++ <= bts->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event.sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event.sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event.sample.header.size = sizeof(struct perf_event_header);
|
||||
|
@@ -100,6 +100,8 @@ struct intel_pt {
|
||||
u64 cyc_bit;
|
||||
u64 noretcomp_bit;
|
||||
unsigned max_non_turbo_ratio;
|
||||
|
||||
unsigned long num_events;
|
||||
};
|
||||
|
||||
enum switch_state {
|
||||
@@ -972,6 +974,10 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
|
||||
if (pt->branches_filter && !(pt->branches_filter & ptq->flags))
|
||||
return 0;
|
||||
|
||||
if (pt->synth_opts.initial_skip &&
|
||||
pt->num_events++ < pt->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
@@ -1029,6 +1035,10 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
|
||||
union perf_event *event = ptq->event_buf;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
if (pt->synth_opts.initial_skip &&
|
||||
pt->num_events++ < pt->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
@@ -1087,6 +1097,10 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)
|
||||
union perf_event *event = ptq->event_buf;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
if (pt->synth_opts.initial_skip &&
|
||||
pt->num_events++ < pt->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
@@ -1199,14 +1213,18 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
|
||||
ptq->have_sample = false;
|
||||
|
||||
if (pt->sample_instructions &&
|
||||
(state->type & INTEL_PT_INSTRUCTION)) {
|
||||
(state->type & INTEL_PT_INSTRUCTION) &&
|
||||
(!pt->synth_opts.initial_skip ||
|
||||
pt->num_events++ >= pt->synth_opts.initial_skip)) {
|
||||
err = intel_pt_synth_instruction_sample(ptq);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pt->sample_transactions &&
|
||||
(state->type & INTEL_PT_TRANSACTION)) {
|
||||
(state->type & INTEL_PT_TRANSACTION) &&
|
||||
(!pt->synth_opts.initial_skip ||
|
||||
pt->num_events++ >= pt->synth_opts.initial_skip)) {
|
||||
err = intel_pt_synth_transaction_sample(ptq);
|
||||
if (err)
|
||||
return err;
|
||||
|
@@ -31,6 +31,8 @@
|
||||
#include <perl.h>
|
||||
|
||||
#include "../../perf.h"
|
||||
#include "../callchain.h"
|
||||
#include "../machine.h"
|
||||
#include "../thread.h"
|
||||
#include "../event.h"
|
||||
#include "../trace-event.h"
|
||||
@@ -248,10 +250,78 @@ static void define_event_symbols(struct event_format *event,
|
||||
define_event_symbols(event, ev_name, args->next);
|
||||
}
|
||||
|
||||
static SV *perl_process_callchain(struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct addr_location *al)
|
||||
{
|
||||
AV *list;
|
||||
|
||||
list = newAV();
|
||||
if (!list)
|
||||
goto exit;
|
||||
|
||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||
goto exit;
|
||||
|
||||
if (thread__resolve_callchain(al->thread, evsel,
|
||||
sample, NULL, NULL,
|
||||
PERF_MAX_STACK_DEPTH) != 0) {
|
||||
pr_err("Failed to resolve callchain. Skipping\n");
|
||||
goto exit;
|
||||
}
|
||||
callchain_cursor_commit(&callchain_cursor);
|
||||
|
||||
|
||||
while (1) {
|
||||
HV *elem;
|
||||
struct callchain_cursor_node *node;
|
||||
node = callchain_cursor_current(&callchain_cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
elem = newHV();
|
||||
if (!elem)
|
||||
goto exit;
|
||||
|
||||
hv_stores(elem, "ip", newSVuv(node->ip));
|
||||
|
||||
if (node->sym) {
|
||||
HV *sym = newHV();
|
||||
if (!sym)
|
||||
goto exit;
|
||||
hv_stores(sym, "start", newSVuv(node->sym->start));
|
||||
hv_stores(sym, "end", newSVuv(node->sym->end));
|
||||
hv_stores(sym, "binding", newSVuv(node->sym->binding));
|
||||
hv_stores(sym, "name", newSVpvn(node->sym->name,
|
||||
node->sym->namelen));
|
||||
hv_stores(elem, "sym", newRV_noinc((SV*)sym));
|
||||
}
|
||||
|
||||
if (node->map) {
|
||||
struct map *map = node->map;
|
||||
const char *dsoname = "[unknown]";
|
||||
if (map && map->dso && (map->dso->name || map->dso->long_name)) {
|
||||
if (symbol_conf.show_kernel_path && map->dso->long_name)
|
||||
dsoname = map->dso->long_name;
|
||||
else if (map->dso->name)
|
||||
dsoname = map->dso->name;
|
||||
}
|
||||
hv_stores(elem, "dso", newSVpv(dsoname,0));
|
||||
}
|
||||
|
||||
callchain_cursor_advance(&callchain_cursor);
|
||||
av_push(list, newRV_noinc((SV*)elem));
|
||||
}
|
||||
|
||||
exit:
|
||||
return newRV_noinc((SV*)list);
|
||||
}
|
||||
|
||||
static void perl_process_tracepoint(struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct thread *thread)
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct thread *thread = al->thread;
|
||||
struct event_format *event = evsel->tp_format;
|
||||
struct format_field *field;
|
||||
static char handler[256];
|
||||
@@ -295,6 +365,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,
|
||||
XPUSHs(sv_2mortal(newSVuv(ns)));
|
||||
XPUSHs(sv_2mortal(newSViv(pid)));
|
||||
XPUSHs(sv_2mortal(newSVpv(comm, 0)));
|
||||
XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));
|
||||
|
||||
/* common fields other than pid can be accessed via xsub fns */
|
||||
|
||||
@@ -329,6 +400,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,
|
||||
XPUSHs(sv_2mortal(newSVuv(nsecs)));
|
||||
XPUSHs(sv_2mortal(newSViv(pid)));
|
||||
XPUSHs(sv_2mortal(newSVpv(comm, 0)));
|
||||
XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));
|
||||
call_pv("main::trace_unhandled", G_SCALAR);
|
||||
}
|
||||
SPAGAIN;
|
||||
@@ -366,7 +438,7 @@ static void perl_process_event(union perf_event *event,
|
||||
struct perf_evsel *evsel,
|
||||
struct addr_location *al)
|
||||
{
|
||||
perl_process_tracepoint(sample, evsel, al->thread);
|
||||
perl_process_tracepoint(sample, evsel, al);
|
||||
perl_process_event_generic(event, sample, evsel);
|
||||
}
|
||||
|
||||
@@ -490,7 +562,27 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
||||
fprintf(ofp, "use Perf::Trace::Util;\n\n");
|
||||
|
||||
fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
|
||||
fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n");
|
||||
fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n");
|
||||
|
||||
|
||||
fprintf(ofp, "\n\
|
||||
sub print_backtrace\n\
|
||||
{\n\
|
||||
my $callchain = shift;\n\
|
||||
for my $node (@$callchain)\n\
|
||||
{\n\
|
||||
if(exists $node->{sym})\n\
|
||||
{\n\
|
||||
printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\
|
||||
}\n\
|
||||
else\n\
|
||||
{\n\
|
||||
printf( \"\\t[\\%%x]\\n\", $node{ip});\n\
|
||||
}\n\
|
||||
}\n\
|
||||
}\n\n\
|
||||
");
|
||||
|
||||
|
||||
while ((event = trace_find_next_event(pevent, event))) {
|
||||
fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
|
||||
@@ -502,7 +594,8 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
||||
fprintf(ofp, "$common_secs, ");
|
||||
fprintf(ofp, "$common_nsecs,\n");
|
||||
fprintf(ofp, "\t $common_pid, ");
|
||||
fprintf(ofp, "$common_comm,\n\t ");
|
||||
fprintf(ofp, "$common_comm, ");
|
||||
fprintf(ofp, "$common_callchain,\n\t ");
|
||||
|
||||
not_first = 0;
|
||||
count = 0;
|
||||
@@ -519,7 +612,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
||||
|
||||
fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
|
||||
"$common_secs, $common_nsecs,\n\t "
|
||||
"$common_pid, $common_comm);\n\n");
|
||||
"$common_pid, $common_comm, $common_callchain);\n\n");
|
||||
|
||||
fprintf(ofp, "\tprintf(\"");
|
||||
|
||||
@@ -581,17 +674,22 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
||||
fprintf(ofp, "$%s", f->name);
|
||||
}
|
||||
|
||||
fprintf(ofp, ");\n");
|
||||
fprintf(ofp, ");\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
|
||||
|
||||
fprintf(ofp, "}\n\n");
|
||||
}
|
||||
|
||||
fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "
|
||||
"$common_cpu, $common_secs, $common_nsecs,\n\t "
|
||||
"$common_pid, $common_comm) = @_;\n\n");
|
||||
"$common_pid, $common_comm, $common_callchain) = @_;\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
|
||||
"$common_secs, $common_nsecs,\n\t $common_pid, "
|
||||
"$common_comm);\n}\n\n");
|
||||
"$common_comm, $common_callchain);\n");
|
||||
fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
|
||||
fprintf(ofp, "}\n\n");
|
||||
|
||||
fprintf(ofp, "sub print_header\n{\n"
|
||||
"\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n"
|
||||
|
Reference in New Issue
Block a user