IBM Rational PurifyPlus是一组程序运行时的分析软件。她包括了程序性能瓶颈分析软件Quantify, 程序覆盖面分析软件PureCoverage,和本文的主角:程序运行错误分析软件Purify。Purify可以发现程序运行时的内存访问,内存泄漏和其他难以发现的问题。
同时她也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外她不仅可以检查C/C++,还可以对Java或.NET中的内存泄漏问题给出报告。
程序运行时的分析可以采用多种方法。Purify使用了具有专利的目标代码插入技术(OCI:Object Code Insertion)。她在程序的目标代码中插入了特殊的指令用来检查内存的状态和使用情况。这样做的好处是不需要修改源代码,只需要重新编译就可以对程序进行分析。
对于所有程序中使用的动态内存,Purify将它们按照状态进行归类。这可以由下图来说明(来自[DEV205]):
参见本文中以上给出的代码,在程序第5行执行后,str2处于黄色状态。当在第7行进行读的时候,系统就会报告一个访问未初始化内存错误(Uninitialized Memory Read)。因为只有在绿色状态下,内存才可以被合法访问。
为了检查数据越界错误(ABR,ABW),Purify还在每个分配的内存前后插入了红色区域。这样一来,超过边界的访问指令必定落在非法区域,从而触发ABR或者ABW错误报告。这里需要指出一点。访问未初始化内存错误UMR在某些情况下其实是合法的操作,例如内存拷贝。所以在分析报告时可以把UMR放到最后,或者干脆从结果中滤除。
这里简单介绍一下Purify在Windows和UNIX环境下的使用。
在Windows中,只要运行Purify,填入需要分析的程序及参数就可。Purify会自动插入检测代码并显示报告。报告的格式如下(来自[DEV205]):
蓝色的图标代表一些运行的信息,比如开始和结束等。黄色是Purify给出的警告。通常UMR会作为警告列出。红色则代表严重的错误。每一种相同的错误,尤其是在循环中的,会被集中在一起显示,并且标明发生的次数。由每个错误的详细信息,用户可以知道相应的内存地址和源代码的位置,并直接修改。另外用户还可以设置不同的滤过器,用来隐藏暂时不关心的消息。
在UNIX系统中,使用Purify需要重新编译程序。通常的做法是修改Makefile中的编译器变量。下面是用来编译本文中程序的Makefile:
CC=purify gcc all: pplusdemo pplusdemo: pplusdemo.o $(CC) -o pplusdemo pplusdemo.o -lstdc++ pplusdemo.o: pplusdemo.cpp $(CC) -g -c -w pplusdemo.cpp clean: -rm pplusdemo pplusdemo.o
下面是本文中的程序在Linux上Purify运行的结果
**** Purify instrumented ./pplusdemo (pid 30669) **** UMR: Uninitialized memory read: * This is occurring while in: strlen [rtlib.o] std::basic_ostream< char,std::char_traits< char>> & std::operator <<<std::char_traits< char>>(std::basic_ostream< char,std::char_traits< char>> &, char const *) [libstdc++.so.5] main [pplusdemo.cpp:7] __libc_start_main [libc.so.6] _start [crt1.o] * Reading 1 byte from 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] **** Purify instrumented ./pplusdemo (pid 30669) **** ABW: Array bounds write: * This is occurring while in: strcpy [rtlib.o] main [pplusdemo.cpp:8] __libc_start_main [libc.so.6] _start [crt1.o] * Writing 5 bytes to 0x80b45e0 in the heap (1 byte at 0x80b45e4 illegal). * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] **** Purify instrumented ./pplusdemo (pid 30669) **** ABR: Array bounds read: * This is occurring while in: strlen [rtlib.o] std::basic_ostream< char,std::char_traits< char>> & std::operator <<<std::char_traits< char>>(std::basic_ostream< char,std::char_traits< char>> &, char const *) [libstdc++.so.5] main [pplusdemo.cpp:9] __libc_start_main [libc.so.6] _start [crt1.o] * Reading 5 bytes from 0x80b45e0 in the heap (1 byte at 0x80b45e4 illegal). * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] **** Purify instrumented ./pplusdemo (pid 30669) **** FMM: Freeing mismatched memory: * This is occurring while in: operator delete( void *) [rtlib.o] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] * Attempting to free block at 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * This block of memory was obtained using an allocation routine which is not compatible with the routine by which it is being freed. **** Purify instrumented ./pplusdemo (pid 30669) **** FMR: Free memory read: * This is occurring while in: main [pplusdemo.cpp:11] __libc_start_main [libc.so.6] _start [crt1.o] * Reading 1 byte from 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a freed block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * There have been 0 frees since this block was freed from: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] **** Purify instrumented ./pplusdemo (pid 30669) **** FMW: Free memory write: * This is occurring while in: main [pplusdemo.cpp:11] __libc_start_main [libc.so.6] _start [crt1.o] * Writing 1 byte to 0x80b45e0 in the heap. * Address 0x80b45e0 is at the beginning of a freed block of 4 bytes. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * There have been 0 frees since this block was freed from: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] **** Purify instrumented ./pplusdemo (pid 30669) **** FUM: Freeing unallocated memory: * This is occurring while in: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:12] __libc_start_main [libc.so.6] _start [crt1.o] * Attempting to free block at 0x80b45e0 already freed. * This block was allocated from: malloc [rtlib.o] operator new( unsigned) [libstdc++.so.5] operator new []( unsigned) [libstdc++.so.5] main [pplusdemo.cpp:5] __libc_start_main [libc.so.6] _start [crt1.o] * There have been 1 frees since this block was freed from: free [rtlib.o] _ZdLpV [libstdc++.so.5] main [pplusdemo.cpp:10] __libc_start_main [libc.so.6] _start [crt1.o] **** Purify instrumented ./pplusdemo (pid 30669) **** Current file descriptors in use: 5 FIU: file descriptor 0: <stdin> FIU: file descriptor 1: <stdout> FIU: file descriptor 2: <stderr> FIU: file descriptor 26: <reserved for Purify internal use> FIU: file descriptor 27: <reserved for Purify internal use> **** Purify instrumented ./pplusdemo (pid 30669) **** Purify: Searching for all memory leaks... Memory leaked: 0 bytes (0%); potentially leaked: 0 bytes (0%) Purify Heap Analysis (combining suppressed and unsuppressed blocks) Blocks Bytes Leaked 0 0 Potentially Leaked 0 0 In-Use 0 0 ---------------------------------------- Total Allocated 0 0 **** Purify instrumented ./pplusdemo (pid 30669) **** * Program exited with status code 0. * 7 access errors, 7 total occurrences. * 0 bytes leaked. * 0 bytes potentially leaked. * Basic memory usage (including Purify overhead): 290012 code 152928 data/bss 6816 heap (peak use) 7800 stack
我们对照程序可以发现Purify查出了程序中所有的错误。对于每个错误,她不但给出了源代码的位置还指出这些内存最初分配的源代码位置。这对于查找问题提供了很大帮助。对于程序12行的解释,Purify将其认为是不匹配的内存释放(FMM: Freeing mismatched memory),因为她认为这样的释放方式不符合严格的规定。
Purify在其报告和文档中使用了很多的缩写,在此一并列出,以便读者在使用时参考(来自[Purify]):
这里简单介绍一下Purify提供的几个特性。有关这些特性的详细信息,请查阅文档[Purify]。
| 第1页: 1.内存问题的原因及分类 | 第2页: 2.Purify的原理及使用 |
| 第3页: 总结 |