Sun Studio 12:使用 dbx 调试程序

第 10 章 修复并继续

可以使用 fix 命令快速重新编译经过编辑的本地源代码,而不必停止调试进程。但不能使用 fix 命令重新编译 Java 代码。


注 –

在 Linux 平台上不能使用 fix 命令。


本章由以下部分组成:

使用修复并继续

使用修复并继续功能,可以修改和重新编译本地源文件并继续执行,而无需重新生成整个程序。通过更新 .o 文件并将其拼接到程序中,即可不必重新进行链接。

使用修复并继续的优点是:

修复并继续如何操作

使用 fix 命令之前,必须在编辑窗口中编辑源代码。(有关修改代码的方式,请参见使用修复并继续修改源码。)保存更改后,请键入 fix。有关 fix 命令的信息,请参见fix 命令

调用 fix 命令后,dbx 会使用适当的编译器选项来调用编译器。编译已修改的文件并创建共享对象 (.so) 文件。通过比较新旧文件进行语义测试。

使用运行时链接程序将新的目标文件链接到正在运行的进程。如果正在修复位于栈顶的函数,则新的停止于函数便是新函数中同一行的开头。旧文件中的所有断点均被移到新文件中。

无论文件在编译时是否使用了调试信息,都可以对其使用修复并继续功能,但对于在最初编译时未使用调试信息的文件,fix 命令和 cont 命令有一些功能性限制。有关更多信息,请参见fix 命令中有关 -g 选项的说明。

可以修复共享目标 (.so) 文件,但必须在特殊模式下打开这些文件。在调用 dlopen 函数时,可以使用 RTLD_NOW|RTLD_GLOBAL RTLD_LAZY|RTLD_GLOBAL

Sun Studio C 和 C++ 编译器的预编译头功能要求重新编译时编译器选项相同。由于 fix 命令会对编译器选项稍加更改,因此请勿对使用预编译头创建的目标文件使用 fix 命令。

使用修复并继续修改源码

使用修复并继续时,可以通过以下方式修改源码:

将函数从旧文件映射到新文件时,可能会出现问题。要在编辑源文件时尽量减少此类问题:

如果进行上述任一更改,请重新生成整个程序,而不要使用修复并继续。

修复程序

完成更改后,可以使用 fix 命令重新链接源文件,而不必重新编译整个程序。然后即可继续执行程序。

Procedure修复文件

  1. 将所作更改保存到源文件。

  2. dbx 提示符下键入 fix

    虽然修复的次数不受限制,但如果在一行中进行了多次修复,则需要考虑重新生成程序。fix 命令会更改内存中的程序映像,但不更改磁盘上的程序映像。当您进行更多的修复时,内存映像将不再与磁盘中的映像同步。

    fix 命令不在可执行文件中进行更改,而只更改 .o 文件和内存映像。完成程序调试后,必须重新生成程序,以便将所做更改合并到可执行文件中。退出调试时,会出现一条消息,提醒您重新生成程序。

    如果调用 fix 命令时使用 -a 之外的某个选项,并且不带文件名参数,则只修复当前修改的源文件。

    调用 fix 时,会在执行编译行之前搜索编译时操作的文件的当前工作目录。由于从编译时到调试时文件系统结构发生变化,因此可能会在查找正确目录时出现问题。为了避免此问题,请使用 pathmap 命令创建从一个路径名到另一个路径名的映射。映射是应用于源文件路径和目标文件路径。

修复后继续

可以使用 cont 命令继续执行(请参见cont 命令)。

在恢复程序执行之前,需要注意下列决定更改效果的条件。

更改已执行的函数

如果在已执行的函数中进行了更改,这些更改要在出现以下情况后才会生效:

如果不只是对变量进行简单更改,应先使用 fix 命令,然后使用 run 命令。使用 run 命令比较快,因为它不会重新链接程序。

更改一个尚未被调用的函数

如果在尚未调用的函数中进行了更改,这些更改将在调用该函数时生效。

更改当前正在执行的函数

如果对当前正在执行的函数进行了更改,fix 命令的作用取决于更改相对于停止于函数的位置:

更改当前位于栈中的函数

如果已经对当前位于栈中的函数而不是停止于函数进行了更改,当前的函数调用不会使用已更改的代码。 当停止于函数返回时,会执行栈中的旧版本函数。

下列几种方法可以解决此问题:

如果栈中已修改的函数内存在断点,则会将断点移动到函数的新版本中。如果执行了旧版本,则程序不会在这些函数中停止。

修复后更改变量

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 行更改为 copybuf(而不是 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++ 模板定义

无法直接修复 C++ 模板定义。而需要修复含有模板实例的文件。如果尚未更改模板定义文件,可以使用 -f 选项覆盖日期检查。对于使用 Sun Studio C 编译器编译的程序,dbx 在缺省系统信息库目录 SunWS_cache 中查找模板定义 .o 文件。dbx 中的 fix 命令不支持 -ptr 编译器选项。