Insure++

作者:Jim Nance
Insure++
  • 制造商:Parasoft Corporation

  • 电子邮件:info@parasoft.com

  • 网址:http://www.parasoft.com/

  • 价格:1,995 美元

  • 评论员:Jim Nance

在过去的四年里,我一直担任程序员,编写软件来查找集成电路设计中的错误。在此期间,我学到了很多关于追查错误的知识。理想情况下,您希望在将程序交付给客户之前找到并修复程序中的错误。值得注意的是,客户似乎是非常有创造力的人,他们可以找出以程序员从未预料到的方式使用(和破坏)程序的方法。

为了应对“有创造力的用户”问题,有一种程序可以接受源代码或目标文件,并生成一个在运行时分析自身错误的版本。这种程序的美妙之处在于,它允许您找到那些没有引起任何可见问题的错误,以便您可以在它们给任何人带来麻烦之前修复它们。我们发现在工作中这些程序非常宝贵。

Parasoft Corporation 生产了其中一种程序,他们以 Insure++ 的名称销售。我们最近在工作中评估了 Solaris 版本的 Insure++,我很高兴得知他们也有 Linux 版本。

Insure++
入门

几周后,我收到了 Parasoft 一位销售人员的电子邮件。她介绍了自己,并表示如果我有任何技术问题,可以让我与他们的程序员联系。她接着告诉我,他们的产品目前仅适用于 libc5,而不适用于 glibc,但他们正在努力提供 glibc 支持。她乐于助人的态度以及她了解 glibc (glibc 仅发布了两个月) 的事实都给我留下了深刻的印象。

几天后,Insure++ 送到了我家。 коробка 里有一个 CD-ROM、一本 10 页的小册子 (包含安装说明) 和一本 500 页的用户手册。我在五分钟内就将软件安装到了电脑上,即使我在他们的安装脚本中遇到了一个小问题。然后我打电话给 Parasoft 获取许可证密钥。我对接电话的销售人员印象深刻。他给了我密钥后,帮助我创建了一个 $HOME/.psrc 文件 (Insure++ 的启动文件),并带我浏览了 CD-ROM 上包含的示例之一。然后他向我展示了产品的几个功能,并给了我他的电话分机号,告诉我如果我有任何问题可以给他打电话。

了解 Insure++

Insure++ 的工作原理是获取您的 C 或 C++ 源代码,并创建一个新文件,其中包含您的代码以及一些自动生成的语句。这些语句的目的是分析您的程序如何使用内存、函数调用和变量,以便可以找到潜在的问题。Insure++ 的分析非常详细。它知道您何时使用未初始化的变量或内存。它知道何时不再有指向已分配内存的指针 (泄漏)。它知道您何时引用超出数组或结构的末尾。它知道您何时不正确地调用函数。它甚至知道更多。Insure++ 的分析也非常强大。它可以处理使用线程的程序和使用从 mmap 或 SysV 共享内存对象创建的文件中获取内存的程序。

Insure++ 也易于使用。您可以使用名为 insure 的程序编译和链接您的程序,而不是使用 gcc 编译程序。insure 编译器负责生成修改后的源文件,使用 gcc 编译它们,然后删除它们。它还执行编译时错误检查。程序编译完成后,您可以像往常一样运行它,并且它会像往常一样运行,只不过它会分析自身是否存在错误。在编译时或运行时发现的错误可以记录到文件、stderr 或 stdout,并且可以自定义错误消息,以便由 Emacs 等程序解释。默认行为是将错误消息发送到名为 Insra 的基于 X11 的程序。Insra 以易于理解的方式显示错误消息,并且它充当与您的编辑器的接口。Insra 还可以保存错误,允许您重新加载它们并在以后修复问题。

大多数程序都不是完全独立的。相反,它们使用来自系统库 (如 C 库或 X11 库) 的代码。为了完全检查您的程序是否存在错误,必须使用 insure 编译这些库。由于大多数人对重新编译像 X11 库这样的东西不感兴趣,因此 Insure++ 附带了几个系统库的预编译版本,包括 libc、libm、libX11、libXaw、libXt 和 libdlsym。如果您需要使用 Insure++ 未包含的库,并且您不能或不想自己重新编译它,您可以只链接标准库。Insure++ 仍然能够对库函数进行一些错误检查,但它不会像使用 insure 编译库那样详细或完整。

安装

