我的上篇文章《Vistual Studio原生开发的10个调试技巧》引发了很多人的兴趣,所以我决定跟大家分享更多的调试技巧。接下来你又能看到一些对于原生应用程序的很有帮助的调试技巧(接着上一篇文章来编号)。这些技巧需要应用在Vistual Studio 2005 或者更新的版本中(当然也有一些适用于旧版本)。如果你能阅读本文中推荐的一些相关文章,就可以知道每一个技巧的更多信息。

  • 11. 数据断点
  • 12. 线程重命名
  • 13. 给指定线程设置断点
  • 14.(粗略)估算执行时间
  • 15.  数字格式化
  • 16. (内存)数据格式化
  • 17.系统DLL中断
  • 18.加载符号表
  • 19.  监测MFC中的内存泄漏
  • 20.  调试ATL

技巧11: 数据断点

当数据所在的内存位置发生变化时,可以通知调试器进行中断,但是每次只能创建4个字节这样的硬件数据断点。数据断点只能在调试期间添加,可以通过菜单(Debug>New Breakpoint>New Data Breakpoint) 或者断点窗口来添加。

你可以使用内存地址或者地址表达式。尽管栈上和堆上的值你都可以看到,但是我认为当堆上的数值发生变化时,这个功能才会更有用处。它对于识别内存损坏有很大的帮助。

下面的例子中,指针的值发生了变化,不再是它所指向对象的值。为了找出在什么地方发生改变的,我在存储指针值的位置设置了一个断点,即&ptr(注意必须在指针初始化之后)。数据发生变化就意味着有人修改了指针的值,调试器发生中断,我就能找出是哪段代码引起的改变。

更多阅读:

1.怎样查明指针是否损坏内存

2.怎样查明指针在什么地方发生改变

技巧12: 线程重命名

在调试多线程应用程序时,线程窗口会显示创建了哪些线程以及当前正在运行的线程。线程越多,想找到你想要的线程就越困难(尤其是当一段程序被多个线程同时执行的时候,你不能确切地知道哪个才是当前正在执行的线程实例)。

调试器允许修改线程的名字,可以在线程窗口使用线程的快捷菜单,给线程重命名。

也可以在程序里给线程命名,尽管有点棘手,而且必须在线程启动之后给它命名,否则调试器会以默认命名规范将它重新初始化。定义一个线程,并用下面的函数重命名该线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct tagTHREADNAME_INFO
{
    DWORD dwType;        // must be 0x1000
    LPCSTR szName;       // pointer to name (in same addr space)
    DWORD dwThreadID;    // thread ID (-1 caller thread)
    DWORD dwFlags;       // reserved for future use, most be zero
} THREADNAME_INFO;
 
void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName)
{
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = szThreadName;
    info.dwThreadID = dwThreadID;
    info.dwFlags = 0;
 
    __try
    {
        RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);
    }
    __except (EXCEPTION_CONTINUE_EXECUTION)
    {
    }
}

更多阅读:

设置线程名字(非托管)

技巧13: 给指定线程设置断点

对于多线程应用程序来说,另一个有用的技巧就是给指定的线程,进程,甚至是计算机中的断点设置过滤.可以通过断点的Filter命令来实现此功能.

调试器允许你指定线程名,线程ID,进程名,进程ID和机器名的任意组合(使用AND,OR,NOT)来设置过滤。了解怎样设置线程名字也使得这项过滤操作变得更加简单。

更多阅读:

  1. 怎样指定断点过滤器
  2. 设置断点过滤

技巧14: (粗略)估算执行时间

在上一篇文章中,我有写关于Watch窗口中的伪变量,有一个没提到的是@clk,它用于显示计数器的值,可以粗略地计算出两个断点之间的代码的执行时间,单位是微秒(μS)。但是,千万不要用这个方法来分析程序的执行效率,应该使用Visual Studio 分析工具或者性能计时器来分析。

可以在Watch 窗口或者即时窗口添加@clk=0来完成对计时器的重置。因此要想估算执行一段代码需要多长时间,可以按照下面的步骤来操作:

  1. 在代码块的开始位置设置断点
  2. 在代码块的结束位置设置断点
    1. 在Watch窗口添加 @clk
    2. 程序进入到第一个断点时,在即时窗口输入@clk=0
    3. 运行程序直到执行进入代码块末尾的断点,查看Watch窗口 @clk的值。

