Rational Purify 使用及分析实例

蔡 林, IBM 中国软件开发中心软件工程师

简介: 本文介绍了 IBM Rational Purify的基本概念和在不同操作系统中使用Purify对C/C++源程序中存在的内存问题进行勘察和分析,并且提供了有关的实例以便读者在实际操作中作为参考。

发布日期: 2006 年 2 月 23 日 
级别: 初级 
访问情况 : 8989 次浏览 
评论:  (查看 | 添加评论 - 登录)

 平均分 (11个评分)
为本文评分

简介

本文介绍了IBM Rational Purify的基本概念和在不同操作系统中使用Purify对C/C++源程序中存在的内存问题进行勘察和分析,并且提供了有关的实例以便读者在实际操作中作为参考。

回页首

1.内存问题的原因及分类

在C/C++程序中,有关内存使用的问题是最难发现和解决的。这些问题可能导致程序莫名其妙地停止、崩溃,或者不断消耗内存直至资源耗尽。由于C/C++语言本身的特质和历史原因,程序员使用内存需要注意的事项较多,而且语言本身也不提供类似Java的垃圾清理机制。编程人员使用一定的工具来查找和调试内存相关问题是十分必要的。

总的说来,与内存有关的问题可以分成两类:内存访问错误和内存使用错误。内存访问错误包括错误地读取内存和错误地写内存。错误地读取内存可能让你的模块返回意想不到的结果,从而导致后续的模块运行异常。错误地写内存可能导致系统崩溃。内存使用方面的错误主要是指申请的内存没有正确释放,从而使程序运行逐渐减慢,直至停止。这方面的错误由于表现比较慢很难被人工察觉。程序也许运行了很久才会耗净资源,发生问题。

1.1 内存解剖

一个典型的C++内存布局如下图所示:

自底向上,内存中依次存放着只读的程序代码和数据,全局变量和静态变量,堆中的动态申请变量和堆栈中的自动变量。自动变量就是在函数内声明的局部变量。当函数被调用时,它们被压入栈;当函数返回时,它们就要被弹出堆栈。堆栈的使用基本上由系统控制,用户一般不会直接对其进行控制,所以堆栈的使用还是相对安全的。动态内存是一柄双刃剑:它可以提供程序员更灵活的内存使用方法,而且有些算法没有动态内存会很难实现;但是动态内存往往是内存问题存在的沃土。

1.2 内存访问错误

相对用户使用的语言,动态内存的申请一般由malloc/new来完成,释放由free/delete完成。基本的原则可以总结为:一对一,不混用。也就是说一个malloc必须对应一且唯一的free;new对应一且唯一的delete; malloc不能和delete, new不能和free对应。另外在C++中要注意delete和delete[]的区别。delete用来释放单元变量,delete[]用来释放数组等集聚变量。有关这方面的详细信息可以参考[C++Adv]。

我们可以将内存访问错误大致分成以下几类:数组越界读或写、访问未初始化内存、访问已经释放的内存和重复释放内存或释放非法内存。

下面的代码集中显示了上述问题的典型例子:

1   #include <iostream>
2   using namespace std;
3   int main(){
4      char* str1="four";
5      char* str2=new char[4]; //not enough space
6      char* str3=str2;
7      cout<<str2<<endl;    //UMR
8      strcpy(str2,str1);   //ABW
9      cout<<str2<<endl;  //ABR
10     delete str2;
11     str2[0]+=2;    //FMR and FMW
12     delete str3; //FFM
13   }

由以上的程序,我们可以看到:在第5行分配内存时,忽略了字符串终止符"\0"所占空间导致了第8行的数组越界写(Array Bounds Write)和第9行的数组越界读(Array Bounds Read); 在第7行,打印尚未赋值的str2将产生访问未初始化内存错误(Uninitialized Memory Read);在第11行使用已经释放的变量将导致释放内存读和写错误(Freed Memory Read and Freed Memory Write);最后由于str3和str2所指的是同一片内存,第12行又一次释放了已经被释放的空间 (Free Freed Memory)。

这个包含许多错误的程序可以编译连接,而且可以在很多平台上运行。但是这些错误就像定时炸弹,会在特殊配置下触发,造成不可预见的错误。这就是内存错误难以发现的一个主要原因。

1.3 内存使用错误