我使用 Insure++ 做的第一件事是拿出 Sunsite 存档 CD 并开始编译程序。这证明是学习如何使用该产品的绝佳方法。我尝试的所有程序都证明很容易编译。在每种情况下,我只是在 make 命令行上覆盖了适当的变量,以使程序使用 insure 编译器和 -g 调试标志构建。例如,此命令适用于许多 X11 程序

make CC=insure CDEBUGOPTS=-g

当 make 调用第一个 insure 编译器时,编译器会启动 Insra GUI 以显示 Insure++ 在编译时检测到的任何问题。编译器所有后续的调用都与现有的 Insra 进程对话,因此您不会为运行的每个编译器进程弹出一个新窗口。

程序构建完成后,您只需像往常一样运行它。启动时,它会将有关它发现的任何错误的消息发送到 Insra。Insra 将显示发生错误时的源代码和堆栈跟踪。如果单击堆栈跟踪中的元素,Insra 将在相应的文件中启动您的编辑器,以便您可以修复问题。

当运行使用 insure 编译的程序时,您会注意到它们运行得 慢得多。我编写了一些测试程序来尝试量化这种现象。如果程序在一个紧密的循环中运行,不进行任何函数调用,也不访问任何数组,则它根本不会减速。如果程序的大部分时间都用于调用函数或访问数组,则它会减速大约 80 倍。大多数程序都会调用函数并访问数组,因此您可以预期会看到明显的减速。一个好的经验法则是程序通常花费的每秒用户时间将转换为使用 Insure++ 的一分钟用户时间。

我在使用 Insure++ 时遇到了一些小问题,例如当代码分布在多个目录中时,Insra 无法找到源代码。我总是能够通过查阅手册快速解决这些问题,该手册索引良好且编写良好。事实上,它写得很好,即使您不尝试解决问题,阅读起来也很有趣。

有一次,我以为我在 Insure++ 中发现了一个错误。我编写了以下测试程序

int main()
 {
        char *x = malloc(30);
        char z = x[1];
        char y = x[31];
        int zz;
        x[0] = 0;
        z += 3;
        free(x);
        zz = x[0];
        return z*z;
 }

Insure++ 检测到我在初始化 y 时访问了超出 x[] 数组末尾的位置,并且在我释放其内存后使用了 x[]。但是,它没有检测到我使用 x[] 的未初始化成员初始化了 z (即,x[1] 尚未初始化)。我感到兴奋,因为这个遗漏让我有机会尝试 Parasoft 的技术支持。我将测试程序放在我的一个网页上,以便我可以将其展示给 Parasoft 的人员。然后,我打电话给向我提供许可证密钥的销售人员,告诉他我发现了一个错误。他将我转接给他们的一位程序员。我给了程序员代码所在的 URL,他在与我通电话时查看了代码。他告诉我,默认情况下,Insure++ 不检查长度小于 4 字节的变量是否未初始化。然后他告诉我该在我的 .psrc 文件中输入什么来更改此设置。然后他给了我他的电子邮件地址和电话分机号,以便我可以联系他,如果我有任何其他问题。

我与 Parasoft 的三位不同人员进行了三次单独的互动,每次互动都非常积极。我以为为杂志撰写评论的人会得到 VIP 待遇,但我想知道其他人会受到怎样的待遇。我在工作中找到了一位以前工作时使用过 Insure++ 的人,我问他对 Parasoft 的技术支持有何看法。他告诉我,他也认为技术支持非常出色——看来我的特殊待遇也没什么特别的。

高级调试

在我熟悉了使用 Insure++ 的基础知识后,我决定尝试一些更高级的功能。我研究的第一个功能是 gdb 和使用 insure 编译的程序之间的交互。Insure 在向调试器隐藏其对源代码所做的修改方面做得很好。在大多数情况下,您无法分辨出有任何不同之处。不同之处之一是,每当程序检测到代码中存在问题时,它都会调用函数 _Insure_trap_error。通过在此函数中设置调试器断点,您可以使程序在 Insure++ 每次发现问题时停止。然后,您可以使用调试器检查程序的变量,并找出问题发生的原因。我在几个程序上尝试了此功能,发现它非常有用。您还可以从调试器调用其他函数,以获取有关程序内存中哪个位置分配给变量以及当前分配了多少内存的信息。

