perf tool: Support the deferred unwinding infrastructure
Subsystems (3)
v16 cover perf tool: Support the deferred unwinding infrastructure
This is the user space side of perf changes to handle deferred unwinding. It is based on the kernel side of perf patch series here:
https://lore.kernel.org/linux-trace-kernel/[email protected]/
Changes since v15: https://lore.kernel.org/linux-trace-kernel/[email protected]/
- Separate out the kernel changes from the user space changes of perf.
- Have the matching deferred code only skip when TID does not match. Otherwise, process the sample but do not merge if the cookies do not match. (Namhyung Kim)
Namhyung Kim (4):
perf tools: Minimal CALLCHAIN_DEFERRED support perf record: Enable defer_callchain for user callchains perf script: Display PERF_RECORD_CALLCHAIN_DEFERRED perf tools: Merge deferred user callchains
----
tools/lib/perf/include/perf/event.h | 8 +++ tools/perf/Documentation/perf-script.txt | 5 ++ tools/perf/builtin-script.c | 92 +++++++++++++++++++++++++++++++ tools/perf/util/callchain.c | 24 ++++++++ tools/perf/util/callchain.h | 3 + tools/perf/util/event.c | 1 + tools/perf/util/evlist.c | 1 + tools/perf/util/evlist.h | 1 + tools/perf/util/evsel.c | 42 ++++++++++++++ tools/perf/util/evsel.h | 1 + tools/perf/util/machine.c | 1 + tools/perf/util/perf_event_attr_fprintf.c | 1 + tools/perf/util/sample.h | 4 +- tools/perf/util/session.c | 81 +++++++++++++++++++++++++++ tools/perf/util/tool.c | 2 + tools/perf/util/tool.h | 4 +- 16 files changed, 269 insertions(+), 2 deletions(-)
PATCH v16 1/4 perf tools: Minimal CALLCHAIN_DEFERRED support
From: Namhyung Kim <[email protected]>
Add a new event type for deferred callchains and a new callback for the struct perf_tool. For now it doesn't actually handle the deferred callchains but it just marks the sample if it has the PERF_CONTEXT_ USER_DEFFERED in the callchain array.
At least, perf report can dump the raw data with this change. Actually this requires the next commit to enable attr.defer_callchain, but if you already have a data file, it'll show the following result.
$ perf report -D ... [email protected] [0x40]: event: 22 . . ... raw event: size 64 bytes . 0000: 16 00 00 00 02 00 40 00 02 00 00 00 00 00 00 00 ......@......... . 0010: 00 fe ff ff ff ff ff ff 4b d3 3f 25 45 7f 00 00 ........K.?%E... . 0020: 21 03 00 00 21 03 00 00 43 02 12 ab 05 00 00 00 !...!...C....... . 0030: 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 ................
0 24344920643 0x5fe0 [0x40]: PERF_RECORD_CALLCHAIN_DEFERRED(IP, 0x2): 801/801: 0 [300000001] ... FP chain: nr:2
..... 0: fffffffffffffe00
..... 1: 00007f45253fd34b
: unhandled!
--- tools/lib/perf/include/perf/event.h | 8 ++++++++ tools/perf/util/event.c | 1 + tools/perf/util/evsel.c | 18 ++++++++++++++++++ tools/perf/util/machine.c | 1 + tools/perf/util/perf_event_attr_fprintf.c | 1 + tools/perf/util/sample.h | 4 +++- tools/perf/util/session.c | 18 ++++++++++++++++++ tools/perf/util/tool.c | 1 + tools/perf/util/tool.h | 3 ++- 9 files changed, 53 insertions(+), 2 deletions(-)
tools/lib/perf/include/perf/event.h +8 -0
@@ -151,6 +151,13 @@ struct perf_record_switch {__u32 next_prev_tid; }; +struct perf_record_callchain_deferred { +struct perf_event_header header; +__u64 cookie; +__u64 nr; +__u64 ips[]; +}; +struct perf_record_header_attr {struct perf_event_header header;struct perf_event_attr attr;
@@ -522,6 +529,7 @@ union perf_event {struct perf_record_read read;struct perf_record_throttle throttle;struct perf_record_sample sample; +struct perf_record_callchain_deferred callchain_deferred;struct perf_record_bpf_event bpf;struct perf_record_ksymbol ksymbol;struct perf_record_text_poke_event text_poke;
tools/perf/util/event.c +1 -0
@@ -61,6 +61,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_CGROUP] = "CGROUP", [PERF_RECORD_TEXT_POKE] = "TEXT_POKE", [PERF_RECORD_AUX_OUTPUT_HW_ID] = "AUX_OUTPUT_HW_ID", + [PERF_RECORD_CALLCHAIN_DEFERRED] = "CALLCHAIN_DEFERRED", [PERF_RECORD_HEADER_ATTR] = "ATTR", [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
tools/perf/util/evsel.c +18 -0
@@ -3002,6 +3002,18 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->data_src =PERF_MEM_DATA_SRC_NONE ; data->vcpu =-1 ; +if (event->header.type ==PERF_RECORD_CALLCHAIN_DEFERRED ) { +const u64 max_callchain_nr =UINT64_MAX /sizeof (u64); + + data->callchain = (struct ip_callchain *)&event->callchain_deferred.nr; +if (data->callchain->nr > max_callchain_nr) +return -EFAULT ; + +if (evsel->core.attr.sample_id_all) +perf_evsel__parse_id_sample (evsel, event, data); +return 0 ; + } +if (event->header.type !=PERF_RECORD_SAMPLE ) {if (!evsel->core.attr.sample_id_all)return 0 ;
@@ -3132,6 +3144,12 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,if (data->callchain->nr > max_callchain_nr)return -EFAULT ; sz = data->callchain->nr *sizeof (u64); +if (evsel->core.attr.defer_callchain && + data->callchain->nr >=PERF_DEFERRED_ITEMS && + data->callchain->ips[data->callchain->nr -PERF_DEFERRED_ITEMS ] ==PERF_CONTEXT_USER_DEFERRED ) { + data->deferred_callchain = true; + data->deferred_cookie = data->callchain->ips[data->callchain->nr -1 ]; + }OVERFLOW_CHECK (array, sz, max_size); array = (void *)array + sz; }
tools/perf/util/machine.c +1 -0
@@ -2124,6 +2124,7 @@ static int add_callchain_ip(struct thread *thread, *cpumode =PERF_RECORD_MISC_KERNEL ;break ;case PERF_CONTEXT_USER : +case PERF_CONTEXT_USER_DEFERRED : *cpumode =PERF_RECORD_MISC_USER ;break ;default :
tools/perf/util/perf_event_attr_fprintf.c +1 -0
@@ -343,6 +343,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,PRINT_ATTRf (inherit_thread, p_unsigned);PRINT_ATTRf (remove_on_exec, p_unsigned);PRINT_ATTRf (sigtrap, p_unsigned); +PRINT_ATTRf (defer_callchain, p_unsigned);PRINT_ATTRn ("{ wakeup_events, wakeup_watermark }" , wakeup_events, p_unsigned, false);PRINT_ATTRf (bp_type, p_unsigned);
tools/perf/util/sample.h +3 -1
@@ -106,7 +106,9 @@ struct perf_sample {u16 ins_lat;/** @weight3: On x86 holds retire_lat, on powerpc holds p_stage_cyc. */ u16 weight3; -bool no_hw_idx;/* No hw_idx collected in branch_stack */ +bool no_hw_idx;/* No hw_idx collected in branch_stack */ +bool deferred_callchain;/* Has deferred user callchains */ +u64 deferred_cookie;char insn[MAX_INSN ];void *raw_data;struct ip_callchain *callchain;
tools/perf/util/session.c +18 -0
@@ -720,6 +720,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_CGROUP] = perf_event__cgroup_swap, [PERF_RECORD_TEXT_POKE] = perf_event__text_poke_swap, [PERF_RECORD_AUX_OUTPUT_HW_ID] = perf_event__all64_swap, + [PERF_RECORD_CALLCHAIN_DEFERRED] = perf_event__all64_swap, [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
@@ -1123,6 +1124,20 @@ static void dump_sample(struct evsel *evsel, union perf_event *event,sample_read__printf (sample, evsel->core.attr.read_format); } +static void dump_deferred_callchain (struct evsel *evsel,union perf_event *event, +struct perf_sample *sample) +{ +if (!dump_trace) +return ; + +printf ("(IP, 0x%x): %d/%d: %#" PRIx64" [%llx]\n" , + event->header.misc, sample->pid, sample->tid, sample->ip, + event->callchain_deferred.cookie); + +if (evsel__has_callchain (evsel)) +callchain__printf (evsel, sample); +} +static void dump_read (struct evsel *evsel,union perf_event *event) {struct perf_record_read *read_event = &event->read;
@@ -1353,6 +1368,9 @@ static int machines__deliver_event(struct machines *machines,return tool->text_poke (tool, event, sample, machine);case PERF_RECORD_AUX_OUTPUT_HW_ID :return tool->aux_output_hw_id (tool, event, sample, machine); +case PERF_RECORD_CALLCHAIN_DEFERRED : +dump_deferred_callchain (evsel, event, sample); +return tool->callchain_deferred (tool, event, sample, evsel, machine);default : ++evlist->stats.nr_unknown_events;return -1 ;
tools/perf/util/tool.c +1 -0
@@ -279,6 +279,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events) tool->read = process_event_sample_stub; tool->throttle = process_event_stub; tool->unthrottle = process_event_stub; + tool->callchain_deferred = process_event_sample_stub; tool->attr = process_event_synth_attr_stub; tool->event_update = process_event_synth_event_update_stub; tool->tracing_data = process_event_synth_tracing_data_stub;
tools/perf/util/tool.h +2 -1
@@ -42,7 +42,8 @@ enum show_feature_header {struct perf_tool {event_sample sample, - read; + read, + callchain_deferred;event_op mmap, mmap2, comm,
Signature · 1 line
2.50.1
PATCH v16 2/4 perf record: Enable defer_callchain for user callchains
In reply to Steven RostedtFrom: Namhyung Kim <[email protected]>
And add the missing feature detection logic to clear the flag on old kernels.
$ perf record -g -vv true
...
------------------------------------------------------------
perf_event_attr:
type 0 (PERF_TYPE_HARDWARE)
size 136
config 0 (PERF_COUNT_HW_CPU_CYCLES)
{ sample_period, sample_freq } 4000
sample_type IP|TID|TIME|CALLCHAIN|PERIOD
read_format ID|LOST
disabled 1
inherit 1
mmap 1
comm 1
freq 1
enable_on_exec 1
task 1
sample_id_all 1
mmap2 1
comm_exec 1
ksymbol 1
bpf_event 1
defer_callchain 1
------------------------------------------------------------
sys_perf_event_open: pid 162755 cpu 0 group_fd -1 flags 0x8
sys_perf_event_open failed, error -22
switching off deferred callchain support--- tools/perf/util/evsel.c | 24 ++++++++++++++++++++++++ tools/perf/util/evsel.h | 1 + 2 files changed, 25 insertions(+)
tools/perf/util/evsel.c +24 -0
@@ -1061,6 +1061,14 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o } } +if (param->record_mode ==CALLCHAIN_FP && !attr->exclude_callchain_user) { +/* +* Enable deferred callchains optimistically. It'll be switched +* off later if the kernel doesn't support it. +*/ + attr->defer_callchain =1 ; + } +if (function) {pr_info ("Disabling user space callchains for function trace event.\n" ); attr->exclude_callchain_user =1 ;
@@ -2121,6 +2129,8 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,static void evsel__disable_missing_features (struct evsel *evsel) { +if (perf_missing_features.defer_callchain) + evsel->core.attr.defer_callchain =0 ;if (perf_missing_features.inherit_sample_read && evsel->core.attr.inherit && (evsel->core.attr.sample_type &PERF_SAMPLE_READ )) evsel->core.attr.inherit =0 ;
@@ -2395,6 +2405,15 @@ static bool evsel__detect_missing_features(struct evsel *evsel, struct perf_cpu/* Please add new feature detection here. */ + attr.defer_callchain = true; + attr.sample_type =PERF_SAMPLE_CALLCHAIN ; +if (has_attr_feature (&attr,/*flags=*/ 0 )) + goto found; + perf_missing_features.defer_callchain = true; +pr_debug2 ("switching off deferred callchain support\n" ); + attr.defer_callchain = false; + attr.sample_type =0 ; + attr.inherit = true; attr.sample_type =PERF_SAMPLE_READ ;if (has_attr_feature (&attr,/*flags=*/ 0 ))
@@ -2506,6 +2525,11 @@ static bool evsel__detect_missing_features(struct evsel *evsel, struct perf_cpu errno = old_errno; check: +if (evsel->core.attr.defer_callchain && + evsel->core.attr.sample_type &PERF_SAMPLE_CALLCHAIN && + perf_missing_features.defer_callchain) +return true; +if (evsel->core.attr.inherit && (evsel->core.attr.sample_type &PERF_SAMPLE_READ ) && perf_missing_features.inherit_sample_read)
tools/perf/util/evsel.h +1 -0
@@ -221,6 +221,7 @@ struct perf_missing_features {bool branch_counters;bool aux_action;bool inherit_sample_read; +bool defer_callchain; };extern struct perf_missing_features perf_missing_features;
Signature · 1 line
2.50.1
PATCH v16 3/4 perf script: Display PERF_RECORD_CALLCHAIN_DEFERRED
In reply to Steven RostedtFrom: Namhyung Kim <[email protected]>
Handle the deferred callchains in the script output.
$ perf script
perf 801 [000] 18.031793: 1 cycles:P:
ffffffff91a14c36 __intel_pmu_enable_all.isra.0+0x56 ([kernel.kallsyms])
ffffffff91d373e9 perf_ctx_enable+0x39 ([kernel.kallsyms])
ffffffff91d36af7 event_function+0xd7 ([kernel.kallsyms])
ffffffff91d34222 remote_function+0x42 ([kernel.kallsyms])
ffffffff91c1ebe1 generic_exec_single+0x61 ([kernel.kallsyms])
ffffffff91c1edac smp_call_function_single+0xec ([kernel.kallsyms])
ffffffff91d37a9d event_function_call+0x10d ([kernel.kallsyms])
ffffffff91d33557 perf_event_for_each_child+0x37 ([kernel.kallsyms])
ffffffff91d47324 _perf_ioctl+0x204 ([kernel.kallsyms])
ffffffff91d47c43 perf_ioctl+0x33 ([kernel.kallsyms])
ffffffff91e2f216 __x64_sys_ioctl+0x96 ([kernel.kallsyms])
ffffffff9265f1ae do_syscall_64+0x9e ([kernel.kallsyms])
ffffffff92800130 entry_SYSCALL_64+0xb0 ([kernel.kallsyms])perf 801 [000] 18.031814: DEFERRED CALLCHAIN
7fb5fc22034b __GI___ioctl+0x3b (/usr/lib/x86_64-linux-gnu/libc.so.6)
---
Changes since v15: https://lore.kernel.org/[email protected]
- Removed initialization of script.tool.merge_deferred_callchains as it is introduced in the next patch.
tools/perf/builtin-script.c | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+)
tools/perf/builtin-script.c +88 -0
@@ -2484,6 +2484,93 @@ static int process_sample_event(const struct perf_tool *tool,return ret; } +static int process_deferred_sample_event (const struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct evsel *evsel, +struct machine *machine) +{ + struct perf_script *scr = container_of(tool, struct perf_script, tool); +struct perf_event_attr *attr = &evsel->core.attr; +struct evsel_script *es = evsel->priv; +unsigned int type =output_type (attr->type); +struct addr_location al; +FILE *fp = es->fp; +int ret =0 ; + +if (output[type].fields ==0 ) +return 0 ; + +/* Set thread to NULL to indicate addr_al and al are not initialized */ +addr_location__init (&al); + +if (perf_time__ranges_skip_sample (scr->ptime_range, scr->range_num, + sample->time)) { + goto out_put; + } + +if (debug_mode) { +if (sample->time < last_timestamp) { +pr_err ("Samples misordered, previous: %" PRIu64 +" this: %" PRIu64"\n" , last_timestamp, + sample->time); + nr_unordered++; + } + last_timestamp = sample->time; + goto out_put; + } + +if (filter_cpu (sample)) + goto out_put; + +if (machine__resolve (machine, &al, sample) <0 ) { +pr_err ("problem processing %d event, skipping it.\n" , + event->header.type); + ret =-1 ; + goto out_put; + } + +if (al.filtered) + goto out_put; + +if (!show_event (sample, evsel, al.thread, &al,NULL )) + goto out_put; + +if (evswitch__discard (&scr->evswitch, evsel)) + goto out_put; + +perf_sample__fprintf_start (scr, sample, al.thread, evsel, +PERF_RECORD_CALLCHAIN_DEFERRED , fp); +fprintf (fp,"DEFERRED CALLCHAIN" ); + +if (PRINT_FIELD (IP )) { +struct callchain_cursor *cursor =NULL ; + +if (symbol_conf.use_callchain && sample->callchain) { + cursor =get_tls_callchain_cursor (); +if (thread__resolve_callchain (al.thread, cursor, evsel, + sample,NULL ,NULL , + scripting_max_stack)) { +pr_info ("cannot resolve deferred callchains\n" ); + cursor =NULL ; + } + } + +fputc (cursor ?'\n' :' ' , fp); +sample__fprintf_sym (sample, &al,0 , output[type].print_ip_opts, + cursor, symbol_conf.bt_stop_list, fp); + } + +fprintf (fp,"\n" ); + +if (verbose >0 ) +fflush (fp); + +out_put: +addr_location__exit (&al); +return ret; +} +// Used when scr->per_event_dump is not set static struct evsel_script es_stdout;
@@ -4080,6 +4167,7 @@ int cmd_script(int argc, const char **argv)perf_tool__init (&script.tool, !unsorted_dump); script.tool.sample = process_sample_event; + script.tool.callchain_deferred = process_deferred_sample_event; script.tool.mmap = perf_event__process_mmap; script.tool.mmap2 = perf_event__process_mmap2; script.tool.comm = perf_event__process_comm;
Signature · 1 line
2.50.1
PATCH v16 4/4 perf tools: Merge deferred user callchains
In reply to Steven RostedtFrom: Namhyung Kim <[email protected]>
Save samples with deferred callchains in a separate list and deliver them after merging the user callchains. If users don't want to merge they can set tool->merge_deferred_callchains to false to prevent the behavior.
With previous result, now perf script will show the merged callchains.
$ perf script
perf 801 [000] 18.031793: 1 cycles:P:
ffffffff91a14c36 __intel_pmu_enable_all.isra.0+0x56 ([kernel.kallsyms])
ffffffff91d373e9 perf_ctx_enable+0x39 ([kernel.kallsyms])
ffffffff91d36af7 event_function+0xd7 ([kernel.kallsyms])
ffffffff91d34222 remote_function+0x42 ([kernel.kallsyms])
ffffffff91c1ebe1 generic_exec_single+0x61 ([kernel.kallsyms])
ffffffff91c1edac smp_call_function_single+0xec ([kernel.kallsyms])
ffffffff91d37a9d event_function_call+0x10d ([kernel.kallsyms])
ffffffff91d33557 perf_event_for_each_child+0x37 ([kernel.kallsyms])
ffffffff91d47324 _perf_ioctl+0x204 ([kernel.kallsyms])
ffffffff91d47c43 perf_ioctl+0x33 ([kernel.kallsyms])
ffffffff91e2f216 __x64_sys_ioctl+0x96 ([kernel.kallsyms])
ffffffff9265f1ae do_syscall_64+0x9e ([kernel.kallsyms])
ffffffff92800130 entry_SYSCALL_64+0xb0 ([kernel.kallsyms])
7fb5fc22034b __GI___ioctl+0x3b (/usr/lib/x86_64-linux-gnu/libc.so.6)
...The old output can be get using --no-merge-callchain option. Also perf report can get the user callchain entry at the end.
$ perf report --no-children --percent-limit=0 --stdio -q -S __intel_pmu_enable_all.isra.0
# symbol: __intel_pmu_enable_all.isra.0
0.00% perf [kernel.kallsyms]
|
---__intel_pmu_enable_all.isra.0
perf_ctx_enable
event_function
remote_function
generic_exec_single
smp_call_function_single
event_function_call
perf_event_for_each_child
_perf_ioctl
perf_ioctl
__x64_sys_ioctl
do_syscall_64
entry_SYSCALL_64
__GI___ioctl---
Changes since v15: https://lore.kernel.org/[email protected]
- Have the matching deferred code only skip when TID does not match. Otherwise, process the sample but do not merge if the cookies do not match. (Namhyung Kim)
tools/perf/Documentation/perf-script.txt | 5 ++ tools/perf/builtin-script.c | 4 ++ tools/perf/util/callchain.c | 24 +++++++++ tools/perf/util/callchain.h | 3 ++ tools/perf/util/evlist.c | 1 + tools/perf/util/evlist.h | 1 + tools/perf/util/session.c | 65 +++++++++++++++++++++++- tools/perf/util/tool.c | 1 + tools/perf/util/tool.h | 1 + 9 files changed, 104 insertions(+), 1 deletion(-)
tools/perf/Documentation/perf-script.txt +5 -0
@@ -527,6 +527,11 @@ include::itrace.txt[] The known limitations include exception handing such as setjmp/longjmp will have calls/returns not match. +--merge-callchains:: + Enable merging deferred user callchains if available. This is the + default behavior. If you want to see separate CALLCHAIN_DEFERRED + records for some reason, use --no-merge-callchains explicitly. + :GMEXAMPLECMD: script :GMEXAMPLESUBCMD: include::guest-files.txt[]
tools/perf/builtin-script.c +4 -0
@@ -3785,6 +3785,7 @@ int cmd_script(int argc, const char **argv)bool header_only = false;bool script_started = false;bool unsorted_dump = false; +bool merge_deferred_callchains = true;char *rec_script_path =NULL ;char *rep_script_path =NULL ;struct perf_session *session;
@@ -3938,6 +3939,8 @@ int cmd_script(int argc, const char **argv) "Guest code can be found in hypervisor process"),OPT_BOOLEAN ('\0' ,"stitch-lbr" , &script.stitch_lbr,"Enable LBR callgraph stitching approach" ), +OPT_BOOLEAN ('\0' ,"merge-callchains" , &merge_deferred_callchains, +"Enable merge deferred user callchains" ),OPTS_EVSWITCH (&script.evswitch), OPT_END() };
@@ -4194,6 +4197,7 @@ int cmd_script(int argc, const char **argv) script.tool.throttle = process_throttle_event; script.tool.unthrottle = process_throttle_event; script.tool.ordering_requires_timestamps = true; + script.tool.merge_deferred_callchains = merge_deferred_callchains; session =perf_session__new (&data, &script.tool);if (IS_ERR (session))return PTR_ERR (session);
tools/perf/util/callchain.c +24 -0
@@ -1828,3 +1828,27 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, }return 0 ; } + +int sample__merge_deferred_callchain (struct perf_sample *sample_orig, +struct perf_sample *sample_callchain) +{ +u64 nr_orig = sample_orig->callchain->nr -PERF_DEFERRED_ITEMS ; +u64 nr_deferred = sample_callchain->callchain->nr; +struct ip_callchain *callchain; + + callchain =calloc (1 + nr_orig + nr_deferred,sizeof (u64)); +if (callchain ==NULL ) { + sample_orig->deferred_callchain = false; +return -ENOMEM ; + } + + callchain->nr = nr_orig + nr_deferred; +/* copy except for the last PERF_CONTEXT_USER_DEFERRED */ +memcpy (callchain->ips, sample_orig->callchain->ips, nr_orig *sizeof (u64)); +/* copy deferred use callchains */ +memcpy (&callchain->ips[nr_orig], sample_callchain->callchain->ips, + nr_deferred *sizeof (u64)); + + sample_orig->callchain = callchain; +return 0 ; +}
tools/perf/util/callchain.h +3 -0
@@ -317,4 +317,7 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, struct perf_sample *sample, int max_stack, bool symbols, callchain_iter_fn cb, void *data); +int sample__merge_deferred_callchain (struct perf_sample *sample_orig, +struct perf_sample *sample_callchain); +#endif /* __PERF_CALLCHAIN_H */
tools/perf/util/evlist.c +1 -0
@@ -85,6 +85,7 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, evlist->ctl_fd.pos =-1 ; evlist->nr_br_cntr =-1 ;metricgroup__rblist_init (&evlist->metric_events); +INIT_LIST_HEAD (&evlist->deferred_samples); }struct evlist *evlist__new (void )
tools/perf/util/evlist.h +1 -0
@@ -92,6 +92,7 @@ struct evlist {* of struct metric_expr. */ struct rblist metric_events; +struct list_head deferred_samples; };struct evsel_str_handler {
tools/perf/util/session.c +64 -1
83 lines not on this page · raw message
tools/perf/util/tool.c +1 -0
@@ -258,6 +258,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events) tool->cgroup_events = false; tool->no_warn = false; tool->show_feat_hdr =SHOW_FEAT_NO_HEADER ; + tool->merge_deferred_callchains = true; tool->sample = process_event_sample_stub; tool->mmap = process_event_stub;
tools/perf/util/tool.h +1 -0
@@ -88,6 +88,7 @@ struct perf_tool {bool cgroup_events;bool no_warn;bool dont_split_sample_group; +bool merge_deferred_callchains;enum show_feature_header show_feat_hdr; };
Signature · 1 line
2.50.1
Steven Rostedt
maintainerRe: [PATCH v16 4/4] perf tools: Merge deferred user callchains
Quoted 8 lines · On Mon, 08 Sep 2025 13:53:23 -0400 Steven Rostedt <[email protected]> wrote:
tools/perf/util/session.c +8
-0+static int evlist__deliver_deferred_samples (struct evlist *evlist, +const struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct machine *machine) +{ +struct deferred_event *de, *tmp; +struct evsel *evsel;
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1308
Quoted 10 lines
tools/perf/util/session.c +10
-0+int ret =0 ; + +if (!tool->merge_deferred_callchains) { + evsel =evlist__id2evsel (evlist, sample->id); +return tool->callchain_deferred (tool, event, sample, + evsel, machine); + } + +list_for_each_entry_safe (de, tmp, &evlist->deferred_samples, list) { +struct perf_sample orig_sample;
orig_sample is not initialized and can then contain junk.
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1320
Quoted 12 lines
tools/perf/util/session.c +12
-0+ + ret =evlist__parse_sample (evlist, de->event, &orig_sample); +if (ret <0 ) { +pr_err ("failed to parse original sample\n" ); +break ; + } + +if (sample->tid != orig_sample.tid) +continue ; + +if (event->callchain_deferred.cookie == orig_sample.deferred_cookie) +sample__merge_deferred_callchain (&orig_sample, sample);
The sample__merge_deferred_callchain() initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:
else orig_sample.deferred_callchain = false;
Quoted 5 lines
tools/perf/util/session.c +5
-0+ + evsel =evlist__id2evsel (evlist, orig_sample.id); + ret =evlist__deliver_sample (evlist, tool, de->event, + &orig_sample, evsel,> machine); + + if (orig_sample.deferred_callchain)
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1336
Quoted 10 lines
tools/perf/util/session.c +10
-0+free (orig_sample.callchain); + +list_del (&de->list); +free (de); + +if (ret) +break ; + } +return ret; +}
-- Steve
Peter Zijlstra
maintainerRe: [PATCH v16 4/4] perf tools: Merge deferred user callchains
Quoted 42 lines · Steven Rostedt
Quoted 8 lines · On Mon, 08 Sep 2025 13:53:23 -0400 Steven Rostedt <[email protected]> wrote:
tools/perf/util/session.c +8
-0+static int evlist__deliver_deferred_samples (struct evlist *evlist, +const struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct machine *machine) +{ +struct deferred_event *de, *tmp; +struct evsel *evsel;tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1308
Quoted 10 lines
tools/perf/util/session.c +10
-0+int ret =0 ; + +if (!tool->merge_deferred_callchains) { + evsel =evlist__id2evsel (evlist, sample->id); +return tool->callchain_deferred (tool, event, sample, + evsel, machine); + } + +list_for_each_entry_safe (de, tmp, &evlist->deferred_samples, list) { +struct perf_sample orig_sample;
orig_sampleis not initialized and can then contain junk.tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1320
Quoted 12 lines
tools/perf/util/session.c +12
-0+ + ret =evlist__parse_sample (evlist, de->event, &orig_sample); +if (ret <0 ) { +pr_err ("failed to parse original sample\n" ); +break ; + } + +if (sample->tid != orig_sample.tid) +continue ; + +if (event->callchain_deferred.cookie == orig_sample.deferred_cookie) +sample__merge_deferred_callchain (&orig_sample, sample);The
sample__merge_deferred_callchain()initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:
Quoted 2 lines
else orig_sample.deferred_callchain = false;
Ah, so I saw crashes from here and just deleted both free()s and got on with things ;-)
Quoted 17 lines
Quoted 5 lines
tools/perf/util/session.c +5
-0+ + evsel =evlist__id2evsel (evlist, orig_sample.id); + ret =evlist__deliver_sample (evlist, tool, de->event, + &orig_sample, evsel,> machine); + + if (orig_sample.deferred_callchain)tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1336
Quoted 10 lines
tools/perf/util/session.c +10
-0+free (orig_sample.callchain); + +list_del (&de->list); +free (de); + +if (ret) +break ; + } +return ret; +}-- Steve
Steven Rostedt
maintainerRe: [PATCH v16 4/4] perf tools: Merge deferred user callchains
Quoted 10 lines · On Fri, 24 Oct 2025 15:02:03 +0200 Peter Zijlstra <[email protected]> wrote:
Quoted 7 lines
The
sample__merge_deferred_callchain()initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:else orig_sample.deferred_callchain = false;Ah, so I saw crashes from here and just deleted both free()s and got on with things ;-)
I just downloaded your tree again and it doesn't look like it was updated.
Just didn't want you to forget about this ;)
-- Steve
Peter Zijlstra
maintainerRe: [PATCH v16 4/4] perf tools: Merge deferred user callchains
Quoted 11 lines · Steven Rostedt
Quoted 8 lines · On Fri, 24 Oct 2025 15:02:03 +0200 Peter Zijlstra <[email protected]> wrote:
Quoted 7 lines
The
sample__merge_deferred_callchain()initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:else orig_sample.deferred_callchain = false;
Quoted 6 lines
Quoted 2 lines
Ah, so I saw crashes from here and just deleted both free()s and got on with things ;-)
I just downloaded your tree again and it doesn't look like it was updated.
Just didn't want you to forget about this ;)
Done, this should all be in tip/perf/core now. Thanks!
Re: [PATCH v16 4/4] perf tools: Merge deferred user callchains
In reply to Peter ZijlstraHello Namhyung,
could you please adapt your patches from this series to Peter's latest changes to unwind user and related perf support, especially his new version c69993ecdd4d ("perf: Support deferred user unwind") available at:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core
Quoted 43 lines · Peter Zijlstra
Quoted 42 lines · Steven Rostedt
Quoted 8 lines · On Mon, 08 Sep 2025 13:53:23 -0400 Steven Rostedt <[email protected]> wrote:
tools/perf/util/session.c +8
-0+static int evlist__deliver_deferred_samples (struct evlist *evlist, +const struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct machine *machine) +{ +struct deferred_event *de, *tmp; +struct evsel *evsel;tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1308
Quoted 10 lines
tools/perf/util/session.c +10
-0+int ret =0 ; + +if (!tool->merge_deferred_callchains) { + evsel =evlist__id2evsel (evlist, sample->id); +return tool->callchain_deferred (tool, event, sample, + evsel, machine); + } + +list_for_each_entry_safe (de, tmp, &evlist->deferred_samples, list) { +struct perf_sample orig_sample;
orig_sampleis not initialized and can then contain junk.tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1320
Quoted 12 lines
tools/perf/util/session.c +12
-0+ + ret =evlist__parse_sample (evlist, de->event, &orig_sample); +if (ret <0 ) { +pr_err ("failed to parse original sample\n" ); +break ; + } + +if (sample->tid != orig_sample.tid) +continue ; + +if (event->callchain_deferred.cookie == orig_sample.deferred_cookie) +sample__merge_deferred_callchain (&orig_sample, sample);The
sample__merge_deferred_callchain()initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:
Quoted 5 lines
Quoted 2 lines
else orig_sample.deferred_callchain = false;Ah, so I saw crashes from here and just deleted both free()s and got on with things ;-)
This needs to be properly resolved. In the meantime I am using Steven's suggestion above to continue my work on unwind user sframe (s390).
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
tools/perf/util/session.c +9
-0+ + evsel =evlist__id2evsel (evlist, orig_sample.id); + ret =evlist__deliver_sample (evlist, tool, de->event, + &orig_sample, evsel,> machine); + + if (orig_sample.deferred_callchain) +free (orig_sample.callchain); + +list_del (&de->list); +free (de);
Quoted 8 lines
Quoted 8 lines
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1336
Quoted 6 lines
tools/perf/util/session.c +6
-0+ +if (ret) +break ; + } +return ret; +}-- Steve
Thanks and regards,
Jens
Signature · 9 lines
Jens Remus Linux on Z Development (D3303) +49-7031-16-1128 Office [email protected] IBM IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Böblingen; Registergericht: Amtsgericht Stuttgart, HRB 243294 IBM Data Privacy Statement: https://www.ibm.com/privacy/
Namhyung Kim
maintainerRe: [PATCH v16 4/4] perf tools: Merge deferred user callchains
Hello,
Quoted 8 lines · Jens Remus
Hello Namhyung,
could you please adapt your patches from this series to Peter's latest changes to unwind user and related perf support, especially his new version
c69993ecdd4d("perf: Support deferred user unwind") available at:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.gitperf/core
Sure, will take a look.
Thanks,
Namhyung
Quoted 86 lines · Peter Zijlstra
Quoted 43 lines · Steven Rostedt
Quoted 42 lines
Quoted 8 lines · On Mon, 08 Sep 2025 13:53:23 -0400 Steven Rostedt <[email protected]> wrote:
tools/perf/util/session.c +8
-0+static int evlist__deliver_deferred_samples (struct evlist *evlist, +const struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct machine *machine) +{ +struct deferred_event *de, *tmp; +struct evsel *evsel;tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1308
Quoted 10 lines
tools/perf/util/session.c +10
-0+int ret =0 ; + +if (!tool->merge_deferred_callchains) { + evsel =evlist__id2evsel (evlist, sample->id); +return tool->callchain_deferred (tool, event, sample, + evsel, machine); + } + +list_for_each_entry_safe (de, tmp, &evlist->deferred_samples, list) { +struct perf_sample orig_sample;
orig_sampleis not initialized and can then contain junk.tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1320
Quoted 12 lines
tools/perf/util/session.c +12
-0+ + ret =evlist__parse_sample (evlist, de->event, &orig_sample); +if (ret <0 ) { +pr_err ("failed to parse original sample\n" ); +break ; + } + +if (sample->tid != orig_sample.tid) +continue ; + +if (event->callchain_deferred.cookie == orig_sample.deferred_cookie) +sample__merge_deferred_callchain (&orig_sample, sample);The
sample__merge_deferred_callchain()initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:Quoted 5 lines
Quoted 2 lines
else orig_sample.deferred_callchain = false;Ah, so I saw crashes from here and just deleted both free()s and got on with things ;-)
This needs to be properly resolved. In the meantime I am using Steven's suggestion above to continue my work on unwind user sframe (s390).
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
tools/perf/util/session.c +9
-0+ + evsel =evlist__id2evsel (evlist, orig_sample.id); + ret =evlist__deliver_sample (evlist, tool, de->event, + &orig_sample, evsel,> machine); + + if (orig_sample.deferred_callchain) +free (orig_sample.callchain); + +list_del (&de->list); +free (de);Quoted 8 lines
Quoted 8 lines
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1336
Quoted 6 lines
tools/perf/util/session.c +6
-0+ +if (ret) +break ; + } +return ret; +}-- Steve
Thanks and regards,
JensSignature · 9 lines
Jens Remus Linux on Z Development (D3303) +49-7031-16-1128 Office [email protected] IBM IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Böblingen; Registergericht: Amtsgericht Stuttgart, HRB 243294 IBM Data Privacy Statement: https://www.ibm.com/privacy/
Namhyung Kim
maintainerRe: [PATCH v16 4/4] perf tools: Merge deferred user callchains
In reply to Peter ZijlstraHello,
Sorry for the delay. And I'm happy that the kernel part is merge to the tip tree! :)
Quoted 16 lines · Peter Zijlstra
Quoted 15 lines · Steven Rostedt
Quoted 12 lines · On Mon, 08 Sep 2025 13:53:23 -0400 Steven Rostedt <[email protected]> wrote:
tools/perf/util/session.c +12
-0+static int evlist__deliver_deferred_samples (struct evlist *evlist, +const struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct machine *machine) +{ +struct deferred_event *de, *tmp; +struct evsel *evsel; +int ret =0 ; + +if (!tool->merge_deferred_callchains) { + evsel =evlist__id2evsel (evlist, sample->id);
Quoted 8 lines
Quoted 8 lines
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1308
Quoted 6 lines
tools/perf/util/session.c +6
-0+return tool->callchain_deferred (tool, event, sample, + evsel, machine); + } + +list_for_each_entry_safe (de, tmp, &evlist->deferred_samples, list) { +struct perf_sample orig_sample;
orig_sampleis not initialized and can then contain junk.
Yep.
Quoted 3 lines
Quoted 3 lines
Review
Quoted 3 lines
(quoted diff) +2
-0+ + ret = evlist__parse_sample(evlist, de->event, &orig_sample);
But here you call evlist__parse_sample() and evsel__parse_sample() which should initialize the sample properly.
Quoted 16 lines
Quoted 16 lines
tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1320
Quoted 10 lines
tools/perf/util/session.c +10
-0+if (ret <0 ) { +pr_err ("failed to parse original sample\n" ); +break ; + } + +if (sample->tid != orig_sample.tid) +continue ; + +if (event->callchain_deferred.cookie == orig_sample.deferred_cookie) +sample__merge_deferred_callchain (&orig_sample, sample);The
sample__merge_deferred_callchain()initializes both orig_sample.deferred_callchain and the callchain. But now that it's not being called, it can cause the below free to happen with junk as the callchain. This needs:
Quoted 5 lines
Quoted 2 lines
else orig_sample.deferred_callchain = false;Ah, so I saw crashes from here and just deleted both free()s and got on with things ;-)
I don't understand how it can have the garbage. But having the else part would be safer.
Thanks,
Namhyung
Quoted 17 lines
Quoted 17 lines
Quoted 5 lines
tools/perf/util/session.c +5
-0+ + evsel =evlist__id2evsel (evlist, orig_sample.id); + ret =evlist__deliver_sample (evlist, tool, de->event, + &orig_sample, evsel,> machine); + + if (orig_sample.deferred_callchain)tools/perf/util/session.c static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool · line 1336
Quoted 10 lines
tools/perf/util/session.c +10
-0+free (orig_sample.callchain); + +list_del (&de->list); +free (de); + +if (ret) +break ; + } +return ret; +}-- Steve
Peter Zijlstra
maintainerPer the hackery I did:
https://lkml.kernel.org/r/[email protected]
The userspace bits need something like so on top..
--- tools/perf/util/callchain.c | 2 +- tools/perf/util/evsel.c | 10 +++++++--- tools/perf/util/perf_event_attr_fprintf.c | 1 + 3 files changed, 9 insertions(+), 4 deletions(-)
tools/perf/util/callchain.c +1 -1
@@ -1832,7 +1832,7 @@ int sample__for_each_callchain_node(struint sample__merge_deferred_callchain (struct perf_sample *sample_orig,struct perf_sample *sample_callchain) { -u64 nr_orig = sample_orig->callchain->nr -PERF_DEFERRED_ITEMS ; +u64 nr_orig = sample_orig->callchain->nr -1 ;u64 nr_deferred = sample_callchain->callchain->nr;struct ip_callchain *callchain;
tools/perf/util/evsel.c +7 -3
@@ -1520,6 +1520,7 @@ void evsel__config(struct evsel *evsel, attr->mmap2 = track && !perf_missing_features.mmap2; attr->comm = track; attr->build_id = track && opts->build_id; + attr->defer_output = track && !perf_missing_features.defer_callchain;/* * ksymbol is tracked separately with text poke because it needs to be
@@ -2206,8 +2207,10 @@ static int __evsel__prepare_open(structstatic void evsel__disable_missing_features (struct evsel *evsel) { -if (perf_missing_features.defer_callchain) +if (perf_missing_features.defer_callchain) { evsel->core.attr.defer_callchain =0 ; + evsel->core.attr.defer_output =0 ; + }if (perf_missing_features.inherit_sample_read && evsel->core.attr.inherit && (evsel->core.attr.sample_type &PERF_SAMPLE_READ )) evsel->core.attr.inherit =0 ;
@@ -2489,6 +2492,7 @@ static bool evsel__detect_missing_featur perf_missing_features.defer_callchain = true;pr_debug2 ("switching off deferred callchain support\n" ); attr.defer_callchain = false; + attr.defer_output = false; attr.sample_type =0 ; attr.inherit = true;
@@ -3255,8 +3259,8 @@ int evsel__parse_sample(struct evsel *evreturn -EFAULT ; sz = data->callchain->nr *sizeof (u64);if (evsel->core.attr.defer_callchain && - data->callchain->nr >=PERF_DEFERRED_ITEMS && - data->callchain->ips[data->callchain->nr -PERF_DEFERRED_ITEMS ] ==PERF_CONTEXT_USER_DEFERRED ) { + data->callchain->nr >=2 && + data->callchain->ips[data->callchain->nr -2 ] ==PERF_CONTEXT_USER_DEFERRED ) { data->deferred_callchain = true; data->deferred_cookie = data->callchain->ips[data->callchain->nr -1 ]; }
tools/perf/util/perf_event_attr_fprintf.c +1 -0
@@ -344,6 +344,7 @@ int perf_event_attr__fprintf(FILE *fp, sPRINT_ATTRf (remove_on_exec, p_unsigned);PRINT_ATTRf (sigtrap, p_unsigned);PRINT_ATTRf (defer_callchain, p_unsigned); +PRINT_ATTRf (defer_output, p_unsigned);PRINT_ATTRn ("{ wakeup_events, wakeup_watermark }" , wakeup_events, p_unsigned, false);PRINT_ATTRf (bp_type, p_unsigned);