由于对象处于错误状态而导致的崩溃或断言,程序在调试器中停止是很常见的,要跟踪问题,您需要弄清楚它是如何出现的。在许多情况下,对象创建的调用堆栈可以提供有价值的线索,但尝试获取该调用堆栈可能是一项重要的工作。最明显的技术是在对象的构造函数上设置断点,当创建大量相同类型的对象时(其中只有一个对象有问题)会变得很麻烦。
在这里,我们将探索另一种方法,通过添加少量的插装代码,我们可以在出现问题时检查watch或locals窗口中的对象,并立即看到对象创建的调用堆栈。
首先,我们需要代码从对象的构造函数中实际捕获堆栈跟踪。幸运的是,Windows为我们提供了一个函数, CaptureStackBackTrace() ,它在堆栈中遍历给定数量的帧,并将看到的每个帧的地址存储在一个空的**缓冲区中。我们首先将函数包装在StackTrace类中,该类在其构造函数中捕获堆栈跟踪并将其存储在成员变量中,如下所示:
#include <Windows.h> class StackTrace { private: enum { NFrames = 20}; int m_frameCount; void* m_frames[NFrames]; public: StackTrace() { m_frameCount = CaptureStackBackTrace(1, NFrames, m_frames, NULL); } };
现在,我们所要做的就是将这些StackTrace对象中的一个粘贴到我们感兴趣的每个类中,以记录其堆栈跟踪。例如:
class MyObject { private: // Existing members... StackTrace stackTrace; public: MyObject() { // Existing constructor code... } };
现在,每次创建“MyObject”实例时,从“MyObject”构造函数开始的创建堆栈跟踪都将保存在MyObject的“stackTrace”字段中(为了避免给应用程序增加不必要的性能开销,建议您在调查完问题后删除StackTrace类的使用,或者将StackTrace类的使用包装在“#ifdefŠDEBUG”中,以将其从零售版本中排除)。
到目前为止,我们所做的一切都可以使用任何版本的visualstudio来完成。但是,当需要查看调试器下捕获的堆栈跟踪并查看有用的内容时,VisualStudio2013是最好的。在以前的版本中,堆栈跟踪的内容只是不透明void*的集合,如下所示:
但是,在Visual Studio 2013中,堆栈跟踪如下所示:
您甚至可以右键单击感兴趣的特定帧来导航到源代码或反汇编,如下所示:
到目前为止,我们所看到的并不需要任何特殊的努力来启用—只要VisualStudio2013调试器看到指向函数内部代码的指针,调试器就会自动显示函数名和行号,并允许源代码和反汇编导航。
但是,如果你愿意写一篇natvis的文章,你可以让体验变得更好,比如:
<?xml version="1.0" encoding="utf-8"?> <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> <Type Name="StackTrace"> <Expand> <ExpandedItem>frames,[frameCount]stackTrace</ExpandedItem> </Expand> </Type> <Type Name="MyObject"> <!-- Existing visualization for MyObject--> <Expand> <Item Name="[Stack Trace]">stackTrace</Item> </Expand> </Type> </AutoVisualizer>
上面的natvis条目做了几件事。首先,它主要调用MyObject的堆栈跟踪,这样就不必从一个可能很长的字段列表中挖掘。其次,StackTrace类的可视化工具使用数组长度格式说明符来避免显示堆栈跟踪缓冲区中未使用的部分。最后,它使用特殊的“stackTrace”格式说明符,它向调试器提示成员变量“frames”的内容实际上代表堆栈跟踪的帧。特别是,“stackTrace”格式说明符会导致调试器忽略帧的内存地址,只显示函数,如果启用JustMyCode,则会将表示非用户代码的帧折叠为“外部代码”帧。在本例中,“[External Code]”块引用kernel32.dll和ntdll.dll中的帧,它们构成每个Windows线程的开始。
试试看!
埃里克费维森 是微软上VisualC++团队的开发人员。如果你有问题,请在评论中发表。