不同发行版本下面的ltrace还是存在一些差异,同样使用的是0.7.3版本的ltrace,在ubuntu18及以下,执行ltrace ls 会显示一大堆内容,而在ubuntu20与ubuntu22则基本无内容显示.如下:

ltrace ls

bin  boot  dev    etc  home  lib    lib32  lib64  libx32  media  mnt  opt  proc  root  run    sbin  srv  sys    tmp  usr  var
+++ exited (status 0) +++

在ubuntu18.04上运行,则会出现这样的结果.

ltrace ls
__libc_start_main(0x5661e5c0, 1, 0xfff143c4, 0x566326d0 <unfinished ...>
strrchr("ls", '/')                                                                                         = nil
setlocale(LC_ALL, "")                                                                                      = "C"
bindtextdomain("coreutils", "/usr/share/locale")                                                           = "/usr/share/locale"
textdomain("coreutils")    
...
fflush(0xf7ec7ce0)                                                                                         = 0
fclose(0xf7ec7ce0)                                                                                         = 0
+++ exited (status 0) +++

而ltrace的版本信息都是V0.7.3

ltrace -V
ltrace version 0.7.3.
Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.
This is free software; see the GNU General Public Licence
version 2 or later for copying conditions.  There is NO warranty.

这些都不是重点,重点是知道是怎么样用的就好.要让显示一致,就在ubuntu18及以下的命令运行时,使用ltrace -L 有时可以达到相同的显示效果,可以试试看 ltrace -L -x "getenv" ls -lh

ltrace -x "getenv" ls     
getenv@libc.so.6("LOCPATH")                                                                                = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_IDENTIFICATION")                                                                      = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_MEASUREMENT")                                                                         = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_TELEPHONE")                                                                           = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
...

注意事项:
ltrace 不能用于静态编译链接的程序,只能用于动态库链接的程序.他的作用是Trace library calls of a given program.如果都没有library,你可以需要的是strace.

典型的就是你想去ltrace一个go编译的程序的时候,出现下面这个东西.
Couldn't find .dynsym or .dynstr in "/proc/3596/exe"
哪很有可能就是,你搞错了要trace的对象了.

常用的使用方式:

  1. strace/ltrace 直接加上指令,如: ltrace ls -l。
  2. strace/ltrace 加上-o 選項的參數而導到某個檔案,如: ltrace -o strace.out ls -l。
  3. strace/ltrace 加上-p 選項的參數(Process ID)來觀測某個process的system call/library API,如: ltrace -p 12345。

ltrace常用的参数:

-l 只打印某个库中的调用。
-L 不打印库调用。
-o, --output=file 把输出定向到文件。
-p PID 附着在值为PID的进程号上进行ltrace。
-r 打印相对时间戳。
-s STRLEN 设置打印的字符串最大长度。
-S 显示系统调用。
-t, -tt, -ttt 打印绝对时间戳。
-T 输出每个调用过程的时间开销。
-x 显示指定的函数名,可以用透配符,个人感觉这个好使.

ltrace 把输出保存到文件

root@bf9c8c24edc5:/# ltrace -o /tmp/debug.log -x "gete*" ls
bin  boot  dev    etc  home  lib    lib32  lib64  libx32  media  mnt  opt  proc  root  run    sbin  srv  sys    tmp  usr  var
root@bf9c8c24edc5:/# cat /tmp/debug.log 
getenv@libc.so.6("LOCPATH")                                                                                = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_IDENTIFICATION")                                                                      = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_MEASUREMENT")                                                                         = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_TELEPHONE")                                                                           = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_ADDRESS")                                                                             = nil

ltrace 设置打印的长度,默认好像是32字节

ltrace -o /tmp/debug.log -x 'write'  ping -c 4 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=9.12 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=6.52 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=6.57 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=6.26 ms

--- 223.5.5.5 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 6.262/7.118/9.117/1.160 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84"..., 49)                                              = 49
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "\n", 1)                                                                                = 1
write@libc.so.6(1, "--- 223.5.5.5 ping statistics --"..., 34)                                              = 34
write@libc.so.6(1, "4 packets transmitted, 4 receive"..., 63)                                              = 63
write@libc.so.6(1, "rtt min/avg/max/mdev = 6.262/7.1"..., 50)                                              = 50
+++ exited (status 0) +++

看上面的/tmp/debug.log中,write的数据就没有显示完成,加上-S参数来显示完成试试看.

ltrace -o /tmp/debug.log -x 'write' -s 64  ping -c 4 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.27 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=6.09 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=6.33 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=6.52 ms

--- 223.5.5.5 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 6.091/6.300/6.519/0.152 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)                               = 49
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.27 ms\n", 57)                       = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=6.09 ms\n", 57)                       = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=6.33 ms\n", 57)                       = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=6.52 ms\n", 57)                       = 57
write@libc.so.6(1, "\n", 1)                                                                                = 1
write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                                              = 34
write@libc.so.6(1, "4 packets transmitted, 4 received, 0% packet loss, time 3006ms\n", 63)                 = 63
write@libc.so.6(1, "rtt min/avg/max/mdev = 6.091/6.300/6.519/0.152 ms\n", 50)                              = 50
+++ exited (status 0) +++

ltrace 加上时间
时间参数就三个,-t, -tt , -ttt,-t就只有时分秒,-tt时分秒带微秒,-ttt就是unixtime带微秒.看结果.

