免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1061 | 回复: 0
打印 上一主题 下一主题

C/C++内存问题检查利器——Purify(2) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-23 17:11 |只看该作者 |倒序浏览

六、             控制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 而且,并不支持其精度定义。
十、             设置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 运行后,我们可以看到以下画面:


  
  
  
  
  
  
  
  
  
  
  
  




file:///C:/DOCUME%7E1/ADMINI%7E1/LOCALS%7E1/Temp/msohtml1/01/clip_image001.jpg
我们可以看到,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月写这篇文章的。不过实在没有办法,前些时候太忙了,忙得脑子一堆浆糊,只要拖到现在,现在较好一点,不过脑子也不好用,写作过程中发现脑袋很拙笨。所以写出来的东西一点有错误,特别是我用的是五笔输入法,所以有错字错词会有可能让你看不懂,还请各位见谅。

               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94916/showart_1908356.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP