华唐产品 | 解决方案 | 技术支持 | 成功案例 | 下载中心 | 培训中心 | AEP理论中心
 >  首页 > 产品
华唐产品导航
  高频EDA设计仿真工具
  自动化软件测试工具
   
C/C++测试
   
  C++test
  Insure++
  CodeTEST
  VectorCAST
   
Java测试
Web应用测试
WEB服务测试
.NET测试
  软件开发工具
  仿真器
  数字建模系统
  协议一致性测试
  无线网桥
     

 

Insure++:自动化的运行时错误检测工具

C/C++开发人员面临着一个共同的问题:经过自己的测试还遗漏了许多错误。像内存破坏这样一些狡猾的问题可能在一个机器上躲过去了,但在另一个机器上发生了。要在发布软件产品之前发现并改正这类问题,你需要一个像X光机这样的工具暴露代码中隐藏的错误。你需要Insure++!

Insure++是一个针对C/C++应用的运行时错误自动检测工具,发现诸如内存破坏、内存泄露、指针错误和I/O错误等大量问题。Insure++彻底检查和测试代码,报告错误并指出其准确的位置。Insure++还能执行覆盖性分析,清楚地指出那些代码已经被测试过;以及帮助内存优化,实时显示程序如何使用内存。通过将Insure++集成到您的开发环境中,开发人员能够有效地减少调试时间,并生产更健壮、更优化和更可靠的软件。

Insure++突破性的技术

Insure++比其它工具能够检测更多的错误,因为其技术具有对代码更深刻的理解和挖掘出绝大多数更隐秘问题的能力。通过使用源码插装和运行时指针跟踪专利技术,Insure++掌握了被测软件的综合知识。在编译时,Insure++插入测试和分析代码,它建立一个有关程序中各种对象的数据库,然后在运行时通过检查数据值和内存引用验证对象的一致性和正确性。

使用这些独有的技术,包括变异测试技术等,Insure++能够从里到外的彻底检查和测试你的代码,精确定位错误的准确位置并给出详细的诊断信息。Insure++能够可视化实时内存操作,优化内存算法。Insure++还能执行覆盖性分析,清楚地指示那些代码已经测试过。将Insure++集成到您的开发环境中,能够极大地减少调试时间并有效地防止错误。

精确定位错误

软件开发中两件最让人头痛的事就是时间和调试错误,Insure++能够有效地提高你的调试效率并保证调试质量。

Insure++能够发现大量的编程和内存操作错误:

  • 由于读写越界造成的内存破坏,包括全局、局部、共享和动态分配的内存对象。
  • 对未初始化、NULL和“野”指针的操作。
  • 内存泄露
  • 动态内存分配和释放错误
  • 字符串操作错误。
  • 指针错误,如引用不相关的数据块。
  • 无效的指针操作。
  • 不兼容的变量说明。
  • printf/scanf中不匹配的变量类型。

Insure++真实检验每一次内存操作的有效性,包括静态(全局)和堆栈以及动态分配内存,而不是用陷阱去捕获内存操作错误这样的“统计”方法。因此,只有Insure++在发现一个错误时,能够报告相关对象名、含有错误的源代码行、关于错误的描述和执行轨迹。

Insure++还能发现函数/库接口引用错误:

  • 不匹配的参数类型或函数说明。
  • 调用中超出范围或无效参数。
  • 错误的调用返回。

