内容


IBM Rational Purify 的高级特性

将 Purify 整合到软件开发和测试过程中

利用转换符号和选项来自动化 Purify 的使用

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: IBM Rational Purify 的高级特性

敬请期待该系列的后续内容。

此内容是该系列的一部分:IBM Rational Purify 的高级特性

敬请期待该系列的后续内容。

IBM® Rational® Purify® 是一种高级存储错误检测的工具,能够帮助您们精确地找到很难被调试得存储毁坏错误。您需要通过使用 Purify 来测试您的软件,当您运行这个可测量的软件的时候,Purify 就会仔细检查每个存储存取,并在它发生之前报告所有的毁坏错误。

Purify 在软件开发生命周期中是一种非常有用的工具。开发人员可以用它来确保他们已经编写的新代码不会在不防之时导致任何存储毁坏错误或者渗漏。测试工程师可以利用它获取功能验证和系统整合测试期间的存储错误。现场和支持技术工程师可以利用它诊断软件部署完成之后遇到的存储问题。由于在软件开发生命周期早期检测和修复缺陷的成本最低,所以最好在开发和测试阶段获取和修复尽可能多的问题。您可以通过在软件开发生命周期中有条理且系统地使用 Purify,从而达到理想的状态。实现它最好的办法是自动化 Purify 的使用过程,并讲它整合到您的软件开发和测试过程中。

自动化工具的操作可以减少普通开支,并更有效地使用它,同时还会减少将它作为这个过程一部分的采用阻力。因此,自动化操作在流线型过程中是非常关键的。例如,您可以将 Purify 与您的单元或者烟雾测试单元整合在一起,这些单元是开发人员必须在报告任何代码变更前必须运行的单元,并需要它们修复 Purify 所报告的所有新的存储错误。用这种方法,只要发现错误就会马上被报告和尽早修复,因为这个代码变更在开发人员的脑子里仍然十分新鲜。类似的,您可以将 Purify 和您功能性和系统验证测试单元整合在一起,您可能每夜或者每个周都在运行它。测试人员可以分析和现场检测 Purify 所报告的存储错误。这样确保了新的存储错误可以从他们引见的时间的一天或者一周内捕获,这样比释放软件之后捕获要好得多。

在这篇文章中,您可以学习使用 Purify 以及将它整合到您的 makefiles 中:可以在一个渗漏的船舶中导航 C 吗?尝试一下 Purify。如果您已经对 Purify 十分熟悉,您可以跳过或者略过那篇文章。在这篇文章中,您将首先学习如何更改您的构建和测试环境,从而将 Purify 合并予其中,以及学习您可以用来与 Purify 选项整合在一起从而自动化使用 Purify 的转换符号。然后您将看到一个例子,在这个例子中所有性能都是用来自动操作 Web 页面上 Purify 错误概要的报告行为。

将 Purify 整合到您的构建和测试环境中

在将 Purify 整合到您的软件开发和测试过程中的第一步是修改您的构建和测试系统。这个构建系统构建了一个应用软件,而测试系统利用一个测试单元运行了这个应用软件 (现实在图 1中蓝色区域中)。一般情况下,这个构建应用软件和运行测试单元的过程都是自动的安排有序地每夜或者每周运行的工作。

您需要修改您的构建系统在没有任何调试信息(释放字节)的情况下构建 Purify 的应用软件。比如构建一个 Purify 的应用软件,利用调试信息(释放字节)来构建应用软件是合理的(尽管不是必要的),然后对它进行净化。您还需要修改您的测试系统来运行带有 Purify 的应用软件的测试单元,另外还要将它与正常应用软件进行运行。这些附加的构件和测试步骤显示在下面图 1中绿色的区域。做了这些变更之后,添加构建 Purify 的应用软件并利用您的测试单元来运行它,从而每夜或者每周自动操作您的工作。在这篇文章的后面部分, 您将学习各种控制和自动操作这些行为的方法,当 Purify 检测记忆错误或者漏洞的时候就会采取这些行为。

图 1. 构建和测试系统中的修改
修改前后的比较

使用转换符号

Purify 提供了各种转换符号,您可以用来确定各种选项的值,比如 -view-file-log-file(这些选项将 Purify 输出分别发送到一个 Purify 视图文件和 ASCII 日志文件分别)。Purify 用意味深长的扩展取代这些符号,并为保存数据计算一个唯一的文件名称。 例如,您可以将这个程序名称和过程 ID 置入这个日志文件的名称中:

