Home / Alt manpages / mtrace(1)

  • mtrace(1)
  • User command
  • linux

Find a Leak with GNU mtrace

A program is quietly eating memory, and mtrace points at the exact allocation that leaked it. You will compile a small C program with debug information, collect its GNU malloc trace, and ask mtrace to identify the memory that was left behind. Allow about fifteen minutes. You need a shell, a C compiler, GNU C Library tracing support, and permission to write a temporary directory. This is a diagnostic workflow: it does not change system services or install anything.

The examples describe the Linux man-pages 6.7 interface. On the inspected host, package manpages is version 6.7-2; the manual page is installed, but the mtrace executable is not on PATH. Treat that as a real prerequisite failure, not as evidence that the test program has no leak.

1. Check the interpreter before creating a trace

mtrace is a Perl script according to the local manual. It interprets a trace file produced by the C library's mtrace(3) function. Check the executable and its help output first:

$ command -v mtrace
/usr/bin/mtrace
$ mtrace --version
GNU mtrace (GNU libc) ...

The exact version text depends on the GNU C Library installation. That output is representative of a host where the tool is installed, not output from this inspected host. Here, command -v mtrace returns status 1 and prints nothing. Stop there and install or enable the GNU C Library debugging tooling through your normal package-management process. Do not copy a script from an untrusted website into /usr/bin.

Once the command exists, record its location and version:

$ command -v mtrace
$ mtrace --version

Checkpoint

Continue only when the interpreter runs and the command's version output is visible.

2. Create a deliberately leaky test

Use a private working directory so the trace and executable are easy to remove afterwards. The test program calls mtrace(), allocates memory, and intentionally leaves it allocated at exit. Compile with -g; the extra debug information is what lets the interpreter attach source locations to its reports.

$ work=/tmp/mtrace-demo
$ mkdir -p "$work"
$ cd "$work"
$ cat > demo.c <<'EOF'
#include <mcheck.h>
#include <stdlib.h>

int
main(void)
{
    mtrace();
    malloc(128);
    return 0;
}
EOF
$ cc -g demo.c -o demo

The source is intentionally a test fixture: do not carry this pattern into production code. The compiler may warn about ignoring the return value; that warning does not stop the tracing demonstration from working.

Verify the binary is present and holds debug information, if file is available:

$ file demo
demo: ELF 64-bit ... with debug_info, not stripped

The architecture wording will differ on your machine. What matters is that compilation succeeded and the binary is not stripped.

3. Run the program with a fresh trace file

Set MALLOC_TRACE to a writable pathname and run the program. The C library opens that pathname when mtrace() is called and truncates an existing file there. Use a new file in the private directory so an older trace cannot confuse the result.

$ trace="$work/trace.log"
$ rm -f "$trace"
$ MALLOC_TRACE="$trace" ./demo
$ test -s "$trace" && echo "trace created"
trace created

There is no need for sudo. If the pathname is invalid or not writable, the C library simply installs no tracing hooks, and the program can still exit successfully. That is exactly why checking the trace file matters.

Warning

Do not point MALLOC_TRACE at a valuable existing file. The tracing function truncates whatever file it names. The test above only removes a file inside /tmp/mtrace-demo, but that removal is still irreversible for that one file.

Checkpoint

The trace pathname exists and is non-empty. If it does not, fix the pathname or permissions before interpreting anything.

4. Interpret the trace with the matching binary

Pass the executable first and the trace file second. Supplying the binary is what lets mtrace add source file and line information when the executable carries suitable debug data.

$ mtrace ./demo "$trace"
Memory not freed:
-----------------
   Address     Size     Caller
0x...          0x80     at .../demo.c:10

Addresses and the exact table spacing will vary. What matters is the Memory not freed section and a caller pointing at the allocation. A clean test instead produces a message such as No memory leaks. The interpreter can also report attempts to free memory that was never allocated. Read those diagnostics as events the trace recorded, not as a complete proof of memory safety.

For this deliberately leaky program, line 10 should be the malloc(128) call once you account for the exact file layout. If the report has no source line, check that you passed the correct executable and compiled it with -g. Never interpret a trace made by one binary against a different, rebuilt binary.

5. Apply the workflow to a real program

Put mtrace(); early in the program's normal startup path, compile a diagnostic build with debug information, and run the smallest input that reproduces the suspected leak:

$ cc -g -O0 src/app.c -o build/app
$ trace=/tmp/app-mtrace.log
$ rm -f "$trace"
$ MALLOC_TRACE="$trace" build/app --input /path/to/reproducible-input
$ test -s "$trace" && mtrace build/app "$trace"

Use an absolute trace pathname when a launcher changes the working directory. Keep the trace private if the program handles sensitive input, because it records allocation activity and can contain file or source-location details. Remove it after review:

$ rm -f /tmp/app-mtrace.log

Warning

That deletion is irreversible. Preserve the file instead when another engineer needs to reproduce or review the finding.

6. Diagnose misleading results

  • No trace file appears. Check MALLOC_TRACE, directory permissions, and whether the program actually calls mtrace(). A successful program exit is not enough on its own.
  • No source locations show up. Use the exact executable that generated the trace and rebuild with -g. The manpage warns that reported line numbers are not always precise and can point at a nearby non-blank line.
  • No leaks are reported. Check that the input actually exercised the suspected path and that the trace is non-empty. The tool only reports activity recorded after tracing was enabled.
  • Behaviour changes under tracing. Tracing adds a performance cost. Compare against an ordinary run only after the diagnostic run is complete, and do not leave MALLOC_TRACE enabled in normal service operation.
  • The program is set-user-ID or set-group-ID. The C library ignores MALLOC_TRACE for such programs. Do not weaken file ownership or permission checks to force tracing; reproduce the issue in a non-privileged diagnostic build instead.

Done means

  • Confirmed mtrace --version runs and you know which GNU C Library tool you are using.
  • Compiled the test binary with debug information and made the trace with that same binary.
  • Pointed MALLOC_TRACE at a fresh, writable file and verified it was non-empty.
  • Interpreted the trace with mtrace binary tracefile and checked the reported allocation path.
  • Kept or removed temporary traces and test binaries deliberately, not by accident.