ctrace [options] [file]
ctrace 命令允许用户在每个程序语句执行时监视 C 程序的顺序执行。作用类似于使用 -x 选项执行 shell 过程。ctrace 将在 file 中读取 C 程序(如果用户未指定 file,则从标准输入中读取),插入语句以输出每个可执行语句的文本以及引用或修改的所有变量的值,然后将修改的程序写入标准输出。ctrace 的输出必须放入临时文件,因为 cc(1) 命令不允许使用管道。随后便可编译和执行此文件。
程序中的每个语句执行时,都将在终端列出,后跟语句中引用或修改的所有变量的名称和值;这些变量名称和值后跟语句的任何输出。将会检测跟踪输出中的循环,直到退出循环或执行循环中不同序列的语句时,跟踪才将停止。每 1000 个循环周期后会输出一条警告消息,帮助用户检测无限循环。跟踪输出转至标准输出,以便用户可将其放入文件,使用编辑器或 tail(1) 命令进行检查。
常用选项包括:
仅跟踪这些 function。
跟踪除这些 function 之外的所有函数。
用户可能要增加输出变量的缺省格式。长变量和指针变量始终输出为带符号整数。在适当的情况下,字符数组的指针也会输出为字符串。char、short 和 int 变量也会输出为带符号整数;在适当的情况下,还会输出为字符。double 变量输出为以科学记数法表示的浮点数。在适当的情况下,用户可使用以下选项请求以其他格式输出变量:
八进制
十六进制
无符号
浮点
以下选项只在特殊情况下使用:
从 n 个(而非缺省值 20 个)连续执行的语句中检查循环跟踪输出。使用 0 将从循环中获得所有跟踪输出。
禁止简单分配语句和字符串复制函数调用中的冗余跟踪输出。此选项可隐藏因使用 = 运算符代替 == 运算符引起的错误。
每个语句跟踪 n 个变量,而非缺省值 10 个(最大数量是 20 个)。诊断部分说明了何时使用此选项。
在跟踪输入之前对其进行预处理。用户也可以使用 -D、-I 和 -U cc(1) 选项。
从缺省值 printf 更改跟踪输出函数。例如,fprintf(stderr 会将跟踪发送到标准错误输出。
使用文件 f 替换 runtime.c 跟踪函数软件包。通过这种替换,用户可更改整个输出函数,而不仅仅是名称和前导参数(请参见 -p 选项)。
在标准错误中输出版本信息。
如果 arg 是 y,会将关于 ctrace 的识别信息添加到输出文件中。此选项对软件管理十分有用。为 arg 指定 n 将显式要求不提供此类信息,这是缺省行为。
如果文件 lc.c 包含以下 C 程序:
1 #include <stdio.h> 2 main() /* count lines in input */ 3 { 4 int c, nl; 5 6 nl = 0; 7 while ((c = getchar()) != EOF) 8 if (c = '\n') 9 ++nl; 10 printf("%d\n", nl); 11 }
将输入以下命令和测试数据:
cc lc.c a.out 1 (cntl-d)
将编译并执行程序。程序的输出将是数字 2,这不正确,因为测试数据中只有一行。此程序中的错误很常见,但是不易察觉。如果用户使用以下命令调用 ctrace:
ctrace lc.c >temp.c cc temp.c a.out
输出将是:
2 main() 6 nl = 0; /* nl == 0 */ 7 while ((c = getchar()) != EOF)
程序现在等待输入。如果用户输入与之前相同的测试数据,输出将是:
/* c == 49 or '1' */ 8 if (c = '\n') /* c == 10 or '\n' */ 9 ++nl; /* nl == 1 */ 7 while ((c = getchar()) != EOF) /* c == 10 or '\n' */ 8 if (c = '\n') /* c == 10 or '\n' */ 9 ++nl; /* nl == 2 */ 7 while ((c = getchar()) != EOF)
如果输入了文件结尾字符 (cntl-d),最终输出将是:
/* c == -1 */ 10 printf("%d\n", nl); /* nl == 2 */2 return
请注意第 10 行后面 nl 变量的跟踪行结尾输出的信息。另请注意 ctrace 在跟踪输出结尾添加的 return 注释。这显示在函数中的终止大括号处隐式返回。
跟踪输出显示,在第 7 行中为变量 c 分配的值是 '1',但在第 8 行中其值是 '\n'。用户注意到此 if 语句后,或许会意识到,使用赋值运算符 (=) 代替了等号运算符 (==)。此错误很容易在代码读取过程中漏掉。
除非使用 -f 或 -v 选项跟踪特定函数,否则 ctrace 的缺省操作是跟踪整个程序文件。通过缺省操作,用户无法对跟踪进行逐语句控制,也不能在执行跟踪的程序时关闭和打开跟踪。
用户可向程序中添加 ctroff() 和 ctron() 函数调用以分别在执行时关闭和打开跟踪,由此执行以上两项操作。因此,可对复杂的条件进行任意编码以使用 if 语句控制跟踪,甚至可以有条件地包括此代码,因为 ctrace 定义了 CTRACE 预处理程序变量。例如:
#ifdef CTRACE if (c == '!' && i > 1000) ctron(); #endif
如果使用 -g 选项编译这些函数,也可从 (dbx)1 调用它们。例如,要在主函数中跟踪除第 7 行到第 10 行之外的所有行,请输入:
dbx a.out when at 7 {call ctroff();} when at 7 {call ctron();} run
通过将静态变量 tr_ct_ 分别设置为 0 和 1 可关闭和打开跟踪。如果用户使用的调试器无法直接调用这些函数,则此开/关选项非常有用。
runtime.c 运行时跟踪软件包
bfs (1) 、 dbx (1) 、 tail (1) 、 ctype (3C) 、 fclose (3C) 、 fprint (3C) 、 string (3C)
此部分包含来自 ctrace 和 cc(1) 的诊断消息,因为跟踪的代码经常收到一些 cc 警告消息。在极少数情况下,用户会收到 cc 错误消息,这些消息都能避免。
在一个语句中仅跟踪 10 个变量,以防止出现 C 编译器 "out of tree space; simplify expression"(树空间不足;请简化表达式)错误。使用 -t 选项可增大此数字。
此语句的长度超过 400 个字符。确保使用制表符缩排代码,而不要使用空格。
这通常由 C 语句中间的 #ifdef/#endif 预处理程序语句或者由 #define 预处理程序语句结尾的分号引起。
请从中间删除 else 来分割序列。
使用 -P 选项以及任何适当的 -D、-I 和 -U 预处理程序选项预处理 ctrace 输入。
如果参数数量发生更改,定义与系统函数名称相同的函数可能会导致语法错误。请使用不同的名称。
ctrace 假定 BADMAG 是预处理程序宏,EOF 和 NULL 是用 #define 定义的常量。声明其中任何一项(例如 "int EOF;")是变量都会导致语法错误。
指针值始终被视为字符串的指针。
ctrace 不知道聚合的组成部分,例如结构、联合和数组。当针对整个聚合进行赋值时,无法选择一种格式输出聚合的所有组成部分。ctrace 可选择输出聚合的地址,或者在输出聚合的值时使用错误格式(例如 3.149050e-311-对于有两个整数成员的结构)。
对于多文件程序的每个文件,循环跟踪输出消除单独完成。单独的输出消除会导致仍然跟踪从循环调用的函数,或者从文件中的一个函数消除跟踪输出,直到调用同一个文件中的其他函数。