Turn Linux perf Branch Traces into an LLVM Sample Profile

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.

1. Check the installed tool

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

2. Check the trace and binary before generating anything

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.

3. Generate the sample profile

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.

4. Pick a profile format deliberately

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.

5. Inspect the generated profile

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.

6. Turn on diagnostics only when they answer a question

--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.

Common failure traps

Done means