-
-
[原创]软件调试基础--07用户态栈回溯数据库UST
-
发表于: 2016-1-27 15:08 4577
-
在程序中,我们最常使用的堆内存和栈内存,都有一定的保护机制和调试支持。了解这些特性,对我们后续解决软件问题有很大帮助。这节,我们讲解堆内存的一个重量级调试支持--用户态栈回溯数据库(User Mode Stack Trace Database,简称UST)。
很多时候,我们程序出现问题,是在堆内存上发生的,如果此时,我们能拿到发生问题这个堆,是在哪个函数里的哪行代码分配的,可能对我们分析问题很有帮助,最好的情况下,能拿到这个堆内存分配时的完整调用堆栈,那就更好了。本节的用户态栈回溯数据库,就是为了完成这个功能而设计的。
这个功能,是操作系统为我们提供的,当程序执行时,操作系统记录下当前进程创建堆内存时的完整堆栈,便于我们后续分析时,回顾某内存当时被创建时的调用堆栈。讲了这么多,你应该很容易想到这个东西的一个应用场景了。没错,解决内存泄露!
下面还是以例子来说明问题。代码如下:
void test1()
{
char *p = new char[100];
char szAddress[32] = { 0 };
sprintf(szAddress, "%p", p);
OutputDebugString(szAddress);
}
void test2()
{
printf("test2\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
test1();
test2();
system("pause");
return 0;
}
编译链接之后,我们来调试这个程序,调试环境,符号设置之类的东西,我就不再重复了。我们在test2函数入口处下断点,此时test1被调用的过程早已经完成,但此时,我们想看那个100字节当时分配时的执行情景(调用堆栈)。那就需要操作系统为我们记录下,堆内存分配时的用户态栈回溯数据库了。
使用gflags有界面版,来为CodeTest.exe进行开启UST,如下图:
也可以用命令行版,来开启,具体方法请自行百度!
然后我们就可以开始调试了,有了前面的基础,我就直接上图了,请原谅我的直接^_^
流程就是这样了。程序中,我们加入了一个输出,输出那100字节的堆内存的首地址,方便我们调试时候,打印这个内存分配时的调用堆栈。到这里,我们想想,假如我们能找到程序中,内存泄露的地址,再加上这个功能,那就很容易找到内存泄露的源码位置了。后面我会介绍,怎么找到发生内存泄露的内存的首地址。