注意网上有一些技巧说在Watch窗口添加两个表达式:@clk和@clk=0,需要在每次执行断点的时候都要重置计时器。这种用法只适用于Visual Studio的老版本,在VS2005及以上版本不再适用。

更多阅读:

调试技巧-@CLK

技巧15:数字格式化

当你在Watch或者Quick Watch窗口查看变量时, 这些值是以默认的预定义可视化形式显示的。而对于数字,则是根据数据类型(integer, float, double),用十进制形式显示的。但是你可以使用调试器把数字用不同的类型或者进制数显示出来。

想要改变显示类型可在变量前加以下前缀:

  1. by –unsigned char (又称为unsigned byte)
  2. wo – unsigned shot(又称为 unsigned word)
  3. dw – unsigned long(又称为 unsigned double word)

要改变显示的进制数在变量前加下列前缀:

  1. d 或者 i– 有符号十进制数
  2. u – 无符号十进制数
  3. o -  无符号八进制数
  4. x – 小写十六进制数
  5. X – 大写十六进制数

更多阅读:

C++ 调试技巧

技巧16:(内存数据)格式化

除了数字,调试器还可以在Watch窗口显示格式化的内存数据,最多64 bytes。 你可以使用在表达式(变量或内存地址)后添加下列说明符作为后缀来格式化数据:

  1. mb 或者 m – 十六进制显示的16字节数据,后跟16个ASCII 字符
  2. mw – 8 words
  3. md – 4 double words
  4. mq -  2 quad-words
  5. ma – 64个ASCII字符
  6. mu – 2字节的UNICODE字符

更多阅读:

  1. C++中的格式说明符
  2. Developer Studio的调试技巧

技巧17: 系统DLL的中断

有时候在DLL中的函数被调用时进行中断是很有用的,像系统DLL(比如 Kernel32.dll 或者user32.dll).实现此中断,需要使用本机调试器提供的上下文运算符.你可以设定断点位置,变量名或者表达式:

1.{[函数],[源码],[模块]}位置

2. [函数],[源码],[模块]}变量名

3. [函数],[源码],[模块]}表达式

花括号里可以是函数名,源代码和模块的任意组合,但是逗号不能省略.

我们假设想要在CreateThread函数被调用时发生中断,这个函数是从kernel32.dll中导出的,所以上下文运算符应该为:  {,,kernel32.dll}CreateThread. 然而,这样并不可行,因为上下文运算符需要CreatThread的修饰符,可以使用DBH.exe来获取一个特定函数的修饰符。

下面就是如何得到CreateThread函数的修饰符的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
C:\Program Files (x86)\Debugging Tools for Windows (x86)>dbh.exe -s:srv*C:\Symbo
ls*http://msdl.microsoft.com/Download/Symbols -d C:\Windows\SysWOW64\kernel32.dl
l enum *CreateThread*
Symbol Search Path: srv*C:\Symbols*http://msdl.microsoft.com/Download/Symbols
 
 index            address     name
     1            10b4f65 :   _BaseCreateThreadPoolThread@12
     2            102e6b7 :   _CreateThreadpoolWork@12
     3            103234c :   _CreateThreadpoolStub@4
     4            1011ea8 :   _CreateThreadStub@24
     5            1019d40 :   _NtWow64CsrBasepCreateThread@12
     6            1019464 :   ??_C@_0BC@PKLIFPAJ@SHCreateThreadRef?$AA@
     7            107309c :   ??_C@_0BD@CIEDBPNA@TF_CreateThreadMgr?$AA@
     8            102ce87 :   _CreateThreadpoolCleanupGroupStub@0
     9            1038fe3 :   _CreateThreadpoolIoStub@16
     a            102e6f0 :   _CreateThreadpoolTimer@12
     b            102e759 :   _CreateThreadpoolWaitStub@12
     c            102ce8e :   _CreateThreadpoolCleanupGroup@0
     d            102e6e3 :   _CreateThreadpoolTimerStub@12
     e            1038ff0 :   _CreateThreadpoolIo@16
     f            102e766 :   _CreateThreadpoolWait@12
    10            102e6aa :   _CreateThreadpoolWorkStub@12
    11            1032359 :   _CreateThreadpool@4

