🧬 ltrace Quick Reference

ltrace cheat sheet / survival guide

Video: ltrace Cheat Sheet (sysadmin video)
Prefer to watch? This video runs the guide's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube. It is part of the Troubleshooting and Performance playlist.
ltrace [options] command [args]
ltrace -p PID

ltrace shows the library calls a program makes: functions like malloc, strcmp, fopen and
printf from shared libraries (libc and others), plus the values passed and returned. It is
the sibling of strace. strace watches calls into the kernel; ltrace watches calls into
libraries. Use ltrace to see what a program compares, allocates and prints.
Install it with: sudo apt install ltrace | sudo dnf install ltrace
Attaching to another user's process needs sudo. Tracing slows programs down heavily.

Start tracing

ltrace ls /tmp                          # run a command under ltrace
ltrace -o out.txt ls /tmp               # write the trace to a file
ltrace -p 1234                          # attach to a running process
sudo ltrace -p 1234                     # ...owned by someone else
ltrace -f ./script.sh                   # also follow child processes
ltrace -c ls /tmp                       # summary table: calls, counts and time
ltrace -S ls /tmp                       # show system calls too (like a mini strace)
Stop an attached trace with Ctrl+C. That detaches and the program keeps running.

Common options

-o FILE      Write output to FILE                  -f       Follow child processes
-p PID       Attach to a running process           -c       Count calls and time; print a summary
-S           Also trace system calls               -C       Demangle C++ names
-s N         Show up to N characters of strings (default 32; try -s 200)
-n N         Indent nested calls by N spaces       -i       Print the instruction pointer
-t / -tt / -ttt   Timestamps: time of day, with microseconds, or epoch seconds
-r           Relative timestamp since the previous call        -T   Time spent in each call
-u USER      Run the command as another user       -a N     Column at which to align return values
-b           Do not show signals
-l PATTERN   Trace only functions in libraries matching a filename pattern
-e EXPR      Filter which calls to show (see below)
-x FILTER    Trace symbols in the program itself (not just libraries)
-L           Do not print library calls (use with -S to see only system calls)
-F FILE      Use a config file describing function prototypes
-w N         Show a stack backtrace of N frames (if built with libunwind)
-A N         Maximum number of array elements to print
-h           Help          --version   Version
Good default for investigating: ltrace -f -tt -s 200 -o trace.txt COMMAND

Reading a line

strcmp("secret", "letmein")               = -1
call    arguments                           return value
fopen("/etc/app.conf", "r")               = 0x55a4c3b2d2a0
malloc(1024)                              = 0x55a4c3b2e6c0
printf("Hello %s\n", "world")             = 12
- Strings appear in quotes, pointers as hex addresses, and integers as numbers
- The value after = is what the function returned (NULL is 0; a pointer is a hex address)
- <unfinished ...> and <... resumed> mean another thread or process interleaved
- +++ exited (status 0) +++ is the exit status; --- SIGSEGV --- is a crash
- Lines are prefixed with the library when using -l or an ltrace built with library names

Filter what you trace

ltrace -e malloc+free cmd                 # only these functions (+ joins names)
ltrace -e '*alloc' cmd                    # glob: malloc, calloc, realloc
ltrace -e 'str*' cmd                      # strcmp, strcpy, strlen ... (quote the *)
ltrace -e '@libc.so.6' cmd                # only calls into one library
ltrace -e 'malloc@libc.so.6' cmd
ltrace -e '!malloc+free' cmd              # everything EXCEPT malloc and free
ltrace -l '*/libssl*' cmd                 # only functions in matching libraries
ltrace -x 'main' cmd                      # a symbol in the program itself
ltrace -x '*' cmd                         # all symbols in the main binary (very noisy)
Older versions used -e func or -e '!func'. Newer ones also accept the @library form.
Quote patterns so your shell does not expand the * before ltrace sees it.

Combine with system calls

ltrace -S cmd                             # library calls and system calls together
ltrace -S -L cmd                          # system calls only (like strace)
ltrace -S -e 'open*+read+write' cmd       # specific ones of both kinds
ltrace -c -S cmd                          # summary including system calls

Summary mode (-c)

ltrace -c ls /tmp
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
 41.20    0.000120           4        30 malloc
 22.10    0.000064           3        21 strlen
Sort by time or calls to find the hot spot. Heavy use of strcmp or malloc in a loop is often a clue.

Common library functions you will see

