Visual Studio调试之断点基础篇.docx
- 文档编号:1743821
- 上传时间:2023-05-01
- 格式:DOCX
- 页数:16
- 大小:396.35KB
Visual Studio调试之断点基础篇.docx
《Visual Studio调试之断点基础篇.docx》由会员分享,可在线阅读,更多相关《Visual Studio调试之断点基础篇.docx(16页珍藏版)》请在冰点文库上搜索。
VisualStudio调试之断点基础篇
VisualStudio调试之断点基础篇
我曾经问过很多人,你一般是怎么调试你的程序的?
F9,F5,F11,F……
有很多书和文章都是介绍怎么使用VisualStudio编写WinForm啦,、ASP.NET之类的程序;知道如何编写固然重要,但是我觉得程序员可能只会花费30%的时间在编写代码上,剩下的大部分时间都是在调试程序。
在网上看到很多人介绍Windbg的用法,但是没有看到几篇讲解使用VisualStudio调试的文章。
Windbg固然强大,但是问题是它的学习曲线太陡了,而且很多调试并不需要使用Windbg来调试(当然啦,并不是指我不会Windbg调试啦—这是以后的调试系列文章将要讲的),为什么不使用我们最熟悉的VisualStudio来进行调试呢?
调试嘛,无非就是要看看程序在运行时候,内部的状态,例如一些变量的值是多少,看一看程序调用的路径啦之类的。
当然最直接的方式就是直接中断程序的执行,用调试器去检查一下程序的情况嘛。
于是F9,F5,F10,F11……
那么我们就来说说什么是断点,断点是什么?
不是F9,也不是那个小红球,在Intel系列的CPU(包括AMD生产的CPU)里面,它其实是一个特殊的指令—INT3。
CPU在执行程序的指令集的时候,只要碰到这个指令,就会中断程序的执行(当然啦,CPU会通知操作系统,然后……然后……然后……,断点的实现机制我会在以后的文章里面讲解,现在我们就只要知道那个INT3指令会中断程序的执行好吗?
)。
当然啦,我们需要用事实来证明我上面的话,因此把下面的程序编译并且执行一下,点“Yes”,点“Break”,对对对,程序中断了,我相信你可以看见的:
#include
void main()
{
printf("Before breakpoint"n");
__asm
{
int 3
}
printf("Before breakpoint"n");
}
编译方法:
1. 在开始菜单中打开VisualStudio2008[2005]CommandPrompt(VisualStudio2008[2005]命令行)。
2. 进入保存上面C源代码(int3.c)的文件夹的路径。
3. 执行编译命令(因为我的机器是Windows7RC+VisualStudio2008+x64CPU,直接编译有一点问题,如果你的机器不是我上面的配置,可以尝试执行
cl/Ziint3.c)
cl/Zi/cint3.c
4. 执行链接命令(如果你直接执行了命令cl/Ziint3.c的话就可以跳过这一步)。
link/libpath:
"C:
"ProgramFiles"MicrosoftSDKs"Windows"v6.0A"Lib"int3.obj
5. 运行输出的int3.exe。
这时你应该会看到VisualStudio弹出来,然后在源代码行的int3上面中断,说明我们已经成功地让CPU中断int3.exe程序的执行了。
提示
:
如果你执行int3.exe的时候,没有发现VisualStudio窗口弹出来的话,那么请点击VisualStudio菜单项里面的“Tools(工具)”—“Options(选项)”,接着在“Options(选项)”窗口中选择“Debugging(调试)”—“Just-In-Time(即时调试)”,然后勾选“Native(原生程序)”选项。
如下图所示:
图片看不清楚?
请点击这里查看原图(大图)。
“综上所述,断点是int3这个指令触发的!
(小时候的数学证明题)”。
由int3这个指令(当然是在intel系列的CPU上面)引申出来有哪些函数呢:
语言/工具
名称
描述
C++
DebugBreak
在C++代码中硬编码一个断点。
C#
Debugger.Break
在.NET代码中硬编码一个断点
VisualStudio
断点
设置一个断点
未完待续……
VisualStudio调试之断点进阶篇
2009年09月28日 来源:
博客园 作者:
donjuan 收藏本文
-
在上一篇文章VisualStudio调试之断点基础篇里面介绍了什么是断点,INT是Intel系列CPU的一个指令,可以让程序产生一个中断或者异常。
程序中如果有中断或者异常发生了以后,CPU会中断程序的执行,去一个叫做IDT的部件查找处理这个中断(或者异常)的例程(Handler)。
IDT是操作系统在启动的时候初始化的,至于IDT的细节问题,例如什么是IDT,怎样编写一个IDT的例程,怎样初始化IDT,可以去网上搜索一些资料。
总之,这里我们只要知道,CPU在执行程序指令过程中,碰到INT3中断程序的执行,CPU然后去IDT表里面找到处理断点的例程入口。
这个例程要做的事情就是:
1. 先看看机器里面是不是安装了一个调试器—记住,这一步很重要,之所以重要以后的文章里面会介绍。
2. 如果机器里面没有安装调试器,那么操作系统就会终止程序的执行。
3. 否则操作系统启动调试器,并将调试器附到进程上。
4. 这样,我们才能在调试器里面检查程序内部变量的值。
前面文章里面的INT3(或者DebugBreak(),或者Debugger.Break())指令是我们自己在代码里面硬编码进去的,因此我们在VisualStudio里,在相应的代码行里面点一下,出现一个小红球,也就是说VisualStudio在程序指令集某个地方动态地添加了一个INT3指令。
现在的问题来了,VisualStudio是如何在程序中正确找到插入INT3指令的位置的?
或者更具体一些,我们在源代码(文本文件)里面设置断点的,VisualStudio需要把代码行翻译成在程序指令集中的位置。
VisualStudio之所以需要做翻译,是因为通常一行C++或者C#代码都会对应好几行汇编指令。
因此,VisualStudio需要一个额外的文件来执行这个翻译过程,这个额外的文件叫做调试符号文件(Symbols),是由编译器生成的。
VisualStudio系列的编译器,不论是C#、VB.NET还是C++编译器都会生成这个调试符号文件,.pdb文件。
所以如果你花一点时间看看Debug文件夹的话,你就会发现这个文件。
因此我们来看看VisualStudio支持的各种断点,并解释各种断点的实现方式
条件断点
首先我们先看看如何设置条件断点,条件断点有两种,一种是根据触发的次数来设置,另外一种是根据一条预置的条件来设置。
根据触发次数设置
比如说,你有一个循环,循环1000次,你知道有一个BUG总是在500次之后才会出现,因此肯定希望在循环内设置一个断点,但是前面500次都不会触发这个断点,否则连续按500次的F5的确不是一件轻松的差事。
图片看不清楚?
请点击这里查看原图(大图)。
根据预置条件来设置
如果你已经知道一些条件可能会引发Bug,那么根据条件来设置则最合适不过了。
如下图所示:
图片看不清楚?
请点击这里查看原图(大图)。
在“断点条件(BreakpointCondition)”对话框里面,只需要输入一条正常的C#、C++或者VB.NET的语句就可以了(当然,语法是根据你项目里面的源代码语法一致),这条语句的要求是必须返回bool值—否则就不是一个条件了。
第三个还有断点过滤器,当你在断点上,右键点击弹出的菜单里面,会有一个“过滤(Filter)”菜单,它允许你限制将断点仅设置在特定的线程上。
这里我就不细讲了,有兴趣的话,可以自己写一个多线程或者多进程程序试试这个功能。
知道断点的原理以后,理解条件断点应该就不会是问题了。
监视断点(WatchingPoint)
有的时候,你可能需要查看程序内部一些变量的值,但是你又不希望中断程序的执行。
例如你在调试一个网络协议栈,一个程序可能在接收数据包,你想看看数据包的格式,但如果中断程序的执行,会导致后续的数据包丢失。
因此,我们一般的做法就是在源代码里面加一些日志记录代码,这样可以将一些变量的值记录下来,以便后续分析。
如果日志在产品发布以后还需要的话,在源代码里面加入这些日志代码固然是一个好主意,但是如果你只是想临时看看一些变量的值呢?
这个时候,监视断点就很有用了,VisualStudio的监视断点就可以让你做到在不修改程序源代码的前提下,在调试器窗口中打印一些变量的值。
下图演示了监视断点的用法:
图片看不清楚?
请点击这里查看原图(大图)。
设置监视断点的步骤,或者说是注意事项吧:
1. 设置一个普通的断点
2. 右键单击刚刚设置的断点,在弹出菜单里面选择“WhenHit…”
3. 钩选第一个“打印一条消息(Printamessage)”复选框,输入一串文本,默认情况下,你输入的文本会被直接打印到调试的输出窗口里面来。
除了:
a. 以$符号开头的几个关键字。
比如$FUNCTION就会被替换成断点所在的函数名。
其他有一些关键字在“WhenBreakpointsIsHit”窗口当中有详细的说明。
b. 使用大括号{}包含起来的变量名,这样的字符串会被替换成变量的值。
这下面就是监视断点的效果,注意,你只能在VisualStudio的“输出(Output)”窗口中查看结果。
图片看不清楚?
请点击这里查看原图(大图)。
监视断点相对于日志记录的好处是,你不需要改动源代码,并且重新编译代码。
实际上VisualStudio实现监视断点的原理也很简单,就是插入一个普通的断点,断点触发之后处理并且打印在“WhenBreakpointsIsHit”窗口输出的表达式,最后自动恢复程序的执行。
VisualStudio调试之断点技巧篇
2009年09月30日 来源:
博客园 作者:
donjuan 收藏本文
-
函数断点
在前面的文章VisualStudio调试之避免单步跟踪调试模式里面我讲了如何设置函数断点,说实话,我个人喜欢设置函数断点,而不是在代码行里面设置断点。
一般来说,函数断点在下面几种情形下有用:
1. 例如调试一个网站程序,你通过分析网站的日志发现最有可能发生错误的函数,打开调试器并将调试器附加到程序上去,设置函数断点,重新执行网站……这样做的好处是,不用到处打开源文件去找出错的源代码行,调试器会自动打开源代码,并且在函数的入口处中断(岂不是很方便?
)。
2. 例如你在阅读源代码的时候,通常在读到虚函数调用的时候,因为通常这种调用都是通过基类指针调用的,而你又一时半会不知道到底有哪个继承类的Overloading函数会被调用到,函数断点可以告诉你。
3. 或者一种特殊的情形,你想读一个程序的源代码,但就是找不到入口Main函数,例如.NET程序,那么直接在VisualStudio里面按F11就能帮你找到入口函数—这是函数断点的一个特殊情形。
4. 比如你在调试WebService函数,设置函数断点也是一个快捷的调试方法,这个技巧跟技巧1类似。
当然,可能有些读者没有办法成功设置函数断点,如果设置函数断点失败,请阅读我的文章“不能设置断点的检查步骤”。
如果里面有一些名词术语(术语请参看文章:
调试术语)不知道或者不知道如何设置的话,呃,我会另写一篇文章讲解。
断点编程
有的时候你可能会碰到这种情况,触发一个断点以后,你发现需要修改一些值,才能使程序继续正确执行下去。
例如我以前在中文版本的操作系统上,使用sscli里面(调试版)的csc.exe编译器编译一些包含语法错误或者语法警告的C#源文件的时候,csc.exe总是会莫名其妙地报告内部严重错误,然后就崩溃了。
我将调试器附加上去以后,发现是一个ASSERT错误,ASSERT(lcid==0x409),表示sscli里面的csc.exe总是默认自己在英文操作系统(或者说英文环境)里面运行。
而且这一条语句会被执行很多次,手工修改lcid的值的确有点麻烦。
然后我找源代码找来找去都没有找到csc.exe在哪个地方获取到这个lcid值(当然我最后找到了,下一个技巧将告诉你我是怎么找到的),然而我又不想重启系统(呃……也许我就是那种宁愿花1个钟头去找节省花费5分钟重启系统的方法的那种人……)。
这个时候如果调试器可以自动帮你重置lcid的值该有多好?
幸运的是,VisualStudio提供了方法让你完成这样的工作。
下面是一个简化的代码,因为我一时半会找不到sscli了:
intlcid=System.Globalization.CultureInfo.CurrentUICulture.LCID;
Console.WriteLine("lcid={0}",lcid);
上面的代码在正常情况下,应该返回当前操作系统语言的lcid值,例如英文就是1033,中文的,呃……我忘记了。
假设我们现在希望做的是,每当lcid的值为1033的时候,就自动更正为0。
我们需要:
1. 在Console.WriteLine这一行上设置一个条件断点,条件断点的设置请参看VisualStudio调试之断点进阶篇:
图片看不清楚?
请点击这里查看原图(大图)。
2. 点击VisualStudio菜单栏里面的“工具(Tools)”—“宏(Macro)”—“宏资源管理器(MacroExplorer)”。
然后创建一个新的宏:
ImportsSystem
ImportsEnvDTE
ImportsEnvDTE80
ImportsEnvDTE90
ImportsSystem.Diagnostics
ImportsMicrosoft.VisualBasic
ImportsMicrosoft.VisualBasic.ControlChars
PublicModuleModule1
SubChangeExpression()
DTE.Debugger.ExecuteStatement("lcid=0;")
EndSub
EndModule
上面DTE.Debugger.ExecuteStatement的作用,你可以理解成在立即窗口中执行lcid=0;这条语句。
3. 右键点击刚才设置好的断点,在菜单里面选择“WhenHit…”,这一次在“WhenBreakpointisHit”窗口中勾选“Runamacro:
(执行一个宏)”,然后在下拉框里面选择刚才你创建的宏的名称。
如果你是第一次创建宏,名称应该是:
Macros.MyMacros.Module1.ChangeExpression。
4. 勾选“继续执行(Continueexecution)”,因为我们并不想让程序中断下来。
5. 点击确定以后,执行程序看一看结果,lcid是不是已经被自动改成0了?
图片看不清楚?
请点击这里查看原图(大图)。
数据断点
注意,这个技巧仅对C++程序调试有效(或者说native程序),而且你只能在中断模式下才能设置数据断点,另外你还只能在本机设置数据断点。
上一节的例子里,我们提到了,有的时候一个全局变量被修改了以后,你可能都找不到它是什么时候被修改的,于是夜已深,人已寐,你还在辛苦地调试到底是哪个鬼地方把这个变量的值修改了。
F11,F10,……,SHIFT+F11,……,F5,靠,调过了,重来,F11,F10,……
这种情况下,数据断点就很有用了,VisualStudio允许你在变量被修改的时候,中断程序的执行,是不是很酷?
默认情况下,你是找不到数据断点这个菜单的,需要执行下面的步骤把它拉出来:
1. 打开你要调试的项目。
2. 点击VisualStudio菜单栏里面的“工具(Tools)”—“自定义(Customize…)”。
然后在“自定义(Customize…)”窗口中选择“命令(Commands)”页签里面的“种类(Categories)”列表框里的“调试(Debug)”,找到“新数据断点(NewDataBreakpoint)”,将它拖到菜单栏里面相应的位置。
然后打开或者创建一个C++项目,我们以下面的源代码为例子:
#include"stdafx.h"
intg_Variable=0;
int_tmain(intargc,_TCHAR*argv[])
{
printf("Beforemodifyingdatabreakpoints"n");
g_Variable=1;
printf("Aftermodifyingdatabreakpoints"n");
return0;
}
我们现在要VisualStudio在更改g_Variable的时候中断程序的执行。
1. 单击F11,这样程序就会在_tmain函数里面中断了,我们也就有机会设置数据断点了。
2. 点击菜单里面的“新数据断点(NewDataBreakpoint)”。
注意,数据断点是通过监视内存地址某一段区域更改来实现的,因此你必须提供一个内存地址(或者说就是指针吧),这里g_Variable是一个整形变量,因此你需要使用“&g_Variable”的形式来创建一个数据断点,因为整形的大小是4个字节,因此数据断点监视的区域是4个字节,如下图所示:
图片看不清楚?
请点击这里查看原图(大图)。
3. 继续程序的执行,这时会弹出一个对话框,告诉你有一个内存地址的内容发生了变化(说明我们的数据断点生效了),这时代码行指向的是数据被修改的下一行代码,为什么会是下一行代码,下一篇文章会讲到:
呃,为什么数据断点只能在C++/C程序中才能设置?
是因为托管代码有垃圾回收。
而数据断点的执行原理应该是Windows内存管理里面的GuardPages概念和VirtualProtectEx函数的实现。
这个概念可以自己去查MSDN的内存管理方面的文档。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Visual Studio调试之断点基础篇 Studio 调试 断点 基础