看上去实际名字应该是_CreateThreadStub@24,这样我们就可以创建断点,{,,kernel32.dll}_CreateThreadStub@24

运行程序,发生中断时会有消息提示断点处无相关源代码,直接忽略它。

使用调用栈窗口查看调用该函数的代码。

更多阅读:
1.  在Visual Studio 2010中,没有源代码如何设置断点
2.  上下文运算符(C/C++语言表达式)
3.  怎样给函数设置断点

技巧18:加载符号表

在调试程序的时候,调用栈窗口不会显示完整的调用栈,跳过了系统DLL(比如kernel32.dll 和 user32.dll)的信息。

可以通过加载这些DLL的符号表来获得完整的调用栈信息,直接在调用栈窗口使用快捷菜单就能完成。你可以从预先指定的符号路径或者微软的符号服务器(如果是系统DLL)来下载符号。符号下载完成后,直接导入到调试器,调用栈就会得到更新。</span>

这些符合也可以从组件Modules窗口导入。

一旦下载完成,符号会保存在缓存中,可以在Tools>Options>Debugging>Symbols中配置。

技巧19:监测MFC中的内存泄漏

如果你想要在MFC应用程序中检测内存泄漏,需要使用宏DEBUG_NEW来重新定new运算符,这是new运算符的修改版本,记录了每个对象内存分配的文件名和行号.在发行版中DEBUG_NEW会解析成new运算符.

向导生成的MFC源文件在#includes后包含了下面的预处理指令:

1
2
3
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

这就是怎样重新定义new运算符的。

