可以使用 fix 命令快速重新编译经过编辑的本地源代码,而不必停止调试进程。但不能使用 fix 命令重新编译 Java 代码。
在 Linux 平台上不能使用 fix 命令。
本章由以下部分组成:
使用修复并继续功能,可以修改和重新编译本地源文件并继续执行,而无需重新生成整个程序。通过更新 .o 文件并将其拼接到程序中,即可不必重新进行链接。
使用修复并继续的优点是:
不必重新链接程序。
不必重新装入调试程序。
可以从修复位置恢复运行程序。
请勿在生成过程中使用 fix 命令。
使用 fix 命令之前,必须编辑源代码。(有关修改代码的方式,请参见使用修复并继续修改源码。)保存更改后,请键入 fix。有关 fix 命令的信息,请参见fix 命令。
调用 fix 命令后,dbx 会使用适当的编译器选项来调用编译器。编译已修改的文件并创建共享对象 (.so) 文件。通过比较新旧文件进行语义测试。
使用运行时链接程序将新的目标文件链接到正在运行的进程。如果正在修复位于栈顶的函数,则新的停止于函数便是新函数中同一行的开头。旧文件中的所有断点均被移到新文件中。
无论文件在编译时是否使用了调试信息,都可以对其使用修复并继续功能,但对于在最初编译时未使用调试信息的文件,fix 命令和 cont 命令有一些功能性限制。有关更多信息,请参见fix 命令中有关 -g 选项的说明。
可以修复共享目标 (.so) 文件,但必须在特殊模式下打开这些文件。在调用 dlopen 函数时,可以使用 RTLD_NOW|RTLD_GLOBAL 或 RTLD_LAZY|RTLD_GLOBAL。
Oracle Solaris Studio C 和 C++ 编译器的预编译头功能要求在重新编译时使用相同的编译器选项。由于 fix 命令会对编译器选项稍加更改,因此请勿对使用预编译头创建的目标文件使用 fix 命令。
使用修复并继续时,可以通过以下方式修改源码:
添加、删除或更改函数中的各代码行
添加或删除函数
添加或删除全局变量和静态变量
将函数从旧文件映射到新文件时,可能会出现问题。要在编辑源文件时尽量减少此类问题:
不要更改函数的名称。
不要添加、删除参数或将其类型更改为函数。
不要添加或删除当前在栈中处于活动状态的函数中的局部变量,也不要更改其类型。
不要对模板声明或模板实例进行更改。只能修改 C++ 模板函数定义的主体。
如果进行上述任一更改,请重新生成整个程序,而不要使用修复并继续。
完成更改后,可以使用 fix 命令重新链接源文件,而不必重新编译整个程序。然后即可继续执行程序。
将所做更改保存到源文件。
在 dbx 提示符下键入 fix。
虽然修复的次数不受限制,但如果在一行中进行了多次修复,则需要考虑重新生成程序。fix 命令会更改内存中的程序映像,但不更改磁盘上的程序映像。当您进行更多的修复时,内存映像将不再与磁盘中的映像同步。
fix 命令不在可执行文件中进行更改,而只更改 .o 文件和内存映像。完成程序调试后,必须重新生成程序,以便将所做更改合并到可执行文件中。退出调试时,会出现一条消息,提醒您重新生成程序。
如果调用 fix 命令时使用 -a 之外的某个选项,并且不带文件名参数,则只修复当前修改的源文件。
调用 fix 时,会在执行编译行之前搜索编译时操作的文件的当前工作目录。由于从编译时到调试时文件系统结构发生变化,因此可能会在查找正确目录时出现问题。为了避免此问题,请使用 pathmap 命令创建从一个路径名到另一个路径名的映射。映射是应用于源文件路径和目标文件路径。
可以使用 cont 命令继续执行(请参见cont 命令)。
在恢复程序执行之前,需要注意下列决定更改效果的条件。
如果在已执行的函数中进行了更改,这些更改要在出现以下情况后才会生效:
重新运行程序
下次调用此函数
如果不只是对变量进行简单更改,应先使用 fix 命令,然后使用 run 命令。使用 run 命令比较快,因为它不会重新链接程序。
如果在尚未调用的函数中进行了更改,这些更改将在调用该函数时生效。
如果对当前正在执行的函数进行了更改,fix 命令的作用取决于更改相对于停止于函数的位置:
如果在已执行的代码中进行更改,则不会重新执行这些代码。通过从栈中弹出当前函数(请参见pop 命令)并从所更改函数的调用位置继续执行代码。您需要非常了解代码,以便确定函数是否具有无法撤消的副作用(例如,打开文件)。
如果在尚未执行的代码中进行更改,则会运行新代码。
如果已经对当前位于栈中的函数而不是停止于函数进行了更改,当前的函数调用不会使用已更改的代码。 当停止于函数返回时,会执行栈中的旧版本函数。
下列几种方法可以解决此问题:
在继续执行之前,手动修复数据结构(使用 assign 命令)。
使用 run 命令重新运行程序。
如果栈中已修改的函数内存在断点,则会将断点移动到函数的新版本中。如果执行了旧版本,则程序不会在这些函数中停止。
pop 命令或 fix 命令不会撤消对全局变量所做更改。要手动为全局变量赋适当值,请使用 assign 命令。(请参见assign 命令。)
下例显示如何修复简单的错误。在尝试解除 NULL 指针引用时,应用程序在第 6 行发生段故障。
dbx[1] list 1,$ 1 #include <stdio.h> 2 3 char *from = “ships”; 4 void copy(char *to) 5 { 6 while ((*to++ = *from++) != ’\0’); 7 *to = ’\0’; 8 } 9 10 main() 11 { 12 char buf[100]; 13 14 copy(0); 15 printf("%s\n", buf); 16 return 0; 17 } (dbx) run Running: testfix (process id 4842) signal SEGV (no mapping at the fault address) in copy at line 6 in file “testfix.cc” 6 while ((*to++ = *from++) != ’\0’); |
将第 14 行更改为 copy 至 buf(而不是 0)并保存文件,然后进行修复:
14 copy(buf); <=== modified line (dbx) fix fixing “testfix.cc” ..... pc moved to “testfix.cc”:6 stopped in copy at line 6 in file “testfix.cc” 6 while ((*to++ = *from++) != ’\0’) |
如果程序从这里继续,仍会发生段故障,因为仍然会将零指针推入栈中。可使用 pop 命令从调用栈中弹出一帧:
(dbx) pop stopped in main at line 14 in file “testfix.cc” 14 copy(buf); |
如果程序从这里继续,则程序会运行,但不会输出正确的值,因为全局变量 from 已经递增 1。程序将输出 hips 而不是 ships。可使用 assign 命令恢复全局变量,然后使用 cont 命令。现在,程序会输出正确的字符串:
(dbx) assign from = from-1 (dbx) cont ships |
有时,除了修改源文件以外,还可能需要修改头 (.h) 文件。为确保修改后的头文件可由包含它的程序中的所有源文件访问,必须将包含该头文件的所有源文件的列表作为一个参数提供给 fix 命令。如果不提供源文件列表,则只重新编译主(当前)源文件,并且只有它包含头文件的修改版本。程序中的其他源文件包含的仍是该头文件的原始版本。
无法直接修复 C++ 模板定义。而需要修复含有模板实例的文件。如果尚未更改模板定义文件,可以使用 -f 选项覆盖日期检查。