关键技术和特性

  • 源码插装

    当需要对代码进行更深入和准确的分析,源码插装是最佳模式。该模式通过建立经过处理的执行程序提供更综合的内存和错误检查。Insure++利用源码插装(SCI,ParaSoft专利技术,#5581696)和运行时指针跟踪(RPT,ParaSoft专利技术,# 5842019)技术。Insure++对所有程序元素建立一个综合数据库,包括数据结构、内存使用、指针使用和接口等。Insure++对源文件进行语法分析、处理并转换成一个新的等价的源代码,保存在临时文件中,并将它提交给编译器生成目标码,然后链接成执行程序。这一切不改变任何原始文件,并且对用户是透明的。在编译时Insure++插入测试和分析指令。在运行时Insure++检查每个数据值和内存引用,并验证其一致性和正确性。在执行内存操作指令时,Insure++自动更新内存使用数据库。源码插装技术保证Insure++能够更深入地分析被测应用,因此比任何其它技术具有更高的准确性和完整性。
      
  • 运行时指针跟踪

    该技术使用一个有关指针和内存块的综合数据库检查对内存的每一次读写操作。当执行诸如malloc、new、delete和free等内存管理指令时,Insure++更新内存使用数据库。它保证Insure++能够以最高的精确性跟踪对所有内存段的内存存取操作。由于Insure++监视程序中所有指针和内存块,因此能够检测到其它工具所无法检测到的操作,如重写指向一个内存块的当前指针。其结果是不但能检测到一个内存泄露,还能告诉您何时、在那一行代码上造成了内存泄露。
      
  • 变异测试

    Insure++采用创新的变异测试方法,建立多个“等价”变形版本。这种技术能够进行更有效的错误检查,并能发现源代码中可能存在的任何多义性。 在检查时,Insure++对原始程序进行语法分析并转换成一个新的功能等价的源码,该等价代码作为一个临时文件提交给编译器,整个过程完全自动并且对用户透明。在检测时,“功能等价”的变形版本就象原代码一样运行。如果原始程序是正确的,那么运行就没有差异,否则意味着原始程序中存在一个错误必须被改正。通过这种方法,Insure++能够发现其它方法或工具所无法检测到的代码多义性。这对于C++程序尤其重要。例如,变异测试能够检测下列类型的错误:
    • 没有或坏的复制构造函数
    • 缺少或不准确的构造函数
    • 错误的代码初始化顺序
    • 指针操作问题
        
  • 错误发现

    Insure++真实检验每一次内存操作的有效性,包括静态(全局)和堆栈以及动态分配内存,能够发现内存破坏和内存泄露以及错误的分配和释放动态内存问题。Insure++还能检查第三方库和函数调用。使用Insure++,开发人员能够自动发现诸如字符串操作错误、使用未初始化指针、指针引用指向不相关的数据块、无效指针操作、不兼容变量说明和不匹配的变量类型等错误。
      
  • 功能模块

    Inuse:实时内存可视化工具,帮助您理解内存使用模式并优化其行为。
    TCA:测试覆盖性分析,告诉您那些代码已经被实际测试过。
    Threads++:支持多线程应用并检测线程中的错误。

Insure++能发现什么

Insure++能够发现C/C++应用中很多难以捉摸的错误:

  • 内存破坏
  • 指针错误
  • 内存泄露
  • 动态内存操作
  • 字符串使用
  • 未初始化内存
  • 未使用的变量
  • 数据表示问题
  • 不兼容的变量说明
  • I/O语句
  • 不匹配参数
  • 系统调用中的无效参数
  • 系统调用中的异常错误

[内存破坏]

Insure++检查所有类型的内存引用,包括静态(全局)、堆栈、共享内存以及动态分配内存。即使程序运行顺利并产生正确结果,许多软件产品仍可能存在内存引用错误。这些错误是埋藏在内存空间中的地雷,静静地躺着,直到被某次运行所触发,导致程序发生故障或者崩溃,却无法准确定位问题。

这是一种非常令人不快的错误,但通常它们都隐藏得很好。例如,考虑下列C程序。

1: /*
2: * File: hello.c
3: */
4: main(argc, argv)
5: int argc;
6: char *argv[];
7: {
8:     char str[16];
9:     int i;
10:
11:     str[0] = '';
12:     for(i=0; i<argc; i++) {
13:         strcat(str, argv[i]);
14:         if(i < (argc-1)) strcat(str, " ");
15:     }
16:     printf("You entered: %sn", str);
17: }

正常编译执行,并得到预期结果:

$ cc -o hello hello.c
$ hello world
You entered: hello world
$ hello cruel world
You entered: hello cruel world

如果这就是测试过程,您可能会认为程序工作正确,但事实上它有非常严重的内存破坏错误。如果您用Insure++一起编译,输入“hello cruel world”将产生错误,因为连接后的字符串长度超过16个字节(在第8行分配)。这里Insure++自动发现2个错误(第13和16行),而您无需额外做什么,只需简单运行程序即可!

**WRITE_OVERFLOW** [hello.c:13]
>> strcat(str, argv[i]);
检测到问题的源代码行

Writing overflows memory: str 问题描述和不正确的表达式

    bbbbbbbbbbbbbbbbbbbbbb
    |         16          | 2 |
多写了2个字节(b-目标块,w-写入)
    wwwwwwwwwwwwwwwwwwwwwwwwww

Writing (w): 0xf7fff8a8 thru 0xf7fff8b9(18 bytes) 内存块的位置
To block (b): 0xf7fff8a8 thru 0xf7fff8b7(16 bytes)
              str, declared at hello.c, 8

Stack trace where the error occurred: 引起问题的代码行
    strcat() (interface)
    main() hello.c, 13

**Memory corrupted. Program may crash!!**

**READ_OVERFLOW** [hello.c:16]
>> printf("You entered: %sn", str);

String is not null terminated within range: str
Reading : 0xf7fff8a8 thru 0xf7fff8b9 (18 bytes)
From block : 0xf7fff8a8 thru 0xf7fff8b7 (16 bytes)
             str, declared at hello.c, 8
main() hello.c, 16

You entered: hello cruel world

Insure++能够发现与越界读写对象边界有关的所有错误,不管它是静态分配的(如全局变量)、堆栈上的局部变量、动态分配的(用malloc)或是共享内存块。它还能检测跨块指针,即使这些内存块不是邻接的。

[指针滥用]

这是C/C++程序员经常遇到的问题。Insure++检测下面指针相关的问题:

  • 操作NULL指针。
  • 操作未初始化指针。
  • 操作没有实际指向有效数据的指针。
  • 试图比较不指向同一数据对象的指针。
  • 试图通过没有实际指向函数的函数指针调用函数。

下面程序使用动态内存分配,当用Insure++编译和运行它时,将报告一个"未初始化指针"错误在第22行, 因为在第一次循环时,变量str_so_far尚未被正确设置。

1: /*
2: * File: hello.c
3: */
4: #include <malloc.h>
5:
6: main(argc, argv)
7: int argc;
8: char *argv[];
9: {
10:     char *str, *str_so_far;
11:     int i, length;
12:
13:     length = 1; /* Include last NULL */
14:
15:     for(i=0; i<argc; i++) {
16:         length += strlen(argv[i])+1;
17:         str = malloc(length);
18:         /*
19:         * Copy the str built so far.
20:         */
21:         if(str_so_far != (char *)0)
22:             strcpy(str, str_so_far);
23:         else *str = '';
24:
25:         strcat(str, argv[i]);
26:         if(i < argc-1) strcat(str, " ");
27:         str_so_far = str;
28:     }
29:     printf("You entered: %sn", str);
30: }

[内存泄露]

如果一个动态分配的内存不能被释放,我们就称为“内存泄漏”或“内存丢失”。例如,某指针指向一个动态分配的内存块,程序执行中由于某种原因改变了该指针而没有保存,这时程序中就不再含有指向该内存块的任何指针,也无法再释放它,从而造成内存垃圾。

内存泄漏是最严重的内存错误之一。一个简单的例子是用下列参数运行上述程序:

hello this is a test

在执行到27行之前检查程序的状态:

  • 变量str_so_far指向字符串“hello”,它被分配作为前一个循环的结果。
  • 变量str指向扩展的字符串“hello this”,在当前循环中分配。

即两个指针指向不同的内存块:

str─→

hello this

string_so_far─→

hello

执行第27行以后,

string_so_far = string;

两个变量都指向同一块:

string_so_far, str─→

hello this

hello

上图形象地表示了指针的变化过程。初时两个变量分别指向不同的动态分配内存块;当执行第27行后,两个变量都指向较长的内存块,而没有任何指针指向较短的内存块。这块原先由str_so_far指示的内存块已经没有办法重新使用(但是永久分配的)。这就是“内存泄漏”。Insure++能够发现和报告这类内存泄漏错误。

**LEAK_ASSIGN** [hello.c:27]
>> str_so_far = str;
检测到问题的源代码行

Memory leaked due to reassignment: str 问题描述和问题表达式

Lost block: 0x0001fbb0 thru 0x0001fbb6 (7bytes) 泄漏内存块
block allocated at: malloc() (interface)
                    main() hello.c, 17

Stack trace where the error occurred: 引起问题的准确代码行位置
                main() hello.c, 27

注意:Insure++能够在内存泄漏发生时立刻检测到错误,并准确指出导致问题出现的源代码行,这是发现和修改内存泄漏的关键信息。编写程序时很容易引入难以捉摸的内存泄漏问题,但没有Insure++的帮助是很难发现并改正的。

Insure++能够检测和区分多种类型的内存泄漏,这样便于您快速修复错误:

  • LEAK_ASSIGN:由于指针再分配,引起指针原先指向的内存块被丢失了。
  • LEAK_FREE:释放一个含有指向其它内存块的指针的内存块。如果没有其它指针指向第二个内存块,它将永远地丢失。
  • LEAK_RETURN :一个函数返回一个指向已分配内存块的指针,但该返回值被调用代码忽略。
  • LEAK_SCOPE :一个函数含有指向某内存块的局部变量,但函数返回时没有将指针保存在全局变量或返回到调用代码中。

没有Insure++确实很难检测所有这些内存泄漏错误,因为它们可能需要连续运行很长时间才能导致系统崩溃。低层函数中的小内存块泄漏意味着要导致系统崩溃需要成千上万次调用函数。这种错误在内部测试中是很难发现的,只有在用户现场执行某些大型任务时才可能发现。

[动态内存操作]

使用动态分配内存经常会出问题。许多情况下,一个编程错误引起一系列内存破坏时程序仍能继续运行,有时并不会引起系统崩溃。一种常见的错误是重用已释放的指针,这种"悬着的指针"问题经常不被注意,因为许多平台和编译器允许这种特殊的行为。

Insure++能够检测许多动态内存错误:

  • 多次释放同一内存块。
  • 试图删除或释放静态分配的内存。
  • 释放栈内存(局部变量)
  • 传递给delete/free的指针没有指向一内存块的起点。
  • 用NULL或未初始化指针调用delete/free。
  • 传递没有意义或类型错误的参数给malloc/calloc/realloc/ free。
  • new [ ]和delete [ ]调用不匹配。
  • malloc/new/free/delete混合调用不匹配。
  • 过度的new/delete调用引起的问题。

Insure++能够准确捕捉到引起动态内存问题的根源。例如下列程序在34行的delete操作时有错误。

1: #include <iostream.h>
2: #include <stdlib.h>
3: #include <string.h>
4: #define SIZE 128
5:
6: class store {
7: public:
8:      store(int sz=24){size = sz;}
9:      ~store(){size=0; cout << "Deleting:" << ptr ;}
10:     int copy(char *src){
11:     int len;
12:
13:         len = strlen(src);
14:         if(len > size)return(0);
15:         strcpy(ptr,src);
16:         return(len);
17:     }
18: protected:
19:     char ptr[SIZE];
20:     int size;
21: };
22:
23: main()
24: {
25:     class store *first;
26:     int limit=5,i;
27:     char local[128];
28:
29:     first = new store[limit];
30:     for(i=0;i<limit;i++){
31:         sprintf(local,"Element %dn",i);
32:         first[i].copy(local);
33:     }
34:     delete first;
35: }

第29行的new操作分配了一个类对象数组first。该数组用delete释放,但delete没有用[ ],即delete没有为first数组的每个元素调用store类的析构函数。这种操作会导致C++程序的一系列问题。Insure++在程序执行时报告错误。

[bracket.C:34] **DELETE_MISMATCH**
>> delete first;

Inconsistent usage of delete operator: first

array deleted without [ ]
first, allocated at: main() bracket.c, 29

Stack trace where the error occured:
    main() bracket.c, 34

[字符串]

另一类常见的内存问题是由字符串函数操作引起的。标准C库函数是一个潜在的错误来源,因为它们很少检查操作对象的边界。Insure++能够检测大部分与字符串相关的问题,如越界写缓冲区和使用没有null终止符的字符串。

/*
* File: readovr2.c
*/
main()
{
char junk;
char b[8], c[8];
strncpy(b, "This is a test", sizeof(b));
memset(c, 0, sizeof(c));
printf("%sn", b);
return (0);
}

在这个例子中,虽然使用了strncpy以避免写出其缓冲区,但结果字符串b中并没有NULL结尾。Insure++在printf时检测到这个问题。

[未初始化内存]

Insure++对未初始化数据进行两种类型的检查:

(1)copy:缺省检查。通常Insure++对使用一个未初始化的值赋值一个变量提出警告。
(2)read:当你使用一个未初始化的变量,而且其上下文习惯的语法不能被纠正,例如表达式求值,Insure++报告一个错误。

从下面例子观察其区别:

1: /*
2: * File: readuni1.c
3: */
4: #include <stdio.h>
5:
6: int main()
7: {
8: struct rectangle {
9: int width;
10: int height;
11: };
12:
13: struct rectangle box;
14: int area;
15:
16: box.width = 5;
17: area = box.width*box.height;
18: printf("area = %dn", area);
19: return (0);
20: }

17行,box.height的值无效,因为还未赋值。Insure++检测到这个错误:READ_UNINIT_MEM(read)。假如你将17行改为:

17: area = box.height;

Insure++将针对17和18行报告错误类型:READ_UNINIT_MEM(copy)。

[未使用的变量]

Insure++能够检测到对软件应用的行为没有作用的变量,一种是从未使用的变量,一种是赋了值但从未使用过。大多数情况下,它们没有什么问题。但有时它们可能是某些程序逻辑错误的症状,因此有必要提示你。

[数据表示问题]

许多程序显式或隐式地假设所操作的变量数据类型。在许多平台上,指针和整数具有相同的字节数。编译时可以检测到部分问题,有些代码使用强制类型隐藏这些问题,如:

char *p;
int ip;
ip = (int)p;

在许多平台上,这类操作是有效的且不会导致问题。当这些代码移植到其它平台时,问题可能就会出现。例如从PC(整数16位,指针32位)到64位的DEC Alpha(整数32位,指针64位),上述代码就会有问题。Insure++能够报告丢失了信息的操作。

Insure++还能检测不同源文件之间变量说明的不一致性。例如,在一个文件中一个对象可以被说明成数组,而在另一文件中则为指针。Insure++能够报告变量大小的差别。

[不兼容的变量说明]

Insure++能够检测源文件之间不一致的变量说明。一个常见的例子是,在一个文件中一个对象被说明为数组:

    int myblock[128];

而在另一个文件中是指针:

    extern int *myblock;

Insure++还能报告不一致的大小,例如在一个文件中数组说明一个大小,而在另一文件中是另一大小。

[I/O语句错误]

printf和scanf函数族很容易出现错误或移植性问题。例如输入变量的数据类型为double,而scanf中的相应类型说明不同(如float),就会出现问题。

除检查printf和scanf之外,Insure++还能检查其它I/O语句中的错误。如:

foo(line)
char line[80];
{
    gets(line);
}

上述代码对于小于80个字符的输入都能工作的很好,但对于更长的输入就会失败。Insure++能够检查这类情况并报告错误。

[参数不匹配]

许多程序中,参数不正确的函数调用是一个常见但往往被忽视的问题。Insure++自动检测下面描述的各种参数不匹配问题。

  • 符号错误:同一类型的参数,但一有符号,另一无符号。如int和unsigned int。
  • 兼容类型:在一个机器上,两个参数具有相同的长度和不同的类型,如int和long,都是32位。在当前机器上不会有问题,但是有移植性问题。
  • 不兼容类型:数据类型根本不同或需要不同大小的内存。如int与long。
  • 别名错误:如用typedef为数据类型定义新名字,当使用不一致时,会产生错误。

[无效的系统调用参数]

调用库函数通常比较复杂,因为传递不正确的参数会导致不同的错误。调试这样的问题通常比修改自己的代码更困难,一般您对库函数的运行机能了解甚少。Insure++已经掌握了大量系统调用的知识,并能检查其数据类型和范围的正确性。如:

myrewind(fp)
FILE *fp;
{
    fseek(fp, (long)0, 3);
}

将导致错误,因为fseek的最后一个参数不在合法范围内。

[系统调用的异常错误]

检查系统调用的返回代码并正确处理可能出现的错误是非常困难的。用穷举法测试所有可能的组合几乎是不可能的。因此由于没有防备某些系统调用的失败,在用户现场可能会引起整个程序的意外失败。而且往往是一种随机的、无法重复的错误。

Insure++的特殊错误类RETURN_FAILURE能检测这种问题。Insure++对所有系统调用有一个特殊的错误检查代码,它检测每一次系统调用的每一个错误,并输出错误报告。Insure++能检测的错误包括:

  • malloc超出内存空间。
  • 文件不存在。
  • 不正确地设置许可标志。
  • 不正确的I/O函数使用。
  • 超过打开文件的限制。
  • 进程通讯和共享内存错误。
  • 非预期的"中断的系统调用"错误。

Insure++理解标准的UNIX和Windows系统调用以及X Window、Motif和许多其它流行的库。

结语

即使程序通过了编译并产生正确的结果,大量的软件产品也可能含有难以捉摸的错误,如内存引用和内存泄露。Insure++能够在开发中检测这些错误,并防止它们出现在用户现场。

Insure++已经成功地应用于几十万行的源代码。多进程应用、分布在几百个工作站上的程序、操作系统和编译器都已经通过了Insure++的验证。

 

数据清单

产品概述
产品特性
系统要求
演示
PDF文档

Insure++视频介绍
Insure++视频演示

n 加速开发软件过程
n 变异测试:一种自动错误检测的新方法
n Threads++:多线程错误检测
n TCA:测试覆盖性分析
n Inuse:优化内存使用
n Insure++:自动化的运行时错误检测工具

v C/C++解决方案

试用软件下载
  试用须知

查看Insure++原厂商美国Parasoft公司介绍

 

 

版权所有 深圳市华唐软件技术有限公司   粤ICP备06057501号