前言
VS是非常强大的IDE,所以掌握VSVC的常用方法,将会使得我们找出问题解决问题事半功倍。
目录
- Watch窗口查看伪变量
按MSDN的介绍,伪变量就是用来查看特定信息的术语。例如当调用的API失败时,可以用GetLastError获取对应的错误码。然而,很多时候我们不可能随时修改代码来查看当前错误码。这个时候就可以通过伪变量快速在Watch窗口查看当前错误码了。
常用的一些伪变量有:
- $tid – 当前线程的的线程ID
- $pid – 进程ID
- $cmdline – 附加进程的命令行字符串
- $user – 运行在程序中的帐户信息
- $registername – 显示指定寄存器的寄存器内容
- $err – 显示最近错误的错误码
- $err, hr – 显示最近错误的消息
注:@err,err均可以正确使用。
详情参考:https://msdn.microsoft.com/en-us/library/ms164891.aspx
- 查看指针指向的一序列值
例如有1个int类型的Buff指针pnBuff,我们想查看它的一系列值。很多时候,大家都是使用Memory窗口来查看整片数据。Int类型每隔4个BYTE一个,查看起来非常不方便。如果能够像查看数组一样,在Watch窗口是展开查看就非常方便了。
如果你工作在1个展开在Watch中的大数组(至少几百个元素,但是可能更少)然后去查找一些特定范围的元素将是难以处理的。因为你必须大量滚动。但是如果这个数组是分配在堆上,你甚至都不能在Watch窗口中展开它的元素。这里有1个关于那个问题的解决方案。你能够用语法(array + <offset>), <count>去查看特定范围<count> 开始于<offset>位置(你的真实对象的源数组)。如果你想查看全部的数组,你能够简单的使用rray, <count>。
如果你的数组是在堆上,那么你能够在Watch窗口里将它展开,但是要查看1个特定范围,你必须用一个稍微不同的语法: ((T*)array + <offset>), <count>(注意这个语法对在堆上的多维数组也有效)。在这种情况下,T是这种数组的元素的类型。
如果你工作在MFC下并用来自于其中的"array"容器,像CArray,CDWordArray,CStringArray,等等。你当然能够应用这相同的筛选,除此之外你必须查看array的成员变量m_pData,它是拥有数据的真实缓存。
- 内存泄露查找
VS在调试结束退出时,如果有内存泄露,会在output窗口中显示出相应的信息,很多这些信息会直接指出未释放内存的位置,但有的时候却并未直接指出,这个时候就需要些其他的方法来解决了。
例如Output窗口信息如下:
Dumping objects ->
d:mariusvc++debuggingdemosdebuggingdemos.cpp(103) : {341} normal block at 0x00F71F38, 8 bytes long.
Data: < > CD CD CD CD CD CD CD CD
Object dump complete.
方法一,上面{341}的意思是代码第341次分配内存的内存。如果代码没有随机性,这个数字是不会改变的。那么我们可以在当前代码模块的最开始的位置,添加_CrtSetBreakAlloc(341),就表示代码将中断在第341次内存分配的地方,然后再通过Call Stack窗口找到具体的代码位置。
方法二,使用第三方工具,如Visual Leak Detector。下载安装,然后在你觉得最可能导致内存泄露的模块代码里,加上#include <vld.h>即可。如果有stdafx.h,#include <vld.h>直接放在这个文件里也是可以的。然后编译,调试运行,退出,然后就会在Output窗口里显示内存泄露的有关详细信息。
- 调试Release版本
Release版本和Debug版本的主要差别,前者代码编译做了优化,并且一些未初始的临时变量,内存等的值可能是随机的;后者即没有做代码优化,并且未初始化的临时变量默认赋值为0xCCCCCCCC,如果是堆上的内存则默认为0xCDCDCDCD。正因为这两者的不同,可能导致Debug下运行正常,而Release下运行错误的问题。这个时候可能就需要在Release下来进行调试。方法很简单,只需要在Release下关闭优化,并打开调试信息的生成即可。
- C/C++ > Optimization > Optimization should be "Disabled (/Od)"
- Linker > Debugging > Generate Debug Info should be "Yes (/DEBUG)"
详情参考:https://msdn.microsoft.com/en-us/library/fsk896zz.aspx
- 远程调试
如果能够用打印调试信息解决问题的,就尽量不要使用远程调试了。感觉远程调试,总是容易出各种问题。但有的时候还必须得使用远程调试,所以在此简单介绍下。
- 将本地电脑上的Debug程序及相应的PDB文件一起拷贝到远程电脑,注意Debug版程序与PDB文件对应关系和本地电脑保持一致。
- 将VS目录里的Debugging Monitor拷贝到远程电脑上。以管理员身份打开Debugging Monitor,设置为无用户访问模式,并记住服务的名字。
- 打开本地电脑的VS,点击ToolAttach to Process,启动如下窗口,并在Qualifier中填上远程电脑上Debugging Monitor上的服务名。
详情参考:https://msdn.microsoft.com/en-us/vstudio/aa569599.aspx
- 函数断点
函数断点,我们经常用,鼠标移到某一行,按下F9,即设置了最普通的断点。断点还有很多高级功能,熟练掌握,能够让我们调试更方便。
- 条件断点,条件只要是表达式即可。
- 触发次数断点。
- 过滤断点。
- 数据断点。
断点除了我们常用的函数断点外,再就有的就是数据断点了。在Breakpoints窗口左上角可以选择New Data Breakpoint。
数据断点可以用来查找某个变量在复杂的代码里,究竟是在哪里产生的改变。另外,针对内存覆盖导致的崩溃,用数据断点也是比较方便。内存覆盖导致的崩溃,往往是某个重要变量被影响到了导致的。所以我只要找到重要变量,用数据断点监控起来就可以了。
- 代码执行时间
伪变量中有一个特别的@clk,默认显示系统时钟时间,以毫秒为单位。
在第一次断点时,将@clk放入Watch窗口,并将Value清0.
第二次断点时,@clk对应的Value即为两个断点之间代码的大概执行时间。
- 格式化数据
当你在Watch或Quick Watch窗口查看变量时,这值是用默认的预定义可视化显示的。当它变成数字时,它们是通过它们的类型(整形、浮点、双精度)用十进度制来显示的。但你能够强制让这调试器用不同类型或不同进制或是两者来显示这些数字。
给变量加前缀来改变显示 :
- by 针对无符号字符(又称无符号字节)
- wo 针对无符号短整形 (又称无符号字)
- dw 针对无符号长整形(又称无符号双字)
给变量名加后缀来改变显示:
- , d 或者 , i 针对有符号十进制
- , u 针对无符号十进制
- , o 针对无符号八进制
- , x 针对小写的十六进制或 , X 针对大写的十六进制
- 格式化内存
除了格式化数据,调试器也能在Watch窗口中显示格式化的内存值,高达64个字节。你能用下面的说明符在表达式(变量或内存地址)后来格式化数据。
- mb / m - BYTE
- mw - WORD
- md - DWORD
- mq – 8BYTE
- ma – 16BYTE
- mu – 2BYTE UNICODE characters
- 其他
一段代码进入死循环,如何快速找到在哪里呢?直接点击中断所有。
应用程序打开异常,调用DLL异常,使用dependens.exe。