$ purify -log-file=./purifyerrors_%v_%p.plog cc -o progname foo.c bar.c

这个命令将创建一个可测量的可执行的可命名的 progname。如果您运行它,运行它的过程 ID 是1234,所有 Purify 的错误都将被记录在一个名称为purifyerrors_progname_1234.plog的文件。在这个日志文件名称中,Purify 扩展 %v 为这个程序可执行名称以及 %p 为这个过程 ID。

运行 Purify 之后添加一些操作

在运行您的可测试应用软件之前对已经发生的进行自动化操作是很容易的,因为您可以控制一切。您可以控制和自动操作通过开拓各种能让您添加自定义后置处理任务的 Purify 特性来运行这个应用软件之后所发生的事情。您可以利用这个来报告这个可测试程序退出之后所有错误的概要。这样做,您可以使用 Purify-run-at-exit 运行时选项。例如,如果您的可测试应用软件是 test.pure,您可以利用下面这个选项来打印查询的错误列表:

$ setenv PURIFYOPTIONS '-run-at-exit="if %z ; then \
 	echo \"%v : %e errors, %l bytes leaked.\" ; fi"'

紧跟在 -run-at-exit选项后面的字符串是由这个程序退出后的文件执行的。转换符号替代已经制作,比如如果在这个运行过程中没有 Purify 错误或者渗漏,它将 %z 转变为 false。正因为如此,这个例子中所陈述的 if说:“只有当错误出现时才执行 'echo'。”这个 echo命令,相反利用更多的替代字符串来报告存在错误的数量。在现存的程序之上,Purify 发送了一个与另一个十分相似的消息:

      $ test.pure 
      test.pure : 2 errors, 10 bytes leaked

这是一个样本案例。然而,您可以将复杂的过程置于一个脚本,或者一个程序中,并且传递各种转换符号作为变量到这个脚本或者程序中。例如:

$ setenv PURIFYOPTIONS '-run-at-exit="postprocess.csh %v %z %e %l "'

表格 1表格2显示了更多转换符号替代字符串的详细情况。

表格 1. 可以与 Purify 选项一起使用的转换符号
字符转换到
%v可执行程序名称,小写字母 V (您所运行的可测量的执行文件的名称)
%V这个程序的完整路径名称,大写字母 V(/_所代替)
%p进程 ID(pid 或者 PID)
表格 2. 可以使用在 Exit-命令的转换符号 (-run-at-exit)
字符转换为
%z字符串值true或者false,表示是否有任何错误或者渗漏的得链被打印(当 Purify 找到一些您感兴趣的事务,可以利用它使您的退出脚本有条件地执行)
%x项目的退出状态(0如果这个程序没有调用退出命令)
%e打印的明显的访问错误(显示的)
%E打印出的所有错误的总数
%l内存泄露字节的总数(小写字母 L)
%L存储潜在渗漏的字节总数(大写字母 L)

利用这个程序退出状态

您已经学习了如何在可测试程序运行的末尾运行您的脚本。Purify 还给了您们一些穿过它退出状态的一些信息。默认情况下,Purify 是不会修改您程序的正常退出状态的。然而,如果 Purify 发现任何访问错误或者内存泄露,您可以利用一个具体的退出状态来选择使您的程序退出。这是一个减小测试单元中失败运行的便利的方法。利用 -exit-status=yes选项能够使 Purify 插入表明运行时错误的标志。如果有一些大写字母的 Purify 错误,这个状态代码可以通过操作下面值的 bit-wise OR 来处理,根据展现的存储错误的类型:

  • 0x40:存储访问错误
  • 0x20:内存泄露
  • 0x10:潜在内存泄露

另一方面,您可以在您的代码中将这些命令替代为退出(状态),以及替换带有purify_exit(status) 功能的 main() 中的为 返回陈述。(请看这篇文章中关于 Application Programming Interface 功能的参考资料。)如果您只关注这个存储访问错误,您要么可以通过使用 -leaks-at-exit=no选项在退出时关闭渗漏缺陷,要么可以抑制内存泄露和潜在渗漏消息。您还可以忽略退出状态的适当的字节。然而,Purify 报告中的程序概要消息总是在任何其它 Purify 结果状态进入它之前显示您最初的退出状态 。

列表 1是一个例子,它利用这个 -exit-status选项并使用这个退出状态来决定是否有错误在这个程序中被发现。

列表 1. 退出状态选项的例子
$ cat prog.c 
#include <stdio.h>  
int main() {    
	int i,j;    
	i = j+1;  /* UMR: Reading un-initialized variable j */     
	return 0; 
			 }   
