贝贝花花包包店,精品555皮具,钱夹,皮夹

字体: | 推荐给好友 上一篇 | 下一篇

C/C++内存问题检查利器——Purify

发布: 2008-4-30 22:46 | 作者: haoel | 来源: 网络转载 | 查看: 385次

 
六、             控制Purify的输出
1、产生ASCII文本文件
 
在默认情况下,Purify会显示出一个图形窗口来报告信息。当然,如果你的环境所限,你不想Purify出现图形界面,只是生成文本文件来报告,能过设置Purify的参数,你可以很容易做到这一点。
 
在程序编译时,你只需简单的调置Purify的编译参数 –windows=no 即可做到,如:
 
> purify –windows=no gcc –g –o hello hello.c
 
Purify会把其报告信息写到标准错误设备上,在文本方式下,Purify就不报告同种错误出现在个数,而只是报告的信息了。
 
我们可以使用两种方式让Purify的信息输出到文本文件中。
 
第一种是使用操作系统的重定向功能,如:
在csh下: % a.out.pure >& a.out.messages
在sh和ksh下: $ a.out.pure 2> a.out.messages
 
第二种是指定Purify的日志文件参数,如:
-log-file=.plog
 
下面,是一个Purify生成的ASCII文本文件的样子:
> ./hello
****  Purify instrumented hello (pid 25698 at Wed Dec 10 22:29:33 2003)
  * Purify 2003.06.00 Solaris 2 (32-bit) Copyright (C) 1992-2002 Rational Software Corp.  All rights reserved. 
  * For contact information type: "purify -help"
  * Options settings: -follow-child-processes=yes -purify -windows=no \
    -purify-home=/usr/rational/releases/purify.sol.2003.06.00 \
    -gcc3_path=/usr/local/bin/gcc \
    -cache-dir=/usr/rational/releases/purify.sol.2003.06.00/cache \
    -demangle_program=/usr/local/bin/c++filt
  * License successfully checked out.
  * Command-line: ./hello
 
****  Purify instrumented hello (pid 25698)  ****
ABR: Array bounds read:
  * This is occurring while in:
        strlen         [rtlib.o]
        _doprnt        [libc.so.1]
        printf         [libc.so.1]
        main           [hello.c:11]
        _start         [crt1.o]
  * Reading 13 bytes from 0x8ea08 in the heap (1 byte at 0x8ea14 illegal).
  * Address 0x8ea08 is at the beginning of a malloc'd block of 12 bytes.
  * This block was allocated from:
        malloc         [rtlib.o]
        main           [hello.c:8]
        _start         [crt1.o]
Hello, World
 
****  Purify instrumented hello (pid 25698)  ****
Current file descriptors in use: 5
FIU: file descriptor 0:
FIU: file descriptor 1:
FIU: file descriptor 2:
FIU: file descriptor 26:
FIU: file descriptor 27:
 
****  Purify instrumented hello (pid 25698)  ****
Purify: Searching for all memory leaks...
 
Memory leaked: 12 bytes (100%); potentially leaked: 0 bytes (0%)
 
MLK: 12 bytes leaked at 0x8ea08
  * This memory was allocated from:
        malloc         [rtlib.o]
        main           [hello.c:8]
        _start         [crt1.o]
 
Purify Heap Analysis (combining suppressed and unsuppressed blocks)
                         Blocks        Bytes
              Leaked          1           12
  Potentially Leaked          0            0
              In-Use          0            0
  ----------------------------------------
     Total Allocated          1           12
 
****  Purify instrumented hello (pid 25698)  ****
  * Program exited with status code 13.
  * 1 access error, 1 total occurrence.
  * 12 bytes leaked.
  * 0 bytes potentially leaked.
  * Basic memory usage (including Purify overhead):
    351348 code
    101724 data/bss
    8192 heap (peak use)
    1272 stack
  * Shared library memory usage (including Purify overhead):
    992 libpure_solaris2_init.so.1 (shared code)
    280 libpure_solaris2_init.so.1 (private data)
    1079516 libc.so.1_pure_p3_c0_111202132_58_32_1158500S (shared code)
    31404 libc.so.1_pure_p3_c0_111202132_58_32_1158500S (private data)
    2324 libdl.so.1_pure_p3_c0_111202132_58_32_4624S (shared code)
    4 libdl.so.1_pure_p3_c0_111202132_58_32_4624S (private data)
    14048 libinternal_stubs.so.1 (shared code)
    940 libinternal_stubs.so.1 (private data)
 
 
 
