You already have a perf script trace on disk and a binary that needs a sample profile: llvm-profgen-20 turns one into the other. The profile can then feed an LLVM sample-based profile-guided optimisation workflow. This guide uses llvm-profgen-20 from the Debian package llvm-20, version 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139.
Allow 15 to 30 minutes if the trace already exists. You need the trace, the profiled executable, and enough disk space for a new output file. You do not need root for profile generation itself. Recording or reading a system-wide perf trace may have separate kernel permission requirements, so this article starts after collection.
First confirm which executable will run. This is read-only and does not need elevated privileges:
$ command -v llvm-profgen-20
/usr/bin/llvm-profgen-20
$ llvm-profgen-20 --version
Ubuntu LLVM version 20.1.8
Optimized build.
Do not quietly substitute an unversioned llvm-profgen from another LLVM release. Profile formats and command-line options belong to the installed build. If the version differs, rerun the help check and adjust only options that it explicitly lists.
Checkpoint: the binary should report the expected major version, and the package query should identify the installed package:
$ dpkg-query -W -f='${Package} ${Version}\n' llvm-20
llvm-20 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139
llvm-profgen cannot recover missing branch data or use a binary from a different build by magic. Set shell variables to the files you actually intend to use, then verify that both are regular readable files:
$ PERF_SCRIPT='/path/to/perf-script.txt'
$ PROFILED_BINARY='/path/to/profiled-program'
$ OUTPUT_PROFILE='/path/to/output/program.prof'
$ test -r "$PERF_SCRIPT" && test -x "$PROFILED_BINARY"
$ printf 'inputs are readable and the binary is executable\n'
inputs are readable and the binary is executable
Replace every placeholder. The binary must match the executable represented in the trace closely enough for symbolisation, including its code and relevant debug information. If you used a stripped executable for deployment, keep the matching debug-information binary available; the installed tool also exposes --debug-binary for loading DWARF from a separate file.
Warning: the trace should have been produced by perf script. For branch-stack input, the raw recording needs branch stacks, commonly enabled with -b. A trace that contains only unrelated samples is not repaired by adding a profgen option. Treat system-wide performance traces as potentially sensitive: they can reveal process names, addresses and timing.
Run the core conversion as an ordinary user:
$ llvm-profgen-20 \
--perfscript="$PERF_SCRIPT" \
--binary="$PROFILED_BINARY" \
--output="$OUTPUT_PROFILE"
The manpage describes --perfscript, --binary and --output as the required inputs for this workflow. The command reads the trace and binary, then writes the generated profile at the path in OUTPUT_PROFILE. It does not install the profile, edit a compiler configuration, or change a service.
Checkpoint: confirm that the expected output exists and is non-empty:
$ test -s "$OUTPUT_PROFILE"
$ stat -c 'profile: %n (%s bytes)' "$OUTPUT_PROFILE"
profile: /path/to/output/program.prof (SIZE bytes)
SIZE is a placeholder for the number printed on your machine. If the command fails before creating output, keep the error text. Check paths, permissions and binary identity before trying more switches.
The default is not something to guess from the filename. Ask for a format when the next LLVM consumer requires one:
$ llvm-profgen-20 \
--perfscript="$PERF_SCRIPT" \
--binary="$PROFILED_BINARY" \
--output="$OUTPUT_PROFILE" \
--format=text
The installed manpage lists text, binary, extbinary, compbinary and gcc. LLVM's command guide says to use llvm-profdata documentation for the format meanings. Use the format expected by the consumer, not the one that happens to be easiest to inspect. If you are only testing the input path, omit --format until the basic run works.
Warning: do not overwrite a valuable profile while experimenting. Choose a new output path, or stop first if the path already exists:
$ if test -e "$OUTPUT_PROFILE"; then
> printf 'refusing to overwrite %s\n' "$OUTPUT_PROFILE" >&2
> exit 1
> fi
$ llvm-profgen-20 --perfscript="$PERF_SCRIPT" --binary="$PROFILED_BINARY" --output="$OUTPUT_PROFILE"
This check is ordinary shell protection, not an llvm-profgen feature. If you deliberately need to replace a stale generated profile, save or rename it first. Recovery is then just selecting the saved path and rerunning the command.
Use the version-matched profile-data tool to inspect what was written:
$ llvm-profdata-20 show "$OUTPUT_PROFILE"
The exact report depends on the trace and format. A successful command should print profile information rather than a file-open or format error. If the result is unreadable, first check whether llvm-profdata-20 is the same LLVM release as profgen and whether you selected a format it supports. Do not treat a non-zero exit status as a usable profile merely because the file exists.
--show-mmap-events prints mapping events and --show-disassembly prints disassembled code. These can produce a large amount of output, so capture them separately rather than mixing them into a normal build log:
$ llvm-profgen-20 \
--perfscript="$PERF_SCRIPT" \
--binary="$PROFILED_BINARY" \
--output="$OUTPUT_PROFILE" \
--show-mmap-events \
> profgen-mmap.log
$ test -s profgen-mmap.log
$ printf 'diagnostic log: %s bytes\n' "$(stat -c %s profgen-mmap.log)"
For x86 disassembly, --x86-asm-syntax=att is the default and intel selects Intel syntax. These switches change diagnostic output, not the underlying trace. Keep the normal profile command reproducible and add one diagnostic switch at a time.
--binary or --output is absent. The no-argument invocation is a quick syntax check, not a profile run.perf script text and raw perf.data are different inputs. The installed command exposes both --perfscript and --perfdata; use the option matching the file you have.--show-mmap-events and --show-disassembly for diagnosis. They are not required to generate an ordinary profile.llvm-profgen-20 --version reports the expected LLVM 20.1.8 binary.--perfscript, --binary and --output paths.llvm-profdata-20 show.