There will come a time when you will find yourself asking “What the heck is that process doing?”. To uncover the mysteries behind the behaviour of a process, we have a tool called strace.
The program strace is very handy when you want to debug the execution of a program, as it intercepts and records the system calls which are called by a process and the signals which are received by a process.
Let’s dive into some examples.
Trace the execution
strace ls
..is the simple straight-forward way to use it. The output might look something like:
execve("/bin/ls", ["ls"], [/* 32 vars */]) = 0 brk(0) = 0x1117000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe7c5bc2000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=69243, ...}) = 0 mmap(NULL, 69243, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe7c5bb1000 close(3) = 0 open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240d\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=142136, ...}) = 0 mmap(NULL, 2242712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe7c5780000 mprotect(0x7fe7c57a1000, 2093056, PROT_NONE) = 0 ...
The output looks a bit messy, but it can provide very useful information.
Attach to an existing process
You can run strace on a process that’s already running. Use -p, and provide the Process ID (PID):
pidof urxvtd 772 strace -p 772
The output presented to you is similar to the example above.
Trace child processes
Use -f to trace child processes as they are created by currently traced processes. Most of the time when a program launches, it forks, and the fork does the actual work.
strace -f ls
Trace specific system calls
strace -e open ls
The e-flag, along with the call open, displays only open system calls of the ls command, which outputs something along these lines:
... open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 open("/proc/filesystems", O_RDONLY) = 3 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 ghost ghost.zip +++ exited with 0 +++
With the above example, close to the end, you can see two things – ghost and ghost.zip, a folder and file, which is the actual output of the command ls.
A few of the options available after -e trace= are:
- open
- close
- read
- write
Trace multiple system calls
What if you want to trace multiple system calls in one command though? Use the option -e trace=:
strace -e trace=open,read ls
Which outputs:
... open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\23\0\0\0\0\0\0"..., 832) = 832 open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340m\0\0\0\0\0\0"..., 832) = 832 open("/proc/filesystems", O_RDONLY) = 3 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 356 read(3, "", 1024) = 0 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 ghost ghost.zip +++ exited with 0 +++
Write output to file
If you’d like to write the output of strace to a file, add a -o:
strace -o ls.txt ls ghost ghost.zip
When you print the contents of ls.txt, you’ll see that it contains only the output from strace, and not from the command ls:
cat.ls.txt ... write(1, "18.png\t\t Apps Declarations d"..., 164) = 164 write(1, "4238158452532.pdf bin\t Document"..., 175) = 175 close(1) = 0 munmap(0x7f993acb6000, 4096) = 0 close(2) = 0 exit_group(0) = ? write(1, "18.png\t\t Apps Declarations d"..., 164) = 164 write(1, "4238158452532.pdf bin\t Document"..., 175) = 175 close(1) = 0 munmap(0x7f993acb6000, 4096) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++
Add a timestamp
If you add a -t to the command, you’ll be able to add a timestamp to each printed line. You can add up to 3 -t‘s. The more you add, the more verbose and detailed the timestamp will be.
One -t shows you seconds:
strace -t ls ... 11:52:20 close(2) = 0 11:52:20 exit_group(0) = ? 11:52:20 +++ exited with 0 +++
Adding a second -t will display microseconds:
strace -tt ls ... 11:53:07.593382 close(2) = 0 11:53:07.593500 exit_group(0) = ? 11:53:07.593651 +++ exited with 0 +++
A third -t will include the microseconds and the leading portion will be printed as the number of seconds since the epoch.
strace -ttt ls ... 1390042410.543785 close(2) = 0 1390042410.543906 exit_group(0) = ? 1390042410.544077 +++ exited with 0 +++
Print relative timestamp
If you’d like to find out the execution time of each call, -r will certainly do the trick:
strace -r ls ... 0.000105 close(2) = 0 0.000115 exit_group(0) = ? 0.000146 +++ exited with 0 +++
A summary of system calls
Would you like to have a summary of calls, time, and errors for each system call? -c does this:
strace -c ls ghost ghost.zip % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000054 11 5 mprotect 0.00 0.000000 0 4 read 0.00 0.000000 0 1 write 0.00 0.000000 0 6 open 0.00 0.000000 0 9 close 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 1 access 0.00 0.000000 0 3 brk 0.00 0.000000 0 2 ioctl 0.00 0.000000 0 2 munmap 0.00 0.000000 0 15 mmap2 0.00 0.000000 0 7 fstat64 0.00 0.000000 0 2 getdents64 0.00 0.000000 0 1 set_thread_area 0.00 0.000000 0 1 openat ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000054 60 1 total
These are a few of the things you are able to do with strace. For more detailed information about this tool, I suggest you study the output of the command man strace.
Have fun.