[Windbg笔记] 调试偶发性Bug

[Windbg笔记] 调试偶发性Bug

Binhua Liu

 

document_thumb_thumb前言

      很久之前在网上看到两篇不错的windbg文章——<调试Bug的神兵利器:通过WinDbg条件断点收集Log >, <如果一个程序跑10000次只失败一次,你会怎么调试?>。最近重读一下,并做了一些家庭作业,总算弄得比较清楚了。于是把自己写的调试小程序和小改进贴出来,权当笔记。相关知识推荐还是阅读2篇原文和Windbg help文档。

document_thumb_thumb调试场景

    这种调试方法可以用在很难重现的bug上,通过不断重复运行程序来捕获bug,并且通过在关键的代码和数据上设置断点来打印调试信息帮助定位bug。我认为另外一个用途是可以用在自动化测试上,通过脚本自动起调试程序,运行过程中打印调试Log,一旦遇到希望捕获的异常,就创建dump file并和Log一起上传到服务器。话不多说,马上开始,我写了一个小程序WindbgTest1.exe模拟一个难以重现的bug,代码如下:

#include "stdafx.h"
#include "Windows.h"

int GetQuotient(int a)
{
		int dividend=rand()%100;
		return a/dividend;
}
int Test()
{
	int a;
	int result;
	SYSTEMTIME localTime;
	GetLocalTime(&localTime);
	srand(localTime.wMilliseconds);
	a=rand()%100;
	if(a>=90)	
		return GetQuotient(a);
	else
		return a;
}
int _tmain(int argc, _TCHAR* argv[])
{
	int reslut=Test();
	return 0;
}

    Test函数在0-99之间产生一个随机数,如果随机数大于等于90,则调用GetQuotient函数,GetQuotient函数随机在0-99之间产生一个被除数。显然,如果被除数是0,将发生除零异常。异常产生的概率为0.1%。

  document_thumb_thumb调试 

    我们通过运行下面这行命令来捕捉bug:

for /l %i in (1,1,1000) do CDB.exe -c "bu WindbgTest1!Test \".echo Inside Test Function;dv;g\"; bu WindbgTest1!GetQuotient \".echo Inside GetQuotient Function;dv;g\"; sxe -c \"dv;kp;.dump /ma c:\\test.dmp\" dz; g" -G -logo c:\debug.log "D:\Code\WindbgTest1\Debug\WindbgTest1.exe"

    我们来解释一下这行命令,这个命令包含好几层嵌套,只要搞清楚了命令的结构和每部分的功能,事实上不是很难。

for /l %i in (1,1,1000) do {command}

    这是一个cmd命令,意思为重复执行{command}一千遍,所以我们上面的命令虽然很长,但后面部分都属于这个for语句的{command}部分

CDB.exe -c "{commands}" -G -logo c:\debug.log "D:\Code\WindbgTest1\Debug\WindbgTest1.exe"

    这个命令指在CDB.exe下调试WindbgTest1.exe,CDB.exe是windbg的命令行版本。-G表示CDB忽略程序结束的时候的Breakpoint,这样调试完成后会自动退出,然后for语句可以马上起下一次调试。-logo c:\debug.log 指把调试信息打印到debug.log文件中,由于调试不断重复,这个文件也会不断被覆盖,只保留最后一次即bug发生的那次的log。

     关键是 –c “{commands}”  ,这是用来设置调试启动时的初始命令。相当于我们用Windbg调试时,加载.exe文件后,程序运行前的进入的断点,我们在这时进行断点的设置,符号文件的加载等初始化命令,这里也是同样的道理。下面我们就来看看"{commands}”中的内容:

bu WindbgTest1!Test \".echo Inside Test Function;dv;g\"; 

    bu表示设置一个Unresolved Breakpoints断点,WindbgTest1!Test为设置断点的地址,双引号(由于外层还有双引号,所以要加上转义符)中的命令是指一旦断点被触发后会自动执行的命令--首先打印一段话”Inside Test Function”,然后dv命令打印局部变量,最后g语句让程序继续执行。最后的g语句是必须的,因为如果没有g语句,程序就停留在该断点不会继续往下执行了。

bu WindbgTest1!GetQuotient \".echo Inside GetQuotient Function;dv;g\";

    同上

sxe -c \"dv;kp;.dump /ma c:\\test.dmp\" dz;

    该语句是我做的小改进,用来在程序遭遇到期望的异常后会自动产生dump file。sxe [–c “commands”] {exception} 表示当遭遇到指定的first chance的{exception}后,执行commands中的命令。dz表示除零异常,遭遇这个异常后,首先dv打印局部变量,kp打印堆栈,最后.dump /ma c:\test.dmp 创建一个min dump。由于没有g语句,程序停留在这个断点上,另外一个选择是,上传dump和log,然后执行退出命令,再起下一次调试。

g

    初始化命令设置完成,开始执行程序。

document_thumb_thumb最后

    本文涉及到的命令只是做了初略的讲解,由于本人功底尚浅,还以官方help文档为准。

posted @ 2010-08-28 01:07  Binhua Liu  阅读(2382)  评论(0编辑  收藏  举报