2、产生Purify自己的文件
 
通过查看ASCII文本文件,我们发现其很不容易查看,特别是当错误很多时,而用在文件中没有源代码,看起来就是不如图形界面的好。但是我们为了把Purify的报告信息通过电子邮件传送给别人查看时,文件和图形界面兼得,我们可以使用Purify自己的文件,叫Purify View文件。我们可以使用Purify的图形界面打开这个文件,而来在图形化的窗口下查看。
 
我们可以有两种方式得到这个文件。一种是在Purify的图形界面的菜单中点击“File -> Save as”来生成。第二种方法是使用Purify的 -view-file=.pv 参数来设置Purify View文件。
 
而要打开这个文件时,要么简单地在Purify的菜单中选取“Open”菜单,要么使用这样的命令:
       % purify –view .pv
 
3、自动发送邮件
 
使用Purify的-mail-to-user参数可以方便地让Purify自动发送报告邮件。如:
 
% purify -mail-to-user=chris  gcc ...
% purify -mail-to-user=chris,pat  gcc ...
% purify -mail-to-user=devgrp  gcc ...
 
在默认情况下,只要你设置了这个参数,Purify是不会打开图形界面窗口的,如果你要Purify打开图形窗口,那么你就一同使用 –windows=yes 参数。
 
4、输出自己的信息
 
如果你想在Purify中输出自己的信息,你可以在你的程序中使用Purify的API函数:
l         purify_printf(const char *fmt, ...)  使用这个函数可以在Purify的图形界面,文件文件中输出你的自己的信息。
l         purify_logfile_printf(const char *fmt, ...)  使用这个函数可以在Purify的ASCII文本文件中输出你自己的信息。
l         purify_printf_with_call_chain(const char *fmt, ...) 使用这个函数可以在Purify的输出的同时,打印出函数调用栈的信息。这个函数和purify_printf很类似。
注意,以上三个函数和标准C中的printf函数几乎是一样的,不过,这几个函数并不支持像printf函数中的所有%的格式,它仅支持:%d, %u, %n,%s, %c, %e, %f, 和 %g 这几种格式,并且就 %e %f %g 而且,并不支持其精度定义。
七、             Purify的退出码
像UNIX下的软件,一般都会提供和别的应用程序的接口,像上面的生成文本文件,也是给别的应用程序提供接口的一种方式。这里,我们所要讲述的是Purify的退出码,我们知道程序都有退出码,以提供给别的程序或操作系统自己运行的信息。被Purify编译过的程序,你可以通过指定-exit-status参数来告诉Purify是否用Purify的退出码,如果这个参数值为yes,那么表示使用Purify的退出码,如果值为no则表示使用程序内的退出码。
 
如果我们这样设置:-exit-status=yes,那么Purify的退出码是这样定义的:
 
内存错误种类
退出码(按位或)
内存存取错误
0x40
内存泄露
0x20
潜在内存泄露
0x10
 
通过上表,我们可以知道,当-exit-status参数被打开后,程序的退出码被Purify完全接管,如果程序中有内存错误,那么退出码所对应的位就会被置为1,这样,我们可以用别的程序来调用Purify所编译出来的程序,并根据其退出码作相应的处理。
 
 
八、             PurifyShell的集成
你可以在UNIX的Shell环境中使用Purify的一些参数和信息,Purify为Shell提供了一些通配符之类的东西,只要你使用 –run-at-exit参数。例如你有一个Shell程序想把Purify生的文件拷贝到别的目录中,或是你想根据Purify的报告中是否有内存错误进行下一步的行动。
 
下面有两个表格,说明了一些Purify和Shell交互的参数:
 
有关内存出错的信息:
通配字符串
含义
%z
指明是否有内存错误或内存泄露。其值是“true”或“false”
%x
程序的退出状态(如果是0,表示程序没有调用exit函数)
%e
程序中内存访问错误的个数。
%E
程序中错误总数。
%l
内存泄露的字节数。
%L
潜在内存泄露的字节数。
 