内存使用错误主要是指内存泄漏,也就是指申请的动态内存没有被正确地释放,或者是没有指针可以访问这些内存。这些小的被人遗忘的内存块占据了一定的地址空间。当系统压力增大时,这些越来越多的小块将最终导致系统内存耗尽。内存使用错误比内存访问错误更加难以发现。这主要有两点原因:第一,内存使用错误是"慢性病",它的症状可能不会在少数、短时间的运行中体现;第二,内存使用错误是因为"不做为"(忘记释放内存)而不是"做错"造成的。这样由于忽略造成的错误在检查局部代码时很难发现,尤其是当系统相当复杂的时候。

回页首

2.Purify的原理及使用

IBM Rational PurifyPlus是一组程序运行时的分析软件。她包括了程序性能瓶颈分析软件Quantify, 程序覆盖面分析软件PureCoverage,和本文的主角:程序运行错误分析软件Purify。Purify可以发现程序运行时的内存访问,内存泄漏和其他难以发现的问题。

同时她也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外她不仅可以检查C/C++,还可以对Java或.NET中的内存泄漏问题给出报告。

2.1 Purify的原理

程序运行时的分析可以采用多种方法。Purify使用了具有专利的目标代码插入技术(OCI:Object Code Insertion)。她在程序的目标代码中插入了特殊的指令用来检查内存的状态和使用情况。这样做的好处是不需要修改源代码,只需要重新编译就可以对程序进行分析。

对于所有程序中使用的动态内存,Purify将它们按照状态进行归类。这可以由下图来说明(来自[DEV205]):

参见本文中以上给出的代码,在程序第5行执行后,str2处于黄色状态。当在第7行进行读的时候,系统就会报告一个访问未初始化内存错误(Uninitialized Memory Read)。因为只有在绿色状态下,内存才可以被合法访问。

为了检查数据越界错误(ABR,ABW),Purify还在每个分配的内存前后插入了红色区域。这样一来,超过边界的访问指令必定落在非法区域,从而触发ABR或者ABW错误报告。这里需要指出一点。访问未初始化内存错误UMR在某些情况下其实是合法的操作,例如内存拷贝。所以在分析报告时可以把UMR放到最后,或者干脆从结果中滤除。

2.2 Purify的使用

这里简单介绍一下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

首先运行Purify安装目录下的purifyplus_setup.sh来设置环境变量,然后运行make重新编译程序。需要指出的是,程序必须编译成调试版本。在gcc中,也就是必须使用"-g"选项。在重新编译的程序运行结束后,Purify会打印出一个分析报告。它的格式和含义与Windows平台大同小异。

下面是本文中的程序在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 isnot 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        BytesLeaked          0            0Potentially Leaked          0            0In-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 code152928 data/bss6816 heap (peak use)7800 stack

我们对照程序可以发现Purify查出了程序中所有的错误。对于每个错误,她不但给出了源代码的位置还指出这些内存最初分配的源代码位置。这对于查找问题提供了很大帮助。对于程序12行的解释,Purify将其认为是不匹配的内存释放(FMM: Freeing mismatched memory),因为她认为这样的释放方式不符合严格的规定。

Purify在其报告和文档中使用了很多的缩写,在此一并列出,以便读者在使用时参考(来自[Purify]):

2.3 Purify的一些特性

这里简单介绍一下Purify提供的几个特性。有关这些特性的详细信息,请查阅文档[Purify]。

  • 观察点(Watchpoint):通过在程序或者调试器中调用Purify 提供的观察点函数,Purify可以报告有关被观察对象的读写或其他操作。
  • 与Rational其他产品的集成:在Puify的用户界面中可以方便地进入ClearCase和ClearQuest。Purify还可以和PureCoverage同时使用,对程序进行分析。
  • Purify的定制:无论是Purify报告中的消息,还是界面中的元素,都可以进行一定程度的定制。另外通过修改配置文件和调用Purify API,用户还可以自动记录运行日志,发送电子邮件等。
  • Purify提供的API:为了更好地把Purify融合到自动化测试的体系中,Purify提供了一系列的公开函数。用户完全可以通过脚本的方式自动运行,记录,和分析Purify。

回页首

3.总结

当使用C/C++进行开发时,采用良好的一致的编程规范是防止内存问题第一道也是最重要的措施。在此前提下,IBM Rational Purify作为一种运行时分析软件可以很好地帮助您发现忽略的内存问题,或成为软件自动测试中的一个重要组成部分。

回页首

4.参考资料

[C++Adv]
[DEV205] Essentials of Rational PurifyPlus
[Purify] IBM Rational PurifyPlus for Linux and UNIX Documentation

关于作者

蔡林,IBM 中国软件开发中心软件工程师,2004年获得美国Baylor University计算机系硕士学位,同年加入IBM 中国软件开发中心,从事Rational ClearQuest G11N的开发工作。

