A truss example
我一直认为,truss是Solaris里面一个非常重要的诊断工具,也是我最喜欢使用的工具。基本上80%的软件问题,我都通过truss找到了线索。Solaris 10提供了更有效的工具dtrace,但是很多时候,truss使用起来更简单,也更容易发现线索(当然,有些问题是truss无法诊断,并且可能导致问题恶化的,比如与时间相关的性能问题等)
truss可以跟踪进程执行的系统调用(system calls)和信号(signals)情况。其用法如下:
truss [-fcaeildDE] [- [tTvx] [!] syscall ,...]
[- [sS] [!] signal ,...] [- [mM] [!] fault ,...]
[- [rw] [!] fd ,...]
[- [uU] [!] lib ,... : [:] [!] func ,...]
[-o outfile] command | -p pid[/lwps]...
参数比较多,我一般的用法是
truss -afield -rall -wall -vall -o outputfile -p pid 或者
truss -afield -rall -wall -vall -o outputfile app
其中outputfile是truss跟踪信息的输出文件,如果不指定-o,缺省会从STDOUT输出,pid是需要跟踪的进程的pid,或者可以直接跟踪一个程序app的执行。
-rall 表示跟踪所有read()系统调用的I/O buffer的内容
-wall 表示跟踪所有write()系统调用的I/O buffer的内容
-vall 表示显示详细信息,比如传递给系统调用的数据结构的信息
-afield实际上就是-a -f -i -e -l -d的连写,为了便于记忆,你可以把它理解成"一个现场工程师"(field engineer),呵呵。
这些参数的含义我就不在这里列出,大家请参考man truss
收集完truss跟踪文件后,我们就可以对跟踪文件进行分析。对此文件的分析需要有一定的系统调用知识。下面是一个具体例子。
-------------------------------------
[ 问题 ]
系统无法telnet或者ftp上去(提示连接被拒绝),但是系统的TCP/IP是激活的,可以获得ping响应
[ 现象 ]
从系统console登录,检查/etc/inetd.conf,有如下内容
ftp stream tcp6 nowait root /usr/sbin/in.ftpd in.ftpd
telnet stream tcp6 nowait root /usr/sbin/in.telnetd in.telnetd
inetd进程在运行
root 952 1 0 May 05 ? 27:39 /usr/sbin/inetd -s
端口号21,23都在侦听
*.21 *.* 0 0 24576 0 LISTEN
*.23 *.* 0 0 24576 0 LISTEN
[ 诊断 ]
请现场工程师在console里运行: truss -afield -rall -wall -o /var/tmp/inetd.truss -p 952,
同时,从另外一台服务器上执行telnet的操作,等到出现连接被拒绝时,Ctrl+C中断上面的truss程序,然后分析inetd.truss
下面是截取的inetd.truss中关键的信息。(建议从文件结尾往前倒着看,因为问题出现时,我们就中断了truss执行,因此线索应该位于跟踪文件的后半段)
...
22707/1: 44.5740 stat64("/usr/lib/security/pam_seos.so", 0xFFBEF7D0) = 0
22707/1: 44.5741 stat("/usr/lib/security/pam_seos.so", 0xFFBEF10C) = 0
22707/1: 44.5742 open("/usr/lib/security/pam_seos.so", O_RDONLY) = 3
22707/1: 44.5743 fstat(3, 0xFFBEF10C) = 0
22707/1: 44.5744 mmap(0x00000000, 8192, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xFF1D0000
22707/1: 44.5745 mmap(0x00000000, 950272, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANON, -1, 0) = 0xFED80000
22707/1: 44.5746 mmap(0xFED80000, 224774, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0xFED80000
22707/1: 44.5746 mmap(0xFEDC6000, 33970, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 221184) = 0xFEDC6000
22707/1: 44.5748 mmap(0xFEDD0000, 620336, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) = 0xFEDD0000
22707/1: 44.5749 munmap(0xFEDB8000, 57344) = 0
22707/1: 44.5751 memcntl(0xFED80000, 74796, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
22707/1: 44.5751 close(3) = 0
22707/1: 44.5766 munmap(0xFF1D0000, 8192) = 0
22707/1: 44.5767 open("/etc/seos.ini", O_RDONLY) Err#2 ENOENT
22707/1: 44.5769 open("/usr/seos/seos.ini", O_RDONLY) Err#2 ENOENT
22707/1: 44.5770 open("/usr/local/seos/seos.ini", O_RDONLY) Err#2 ENOENT
22707/1: 44.5771 open("/usr/seos/data/seos.ini", O_RDONLY) Err#2 ENOENT
22707/1: 44.5772 open("/usr/local/seos/data/seos.ini", O_RDONLY) Err#2 ENOENT
22707/1: 44.5774 open(".//seos.ini", O_RDONLY) Err#2 ENOENT
22707/1: 44.5776 open(".//etc/osver", O_RDONLY) Err#2 ENOENT
22707/1: 44.5777 Incurred fault #6, FLTBOUNDS %pc = 0xFF19131C
22707/1: siginfo: SIGSEGV SEGV_MAPERR addr=0x0000000C
22707/1: 44.5778 Received signal #11, SIGSEGV [default]
22707/1: siginfo: SIGSEGV SEGV_MAPERR addr=0x0000000C
...
此段大概位于truss跟踪文件的98%位置处。
注意看粗体部分。
/usr/lib/security/pam_seos.so 这个动态连接库应该与PAM(Pluggable Authentication Module)模块有关,注意它的pam_前缀。seos应该是CA eTrust的产品。应该是用户对此系统作了安全加固。查看pam.conf,最后几行如下:
#
# eTrust integrate auth and account
#
other auth optional /usr/lib/security/pam_seos.so
rlogin auth optional /usr/lib/security/pam_seos.so
other account optional /usr/lib/security/pam_seos.so
再看上面truss跟踪文件,从open返回ENOENT错误,可以看出inetd无法找到与pam_seos相干的配置文件seos.ini。那么找不到配置文件,会有什么后果呢?看下面紧接着的SIGSEGV就应该知道,后果很严重,直接导致这个22707的子进程终止
22707/1: *** process killed ***
22707执行的是/bin/login程序(从truss跟踪文件可以得到此信息,在此略去),由于/bin/login无法执行,因此用户无法使用telnet/ftp登录。
[ 解决办法 ]
1. 注释掉/etc/pam.conf中与pam_seos相关的行
2. 恢复seos.ini文件。
---------------------