有关程序运行的信息:
通配字符串
含义
%V
运行程序的全路径(“/”被替换成了“_”)
%v
程序的名称
%p
程序的进行ID
 
 
在使用Purify过程中,有两种方法可以传递Purify的参数,一种就是在命令行上指明。另外一种是设置一个和Purify相关的环境变量:PURIFYOPTIONS。现在,我通过这个环境变量要举一个例子,以说明上面表格中的参数在使用中的情况:
 
例如,如果我们这样这置环境变量:(在C-Shell中)
 
setenv PURIFYOPTIONS '-run-at-exit="if %z ; then \
echo \"%v: %e errors, %l+%L bytes leaked.\" ; fi"'
 
 
当我们运行被Purify编译过的程序后,会出现以下结果:
 
hello: 2 errors, 1+10 bytes leaked.
 
我们可以看到,由于hello程序出错了,所以%z为“true”,所以Purify执行echo命令,其中,%v表示了程序名(hello),%e表示了错误的个数(2),%l表示了内存泄露的字节数(1),%L表示了程序中有潜在可能的内存泄露字节数(12)。
 
让我们再来看两个例子:
 
示例一:
指定Purify的参数为: -log-file=./%v.plog
 
示例二:
指定Purify的参数为: -view-file=/home/hchen/%V.pv
 
总这,这些有“%”的变量,都是Purify提供给操作系统Shell的,以供Shell编程使用的。
 
九、             过滤Purify的报告信息
如果你的程序比较大,模块也比较多,有时候出现的信息非常的多,你程序中很可能有某段代码产生了若干个内存错误,所以,我们可以使用Purify的过滤器来让Purify只显示某一种类的信息,这样方便我们进行问题的查找和排错。
 
 
1、  在Purify的X-Window中设置信息过滤,点击图形界面中的菜单“Options” -–> “Suppressions”,将出现“Suppressions”对话框,如下所示:



我们可以看到在上面的对话框中,如果过滤Purify的报告信息。当我们点击“Where to suppress”只要,我们会看到有如下的五个选项:
l         In Call Chain:表示在某个函数调用链中信息。
l         In File:表示只报告在某个文件中的信息。
l         In Library:表示只报告在某个LIB文件中的信息。
l         In Class:这是C++的,表示报告某个类的信息。
l         Everywhere:表示全部范围内的信息。
   
    但是图形界面中,Purify并没有给我们提供一个选取文件或LIB或类的对话框,我们只能通过其文本语法来描述,接下来就让我们来看一看,过滤Purify报告信息的文本语法。
   
 
2、  我们可以使用Purify的过滤语法来要求Purify的过滤信息。并把其存于.purify文件中,这样当我们的Purify起动后载入这个文件,就可以达到过滤信息的目的了。通过文本语法来设置过滤信息比图形界有更为强大的地方。下面还是来看看suppress的语法:
 
语法:
suppress
unsuppress
 
其中,suppress和unsuppress中关键字,分别表示过滤或不过滤。指明要操作的消息,可以使用“*”做通配符,表示函数的调用链,调用的函数链用分号分隔,其同样可以使用“*”做通配符,还可以使用“…”来表示无论中间是什么。
 
还是来看几个示例吧:
1)  suppress AB* 
表示过滤ABR和ABW错误。


2)  suppress *W  
表示过滤ABW、FMW、IPW、NPW、SBW、WPW和ZPW错误。


3)  suppress ABR “libc*”
表示在所有以libc打头的LIB文件中过滤ABR信息。


4)  suppress ABR sortFunction; sort*; qsort; “libc*”
其表示,过滤ABR错误。过滤范围是在sortFunction中,并且是在以libc开头的函数库文件中,其调用链是qsort -> sort* -> sortFunction。换言之,只要有“libc*”文件中的函数调用了qsort,并且qsort调用了开头为sort*的函数,并且这些函数调用了sortFunction,那么,在这一个函数链中,不显示ABR错误信息。


5)  suppress UMR tzsetWall;…; main
其表示,在tzsetWall函数中过滤URM信息,只要tzsetWall函数是被main函数间接调用的,无论有多远,都不显示UMR信息。


6)  suppress FNH Test: :Test
这是C++中使用的语法,表示在类Test所有的构造函数中过滤FNH信息。如果要指明特定的函数,请加上其参数类型,如:suppress FNH Test::Test(const char*)。



注意,“…”语法表示调用链无论有多远。当然,如果你设置了参数“-chain-length=6”,那么,“…”只能到6层函数调用,7层的就不管了。
 
在启动Purify时,我们可以这样来读取.purify文件:


% purify -suppression-file-names=".purify,.purify.sunos4,\
$HOME/purify_suppressions"
 
Purify会在下面的目录中寻找这个文件:
/.purify
/.purify.sunos4
$HOME/.purify
$HOME/.purify.sunos4
/.purify
/.purify.sunos4
$HOME/purify_suppressions
 
十、             设置WatchPoint
你可以在你的程序中,对你所想监控的程序设置一些WatchPoint,以方便于你对程序进行调试,或更容易找出问题的原因。就像我前面说,Purify可以找到你的内存泄露,但其不能找到内存泄露的原因,你可以通过设置WatchPoint来跟踪一块内存,以找到在程序执行过程中该内存的访问情况。
 
Purify的WatchPoint可以产生下例消息:
 
l         WPR(被WatchPoint的内存读)
l         WPW(被WatchPoint的内存写)
l         WPM(被WatchPoint的内存分配)
l         WPF(被WatchPoint的内存释放)
l         WPN(来到被WatchPoint的内存的Scope)
l         WPX(离开被WatchPoint的内存的Scope)
 
一旦你设置好了一个WatchPoint,Purify会自动报告上述这些信息,以告诉你内存的存取情况。很方便你调试程序。
 
WatchPoint一般是在调试程序时跟踪一块内存时候使用的,你也可以用其跟踪一些系统级的全局变量,如:errno。一旦errno被写了,马上会报告一个WPW消息,展开后,你能看到函数的堆栈情况,以及是在哪个系统调用后出现了错误。这个使用很方便我们找到一些非内存方面的问题。
 
大家可能会有一种感觉,那就是在一般的调试器中,如GDB中也有WatchPoint的设置(对GDB的使用请参考我的文章《用GDB调试程序》),那么,在调试器中的WatchPoint和Purify的有什么不同?下面是一些GDB中的WatchPoint不足的地方:
 
1)  GDB中的WatchPoint用于单步跟踪中。
2)  GDB中的WatchPoint只能在其内存的Scope中,离开了Scope,WatchPoint会被删除。
3)  在GDB中设置一个4字节的内存WatchPoint,会让程序的运行效率下降1000个数量级。
 
Purify中的WatchPoint有效地克服了这些问题,它在全局范围内监控所有内存的使用,并且,其速度上大大地快于GDB等一系列的调试器。
 
有两种方式可以让我们设置Purify的WatchPoint,一种是在程序中使用WatchPoint的API函数,一种是直接在调试器中使用(如:GDB),下面我介绍一下这两种用法:
 
1、 在程序中使用。
写下这段程序:


#include
 
main()
{
    int i;
    printf("Note: errno=0x%x\n", errno);
    purify_watch(&errno);
 
    errno = 0;
 
    close(1000);
 
    exit(0);
}
 
   用Purify编译: >sudo purify gcc -g -o watch watch.c
运行后,我们可以看到以下画面:



我们可以看到,Purify成功地监控了errno变量。我们还可以看到被监控的变量改变前和改变后的值。
 
 
2、 GDB中使用。
 
在GDB中,我们可以简单地使用GDB的print命令来达到设置Purify的WatchPoint目的。这正是Purify的强大之处,其对这种技术称为JIT(Just-In-Time)。
 
示例:
gdb) print purify_watch(&my_str)
(gdb) print purify_watch_1(&my_char)
(gdb) print purify_watch_n(buf, sizeof(buf), "rw")
(dbx) print purify_watch_n(write_only_buf,100,"w")
 
下面来让我们看一看Purify的WatchPoint的API函数,其分成三类:
 
•          设置类
int purify_watch(char *addr)  
对所指定的内存进行监视,char* 表示以单字节为单位。

int purify_watch_ (char *addr)    =1,2,4,8
其中的是一个数字,可以是1,2,4,8表示,监控单字节,双字节,四字节,八字节。函数名为:purify_watch_1(),purify_watch_2(),purify_watch_4(),purify_watch_8。

int purify_watch_n(char *addr, unsigned int size, char *type)
                   (type = “r”, “w” or “rw”)
监控特定长度的内存,type取值为“r”,“w”“rw”,意为监控内存的读还是写。


•          查询类
int purify_watch_info().
打印目前设置的WatchPoint的情况(一般在GDB类的调试器中使用)。有点像GDB的info watch命令。


 
•          删除类
Int purify_watch_remove(int watchno)
删除指定的WatchPoint,其watchno为设置WatchPoint的函数的返回值。

