Instrumenting a production binary for PGO data is often off the table, but llvm-profgen-18 turns an ordinary perf recording into a profile instead. The local command is LLVM 18.1.3 from Ubuntu package llvm-18. Allow 20 to 30 minutes for a first run, longer if you need to install or configure perf.
You need the program you want to profile, a representative workload, its executable binary, llvm-profgen-18, and Linux perf with permission to collect branch-stack samples. The binary must be available to symbolise addresses. Run collection as the same user that can execute the workload; use elevated privileges only when your system's perf policy requires them.
Checkpoint: this workflow creates new trace and profile files. It does not change the executable, compiler flags or system configuration. Keep the trace until you have checked the profile, because the trace is the recovery path if conversion needs to be repeated.
Check the executable and package before preparing data. These are ordinary, read-only commands:
$ command -v llvm-profgen-18
/usr/bin/llvm-profgen-18
$ llvm-profgen-18 --version
Ubuntu LLVM version 18.1.3
Optimized build.
$ dpkg-query -W -f='${Package} ${Version}\n' llvm-18
llvm-18 1:18.1.3-1ubuntu1
The installed manpage calls the tool llvm-profgen and documents --perfscript, --binary, --output and the profile formats. The versioned executable above is the one used in this guide. Ask it for its own help if a distribution update changes the available options:
$ llvm-profgen-18 --help
Use perf record with branch stacks, as required by the documented llvm-profgen workflow. Replace the two placeholders with a real executable and workload. This command can add system-wide performance overhead while it runs, so collect during a suitable test window.
$ perf record -b -o /tmp/example.perf.data -- /path/to/program --representative-input
The -b option is significant: llvm-profgen needs branch information for this style of sample-based profile. A successful perf record run should leave a non-empty data file. Check it without changing the file:
$ test -s /tmp/example.perf.data && echo 'perf data is non-empty'
perf data is non-empty
If perf reports a permissions error, first check the host's perf policy and whether the workload is allowed to collect samples. Do not weaken kernel security settings blindly. If the command was interrupted, discard only the incomplete trace after checking its path, then collect again.
Turn the raw recording into the perf-script text that the manpage names as llvm-profgen input. This is a separate, read-only conversion. Keep the output name distinct from any earlier trace:
$ perf script -i /tmp/example.perf.data > /tmp/example.perf.script
$ test -s /tmp/example.perf.script && echo 'perf script is non-empty'
perf script is non-empty
Do not edit the script to make it look plausible. Its event records, mappings and sampled branches have to describe the recording. If the file is empty, stop here and fix collection rather than asking llvm-profgen to guess.
Pass the script, the exact profiled binary and a new output path. Text is a useful first format because it can be inspected. The command writes the profile file, so avoid a path containing an existing profile unless replacement is deliberate:
$ llvm-profgen-18 \
--perfscript=/tmp/example.perf.script \
--binary=/path/to/program \
--output=/tmp/example.prof \
--format=text
--perfscript supplies the samples.--binary supplies the code and symbols used for address translation.--output receives the generated profile.The binary must correspond to the program that produced the trace. A different build, stripped symbols or moved shared objects can reduce symbolisation quality or make the profile unsuitable for optimisation.
Checkpoint: verify the conversion created data before opening it:
$ test -s /tmp/example.prof && echo 'LLVM profile is non-empty'
LLVM profile is non-empty
$ sed -n '1,24p' /tmp/example.prof
The exact profile text depends on the workload and binary. It should not be judged by matching a fixed sample output. If llvm-profgen reports that the input file does not exist, check the path first. If it reports missing or unsymbolised addresses, check that the executable and any required debug information match the recording.
The installed tool accepts --debug-binary for a separate file from which it loads DWARF information. Use it when the executable is stripped but you have the matching debug binary:
$ llvm-profgen-18 \
--perfscript=/tmp/example.perf.script \
--binary=/path/to/stripped-program \
--debug-binary=/path/to/matching/program.debug \
--output=/tmp/example.prof \
--format=text
Matching matters more than merely having a file with debug sections. Do not substitute an arbitrary build. If symbolisation is still incomplete, preserve the profile as diagnostic evidence and compare the executable, libraries and recording environment before changing the command.
The manpage lists text, binary, extbinary, compbinary and gcc. Text is the easiest format for a first conversion; a consuming LLVM workflow may prefer a binary form. Repeat the conversion to a new file so the checked text profile remains available:
$ llvm-profgen-18 \
--perfscript=/tmp/example.perf.script \
--binary=/path/to/program \
--output=/tmp/example.profdata \
--format=extbinary
$ test -s /tmp/example.profdata && echo 'binary LLVM profile is non-empty'
binary LLVM profile is non-empty
Recovery: do not infer that a non-zero-size file is a valid optimisation input. Validate it with the LLVM tool that will consume it, and keep the original text output or perf script until that check passes. If you need to abandon the experiment, remove only the explicitly named files in /tmp; the source executable and system configuration are untouched.
-b at collection time. Do not omit it when the trace needs branch stacks; a normal perf recording is not automatically equivalent.--format confused with input type. It selects the generated profile's encoding; --perfscript still points to perf-script text.--show-mmap-events and --show-disassembly are for investigating mappings or decoded instructions. Keep normal output separate from the profile file.--x86-asm-syntax=intel only when disassembly is being examined in Intel syntax; the documented default is AT&T syntax.llvm-profgen-18 --version reports the installed LLVM version you intended to use.