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.