然而,很多STL头文件和重新定义的new运算符和版本不兼容.如果你重新定义了new运算符后,又包含了<map>,<vector>,<list>,<string>等头文件的话,就会出现下面的错误(以<vector>为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1>c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(43) : error C2665: 'operator new' : none of the 5 overloads could convert all the argument types
1>        c:\program files\microsoft visual studio 9.0\vc\include\new.h(85): could be 'void *operator new(size_t,const std::nothrow_t &) throw()'
1>        c:\program files\microsoft visual studio 9.0\vc\include\new.h(93): or       'void *operator new(size_t,void *)'
1>        while trying to match the argument list '(const char [70], int)'
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(145) : see reference to function template instantiation '_Ty *std::_Allocate<char>(size_t,_Ty *)' being compiled
1>        with
1>        [
1>            _Ty=char
1>        ]
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(144) : while compiling class template member function 'char *std::allocator<_Ty>::allocate(std::allocator<_Ty>::size_type)'
1>        with
1>        [
1>            _Ty=char
1>        ]
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\xstring(2216) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1>        with
1>        [
1>            _Ty=char
1>        ]

解决办法就是总是把包含这些STL头文件放在重新定义new运算符之前.

更多阅读:

DEBUG_NEW

技巧20: 调试ATL

在开发ATL COM组件时,你可以在调试器观察COM对象的QueryInterface,AddRef和Release的调用情况.默认情况下并不支持这些,但是你只要在预处理定义或者预编译头文件时定义两个宏,宏定义好之后,关于这些函数的调用信息就会显示在output窗口.

这两个宏如下:

  1. _ATL_DEBUG_QI: 显示你定义的对象里每一个被查询的接口的名字,必须在atlcom.h被包含之前定义.
  2. _ATL_DEBUG_INTERFACES: 在每次AddRef 或者Release被调用时,显示接口的当前引用计数以及对应的类名和接口名,必须在atlbase.h被包含之前定义.

更多阅读:

  1. 调试技巧
  2. ATL调试技巧
  3. _ATL_DEBUG_INTERFACES是如何工作的?

来自:http://blog.jobbole.com/45447/

Visual Studio原生开发的20条调试技巧(下)的更多相关文章

  1. Visual Studio 原生开发的10个调试技巧(二)

    原文:Visual Studio 原生开发的10个调试技巧(二) 我以前关于 Visual Studio 调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你可以看到写原 ...

  2. Visual Studio原生开发的10个调试技巧(二)

    来源:oschina 发布时间:2013-08-10 阅读次数:397 51   我以前关于Visual Studio调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你 ...

  3. (转)Visual Studio原生开发的10个调试技巧(二)

    我以前关于Visual Studio调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来编号).这些技巧可以应用在VS200 ...

  4. Visual Studio原生开发的10个调试技巧

    这篇文章只介绍了一些有关Visual Studio的基本调试技巧,但是还有其他一些同样有用的技巧.我整理了一些Visual Studio(至少在VS 2008下)原生开发的调试技巧.(如果你是工作在托 ...

  5. Visual Studio原生开发的10个调试技巧(一)

    最近碰巧读了Ivan Shcherbakov写的一篇文章,<11个强大的Visual Studio调试小技巧>.这篇文章只介绍了一些有关Visual Studio的基本调试技巧,但是还有其 ...

  6. Visual Studio原生开发的10个调试技巧(转)

    本文由 伯乐在线 - JingerJoe 翻译自 Marius Bancila.转载请参见文章末尾处的要求.   [感谢@_La_Isla_Bonita 的热心翻译.如果其他朋友也有不错的原创或译文, ...

  7. 10个Visual Studio原生开发调试技巧

    10个Visual Studio原生开发调试技巧(1) 2013-05-29 13:30 佚名 开源中国 我要评论(1) 字号:T | T 以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来 ...

  8. 使用Visual Studio Code开发(编译、调试)C++程序

    总体安装步骤 安装VSC(Visual Studio Code). 安装C/C++编译器(如MinGW-w64),然后配置好环境变量.//完成这步即可在VSC的终端(命令行)下编译.运行.cpp程序了 ...

  9. Visual Studio 2015开发Android App启动调试始终无法完成应用部署的解决方案

    创建一个Android App项目后,直接启动调试发现Visual Studio Emulator for Android已成功运行,但应用始终处于Build中(等待时间超过1小时),并未如预期通过a ...

随机推荐

  1. eval解析JSON字符串的一个小问题

    之前写过一篇 关于 JSON 的介绍文章,里面谈到了 JSON 的解析.我们都知道,高级浏览器可以用 JSON.parse() API 将一个 JSON 字符串解析成 JSON 数据,稍微欠妥点的做法 ...

  2. Bootstrap系列 -- 13. 内联表单

    有时候我们需要将表单的控件都在一行内显示.在Bootstrap框架中实现这样的表单效果是轻而易举的,你只需要在<form>元素中添加类名“form-inline”即可 如果你要在input ...

  3. [HDU5902]GCD is Funny(xjb搞)

    题意:n个数每次选三个数删除,取其中两个数将gcd放回去两次,问最后剩的数可能是多少 分析:考虑最优情况: 先拿出三个数,留下两个x,x  再来一个y,(x,x,y)我们可以删去一个x,留下两个gcd ...

  4. [HDOJ5451]Best Solver(乱搞)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5451 分析:A=5+2根号6 B=6-2根号6 n=1+2^x 那么A^n+B^n是整数 注意到0< ...

  5. python 英文字串首字母改为大写

    #英文字串首字母改为大写 st = "string" St = st[0].upper() + st[1:] 2016-10-22 后来了解到 python 内部有相关实现,感觉  ...

  6. iOS 隐藏/去掉 导航栏返回按钮中的文字

    [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(, -) forBarMetrics:U ...

  7. 64-who 简明笔记

    显示已登录用户的信息 who [options] who am i who程序显示在本地系统上登录的用户的信息.这些信息包括每个用户的用户名.终端设备.登录时间和相应的远程主机名(如果可应用) 参数 ...

  8. github page 和 hexo 搭建在线博客

    目录: 安装node.js与git 常用git命令 安装hexo 配置hexo hexo发布到github 1.安装node.js和git工具 https://nodejs.org/en/ 直接下载安 ...

  9. SVG的使用

    一,svg可以在浏览器中直接打开 二,在html使用<img/>标签引用 三,直接在html中使用svg标签 四,作为css背景 SVG支持ie9+ ,chrome 33.0+,firef ...

  10. extjs5 一个容器中有几个组件公用一个控制器和一个模型

    Ext.define('TestViewModel', { extend: 'Ext.app.ViewModel', alias: 'viewmodel.test', // connects to v ...