我归类为高级的另一个功能与使用自己的内存管理器的程序有关。Insure++ 了解 malloccallocfreenewdelete 和其他标准内存管理函数。这使其可以在您使用这些函数时进行深入的错误检查。程序拥有自己的内存管理器是很常见的,这些管理器分配大块内存并自行分配。为了对这些程序进行详细的错误检查,有必要向 Insure++ 介绍您的内存管理器。我自己没有这样做,但在工作中评估 Insure++ 的人员做了,他们表示这是一项相当简单的任务。甚至可以向 Insure++ 介绍与内存管理无关的函数,并使其在每次调用该函数时验证有关程序状态的任意事项。

示例
Insure++

图 1. Insra 窗口显示错误

Insure++

图 2. 内存泄漏位置的堆栈跟踪

到目前为止,我觉得我对 Insure++ 已经足够了解了,可以为这篇评论做一个示例。我选择了 sunsite.unc.edu 的 apps/circuits 目录中名为 pcb-1.3 的印刷电路布局程序。(有更新版本的 pcb 可用。) 该程序由略微超过 600KB 的源代码组成,因此它是一个相当复杂的程序,即使它绝非庞大。编译程序时,Insure++ 警告了几个可能的问题,但它们看起来都不太严重。然后我使用程序附带的示例数据启动了 pcb。我不知道如何使用 pcb 软件,所以我只是开始点击东西。(这通常是查找错误的好方法。) 在玩了几分钟程序后,我在 Insra 窗口中列出了六个内存泄漏和一个 NULL 指针评估 (参见图 1)。此时,我单击了程序 action.c 第 1352 行的错误,屏幕上弹出了图 2 所示的内容。这显示了在分配内存和泄漏内存时的堆栈跟踪。如果单击红色箭头,指示的源代码将在您喜欢的编辑器中打开。快速查看源代码显示了 Insure++ 为何抱怨。在 action.c 第 1290 行的函数 ActionSave 中,程序从用户那里获取文件名

1289 case F_LayoutAs:
1290  name = GetUserInput("enter filename:", "");
1291  if (name)
1292  SavePCB(name);
1293  break;

此文件名包含在由 malloc 设置的内存中,由变量 name 指向,name 是 ActionSave 中的局部变量。稍后,在第 1352 行,我们离开 ActionSave

1349  break;
1350  }
1351 }
1352 }
ActionSave 中没有代码来释放 name 指向的内存。当我们离开函数 ActionSave 并且局部变量 name 不再存在时,就无法再访问此内存了——它已泄漏。Insure++ 正在警告我们这种情况。

除了几个内存泄漏之外,Insure++ 还在程序中发现了一个与使用 NULL 指针相关的错误。它抱怨了这一行

469 return(&LineSortedByHighX[Layer][index2]);

我看不出这一行有什么问题,所以我使用我之前提到的 _Insure_trap_error 断点在调试器中重新启动了程序。当程序停止时,我检查了表达式,结果发现 LineSortedByHighX[Layer] 为 NULL。程序没有在此行崩溃的原因是,我们正在获取 LineSortedByHighX[Layer][index2] 的地址,而不是尝试取消引用它。据推测,在稍后的某个时间,某个函数将评估此地址并崩溃。Insure++ 使修复问题和防止崩溃成为可能。

结论

正如您可能看出的那样,我认为 Insure++ 是一款很棒的产品。它比我使用过的任何其他产品都能更好地发现编程错误,它在我最喜欢的操作系统下运行,并且 Parasoft 的技术支持非常出色。有一个问题——它很贵。您可以购买的最便宜的配置是 3 用户节点锁定许可证,每个节点的价格为 1,995 美元。我在家用电脑上做的任何事情都不值那么多钱,所以我不会为自己购买 Insure++ 的副本。我怀疑大多数为了乐趣而编程的人也不会购买副本。那么,谁应该购买 Insure++ 呢?从事专业软件开发的人员,或者更可能是公司。当您每月向程序员支付数千美元,并且处理错误会花费大量资金时,那么价格开始变得更具吸引力。当您考虑到 Insure++ 可能会使您更早地交付产品时,它开始变得非常有吸引力。如果您以软件开发为生,则需要此产品。Insure++ 也可以在多种 UNIX 和 Windows 版本下运行。事实上,任何处于编程时间就是金钱的环境中的人都应该考虑评估 Insure++. 这是一款出色的产品。

Insure++
Jim Nance (jlnance@avanticorp.com) 在北卡罗来纳州研究三角园的 Avant! Corporation 验证产品部门担任软件工程师。自内核版本 0.12 以来,他一直在使用 Linux。除了破解 Linux 之外,他还喜欢阅读谋杀悬疑小说、被他 3 岁的女儿指使以及与他的主日学班级共度时光。
加载 Disqus 评论