跟踪 UNIX 应用程序的解决方案( 三 )


getcwd("/root", 1013)= 0 
resolvepath("/root/ageindays", "/root/ageindays", 1023) = 15 
xstat(2, "/root/ageindays", 0x08047880) = 0 
open("/var/ld/ld.config", O_RDONLY) = 3 
fxstat(2, 3, 0x08047760)= 0 
mmap(0x00000000, 144, PROT_READ, MAP_SHARED, 3, 0) = 0xFEFA0000 
close(3)= 0 
sysconfig(_CONFIG_PAGESIZE) = 4096 
xstat(2, "/usr/lib/libc.so.1", 0x08046FA0) = 0 
resolvepath("/usr/lib/libc.so.1", "/lib/libc.so.1", 1023) = 14 
open("/usr/lib/libc.so.1", O_RDONLY)= 3 
mmap(0x00010000, 32768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_ALIGN, 3, 0) 
= 0xFEF90000 
mmap(0x00010000, 1413120, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANON|MAP_ALIGN, -1, 0) 
= 0xFEE30000 
mmap(0xFEE30000, 1302809, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_TEXT, 3, 0) 
= 0xFEE30000 
mmap(0xFEF7F000, 30862, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED| 
MAP_INITDATA, 3, 1306624) = 0xFEF7F000 
mmap(0xFEF87000, 4776, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANON, 
-1, 0) = 0xFEF87000 
munmap(0xFEF6F000, 65536)= 0 
memcntl(0xFEE30000, 187632, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0 
close(3)= 0 
mmap(0x00010000, 24576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON|MAP_ALIGN, 
-1, 0) 
= 0xFEE20000 
munmap(0xFEF90000, 32768)= 0 
getcontext(0x080475D0) 
getrlimit(RLIMIT_STACK, 0x080475C8) = 0 
getpid()= 15691 [15690] 
lwp_private(0, 1, 0xFEE22A00)= 0x000001C3 
setustack(0xFEE22A60) 
sysi86(SI86FPSTART, 0xFEF879BC, 0x0000133F, 0x00001F80) = 0x00000001 
ioctl(1, TCGETA, 0x08046C20)= 0 
fstat64(1, 0x08046B80) = 0 
You have been alive 10654 days 
write(1, " Y o uh a v eb e e".., 31) = 31 
You were born on 24/1/1980 which is a Thursday 
write(1, " Y o uw e r eb o r".., 47) = 47 
_exit(134511508)
在这两个输出中,每个输出行对应于应用程序执行的一个函数调用,其中显示函数的参数和函数调用的返回值 。与调试示例不同,列出的每个函数调用都是系统或系统库中的函数,因此表示调用的函数的更低层接口 。例如,在应用程序中可能使用 C 或 C中的 fpopen() 函数打开文件,但是这个函数实际上是更低层的 open() 函数的包装器 。
了解应用程序正在执行的操作并不需要了解每个函数的情况 。输出中的许多行与操作系统为装载和执行程序所做的初始化相关 。这两个跟踪输出的基本结构是相同的:
调用 execve() 函数以启动一个新程序 。
装载程序所需的库 。在 Solaris 输出中,首先使用 resolvepath() 寻找库,然后使用 open() 打开库 。对于 Linux,使用 stat() 检查库是否存在,然后使用 open() 打开它 。
为进程保留和分配一些内存 。其中一部分内存是为应用程序保留的堆栈空间,一部分用来保存程序,其他内存保存程序使用的变量 。
最后,执行程序,调用 write() 函数输出年龄和生日信息 。
如果执行跟踪并希望了解每个步骤的具体情况,可以使用 man 命令访问每个函数的手册页 。
识别应用程序启动问题
在启动应用程序时的一个典型问题是,程序无法正确地初始化,但是在终止时给出一个不完整或导致误解的消息 。对应用程序运行跟踪常常可以揭示这个问题的根源 。例如,清单 5 显示一个测试应用程序运行失败了 。
清单 5. 应用程序失败
$ ./errnoacc 

推荐阅读