Summary
Extend llvm-profgen to generate LLVM sample profiles directly from basic perf events that contain sampled instruction pointers (IPs).
Background
The LLVM profiling ecosystem currently relies on the out-of-tree AutoFDO create_llvm_prof tool to generate sample profiles from sampled-IP perf data without branch stacks. This leaves users dependent on a separate tool and profile-generation pipeline that may not remain in step with upstream LLVM.
llvm-profgen initially generated profiles from x86 Last Branch Record (LBR) branch stacks. More recently, support for decoding Arm Embedded Trace Macrocell (ETM) traces into LLVM sample profiles was merged. This establishes llvm-profgen as infrastructure for more than one profile source.
Problem
llvm-profgen supports branch-stack and ETM inputs, but it cannot directly consume basic sampled-IP perf events. This gap has also motivated community demand for upstream support in llvm-profgen.
Proposal
Add an explicit --basic-events mode, with -ba as an alias, for perf input containing sampled instruction pointers.
In this mode, llvm-profgen should accept either raw perf.data or perfscript. Generating the source profile also requires the profiled binary and its debug information. Perfscript input should contain sampled IPs, process IDs and memory-mapping records, with task records describing user-space process-lifetime changes. The mode should be architecture-neutral: any architecture that can provide input in this form should use the same path.
The result should be a normal, flat, line-number LLVM sample profile, combining samples across runtime callers in each function’s profile. It should provide function and source-location information through the existing sample-profile path and inform inlining, hot/cold classification, and block or function weighting.
Branch-stack-based perf samples contain recently taken branches from which llvm-profgen derives weighted ranges, branch edges, and call targets. Basic-event profiles can preserve the nesting of functions inlined into their callers, using the binary’s debug information.
--skip-symbolization should save intermediate instruction-address counts. --unsymbolized-profile should reload them for conversion to function and source-location samples.
When the implementation has been validated, the user manual’s section on using sampling
profilers should be updated.
Suggested Implementation
Add PerfContent::BasicEvent to the existing content enum and introduce BasicEventPerfReader, selected by --basic-events. Derive it from PerfScriptReader to reuse the perfscript event loop. For perf.data, extend the existing perf script invocation to request sampled IPs, process IDs, mappings and task events.
AutoFDO’s sampled-IP reader uses per-process mappings to resolve samples before combining counts for the target binary. Basic-event mode should provide this support to cover the same workflows, including recordings with multiple processes, relocated binaries and forked children.
Add an ExecutableMapping structure to record runtime address ranges and their translation into addresses in the profiled binary. Store mappings per process and add task state and a resolveMappedAddress() helper to PerfScriptReader, which owns mmap-event processing. The new reader overrides parseSample() to filter by PID, resolve instruction addresses
and accumulate counts. Track process lifetime and inherited mappings through perf task events. Kernel samples use mappings shared across processes.
The existing SampleCounter structure holds the address-based counts produced by a reader, including counts for instruction ranges and branch edges. Extend it with a BasicSampleCounter member using the new BasicSample address-to-count map type.
Use the existing ContextSampleCounterMap to pass the reader’s counters to profile generation. Each entry holds a SampleCounter for one calling context. The Basic reader should provide a single entry containing all instruction-address counts for the binary.
The existing ProfileGenerator maps binary-address counts to source functions and locations using debug information. Extend it so Basic counts share LBR’s per-instruction symbolisation and counting logic. LBR reaches this logic by walking instructions within its ranges; Basic supplies individual sampled addresses and counts. Use the line-number path for Basic input, including binaries containing pseudo-probe profiling metadata. The existing sample-profile writer then emits the LLVM profile.
Extend the existing writeUnsymbolizedProfile() and UnsymbolizedProfileReader to write and restore Basic counts. Add a header marker identifying the file as Basic input, so the reader selects the address-to-count layout. Preserve markerless LBR profiles.
Open Questions
What terminology should this mode use? This RFC uses basic-events for sampled-IP perf events without branch stacks, but upstream may prefer another mode, flag, or perf-content name.
The command-line interface is already crowded. Should it use a sample-content selector, such as --perf-content=basic, instead of adding a dedicated --basic-events flag? Future profiling mechanisms, such as Arm Statistical Profiling Extension (SPE) on AArch64, may also require new input modes, so a multi-valued selector may be preferable.
Should the unsymbolised input identify Basic content with an in-band marker, or should the content type be selected explicitly on the command line?