Add support for deferred userspace unwind to perf. Where perf currently relies on in-place stack unwinding; from NMI context and all that. This moves the userspace part of the unwind to right before the return-to-userspace. This has two distinct benefits, the biggest is that it moves the unwind to a faultable context. It becomes possible to fault in debug info (.eh_frame, SFrame etc.) that might not otherwise be readily available. And secondly, it de-duplicates the user callchain where multiple samples happen during the same kernel entry. To facilitate this the perf interface is extended with a new record type: PERF_RECORD_CALLCHAIN_DEFERRED and two new attribute flags: perf_event_attr::defer_callchain - to request the user unwind be deferred perf_event_attr::defer_output - to request PERF_RECORD_CALLCHAIN_DEFERRED records The existing PERF_RECORD_SAMPLE callchain section gets a new context type: PERF_CONTEXT_USER_DEFERRED After which will come a single entry, denoting the 'cookie' of the deferred callchain that should be attached here, matching the 'cookie' field of the above mentioned PERF_RECORD_CALLCHAIN_DEFERRED. The 'defer_callchain' flag is expected on all events with PERF_SAMPLE_CALLCHAIN. The 'defer_output' flag is expect on the event responsible for collecting side-band events (like mmap, comm etc.). Setting 'defer_output' on multiple events will get you duplicated PERF_RECORD_CALLCHAIN_DEFERRED records. Based on earlier patches by Josh and Steven.
Showing the changes to include/uapi (1 of 8 files). Show all
Unified · Split
| @@ -463,7 +463,9 @@ struct perf_event_attr { · 462 unmodified lines | |||
| 463 | inherit_thread : 1, /* children only inherit if cloned with CLONE_THREAD */ | 463 | inherit_thread : 1, /* children only inherit if cloned with CLONE_THREAD */ |
| 464 | remove_on_exec : 1, /* event is removed from task on exec */ | 464 | remove_on_exec : 1, /* event is removed from task on exec */ |
| 465 | sigtrap : 1, /* send synchronous SIGTRAP on event */ | 465 | sigtrap : 1, /* send synchronous SIGTRAP on event */ |
| 466 | __reserved_1 : 26; | 466 | defer_callchain: 1, /* request PERF_RECORD_CALLCHAIN_DEFERRED records */ |
| 467 | defer_output : 1, /* output PERF_RECORD_CALLCHAIN_DEFERRED records */ | ||
| 468 | __reserved_1 : 24; | ||
| 467 | 469 | ||
| 468 | union { | 470 | union { |
| 469 | __u32 wakeup_events; /* wake up every n events */ | 471 | __u32 wakeup_events; /* wake up every n events */ |
| @@ -1239,6 +1241,22 @@ enum perf_event_type { · 769 unmodified lines | |||
| 1239 | */ | 1241 | */ |
| 1240 | PERF_RECORD_AUX_OUTPUT_HW_ID = 21, | 1242 | PERF_RECORD_AUX_OUTPUT_HW_ID = 21, |
| 1241 | 1243 | ||
| 1244 | /* | ||
| 1245 | * This user callchain capture was deferred until shortly before | ||
| 1246 | * returning to user space. Previous samples would have kernel | ||
| 1247 | * callchains only and they need to be stitched with this to make full | ||
| 1248 | * callchains. | ||
| 1249 | * | ||
| 1250 | * struct { | ||
| 1251 | * struct perf_event_header header; | ||
| 1252 | * u64 cookie; | ||
| 1253 | * u64 nr; | ||
| 1254 | * u64 ips[nr]; | ||
| 1255 | * struct sample_id sample_id; | ||
| 1256 | * }; | ||
| 1257 | */ | ||
| 1258 | PERF_RECORD_CALLCHAIN_DEFERRED = 22, | ||
| 1259 | |||
| 1242 | PERF_RECORD_MAX, /* non-ABI */ | 1260 | PERF_RECORD_MAX, /* non-ABI */ |
| 1243 | }; | 1261 | }; |
| 1244 | 1262 | ||
| @@ -1269,6 +1287,7 @@ enum perf_callchain_context { · 24 unmodified lines | |||
| 1269 | PERF_CONTEXT_HV = (__u64)-32, | 1287 | PERF_CONTEXT_HV = (__u64)-32, |
| 1270 | PERF_CONTEXT_KERNEL = (__u64)-128, | 1288 | PERF_CONTEXT_KERNEL = (__u64)-128, |
| 1271 | PERF_CONTEXT_USER = (__u64)-512, | 1289 | PERF_CONTEXT_USER = (__u64)-512, |
| 1290 | PERF_CONTEXT_USER_DEFERRED = (__u64)-640, | ||
| 1272 | 1291 | ||
| 1273 | PERF_CONTEXT_GUEST = (__u64)-2048, | 1292 | PERF_CONTEXT_GUEST = (__u64)-2048, |
| 1274 | PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, | 1293 | PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, |