root@bf9c8c24edc5:/# ltrace -o /tmp/debug1.log -x 'write' -s 64 -t  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=7.61 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.613/7.613/7.613/0.000 ms
root@bf9c8c24edc5:/# ltrace -o /tmp/debug2.log -x 'write' -s 64 -tt  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.22 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.220/6.220/6.220/0.000 ms
root@bf9c8c24edc5:/# ltrace -o /tmp/debug3.log -x 'write' -s 64 -ttt  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.25 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.253/6.253/6.253/0.000 ms
root@bf9c8c24edc5:/# cat /tmp/debug1.log 
02:34:59 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)                      = 49
02:34:59 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=7.61 ms\n", 57)              = 57
02:34:59 write@libc.so.6(1, "\n", 1)                                                                       = 1
02:34:59 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                                     = 34
02:34:59 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)           = 60
02:34:59 write@libc.so.6(1, "rtt min/avg/max/mdev = 7.613/7.613/7.613/0.000 ms\n", 50)                     = 50
02:34:59 +++ exited (status 0) +++
root@bf9c8c24edc5:/# cat /tmp/debug2.log 
02:35:04.164310 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)               = 49
02:35:04.170973 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.22 ms\n", 57)       = 57
02:35:04.171221 write@libc.so.6(1, "\n", 1)                                                                = 1
02:35:04.171343 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                              = 34
02:35:04.171504 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)    = 60
02:35:04.171697 write@libc.so.6(1, "rtt min/avg/max/mdev = 6.220/6.220/6.220/0.000 ms\n", 50)              = 50
02:35:04.172049 +++ exited (status 0) +++
root@bf9c8c24edc5:/# cat /tmp/debug3.log 
1687142111.348984 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)             = 49
1687142111.355684 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.25 ms\n", 57)     = 57
1687142111.355885 write@libc.so.6(1, "\n", 1)                                                              = 1
1687142111.355985 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                            = 34
1687142111.356126 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)  = 60
1687142111.356314 write@libc.so.6(1, "rtt min/avg/max/mdev = 6.253/6.253/6.253/0.000 ms\n", 50)            = 50
1687142111.356666 +++ exited (status 0) +++

ltrace 显示每个调用所花的绝对时间就加上-T参数,结果在最右边尖括号内.

ltrace -o /tmp/debug.log -x 'write' -s 64 -ttt -T  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.34 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.343/6.343/6.343/0.000 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142203.255801 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)             = 49 <0.000193>
1687142203.262657 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.34 ms\n", 57)     = 57 <0.000247>
1687142203.262946 write@libc.so.6(1, "\n", 1)                                                              = 1 <0.000103>
1687142203.263084 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                            = 34 <0.000124>
1687142203.263241 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)  = 60 <0.000158>
1687142203.263425 write@libc.so.6(1, "rtt min/avg/max/mdev = 6.343/6.343/6.343/0.000 ms\n", 50)            = 50 <0.000142>
1687142203.263779 +++ exited (status 0) +++

ltrace 显示系统调用就加-S参数

ltrace -o /tmp/debug.log -x 'write' -s 64 -ttt -T -S  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=5.94 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.940/5.940/5.940/0.000 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142356.444336 SYS_brk(0)                                                                               = 0x56108d657000 <0.000140>
1687142356.444611 SYS_arch_prctl(0x3001, 0x7ffea0f00a60, 0x7ff8bd4862d0, 13)                               = -22 <0.000116>
1687142356.444924 SYS_access("/etc/ld.so.preload", 04)                                                     = -2 <0.000296>
1687142356.445362 SYS_openat(0xffffff9c, 0x7ff8bd48fb80, 0x80000, 0)                                       = 3 <0.000114>
1687142356.445548 SYS_fstat(3, 0x7ffea0effc60)                                                             = 0 <0.000045>
1687142356.445637 SYS_mmap(0, 6320, 1, 2)                                                                  = 0x7ff8bd468000 <0.000075>
1687142356.445778 SYS_close(3)                                                                             = 0 <0.000065>
...

ltrace 跟踪原理
ltrace的功能是能够跟踪进程的库函数调用,它是如何实现的呢?

ltrace其实也是基于ptrace。我们知道,ptrace能够主要是用来跟踪系统调用,那么它是如何跟踪库函数呢?

首先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 与 strace 的异同
ltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用,而strace则是跟踪程序的每个系统调用.ltrace比strace更费时.

strace -o /tmp/debug.log  -s 64 -ttt -T -e 'write'  echo "hello world"
hello world
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142834.793675 write(1, "hello world\n", 12) = 12 <0.000010>
1687142834.793840 +++ exited with 0 +++
root@bf9c8c24edc5:/# 
root@bf9c8c24edc5:/# 
root@bf9c8c24edc5:/# ltrace -o /tmp/debug.log  -s 64 -ttt -T -x 'write' echo "hello world"
hello world
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142847.965263 write@libc.so.6(1, "hello world\n", 12)                                                  = 12 <0.000124>
1687142847.965520 +++ exited (status 0) +++

最后:
使用ltrace最好不要在生产系统上面搞,会阻塞住当前线程,如果是多线程程序,可能出现各种奇怪的问题.
参考:
https://www.cnblogs.com/machangwei-8/p/10388938.html
https://zhuanlan.zhihu.com/p/107063011
https://blog.csdn.net/fantasy_ARM9/article/details/115892633
https://man7.org/linux/man-pages/man1/ltrace.1.html

本文链接地址:https://const.net.cn/754.html

标签: none

添加新评论