Rational Purify 使用及分析实例相关推荐

  1. Rational Purify 使用及分析实例(转载)

    本文介绍了 IBM Rational Purify的基本概念和在不同操作系统中使用Purify对C/C++源程序中存在的内存问题进行勘察和分析,并且提供了有关的实例以便读者在实际操作中作为参考. 简介 ...

  2. JVM指令分析实例四(数组、switch)

    本篇为<JVM指令分析实例>的第四篇,相关实例均使用Oracle JDK 1.8编译,并使用javap生成字节码指令清单. 前几篇传送门: JVM指令分析实例一(常量.局部变量.for循环 ...

  3. 《简明电路分析》——1.6节简单电路分析实例

    本节书摘来自华章社区<简明电路分析>一书中的第1章,第1.6节简单电路分析实例,作者钟洪声 吴 涛 孙利佳,更多章节内容可以访问云栖社区"华章社区"公众号查看 1.6 ...

  4. flac3d命令流实例大全_ANSYS APDL 疲劳分析实例附命令流

    图1为一个角型板,边界条件:底端固定,顶端承受0-30kpa的压力,计算指定位置的疲劳寿命,表1给出了材料的S-N数据:图1给出了疲劳分析模型的几何尺寸. 表1 材料的N-S数据 N 100 200 ...

  5. c语言条件编译的例子,C语言条件编译分析实例

    C语言条件编译分析实例 1.利用#ifdef / #endif 将程序功能模块包括进去,以向某用户提供该功能. 在程序首部定义#define HNLD: #ifdef HNLD include&quo ...

  6. python爬取天气预报源代码_python抓取天气并分析 实例源码

    [实例简介] Python代码抓取获取天气预报信息源码讲解.这是一个用Python编写抓取天气预报的代码示例,用python写天气查询软件程序很简单.这段代码可以获取当地的天气和.任意城市的天气预报, ...

  7. PHP5异常处理,PHP5异常处理分析实例

    PHP5异常处理分析实例 导语:PHP 5 添加了类似于其它语言的异常处理模块.下面的是百分网小编为大家搜集的用实例分析PHP5异常处理实例,希望对你能有所帮助. <?php /** * ■㈠P ...

  8. MPEG2-PS格式分析实例

    一. PS 包头格式 1.PS 包头格式 包起始码字段  pack_start_code 值为'0000 0000 0000 00000000 0001 1011 1010' (0x000001BA) ...

  9. rational rose rational purify rational quanlity 安装

    1.下载Rational suite,包括:b-irscd1, b-irscd2, b-irscd3. 2.下载Rose破解包rose2003crack(包括flexlm,license.dat,lm ...

最新文章

  1. bash shell的一些基本概念
  2. window.onload和jquery中$(function(){ })的区别
  3. Python中断多重循环的几种思路
  4. 分享一张理解数据库inner join,left join,right join,full join的图
  5. 11行Python代码,发现了室友U盘的惊天秘密。
  6. mysql bin log日志
  7. GAN能生成3D图像啦!朱俊彦团队公布最新研究成果
  8. Attribute 和 Parameter 的区别
  9. 29.FFmpeg+OpenGLES+OpenSLES播放器实现(三.FFmpeg配置和编译脚本)
  10. matlab除法不对,matlab中除法的使用,错误使用 / 矩阵维度必须一致
  11. 基于51单片机毕业设计 开题选题
  12. DBN(深度置信网络)
  13. web字体文件过大优化方案
  14. 山东大学2021算法期末
  15. 【2】switch语句练习1:显示法定格式的日期
  16. T7983 大芳的逆行板载
  17. 高通推出骁龙888处理器,移动办公使用电子邮箱更便捷!
  18. lyse-building otp applications
  19. php 图片压缩旋转,移动端图片上传旋转、压缩问题的解决方案
  20. Python将图片转换为字符画

热门文章

  1. tp在计算机软件方面是什么意思,计算机软件及应用TP服务器基础知识.pptx
  2. 8051AVR PIC 单片机
  3. c++学习之同名隐藏
  4. 改善服务器响应用时,服务器缓存论文,关于改善网络服务器响应速度建议相关参考文献资料-免费论文范文...
  5. django5数据库查询操作 及字段说明
  6. 期货界传奇苏一刀:初中学历,10万进场,15年赚了一个亿
  7. mysql table exists_mysql的CREATE TABLE IF NOT EXISTS 的使用方法
  8. java 打印byte数组内容_java中打印byte数组
  9. 正态检验 (Normality Test)——常见方法汇总与简述
  10. 怎样成为让人无法拒绝的人?