$ purify -exit-status=yes cc -g prog.c -o prog.pure 
$ prog.pure $ echo $? 
64

这个退出值不是 0(零),如 main 函数中所返回的。它是64,在十六进制中它是 0x40。这是因为 Purify 在这个程序中检测到一个 Uninitialized Memory Read (UMR) 存储访问错误。这个选项可以简单地合并到一个脚本中,这个脚本根据发现的错误核查运行的可执行文件和采取适当行为之后的退出状态,比如利用这个测试程序填充一个缺陷报告,或者在这个 Purify 日志中 记录结果。

如果您想要您的可测试应用软件在检测到第一个错误之上退出,您可以利用 -exit-on-error选项。当您使用这个选项的时候,这个程序退出的时候 Purify 遇到一个错误(通过使用 suppresskill 指令进行隐藏的错误是不计算的)。

邮寄 Purify 结果以及协助分析

Purify 拥有一个-mail-to-user 选项,您可以用来自动报告每日或者每周的 Purify 结果。当您使用这个选项的时候,Purify 将通过电子邮件发送错误报告到测试人员和开发人员的具体地址中,当他们收到这个邮件时会验证这个结果。例如,假设您用这种方法净化您的程序:

$ purify -mail-to-user=yourid cc -g prog.c -o prog.pure

当您运行这个 prog.pure 可执行文件之后,Purify 报告将自动发送到 yourid 邮件地址。

有时候,分析这个错误的时候,查看功能名称和其它详细情况是十分有用的,比如完成这个文件存储的路径或者 PC 值。您可以使 Purify 通过使用这些选项来展示这样的信息:

  • -show-pc 向您显示了完全的 PC 值
  • -show-pc-offset 向您显示了从这个功能的开始的 pc 移位
  • -show-directory 显示了括功能存在的地址列表 (需要有调试的程序构建)

例子

在这部分中,您将看到一个使用了您在这篇文章中学到的绝大多数选项的例子。列表 2显示了GNUMakefile,包括修改的构建和测试系统 (请看下载,从而获取这篇文章所使用的源代码)。如果这个应用软件的名称为 memerrors,那么构建 Purify 的应用软件所附加的新目标的名称就为 memerrors.pure。类似的,利用 Purify 的应用软件可以添加的新目标。

Purify 运行时选项在运行这个测试前就设置好了。对于 -log-file 选项,只能创建一个日志文件名称,利用转换符号和日期命令 (这个文件名称包括程序名称,过程 ID,日期,以及时间)。这个 -run-at-exit 选项用来表明,这个程序退出之后,这个 addsummary.sh脚本就应该运行,凭借转换符号伴随着具体的变量(也就是说,这个日志文件名称,无论是否有错误调令链被打印,退出状态,存储错误发现的数量,内存泄露的规模,以及潜在存储泄漏)。由于这个 -exit-status=yes 选项没有被使用。Purify 将不能覆盖这个退出状态和保持这个程序最初退出时的状态。

列表 2. 附有修改过的构建和测试系统的 GNUMakefile
			# Name of Logfile using Purify conversion symbols and date command
DATEANDTIME     := `date +%Y_%b_%d_%H_%M_%S`
LOGFILENAME     := %v_pid%p_$(DATEANDTIME).plog

# Script to run when Purify'ed program exits
PURIFYEXITSCRIPT:= \"addsummary.sh $(DATEANDTIME) $(LOGFILENAME) %z %x %e %l %L\"

# Purify Options
PURIFYOPTIONS   := -log-file=$(LOGFILENAME) -run-at-exit=$(PURIFYEXITSCRIPT)

# Targets and Rules
all: runtest runpurifytest

# Clean
clean:
	$(RM) memerrors memerrors.pure

# Build Application
memerrors: memerrors.c
	$(CC) -o $@ $? 

# Build Purify'ed application
memerrors.pure: memerrors.c
	purify $(CC) -g -o $@ $? 

# Run Test Suite
runtest: memerrors
	./memerrors

# Run Test Suite with Purify'ed application
runpurifytest: memerrors.pure
	echo Starting test at $(DATEANDTIME) .....
	env PURIFYOPTIONS="$(PURIFYOPTIONS)" ./memerrors.pure

# End of GNUMakefile

