perf tools/kernel: Per event max-stack settings
Arnaldo Carvalho de Melo
maintainerRFC 1/1 perf tools/kernel: Per event max-stack settings
Hi,
So I have the patch below + the kernel code to handle it, i.e. per-event max-stack, in addition to the system wide kernel.perf_event_max_stack knob.
Interested people should try my perf/max-stack branch, where the two patches to implement this are at the top:
https://git.kernel.org/cgit/linux/kernel/git/acme/linux.git/log/?h=perf/max-stack
But people have expressed interested in being able to have kernel/user separate max-stack knobs, which should be easy to do with the way things are laid out now (that perf_callchain_entry_ctx thing).
One way would be to use a one bit flag sample_max_stack_split and then a union to split perf_event_attr.sample_max_stack in two u16, that way we wouldn't extend the perf_event_attr size, but then we wouldn't be able to have both per event limits for the kernel+user stack and separate max-stack limits for for kernel and user, is that an acceptable limitation?
I couldn't find other members I could put in a union where to store the sample_max_stack_{user,kernel} values, ideas?
I think that we should push what I have there anyway, the split would come later, be it by using:
__u64 sample_max_stack_split:1;
__u64 __reserved_1:35;
<SNIP>
union {
__u32 sample_max_stack;
struct {
__u16 sample_max_stack_user;
__u16 sample_max_stack_kernel;
};
};Or by having space for all three.
- Arnaldo
-------------------------------------------------------------------------------------
The tooling counterpart, now it is possible to do have some events using "fp" stacks traces, while others use 'dwarf' or something else with all of them having different max-stack settings, as needed:
# perf record -e sched:sched_switch/max-stack=10/ -e cycles/call-graph=dwarf,max-stack=4/ -e cpu-cycles/call-graph=dwarf,max-stack=1024/ usleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.052 MB perf.data (5 samples) ]
# perf evlist -v
sched:sched_switch: type: 2, size: 112, config: 0x110, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CALLCHAIN|CPU|PERIOD|RAW|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, sample_max_stack: 10
cycles/call-graph=dwarf,max-stack=4/: size: 112, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|IDENTIFIER, read_format: ID, disabled: 1, inherit: 1, freq: 1, enable_on_exec: 1, sample_id_all: 1, exclude_guest: 1, exclude_callchain_user: 1, sample_regs_user: 0xff0fff, sample_stack_user: 8192, sample_max_stack: 4
cpu-cycles/call-graph=dwarf,max-stack=1024/: size: 112, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|IDENTIFIER, read_format: ID, disabled: 1, inherit: 1, freq: 1, enable_on_exec: 1, sample_id_all: 1, exclude_guest: 1, exclude_callchain_user: 1, sample_regs_user: 0xff0fff, sample_stack_user: 8192, sample_max_stack: 1024
# Tip: use 'perf evlist --trace-fields' to show fields for tracepoint eventsUsing just /max-stack=N/ means /call-graph=fp,max-stack=N/, that should be further configurable by means of some .perfconfig knob.
--- tools/perf/util/callchain.h | 1 + tools/perf/util/evsel.c | 16 ++++++++++++++-- tools/perf/util/evsel.h | 2 ++ tools/perf/util/parse-events.c | 8 ++++++++ tools/perf/util/parse-events.h | 1 + tools/perf/util/parse-events.l | 1 + tools/perf/util/session.c | 1 + 7 files changed, 28 insertions(+), 2 deletions(-)
tools/perf/util/callchain.h +1 -0
@@ -95,6 +95,7 @@ struct callchain_param {u32 dump_size;enum chain_mode mode;u32 print_limit; +u32 max_stack;double min_percent;sort_chain_func_t sort;enum chain_order order;
tools/perf/util/evsel.c +14 -2
@@ -571,6 +571,8 @@ void perf_evsel__config_callchain(struct perf_evsel *evsel,perf_evsel__set_sample_bit (evsel,CALLCHAIN ); + attr->sample_max_stack = param->max_stack; +if (param->record_mode ==CALLCHAIN_LBR ) {if (!opts->branch_stack) {if (attr->exclude_user) {
@@ -634,7 +636,8 @@ static void apply_config_terms(struct perf_evsel *evsel,struct perf_event_attr *attr = &evsel->attr;struct callchain_param param;u32 dump_size =0 ; -char *callgraph_buf =NULL ; +u32 max_stack =0 ; +const char *callgraph_buf =NULL ;/* callgraph default */ param.record_mode = callchain_param.record_mode;
@@ -661,6 +664,9 @@ static void apply_config_terms(struct perf_evsel *evsel,case PERF_EVSEL__CONFIG_TERM_STACK_USER : dump_size = term->val.stack_user;break ; +case PERF_EVSEL__CONFIG_TERM_MAX_STACK : + max_stack = term->val.max_stack; +break ;case PERF_EVSEL__CONFIG_TERM_INHERIT :/* * attr->inherit should has already been set by
@@ -676,7 +682,12 @@ static void apply_config_terms(struct perf_evsel *evsel, }/* User explicitly set per-event callgraph, clear the old setting and reset. */ -if ((callgraph_buf !=NULL ) || (dump_size >0 )) { +if ((callgraph_buf !=NULL ) || (dump_size >0 ) || max_stack) { +if (max_stack) { + param.max_stack = max_stack; +if (callgraph_buf ==NULL ) + callgraph_buf ="fp" ; + }/* parse callgraph parameters */ if (callgraph_buf !=NULL ) {
@@ -1328,6 +1339,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,PRINT_ATTRf (clockid, p_signed);PRINT_ATTRf (sample_regs_intr, p_hex);PRINT_ATTRf (aux_watermark, p_unsigned); +PRINT_ATTRf (sample_max_stack, p_unsigned);return ret; }
tools/perf/util/evsel.h +2 -0
@@ -44,6 +44,7 @@ enum {PERF_EVSEL__CONFIG_TERM_CALLGRAPH ,PERF_EVSEL__CONFIG_TERM_STACK_USER ,PERF_EVSEL__CONFIG_TERM_INHERIT , +PERF_EVSEL__CONFIG_TERM_MAX_STACK , PERF_EVSEL__CONFIG_TERM_MAX, };
@@ -56,6 +57,7 @@ struct perf_evsel_config_term {bool time;char *callgraph;u64 stack_user; +u32 max_stack;bool inherit; } val; };
tools/perf/util/parse-events.c +8 -0
@@ -900,6 +900,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size", [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", + [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack", };static bool config_term_shrinked;
@@ -995,6 +996,9 @@ do { \case PARSE_EVENTS__TERM_TYPE_NAME :CHECK_TYPE_VAL (STR );break ; +case PARSE_EVENTS__TERM_TYPE_MAX_STACK : +CHECK_TYPE_VAL (NUM ); +break ;default : err->str =strdup ("unknown term" ); err->idx = term->err_term;
@@ -1040,6 +1044,7 @@ static int config_term_tracepoint(struct perf_event_attr *attr,case PARSE_EVENTS__TERM_TYPE_STACKSIZE :case PARSE_EVENTS__TERM_TYPE_INHERIT :case PARSE_EVENTS__TERM_TYPE_NOINHERIT : +case PARSE_EVENTS__TERM_TYPE_MAX_STACK :return config_term_common (attr, term, err);default :if (err) {
@@ -1109,6 +1114,9 @@ do { \case PARSE_EVENTS__TERM_TYPE_NOINHERIT :ADD_CONFIG_TERM (INHERIT , inherit, term->val.num ?0 :1 );break ; +case PARSE_EVENTS__TERM_TYPE_MAX_STACK : +ADD_CONFIG_TERM (MAX_STACK , max_stack, term->val.num); +break ;default :break ; }
tools/perf/util/parse-events.h +1 -0
@@ -68,6 +68,7 @@ enum {PARSE_EVENTS__TERM_TYPE_STACKSIZE ,PARSE_EVENTS__TERM_TYPE_NOINHERIT ,PARSE_EVENTS__TERM_TYPE_INHERIT , +PARSE_EVENTS__TERM_TYPE_MAX_STACK , __PARSE_EVENTS__TERM_TYPE_NR, };
tools/perf/util/parse-events.l +1 -0
@@ -199,6 +199,7 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE 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); } +max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK); } inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); } no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); } , { return ','; }
tools/perf/util/session.c +1 -0
@@ -608,6 +608,7 @@ do { \bswap_field_64 (sample_regs_user);bswap_field_32 (sample_stack_user);bswap_field_32 (aux_watermark); +bswap_field_32 (sample_max_stack);/* * After read_format are bitfields. Check read_format because
Signature · 1 line
2.5.5