malloc / calloc / realloc / free       Memory allocation
strlen / strcpy / strncpy / strcat     String basics (strcpy is a classic bug source)
strcmp / strncmp / strcasecmp / memcmp Comparisons (arguments show the expected and given values)
strstr / strchr / strtok               Searching and splitting
fopen / fclose / fread / fwrite / fgets   Buffered file I/O
printf / sprintf / snprintf / puts     Printing and formatting
getenv / setenv                        Reading and setting environment variables
getpwnam / getpwuid / getaddrinfo      User and name lookups
socket / connect / send / recv         Sockets (also visible via strace)
dlopen / dlsym / dlclose               Loading libraries at run time
atoi / strtol / strtod                 Text to number
qsort / bsearch                        Sorting and searching
pthread_create / pthread_mutex_lock    Threads and locks
exit / abort / __libc_start_main       Startup and shutdown

Practical one-liners

# What does this program compare my input against? (reverse-engineering a simple check)
ltrace ./checker
#   look for strcmp("your input", "the expected value")

# Watch a program's memory allocations for leaks
ltrace -e malloc+calloc+realloc+free -o mem.txt ./myapp
grep -c malloc mem.txt; grep -c free mem.txt      # far more mallocs than frees = possible leak

# Which environment variables does it read?
ltrace -e getenv cmd

# Which files does it open? (library level; strace shows the kernel level)
ltrace -e fopen+fopen64+open cmd
ltrace -S -e 'openat' cmd

# Find the library function a hung process is stuck in
sudo ltrace -p 1234

# See how many times each function is called
ltrace -c ./myapp

# Which libraries and functions from libssl does it use?
ltrace -l '*/libssl*' -o ssl.txt ./client

# What does it print? (formatted output before it goes to the screen)
ltrace -e printf+puts+fputs -s 500 cmd

# Trace a C++ program with readable names
ltrace -C ./cpp_app

# Trace a program and all its child processes
ltrace -f -o trace.txt ./launcher.sh

# Follow calls into a plugin loaded with dlopen
ltrace -e dlopen+dlsym ./app

# Time each library call to find slow ones
ltrace -T -tt -o timed.txt ./myapp

ltrace vs strace vs gdb
strace     System calls (kernel boundary): files, network, processes, signals
ltrace     Library calls (function boundary): strings, memory, formatting, comparisons
gdb        Full debugger: breakpoints, variables, stack inspection
perf       Sampling profiler: where CPU time goes
valgrind   Memory error and leak checker (much deeper than ltrace)
ldd        Lists the shared libraries a program needs:     ldd /bin/ls
nm -D      Lists functions a binary imports/exports:       nm -D /bin/ls
objdump -T Dynamic symbols in a binary or library
readelf -d Dynamic section (needed libraries, RPATH)

Limits of ltrace (what it cannot see)

- Statically linked programs: no shared library calls to intercept
- Go programs and many Rust programs: they do not use libc the same way
- Calls made inside the same binary (not through a shared library) unless you use -x
- Very new binaries with certain protections (PIE, full RELRO, or seccomp) may need a newer ltrace
- Programs that check for tracers (anti-debugging) may refuse to run
- Java, Python, Node and other runtimes: you see the runtime's calls, not your script's
Check first: file ./prog and ldd ./prog. If it says "statically linked", use strace or gdb.

Common problems

"Couldn't find .dynsym or .dynstr in ..."   The binary is static or stripped of dynamic symbols
"ptrace: Operation not permitted"          Use sudo, or the kernel restricts ptrace (see ptrace_scope)
No output at all                           Statically linked or no library calls matched your filter
Output flies by                            Use -o file, then grep and less it
Strings are cut off                        Add -s 200 or larger
Program crashes or hangs under ltrace      Some binaries and multithreaded apps break ltrace: try strace, or gdb
Very slow                                  Filter with -e or -l, or use -c. Trace overhead is high
Arguments look wrong or missing            ltrace does not know the function prototype: describe it with -F, or accept raw values
Containers                                 Needs SYS_PTRACE capability (docker run --cap-add SYS_PTRACE)

Tips

    Use ltrace when strace does not explain the problem. If the failure is "wrong value", not "missing file", the answer is usually in a library call.

    Start with ltrace -c to see which functions dominate, then trace those with -e.

    Use -f for scripts and launchers so you also see their children.

    Use -s 200 so strings are readable, and -o file so you can grep the result.

    ltrace -S combines both worlds, which is handy when you are not sure which layer the problem is in.

    Compare malloc and free counts for a quick leak hint, but use valgrind for a real answer.

    ltrace only works on dynamically linked programs. Check with ldd first.

    Traces may contain passwords and private data (strcmp shows both strings!). Treat trace files as sensitive.

    Only trace programs and processes you own or are authorized to inspect.