这个显示在列表 3中的 addsummary.sh 脚本创建了一个 HTML 报告。它维护了一个到目前为止对每个 Purify 运行都有一个 HTML 表格行的文件列表,反之则是按照时间顺序的。当这个脚本在现存的 Purify 之上执行时,它创建了一个包含最近每次运行的 HTML 表格行的新文件列表,附加先前行的文件,并用新文件列表取代了旧的列表。然后它通过包装这个文件列表的 HTML 头部和页脚从而产生了一个 HTML 文件。

列表 3. addsummary.sh 命令了脚本的内容
#!/bin/sh

DATEANDTIME=$1
LOGFILENAME=$2
LOGFULLNAME=`pwd`/$LOGFILENAME
ERRORFOUND=$3
EXITSTATUS=$4
ERRORCOUNT=$5
LEAKSIZE=$6
PLEAKSIZE=$7

PURIFYREPORT="purify_reports"
REPORTLIST="$PURIFYREPORT.list"
REPORTNEWLIST="$REPORTLIST.new"
REPORTHTML="$PURIFYREPORT.html"

# Start
echo Processing $LOGFILENAME created at $DATEANDTIME

# Create report list file if it does not exist
touch $REPORTLIST

# Create a row for the latest Purify run
echo "<tr>" >> $REPORTNEWLIST
echo "<td>$DATEANDTIME</td>" >> $REPORTNEWLIST
if ($ERRORFOUND == "true"); then
    echo "<td>FAILED</td>" >> $REPORTNEWLIST
else
    echo "<td>Pass</td>" >> $REPORTNEWLIST
fi
echo "<td>$EXITSTATUS</td>" >> $REPORTNEWLIST
echo "<td>$ERRORCOUNT</td>" >> $REPORTNEWLIST
echo "<td>$LEAKSIZE bytes</td>" >> $REPORTNEWLIST
echo "<td>$PLEAKSIZE bytes</td>" >> $REPORTNEWLIST
echo "<td><a href=\"$LOGFULLNAME\">$LOGFILENAME</a></td>" >> $REPORTNEWLIST
echo "</tr>\n" >> $REPORTNEWLIST

# Add this row at the beginning of the table
cat $REPORTLIST >> $REPORTNEWLIST
mv $REPORTNEWLIST $REPORTLIST

# Create HTML page
# Header
echo "<html>"  > $REPORTHTML
echo "<body>" >> $REPORTHTML
echo "<table border=1>" >> $REPORTHTML
echo "<caption>Purify Test Summary</caption>" >> $REPORTHTML
echo "<tr>" >> $REPORTHTML
echo "<th>Date & Time</th><th>Result</th><th>Exit Status</th>"  >> $REPORTHTML
echo "<th>Errors</th><th>Leaks</th><th>Potential Leaks</th>" >> $REPORTHTML
echo "<th>Log File</th>"  >> $REPORTHTML
echo "</tr>\n" >> $REPORTHTML
# Add rows for Purify results
cat $REPORTLIST >> $REPORTHTML
# Footer
echo "</table>" >> $REPORTHTML
echo "</body>" >> $REPORTHTML
echo "</html>" >> $REPORTHTML

# Done
echo "Successfully updated $REPORTHTML"

# End of addsummary.sh

图 2 显示了三次运行这个测试单元之后的产生的 HTML 页面。每次运行都通过一行来展示,并且每行都有一个 Purify 日志文件的超链接。每次测试单元的连续运行都将在这个表格的开始添加一个新的行。

图2. 浏览器中报告的 Purify Test Summary
浏览器中报告的 Purify Test Summary
浏览器中报告的 Purify Test Summary

总结

正如这篇文章所描述的,当您有规律地并系统地使用 Purify 时,您可以获得最大利益。您现在已经知道如何将 Purify 合并到您地软件开发和测试过程中,从而利用转换符号和选项自动化它的使用操作。

尽管这篇文章使用的例子非常简单,但是它证明了将 Purify 整合到您的构建和测试环境中是多么得简单,还证明了自动操作 Purify 使用的价值。思考在 Web 页面核查 Purify 测试结果总结的简单,每次执行您的测试单元时这个页面就会更新。所有现存的日志文件通过相同的 Web 页面都可以进入。这里的例子是故意设置得十分简单得,只是向您显示这种可能性。您可以创建一个十分复杂的系统,它可以比较结果并发送带有准确寻找额外存储错误和泄漏信息的电子邮件。您可以在它们被引进时尽快给予修复。有了这些知识,您就做好了获取 Rational Purify 最大收益的准备。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational
ArticleID=341782
ArticleTitle=IBM Rational Purify 的高级特性: 将 Purify 整合到软件开发和测试过程中
publish-date=09252008