文章目錄
- 背景
- ltrace原理
- ltrace使用
- 跟蹤程序調用庫函數
- 跟蹤指定pid進程調用
- 參考
本文介紹ltrace跟蹤
背景
ltrace 會攔截并記錄正在執行的進程所調用的動態庫調用以及該進程接收到的信號,它還可以攔截并打印程序執行的系統調用。
其代碼位置在:https://gitlab.com/cespedes/ltrace.git
ltrace原理
ltrace也是基于ptrace。
ptrace主要是用來跟蹤系統調用,ltrace使用了下面的方法來跟蹤庫函數:
- 首先ltrace打開elf文件,對其進行分析。在elf文件中,出于動態連接的需要,需要在elf文件中保存函數的符號,供連接器使用。具體格式,大家可以參考elf文件的格式。這樣ltrace就能夠獲得該文件中,所有系統調用的符號,以及對應的執行指令。
- 然后,ltrace將該指令所對應的4個字節,替換成斷點。其實現可以參考Playing with ptrace, Part II。
- 這樣在進程執行到相應的庫函數后,就可以通知到了ltrace,ltrace將對應的庫函數打印出來之后,繼續執行子進程。
實際上ltrace與strace使用的技術大體相同,但ltrace在對支持fork和clone方面,不如strace。strace在收到frok和clone等系統調用后,做了相應的處理,而ltrace沒有。
ltrace使用
$ ltrace --help
Usage: ltrace [option ...] [command [arg ...]]
Trace library calls of a given program.-a, --align=COLUMN align return values in a secific column.-A MAXELTS maximum number of array elements to print.-b, --no-signals don't print signals.-c count time and calls, and report a summary on exit.-C, --demangle decode low-level symbol names into user-level names.-D, --debug=MASK enable debugging (see -Dh or --debug=help).-Dh, --debug=help show help on debugging.-e FILTER modify which library calls to trace.-f trace children (fork() and clone()).-F, --config=FILE load alternate configuration file (may be repeated).-h, --help display this help and exit.-i print instruction pointer at time of library call.-l, --library=LIBRARY_PATTERN only trace symbols implemented by this library.-L do NOT display library calls.-n, --indent=NR indent output by NR spaces for each call level nesting.-o, --output=FILENAME write the trace output to file with given name.-p PID attach to the process with the process ID pid.-r print relative timestamps.-s STRSIZE specify the maximum string size to print.-S trace system calls as well as library calls.-t, -tt, -ttt print absolute timestamps.-T show the time spent inside each call.-u USERNAME run command with the userid, groupid of username.-V, --version output version information and exit.-x FILTER modify which static functions to trace.Report bugs to ltrace-devel@lists.alioth.debian.org
跟蹤程序調用庫函數
ltrace-demo.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include "demo-lib.h"void func1()
{printf("func1\n");
}int main()
{printf("ltrace-demo\n");pid_t r = fork();if (r == 0) {printf("in child\n");func2();} else if(r > 0) {printf("child pid: %d\n", r);func1();}wait(NULL);return 0;
}
demo-lib.h
void func2();
demo-lib.c
#include "demo-lib.h"
#include <stdio.h>void func2()
{printf("func2\n");
}
編譯測試:
linux-dev@linuxdev:~$ gcc -shared -fPIC -o libdemo-lib.so demo-lib.c
linux-dev@linuxdev:~$ gcc -o ltrace-demo ltrace-demo.c -L. -ldemo-lib
linux-dev@linuxdev:~$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
linux-dev@linuxdev:~$ ltrace -i ./ltrace-demo
[0x5c825eca31fe] puts("ltrace-demo"ltrace-demo
) = 12
[0x5c825eca3203] fork() = 1817
[0x5c825eca3246] printf("child pid: %d\n", 1817child pid: 1817
) = 16
[0x5c825eca31e0] puts("func1"func1
in child
) = 6
[0x5c825eca325a] wait(0func2<no return ...>
[0x7c30b13107a7] --- SIGCHLD (Child exited) ---
[0x5c825eca325a] <... wait resumed> ) = 1817
[0xffffffffffffffff] +++ exited (status 0) +++
跟蹤指定pid進程調用
linux-dev@linuxdev:~$ pidof top
1434
linux-dev@linuxdev:~$ sudo ltrace -i -p 1434
[0x618f588eb679] procps_uptime(0x7ffc8ef2b4f0, 0, 0, 0x7d015f325fde) = 0
[0x618f588eb831] procps_pids_reap(0x618f611f7420, 0, 0, -3616) = 0x618f611f7458
[0x618f588eb814] memcpy(0x618f61218ea0, "0\020J^\001}\0\08\020J^\001}\0\0@\020J^\001}\0\0H\020J^\001}\0\0"..., 1696) = 0x618f61218ea0
[0x618f588eb814] memcpy(0x618f612196b0, "0\020J^\001}\0\08\020J^\001}\0\0@\020J^\001}\0\0H\020J^\001}\0\0"..., 1696) = 0x618f612196b0
[0x618f588eb814] memcpy(0x618f61219ec0, "0\020J^\001}\0\08\020J^\001}\0\0@\020J^\001}\0\0H\020J^\001}\0\0"..., 1696) = 0x618f61219ec0
[0x618f588eb814] memcpy(0x618f6121a6d0, "0\020J^\001}\0\08\020J^\001}\0\0@\020J^\001}\0\0H\020J^\001}\0\0"..., 1696) = 0x618f6121a6d0
[0x618f588dc173] procps_stat_reap(0x618f611de980, 0, 0x618f588f7040, 16) = 0x618f611debd0
[0x618f588dc1e3] time(0) = 1739018691
[0x618f588dc20b] procps_meminfo_select(0x618f611f6670, 0x618f588f8180, 9, 3) = 0x618f6121af38
[0x618f588dbb13] putp(0x618f588fb5e0, 0, 0x618f6121af38, 0x2c6b24) = 0
[0x618f588dbb7e] procps_uptime_sprint(72, 0, 0x2f534, 0) = 0x7d015f4223a0
[0x618f588dd201] __vsnprintf_chk(0x618f589019c0, 2048, 2, 2048) = 68
[0x618f588e00bd] strchr("top - 20:44:51 up 30 min, 3 use"..., '\n') = "\n"
[0x618f588e01f3] __snprintf_chk(0x7ffc8ef2a5e0, 512, 2, 512) = 87
[0x618f588e0300] __snprintf_chk(0x7ffc8ef2ace0, 2048, 2, 2048) = 105
[0x618f588e03c5] strcpy(0x7d015eefd010, "\033(B\033[mtop - 20:44:51 up 30 min, "...) = 0x7d015eefd010
[0x618f588e03cd] putp(0x7d015eefd010, 0x7ffc8ef2acff, 1, 9) = 0
[0x618f588e00bd] strchr("", '\n') = nil
[0x618f588dd201] __vsnprintf_chk(0x618f589019c0, 2048, 2, 2048) = 91
[0x618f588e00bd] strchr("Tasks:~3 212 ~2total,~3 1 ~2ru"..., '\n') = "\n"
[0x618f588e01f3] __snprintf_chk(0x7ffc8ef2a5d0, 512, 2, 512) = 20
[0x618f588e01f3] __snprintf_chk(0x7ffc8ef2a5d0, 512, 2, 512) = 23
[0x618f588e01f3] __snprintf_chk(0x7ffc8ef2a5d0, 512, 2, 512) = 20
[0x618f588e01f3] __snprintf_chk(0x7ffc8ef2a5d0, 512, 2, 512) = 23
[0x618f588e01f3] __snprintf_chk(0x7ffc8ef2a5d0, 512, 2, 512) = 22
參考
ltrace
ltrace
Playing with ptrace, Part I
Playing with ptrace, Part II