Commit d457c963 authored by Kan Liang's avatar Kan Liang Committed by Arnaldo Carvalho de Melo

perf callchain: Per-event type selection support

This patchkit adds the ability to set callgraph mode (fp, dwarf, lbr) per
event. This in term can reduce sampling overhead and the size of the
perf.data.

Here is an example.

  perf record -e 'cpu/cpu-cycles,period=1000,call-graph=fp,time=1/,cpu/instructions,call-graph=lbr/' sleep 1

 perf evlist -v
 cpu/cpu-cycles,period=1000,call-graph=fp,time=1/: type: 4, size: 112,
 config: 0x3c, { sample_period, sample_freq }: 1000, sample_type:
 IP|TID|TIME|CALLCHAIN|PERIOD|IDENTIFIER, read_format: ID, disabled: 1,
 inherit: 1, mmap: 1, comm: 1, enable_on_exec: 1, task: 1, sample_id_all:
 1, exclude_guest: 1, mmap2: 1, comm_exec: 1
 cpu/instructions,call-graph=lbr/: type: 4, size: 112, config: 0xc0, {
 sample_period, sample_freq }: 4000, sample_type:
 IP|TID|TIME|CALLCHAIN|PERIOD|BRANCH_STACK|IDENTIFIER, read_format: ID,
 disabled: 1, inherit: 1, freq: 1, enable_on_exec: 1, sample_id_all: 1,
 exclude_guest: 1
