分享Visual Studio原生开发的10个调试技巧(2)
之前关于Visual Studio调试技巧的文章引起了很大的兴趣,所以决定多分享一些调试知识。您可以在下面的列表中看到编写本机开发的调试技巧(按以前的文章编号)。这些技术可以应用于VS2005或更高版本(当然,有些可以应用于旧版本)。如果你继续,你可以知道每个技能的细节。
技巧11:数据断点。
当数据的内存位置改变时,调试器将被中断。然而,这是唯一一个可以同时创建4个这样的硬件的数据断点。数据断点只能在编译期间添加,并且可以通过菜单(编译新断点、新数据断点)或通过断点窗口添加。
您可以使用内存地址或地址表达式。即使你能在堆栈上看到两个值,我认为当堆栈上的值改变时,这个函数通常是有用的。这对识别内存损坏有很大帮助。
在以下示例中,指针的值已更改为被指向对象的值。为了找出要改变的地方,我在存储指针值的位置设置了一个断点,比如ptr(注意这是在指针初始化之后发生的)。当数据被更改时,调试器会终止,以便有人更改指针值,然后您可以找出是哪些代码导致了更改。
提示12:重命名线程。
调试多线程应用程序时,“线程”窗口将显示已创建的线程和当前线程。线程越多,就越难找到要找的线程(尤其是同一程序同时被多个线程执行时,不知道当前正在执行哪个线程实例)。
调试器允许您重命名线程。右键单击一个线程并重命名它。
您也可以以编程方式命名线程,尽管这有点棘手,必须在线程启动后进行,否则调试器将使用其默认命名规则重新初始化它。下面的函数展示了如何定义和使用线程。
typedef结构tagTHREADNAME _ INFO { DWORD DWTYPe;//必须是两个字节长的LPCSTR szName//指针指向命名(相同地址空间)的DWORD dwThreadID//线程ID(-1调用线程)DWORD dwFlags//保留使用,大多数情况下为0 } THREADNAME _ INFOvoid SetThreadName(DWORD dwThreadID,LPCSTR szrthreadname){ THREADNAME _ INFO;info.dwType=0x1000info.szName=szThreadNameinfo.dwThreadID=dwThreadIDinfo . dwflags=0;_ _尝试{ raiseeexception(0x 406d 1388,0,sizeof(info)/sizeof(DWORD),(DWORD *)info);} _ _ except(exception _ continue _ execution){ } }提示13:指定要设置断点的线程。
对于多线程应用程序,另一种有用的技术是在指定的线程、进程甚至计算机上设置断点。您可以使用断点的Filer命令来实现此功能。
调试器允许您使用线程名称、线程标识、进程名称、进程标识和机器名称的不同组合(使用与、或和非连接)。掌握如何设置线程名称也使这种过滤技术更容易操作。
技巧14:(不准确)定时执行。
在前一篇文章中,我提到了在“观察”窗口中使用伪变量。其中一个没有提到的是@clk,它可以显示一个计数器的值,以获得两个断点之间代码执行所需的大致时间。该值的单位是毫秒(ms)。但是,此方法不能用于配置程序执行。您应该使用Visual Studio探查器或性能计时器来完成这些配置。
通过在“观察”窗口或“立即”窗口中添加@clk=0来重置计时器。因此,如果需要计算执行最终代码所需的时间,请执行以下操作:
在代码块的开头设置断点,在代码块的结尾设置断点。在“监视”窗口中添加@clk。当第一个断点被触发时,在中间窗口中输入@clk=0运行程序,直到遇到代码块末尾的断点,并在Watch窗口中检查@clk的值。注意,网上有技巧,在Watch窗口需要添加两个表达式:@clk和@clk=0。据说定时器每次都可以在断点执行位置复位。这项技术只能在Visual Studio的较低版本中使用,而不能在VS的较高版本中使用,例如VS2005(作者已经测试过了,vs2005不支持这项技术)和更高版本。
技巧15:格式化数字。
当您使用“监视”或“快速监视”窗口查看变量时,这些值将以默认的预定义可视化格式显示。当变量是数字时,显示形式根据其类型(int、float、double)而定,并使用十进制显示。但是,您可以将调试器设置为使用不同类型的显示数字,或者使用不同的基数。
更改变量的显示类型可以在变量前添加以下前缀:
通过——无符号字符(无符号字节)wo ——无符号短(无符号字)dw ——无符号长(无符号双字)来更改变量显示的基数,可以在变量前添加以下前缀:
或者i ——有符号十进制数u ——无符号十进制数o ——无符号八进制数x ——小写十六进制数X ——大写十六进制数。
技巧16:格式化内存数据。
除了数字,调试器还可以在“监视”窗口中显示格式化的内存数据,最大长度为64字节。您可以通过在表达式(变量或内存地址)后添加以下后缀来格式化数据:
Mb或m ——十六进制显示16字节的数据,后跟16个ASCII字符MW 3354 8 WOrd(WORD,通常为1 WORD=2 BYTE)数据md —— 4双字(DWORD,1 DWORD=4 BYTE)数据mq —— 2 Quad WORD数据ma —— 64 ASCII字符mu —— 2字节UNICODE字符。
技巧17:暂停系统DLL调用。
有时调用DLL的函数时暂停是有用的,尤其是系统DLL(如kernel32.dll和user32.dll)。实现这种暂停需要使用本机调试器提供的上下文运算符。您可以设置断点位置、变量名或表达式:
{[函数],[源代码],[模块]}断点位置{[函数],[源代码],[模块]}变量名{[函数],[源代码],[模块]}在表达式大括号中可以找到函数名、源代码和模块的任意组合,但不能省略逗号。
例如,如果我们需要在调用CreateThread函数时暂停。该函数是从kernel32.dll导出的,因此上下文操作符应该如下所示:{,kernel32.dll}CreateThread。但是,这不起作用,因为操作者需要在CreateThread修改后的名称。您可以使用DBH.exe获得特定函数的修改名称(由编译器编译)。
下面是如何获取CreateThread的修饰符名称:
C:\程序文件(x86)\ Windows(x86)的调试工具DBH . exe-s : srv*C:\符号* http://msdl . Microsoft.com/Download/Symbols-d c : \ Windows \ syswow 64 \ kernel 32 . dll enum * CreateThread *符号搜索路径: SRV * c : \符号* http://msdl . Microsoft.com/Download/Symbols索引地址名称1 10b4f65 :[email protected][email protected]@ SHCreateThreadRef?[emailprotected] 7 107309c :[email protected][email protected]@ TF _ CreateThreadMgr?[email protected]8 102 ce 87 :[email protected]9 1038 Fe 3 :[email protected]a 102 e6f 0 :[email protected]b 102 e 759 :[email protected]c 102 ce 8e :[email protected]d 102 e6e 3 :[email protected]e 1038 ff0 3360[email protected]f 1026因此,我们可以创建一个断点{,kernel32.dll} [email protected]。
运行程序,遇到暂停时,直接忽略断点处没有相关源代码的消息提示。
使用调用堆栈窗口查看调用此函数的代码。
技巧18:加载符号。
调试程序时,“调用堆栈”窗口可能不会显示整个调用堆栈,但会忽略系统DLL(如kernel32.dll和user32.dll)的信息。
通过加载这些dll的符号信息,可以得到所有的调用栈信息,在调用栈窗口中,使用上下文菜单(右键菜单)直接设置这个效果。您可以从预定义的符号路径或微软的符号服务器(对于系统DLL)下载这些符号。将这些符号下载并导入调试器后,调用堆栈将更新如下:
这些符号也可以从“模块”窗口导入。
加载后,这些符号会保存在缓存中,并可以在ToolsOptionsDebuggingSymbols中进行配置。
技巧19:在MFC中报告内存泄漏。
如果要监视MFC应用程序中的内存泄漏,可以使用宏DEBUG_NEW重新定义新运算符,这是新运算符的修改版本,可以记录文件名及其分配内存的行数。发行版中内置的DEBUG_NEW将被解析为原始的新运算符。
在MFC向导生成的源代码中,Mina在#include后包含以下预处理指令:
# ifdef _ debug # define new debug _ new # endif上面的代码是如何重新定义新运算符的。
许多STL头文件与这里定义的新运算符不兼容。如果重新定义运算符new后包含mapvectorliststring等头文件,会出现以下错误(以vector为例):
1c: \程序文件(x86)\ Microsoft visual studio 9.0 \ VC \ include \ XML内存(43):错误C2665: "运算符新”:这5个重载都不能转换所有参数类型1 c: \程序文件\ Microsoft visual studio 9.0 \ VC \ include \ new。h(85):可以是“无效*运算符new(size_t,const STD :3360 nothrow int)' 1c : \程序文件(x86)\ Microsoft visual studio 9.0 \ VC \ include \ x内存(145):请参见对函数模板实例化的引用_Ty * STD : _ Allocatechar(size _ t,_ Ty *)'正在与1[1 _ Ty=char 1]1 c : \程序文件(x86)\ Microsoft visual studio 9.0 \ VC \ include \ x内存(144):同时编译解决方法是在包含这些标准模板库文件之后再使用调试_新重定义新的运算符。
技巧20:调试(同自溶素)自溶[菌]素
当你开发ATL COM组件时你可以在调试器中查看你所开发的计算机输出缩微胶片对象的QueryInterface、AddRef和释放;排放;发布的调用情况。默认情况下并不支持这些调用的产看,你需要在预处理定义或者预编译头文件中定义两个宏。这两个宏定义之后,关于这些函数的调用会显示在输出(输出)窗口中。
这两个宏是:
_ATL_DEBUG_QI,显示每个被查询接口的名字。必须在atlcom.h头文件被包含之前定义。ATL_DEBUG_INTERFACES,每当AddRef或释放;排放;发布被调用时显示当前接口的引用次数以及类名、接口名等信息。必须在atlbase.h包含之前定义。
以上就是本文的全部内容,希望大家结合之前分享过的文章进行学习,熟练掌握可视化工作室调试技巧。
版权声明:分享Visual Studio原生开发的10个调试技巧(2)是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。