gprof answers the question every "why is this slow" ticket eventually asks: which function is actually eating the time. You will finish with a repeatable gprof report: a profiled executable, its gmon.out data, and a report showing which functions consumed the sampled time and how they called one another. The examples use GNU gprof from GNU Binutils 2.42, installed here as gprof and the target-prefixed aliases aarch64-linux-gnu-gprof and x86_64-linux-gnu-gprof.
Allow about fifteen minutes. You need GCC, a C or C++ source file compiled with profiling support, and a writable working directory. Run the profiling commands as an ordinary user: no elevated privileges are needed, and sudo would only make the resulting files harder to inspect and could leave root-owned output behind.
Start by confirming which executable is selected and which version you are using:
$ command -v gprof
/usr/bin/gprof
$ gprof --version
GNU gprof (GNU Binutils for Ubuntu) 2.42
Based on BSD gprof, copyright 1983 Regents of the University of California.
The three names in this guide serve the same gprof purpose. The prefixed names matter when a cross-toolchain is installed, but do not assume a target-prefixed profiler can read every executable produced by a different toolchain. Match the profiler to the object format and symbols in the program you actually built.
Checkpoint: You have a gprof binary, and its version is known. If command -v finds nothing, install the distribution's Binutils package through your normal package-management process before continuing.
gprof does not profile an arbitrary executable after the fact. The installed manual expects the program to be built and linked with -pg. Use a small test program, or rebuild the program you actually want to investigate:
$ gcc -pg -O0 -g -o sample sample.c
For C++, use g++ in the same position. The -pg option arranges for profiling support at compile and link time. Keeping optimisation at -O0 makes a first experiment easier to map back to source, although it does change the program's performance compared with a release build. For a production-like investigation, profile a separately built copy with the optimisation settings you actually care about, and record those settings with the report.
Do not profile a privileged service or a live production binary just to get a quick report. Run a representative workload in a controlled copy instead. gprof writes profile data in the process's working directory, so pick a directory where you control the files.
Run the instrumented program from the directory where you want gmon.out to appear:
$ ./sample > run.out
$ test -s gmon.out && echo "profile data ready"
profile data ready
The program's own standard output has nothing to do with gprof's report, which is why the example redirects it. A successful run should leave gmon.out in the current directory. The file carries sampled time and call-graph information; it is not a human-readable text report.
Repeat the run when you need a workload that better represents real use. A short run can produce a report full of zeroes because the timer did not collect enough samples. A single run also only captures that run's workload, not every path the program can take.
Checkpoint: Verify both the exit status and the file location before invoking gprof:
$ printf 'program exit status: %s\n' "$?"
program exit status: 0
$ ls -l gmon.out
If you ran another command before printing the status, rerun the program and check its status immediately. Keep the original executable beside the profile file: gprof needs the executable's symbol table to interpret the addresses in gmon.out.
Pass the executable first and the profile file second:
$ gprof ./sample gmon.out > report.txt
$ sed -n '1,28p' report.txt
Flat profile:
Each sample counts as 0.01 seconds.
...
Call graph (explanation follows)
...
The default output contains a flat profile and a call graph. The flat profile is the short list of functions with sampled time and call counts. The call graph adds callers, children and propagated time. In a tiny or very fast test, the time columns may all read 0.00; that is a measurement limit, not proof the functions cost nothing.
The default profile file name is gmon.out, so this also works when it sits in the current directory:
$ gprof ./sample > report.txt
Use an explicit file when several runs are present. Supply more than one profile file and gprof reports the sum of their profile information, useful for combining comparable workloads but misleading if the runs use different program versions, input sizes or build flags.
Use long options in scripts, since their intent is easier to review later:
$ gprof --flat-profile ./sample gmon.out > flat.txt
$ gprof --graph ./sample gmon.out > call-graph.txt
$ gprof --exec-counts ./sample gmon.out > counts.txt
--flat-profile prints the flat profile, --graph prints the call graph, and --exec-counts prints function call tallies. The short equivalents are -p, -q and -C. Picking one of these output selections changes the default rather than adding a second copy of every section.
To inspect only matching symbols, add a symbol specification to the relevant option. For example:
$ gprof --flat-profile=work ./sample gmon.out
Symbol specifications can be repeated and can include or exclude sets of symbols, but start with the unfiltered report. Filtering too early can hide a caller or child that would have explained the cost you are chasing.
When a report looks empty, inspect the records before touching compiler flags:
$ gprof --file-info ./sample gmon.out
File `gmon.out' (version 1) contains:
1 histogram record
2 call-graph records
0 basic-block count records
The counts depend on the run. A histogram record carries sampled execution time, call-graph records describe observed calls, and basic-block records only appear when the build and data provide them. This command just displays the summary and exits; it changes nothing in the profile.
If the file is missing, empty, or from a different executable, stop and fix that relationship first. Do not make a report look plausible by copying an old gmon.out into a new build directory.
For line-oriented evidence, compile with debug information and request annotated source:
$ gprof --annotated-source ./sample gmon.out > annotated.txt
$ sed -n '1,40p' annotated.txt
The output is a copy of your source files with execution information attached to lines. gprof searches for source files using the paths recorded in the executable. If the source has moved, supply a search directory with --directory-path=/path/to/source, or use --print-path to show the paths gprof is using.
--line enables line-by-line profiling, which can make a large function easier to pick apart. It also increases gprof's work and magnifies sampling uncertainty. Treat a line count as evidence about this workload, not an exact count of every machine instruction executed.
gmon.out. Copy or rename reports and profile files before starting another workload.There is no system-wide undo for any of this. To discard a test result, remove only the generated files after checking their paths:
$ rm -- report.txt flat.txt call-graph.txt counts.txt annotated.txt gmon.out
Warning: this deletion is irreversible. Do not include a source file, executable or an unreviewed wildcard in that command. If you need the data later, keep it in a dated directory instead.
-pg and run against a representative, controlled workload.gmon.out was generated beside the matching executable and inspected with --file-info when needed.