Signed-off-by: default avatarKan Liang <kan.liang@intel.com>
Tested-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/1439289050-40510-1-git-send-email-kan.liang@intel.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 75186a9b
...@@ -53,6 +53,9 @@ OPTIONS ...@@ -53,6 +53,9 @@ OPTIONS
- 'time': Disable/enable time stamping. Acceptable values are 1 for - 'time': Disable/enable time stamping. Acceptable values are 1 for
enabling time stamping. 0 for disabling time stamping. enabling time stamping. 0 for disabling time stamping.
The default is 1. The default is 1.
- 'call-graph': Disable/enable callgraph. Acceptable str are "fp" for
FP mode, "dwarf" for DWARF mode, "lbr" for LBR mode.
- 'stack-size': user stack size for dwarf mode
Note: If user explicitly sets options which conflict with the params, Note: If user explicitly sets options which conflict with the params,
the value set by the params will be overridden. the value set by the params will be overridden.
......
...@@ -588,11 +588,36 @@ perf_evsel__config_callgraph(struct perf_evsel *evsel, ...@@ -588,11 +588,36 @@ perf_evsel__config_callgraph(struct perf_evsel *evsel,
} }
} }
static void apply_config_terms(struct perf_evsel *evsel) static void
perf_evsel__reset_callgraph(struct perf_evsel *evsel,
struct callchain_param *param)
{
struct perf_event_attr *attr = &evsel->attr;
perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
if (param->record_mode == CALLCHAIN_LBR) {
perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
attr->branch_sample_type &= ~(PERF_SAMPLE_BRANCH_USER |
PERF_SAMPLE_BRANCH_CALL_STACK);
}
if (param->record_mode == CALLCHAIN_DWARF) {
perf_evsel__reset_sample_bit(evsel, REGS_USER);
perf_evsel__reset_sample_bit(evsel, STACK_USER);
}
}
static void apply_config_terms(struct perf_evsel *evsel,
struct record_opts *opts)
{ {
struct perf_evsel_config_term *term; struct perf_evsel_config_term *term;
struct list_head *config_terms = &evsel->config_terms; struct list_head *config_terms = &evsel->config_terms;
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
struct callchain_param param;
u32 dump_size = 0;
char *callgraph_buf = NULL;
/* callgraph default */
param.record_mode = callchain_param.record_mode;
list_for_each_entry(term, config_terms, list) { list_for_each_entry(term, config_terms, list) {
switch (term->type) { switch (term->type) {
...@@ -610,10 +635,43 @@ static void apply_config_terms(struct perf_evsel *evsel) ...@@ -610,10 +635,43 @@ static void apply_config_terms(struct perf_evsel *evsel)
else else
perf_evsel__reset_sample_bit(evsel, TIME); perf_evsel__reset_sample_bit(evsel, TIME);
break; break;
case PERF_EVSEL__CONFIG_TERM_CALLGRAPH:
callgraph_buf = term->val.callgraph;
break;
case PERF_EVSEL__CONFIG_TERM_STACK_USER:
dump_size = term->val.stack_user;
break;
default: default:
break; break;
} }
} }
/* User explicitly set per-event callgraph, clear the old setting and reset. */
if ((callgraph_buf != NULL) || (dump_size > 0)) {
/* parse callgraph parameters */
if (callgraph_buf != NULL) {
param.enabled = true;
if (parse_callchain_record(callgraph_buf, &param)) {
pr_err("per-event callgraph setting for %s failed. "
"Apply callgraph global setting for it\n",
evsel->name);
return;
}
}
if (dump_size > 0) {
dump_size = round_up(dump_size, sizeof(u64));
param.dump_size = dump_size;
}
/* If global callgraph set, clear it */
if (callchain_param.enabled)
perf_evsel__reset_callgraph(evsel, &callchain_param);
/* set perf-event callgraph */
if (param.enabled)
perf_evsel__config_callgraph(evsel, opts, &param);
}
} }
/* /*
...@@ -812,7 +870,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) ...@@ -812,7 +870,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
* Apply event specific term settings, * Apply event specific term settings,
* it overloads any global configuration. * it overloads any global configuration.
*/ */
apply_config_terms(evsel); apply_config_terms(evsel, opts);
} }
static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
......
...@@ -41,6 +41,8 @@ enum { ...@@ -41,6 +41,8 @@ enum {
PERF_EVSEL__CONFIG_TERM_PERIOD, PERF_EVSEL__CONFIG_TERM_PERIOD,
PERF_EVSEL__CONFIG_TERM_FREQ, PERF_EVSEL__CONFIG_TERM_FREQ,
PERF_EVSEL__CONFIG_TERM_TIME, PERF_EVSEL__CONFIG_TERM_TIME,
PERF_EVSEL__CONFIG_TERM_CALLGRAPH,
PERF_EVSEL__CONFIG_TERM_STACK_USER,
PERF_EVSEL__CONFIG_TERM_MAX, PERF_EVSEL__CONFIG_TERM_MAX,
}; };
...@@ -51,6 +53,8 @@ struct perf_evsel_config_term { ...@@ -51,6 +53,8 @@ struct perf_evsel_config_term {
u64 period; u64 period;
u64 freq; u64 freq;
bool time; bool time;
char *callgraph;
u64 stack_user;
} val; } val;
}; };
......
...@@ -614,6 +614,12 @@ do { \ ...@@ -614,6 +614,12 @@ do { \
return -EINVAL; return -EINVAL;
} }
break; break;
case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
CHECK_TYPE_VAL(STR);
break;
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
CHECK_TYPE_VAL(NUM);
break;
case PARSE_EVENTS__TERM_TYPE_NAME: case PARSE_EVENTS__TERM_TYPE_NAME:
CHECK_TYPE_VAL(STR); CHECK_TYPE_VAL(STR);
break; break;
...@@ -668,6 +674,12 @@ do { \ ...@@ -668,6 +674,12 @@ do { \
case PARSE_EVENTS__TERM_TYPE_TIME: case PARSE_EVENTS__TERM_TYPE_TIME:
ADD_CONFIG_TERM(TIME, time, term->val.num); ADD_CONFIG_TERM(TIME, time, term->val.num);
break; break;
case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str);
break;
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
break;
default: default:
break; break;
} }
......
...@@ -65,6 +65,8 @@ enum { ...@@ -65,6 +65,8 @@ enum {
PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ,
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
PARSE_EVENTS__TERM_TYPE_TIME, PARSE_EVENTS__TERM_TYPE_TIME,
PARSE_EVENTS__TERM_TYPE_CALLGRAPH,
PARSE_EVENTS__TERM_TYPE_STACKSIZE,
}; };
struct parse_events_term { struct parse_events_term {
......
...@@ -185,6 +185,8 @@ period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } ...@@ -185,6 +185,8 @@ period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
freq { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ); } freq { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ); }
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); } time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); }
call-graph { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); }
stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); }
, { return ','; } , { return ','; }
"/" { BEGIN(INITIAL); return '/'; } "/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); } {name_minus} { return str(yyscanner, PE_NAME); }
......
...@@ -634,7 +634,9 @@ static char *formats_error_string(struct list_head *formats) ...@@ -634,7 +634,9 @@ static char *formats_error_string(struct list_head *formats)
{ {
struct perf_pmu_format *format; struct perf_pmu_format *format;
char *err, *str; char *err, *str;
static const char *static_terms = "config,config1,config2,name,period,freq,branch_type,time\n"; static const char *static_terms = "config,config1,config2,name,"
"period,freq,branch_type,time,"
"call-graph,stack-size\n";
unsigned i = 0; unsigned i = 0;
if (!asprintf(&str, "valid terms:")) if (!asprintf(&str, "valid terms:"))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment