Profile a Program's Allocations with memusage
You will finish with a repeatable memory profile for one program, a text report you can inspect, and optional data for a PNG graph. The guide describes the installed Linux man-pages 6.7 interface and the glibc 2.39 runtime present here. Allow about fifteen minutes, plus the time needed to run the target workload.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need a shell, the memusage script and the matching glibc support files. The target program must be safe to run under LD_PRELOAD, because memusage injects libmemusage.so into it. Do not profile a set-user-ID or otherwise security-sensitive program casually. The preload changes the process environment and can affect how it runs.
1. Check the local tool before profiling
Start with read-only checks. This does not need elevated privileges:
$ command -v memusage
$ memusage --version
$ command -v memusagestat
The first command should print the path to the script. The second prints its version information, and the third tells you whether the optional graph converter is available. On the reference machine, the installed libmemusage.so belongs to glibc 2.39, while the executable scripts are not present on PATH. That is a useful failure to find early: having a manpage or a library alone is not enough to run the workflow.
Checkpoint: if command -v memusage prints nothing, stop and install or enable the package that supplies the tool through your normal system-management process. Do not substitute an unrelated memory monitor. The commands below use the syntax documented by memusage(1); they cannot succeed until the script is actually available.
2. Profile a harmless, bounded command
Use a small command first so that you can separate tool problems from a long application run:
$ memusage --data=/tmp/memusage-date.dat /usr/bin/true
<summary and allocation table are printed here>
The exact summary is workload-dependent. Look for the line beginning Memory usage summary:, followed by the allocation table and a histogram of block sizes. memusage runs the named program and reports allocations intercepted from malloc, calloc, free and realloc.
The data file is a binary trace, not a report to read in an editor. The destination under /tmp is ordinary user-writable state. If you use a file in a shared directory, choose permissions and retention deliberately because allocation traces can reveal workload details.
Checkpoint: check both the status and the file:
$ printf 'program status: %s\n' "$?"
$ test -s /tmp/memusage-date.dat && echo 'data file exists'
memusage returns the profiled program's exit status. A zero status means true returned zero; it does not mean that a real application passed its own tests.
3. Run the real workload and preserve the output
Replace the placeholders with an executable and its arguments. Keep the program path and each argument as separate shell words:
$ memusage --data=/tmp/my-program.memusage.dat \
/path/to/PROGRAM --input /path/to/INPUT
$ status=$?
$ printf 'program status: %s\n' "$status"
$ test "$status" -eq 0
The shell line continuation is only formatting. Quote a path when it contains whitespace, for example "/path/to/my program". The program's normal output and error messages still belong to that program. memusage's report is additional diagnostic output, so capture standard output and standard error separately if the target's output matters:
$ memusage --data=/tmp/my-program.memusage.dat \
/path/to/PROGRAM --input /path/to/INPUT \
> /tmp/my-program.stdout 2> /tmp/my-program.stderr
Do not use sudo just because the program is being profiled. Use the same unprivileged account and environment that normally runs it. Elevated privileges change the test and make the preload boundary more difficult to reason about.
4. Read heap totals without confusing them
The first report line contains heap total, heap peak and stack peak. They answer different questions. Heap total is the accumulated size of allocations, including increases recorded through realloc and, when enabled, mmap. Heap peak is the largest single allocation size represented by the monitored calls, not the process's maximum resident set. Stack peak is calculated from stack-pointer movement and is not a complete measurement of all thread stacks.
That distinction is the main trap in this tool. A workload that repeatedly allocates and frees a small block can have a large heap total but a modest heap peak. Conversely, several live allocations can make the process use more memory than the largest single allocation shown by the report. Use memusage to understand allocation activity, not as a replacement for a resident-memory or whole-process measurement.
For realloc and mremap, the table includes extra fields such as nomove and dec. A shrinking reallocation can make the table's total-memory cells look inconsistent with the free cell. The manual explicitly warns that the realloc total does not represent every reduction in size. Do not add those columns together as if they were a live-byte balance.
5. Include mappings when the workload uses them
By default, the trace focuses on the malloc family. Add --mmap when mapped regions are part of the question:
$ memusage --mmap --data=/tmp/my-program-mmap.dat \
/path/to/PROGRAM --input /path/to/INPUT
This also intercepts mmap, mremap and munmap. It gives a broader allocation trace, but it still is not a complete accounting of every kernel mapping or resident page. Compare two runs with the same input and environment, and change one option at a time.
The default output is buffered. Use --unbuffered when you need records written promptly, accepting the extra I/O cost:
$ memusage --unbuffered --data=/tmp/long-run.dat \
/path/to/PROGRAM --input /path/to/INPUT
For a very busy target, --buffer=SIZE changes how many entries are collected before writing. Keep SIZE a positive, documented choice in scripts. --no-timer disables timer-based SIGPROF sampling of the stack pointer; use it when that signal interaction would interfere with the workload, while accepting less stack sampling.
6. Turn saved data into a graph
If memusagestat is installed, convert the binary data after the profiled process has exited:
$ memusagestat /tmp/my-program.memusage.dat /tmp/my-program.png
$ file /tmp/my-program.png
The PNG is a separate artefact. Add graph-only options to memusage when you want to control the generated graph, for example:
$ memusage --data=/tmp/my-program.memusage.dat \
--png=/tmp/my-program.png --time-based --total \
--title='my program allocation run' \
/path/to/PROGRAM --input /path/to/INPUT
--time-based uses time rather than call count for the horizontal scale, and --total adds a total-memory graph. The title and dimensions affect presentation, not the measurements. Do not treat a visually smooth graph as proof that two runs are equivalent; retain the command, input and environment details alongside the PNG.
7. Clean up temporary traces safely
These examples write new files under /tmp. Inspect them before removal. Deleting a trace is irreversible, so do not paste the cleanup command into an automated first run:
$ ls -lh /tmp/my-program*.dat /tmp/my-program*.png
$ rm -- /tmp/my-program.memusage.dat /tmp/my-program.png
If the command fails before producing a complete file, leave the original input and program untouched, remove only the incomplete output you created, and rerun with a new filename. No service configuration or persistent system state is changed by the profiling examples.
Done means
memusageand, for graphs,memusagestatwere checked before the workload run.- The target ran under the intended unprivileged account and returned the status you expected.
- The report's heap total, heap peak and stack peak were interpreted as different measurements.
--mmap, buffering and timer options were added only for a stated diagnostic reason.- The binary trace and PNG were retained or removed deliberately, without overwriting useful results.