桁架确实会导致程序上下文的环境变化
桁架改变了程序的时间安排,因此它可以改变所寻求问题的性质。
Truss 通过在系统调用入口点停止程序执行、检查参数、恢复执行直到系统调用返回、在读取返回状态时再次暂停执行、然后再次恢复执行直到下一个事件(故障、信号或者系统服务)调用)发生。
这种执行的开始和停止会改变程序的时序。
对于单线程、单进程程序,这不是问题。
但是,如果问题是由于竞争条件或者其他与时间相关的问题引起的,那么桁架可能不是一个好的选择。
如何使用 truss 调试程序执行
通常,当程序异常终止但没有崩溃时,是因为系统服务调用返回了错误。
这可能是由于编程错误,也可能是由于环境。
桁架可以帮助确定问题的原因。
通过提供系统服务调用的跟踪,直到崩溃,Truss 还可以帮助调试程序崩溃的位置。
此输出以及源代码可以帮助确定程序崩溃的位置。
调试带有间接错误的程序
对于具有环境错误的程序示例,假设程序退出时出现错误条件“没有这样的文件或者目录.”。
通过列出程序发出的所有 open() 系统服务调用(包括失败的调用),Truss 可以显示程序正在寻找哪个文件。
例如,假设执行以下命令:
# cat junk
文件“垃圾”不存在的地方。
“cat ”程序的一部分桁架,接近尾声,显示:
fstat(1, 0xEFFFF6C8) = 0 open("junk", O_RDONLY) Err#2 ENOENT sigfillset(0xEF744060) = 0 cat: cannot open write(2, " c a t : c a n n o t ".., 17) = 17 write(2, " j u n k", 4) = 4 write(2, "\n", 1) = 1 lseek(0, 0, SEEK_CUR) = 56338 _exit(2)
为了在屏幕上显示文件“垃圾”,它必须先打开它。
上面列出的“open”系统调用将“junk”显示为第一个参数,将 O_RDONLY 显示为第二个参数,表明 open() 的调用是以只读模式打开文件“junk”。
请注意,参数对应于手册页第 2 部分中为系统调用记录的参数。
open() 调用的返回状态是 ENOENT,或者错误号 2,对应于“没有这样的文件或者目录”,这意味着它无法打开文件。
如果它可以打开文件,它将返回一个有效的文件描述符(由“=”和与文件描述符对应的数字表示)。
注意事件的顺序,失败的 open() 调用是结束的开始;我们看到程序调用 write() 两次,并且可以从 write() 调用的参数中看到该程序正在打印错误消息。
这对应于用户在 cat 无法打开文件时看到的消息:
cat: cannot open junk
从那里往下两行是最后的 exit()。
在此示例中,“cat”在错误消息中显示它无法打开的文件,这与 truss 输出中 open() 调用的参数相关。
其他程序可能不会提供包含文件名的此类错误消息,但是在它们的 truss 输出末尾附近搜索 open() 调用可以显示它们找不到哪个文件。
深入了解工厂 Solaris 程序的工作原理
有没有想过 ps 是如何工作的?
捆绑它会告诉如何。
做某些事情的捆绑程序可以帮助人们发现完成这些任务的新系统调用。
因此,桁架可以成为非常有价值的教育工具。
以 ps 为例,我们看到以下内容:
execve("/usr/bin/ps", 0xEFFFF910, 0xEFFFF918) argc = 1 ... write(1, " P I D T T Y ".., 26) = 26 (1) open("/proc", O_RDONLY|O_NDELAY) = 3 (2) fcntl(3, F_SETFD, 0x00000001) = 0 fstat(3, 0xEFFFF760) = 0 getdents(3, 0x00026928, 1048) = 972 (3) open("/proc/00000", O_RDONLY) = 4 (4) ioctl(4, PIOCPSINFO, 0x00024C58) = 0 (5) close(4) = 0 open("/proc/00001", O_RDONLY) = 4 ioctl(4, PIOCPSINFO, 0x00024C58) = 0 close(4) = 0 ... open("/proc/01316", O_RDONLY) = 4 ioctl(4, PIOCPSINFO, 0x00024C58) = 0 close(4) = 0 1316 pts/1 0:00 ksh write(1, " 1 3 1 6 p t s / 1".., 25) = 25 (6) open("/proc/01317", O_RDONLY) = 4 ioctl(4, PIOCPSINFO, 0x00024C58) = 0 close(4) = 0 1317 pts/1 0:00 truss write(1, " 1 3 1 7 p t s / 1".., 27) = 27 open("/proc/00905", O_RDONLY) = 4 ioctl(4, PIOCPSINFO, 0x00024C58) = 0 close(4) = 0 ... _exit(0)
显然,从这个例子中,ps 打印标题 (1),然后打开“/proc”目录 (2),调用 getdents() 并使用它的第一个参数作为“/proc”文件描述符(3),然后开始打开所有"/proc" 目录中的文件 (4) 并对它们执行 ioctl 以获取信息 (5)。
当信息满足某些条件时(无法从该输出中确定,但可以通过了解 ps 命令的作用来确定),程序会写入一些内容 (6)。
所有这些模糊的东西是什么意思?
查看 getdents() 的手册页,在手册页中搜索“/proc”和“PIOCPSINFO”,然后找出...
Truss 是一种调试工具,它通过打印系统服务调用及其参数和返回状态、故障和信号来深入了解程序的运行方式。
因此,它对于调试错误和弄清楚程序如何工作非常有用。
Truss 易于使用:只需以最简单的形式在任何命令(包括参数)前加上“truss”这个词,就会有大量输出。
默认情况下,truss 输出到 stderr(错误输出),而不是 stdout(正常输出)。
这使得使用shell I/O 重定向标记将 truss 输出与程序输出分开变得容易。
桁架输出也可以使用 -o 开关保存到文件中。
没有开关的桁架将跟踪不分叉的进程的输出。
-f 开关包括跟踪所有分叉的子级以及指定为命令的父级。
带有 -p 开关的 Truss 可用于开始跟踪已运行的进程,而无需重新启动它。
当可疑程序运行一段时间并给出崩溃或者问题迫在眉睫的指示或者条件(例如某种输出)时,这很有用。
例如,假设一个程序运行了几分钟,然后在它异常终止前不久给出了某个输出。
运行程序,然后在特定输出之后但在程序终止之前使用 -p 选项(命令是“truss -p”)开始对其进行桁架。
Truss 将提供有关会发生什么导致程序异常终止的线索,而不必在运行的前几分钟累积输出。
可以使用 -t 开关将 truss 的输出限制为一组给定的系统服务调用。
假设用户知道要查找什么,这也有助于减少寻找答案所需的输出。
调试有编程错误的程序
通过首先搜索 truss 输出的错误返回状态,然后检查系统服务调用的参数,可以发现编程错误。
系统服务在参数不正确或者意外时调用炸弹。
大多数系统调用炸弹都是优雅的,也就是说,不会使程序崩溃。
但是,当发生这种情况时,它们会将它们影响的变量置于意外状态,这可能导致程序崩溃。
例如,尝试使用 malloc() 一个非常大的内存块可能会强制 malloc 返回一个 NULL 指针而不是一个指向内存的指针。
如果稍后在程序中访问该 NULL 指针,则程序将崩溃。
查看 truss 输出,看到 malloc() 返回一个 NULL 并看到在这种情况下传递给它的异常大参数,表明发生了什么。