int purify_watch_remove_all()
删除所有的WatchPoint。
 
 
十一、             使用Purify的参数
Purify的参数很多,具体的参数我就不多说了,还请你参考其使用手册。在这里,我简单地讲一讲其参数的使用规则和方式。
 
Purify的参数使用的规则如下:


1、  必须以连字符开始,也就是减号。
2、  在等号(=)的两端不能有空格。
3、  忽略参数名和变量的大小写。
4、  其参数中的连接符可以是减号,下划线,或是干脆就没有。如:
-leaks-at-exit,-LEAKS_AT_EXIT和 –LeaksAtExit是一回事。
5、  在参数中,如果你要指多个路径,可以用冒号或空格分开。使用空格时请使用引号。如:
% purify -user-path=’/usr/home/program /usr/home/program1’
% purify -user-path=/usr/home/program:/usr/home/program1
6、  指写多个邮件用户时,用逗号分隔。千万不要回空格。如:
% purify -mail-to-user=chris,pat,kam
7、  可以使用通配符或转义字串。如:
program*  和    -log-file=./%v.plog
 
Purify参数的类型有三种——布尔、字符串和整数,如:


-leaks-at-exit=yes 布尔型
-log-file=./pureout 字符串型
-chain-length=10 整数型
 
 
设置参数的方法有三种:


1、  在图形窗口中,通过点击“Options -> Runtime”菜单,在对话框中设置。


2、  通过两个环境变量设置——PURIFYOPTIONS PUREOPTIONS,如:
在csh下:
% setenv PURIFYOPTIONS "-log-file=new $PURIFYOPTIONS“

在sh或ksh下:
$ PURIFYOPTIONS="-log-file=new $PURIFYOPTIONS"; export\
   PURIFYOPTIONS


3、  在Link程序的命令行中。如:
$ purify -cache-dir=$HOME/pcache -always-use-cache-dir $CC ...
 
 
十二、             使用PurifyAPI函数
Purify的函数有许多,我也不在这里一一讲解了,其具体细节还请参考使用手册。我这里只讲一下Purify的API函数的使用方法。总的说来,有以下两种方式我们可以使用Purify的API函数。
 
1.         在我们的调试器中调用,如:
gdb) print purify_describe(addr)
(dbx) call purify_what_colors(buf, sizeof(buf))
(xdb) p purify_describe(addr)

注:对于purify_stop_here这个函数,我们可以这样使用。
(gdb) break purify_stop_here
(dbx) stop in purify_stop_here
(xdb) b purify_stop_here


2.         在自己的程序中调用。要在程序中调用Purify的API函数,我们需要下面两步:
1)加上头文件:#include
2)把LIB文件放到可被搜索到的路径中。主要是一个动态链接库文件libpurify_stubs.so和一个静态链接库文件libpurify_stubs.a



 
十三、             结束语
Purify是一个很强大的工具,但可惜的是其只能是某几个平台中使用。好像其Windows版中的功能要差很多,我用的Sun的Solaris的版本,很不错,因为我们的程序要在所有的UNIX下跑,用C跨平台,所以,使用Solaris来做测试机。对于其它平台的Purify,我没有用过,不知道和Solaris下的是否一样,还希望有经验的同行给我指点。不管怎么样,我想信IBM的Rational部会把它做得越来越好的。
 
对于这篇文章,本来打算在9月或者10月写这篇文章的。不过实在没有办法,前些时候太忙了,忙得脑子一堆浆糊,只要拖到现在,现在较好一点,不过脑子也不好用,写作过程中发现脑袋很拙笨。所以写出来的东西一点有错误,特别是我用的是五笔输入法,所以有错字错词会有可能让你看不懂,还请各位见谅。
 
好了,不多说了,好累了。还是留上我的联系方式,欢迎和我讨论交流。本人目前主要在UNIX下做产品软件设计和管理工作,所以,对UNIX下的软件开发比较熟悉,当然,不单单是技术,对软件工程实施,软件设计,系统分析,项目管理我也略有心得。欢迎大家找我交流。(MSN是:haoel[@]hotmail.com(常用) QQ是:753640(基本不用,因为不安全))
 
 
 
22/2<12
 

评分:0

我来说两句

seccode

最新更新