分享

10.2 Visual Studio调试器

 无云666 2014-12-18

Visual Studio 2008中的调试器是这个IDE中最庞大、最复杂的工具。这样强大的一个工具,我们不可能逐一介绍所有场景。然而,我们希望能够在这一节里展示最常用的特性。接下来,继续将客户资料这个场景作为我们的例子。

10.2.1 调试菜单和工具栏

Debug(调试)菜单和相关工具栏向你提供了第一层访问启动调试会话、逐语句调试代码、管理断点和访问许多Visual Studio调试特性的功能。调试窗口有两种状态:待机(不活动)和调试模式。在待机模式状态下,Debug菜单允许你启动一个调试会话、附加代码到一个正在运行的进程,或者访问一些调试窗口。图10-12展示了待机状态下的该菜单。

10-12 待机状态的Debug菜单

在待机状态下,Debug菜单提供了多个特性来启动调试会话,它将代码附加到正在运行的进程,或者访问一些调试窗口。表10-1列出了待机状态下的Debug菜单中所有可用的特性。

10-1 待机状态下的Debug菜单项

<DIV align=center>

菜 单 项

描  述

WindowsBreakpoints(窗口→断点)

IDE中打开Breakpoints窗口。这个窗口能够访问该解决方案中的所有断点

WindowsOutput(窗口→输出)

IDE中显示Output窗口。Output窗口是IDE、编译器、调试器输出的许多运行时的消息日志的窗口。因此,信息不仅仅是调试会话

WindowsImmediate(窗口→即时)

IDE中打开Immediate窗口。这个窗口允许你执行命令。比如,在设计的时候,你可以通过Immediate窗口直接调用方法。这会启动应用程序,直接进入调试会话

Start Debugging(启动调试)

在调试模式下启动你的应用程序

</DIV>

(续)

<DIV align=center>

菜 单 项

描  述

Start Without Debugging(不通过调试来启动

启动应用程序。但它不把调试器连接到这个进程。在这种模式下,这接近(最终)用户运行程序的时候所看到的情形(而不是因为错误和断点进入IDE

Attach to Process(附加到进程)

允许你将调试器(和你的代码)附加到一个正在运行的进程(可执行的程序)。举个例子,假如你没有经过调试启动了应用程序,那么你要附加到运行进程并开始调试

Exceptions(异常)

打开Exceptions选项对话框。这个对话框允许你选择调试器如何中断给定的异常

Step Into(逐语句)

将程序从设计模式启动为调试模式。对于许多项目来说,点击Step Into命令会在程序的第一行可执行代码上调用调试器。这样,你可以从第一行逐语句进入程序。

Step Over(逐过程)

不在调试会话中的时候,逐过程命令仅开启应用程序,与点击“运行”按钮的方式相同

Toggle Breakpoint(切换断点)

在文本编辑器中,为当前活动代码行打开或关闭断点。如果你在IDE中没有活动的代码窗口,则该选项是非活动的

New BreakpointBreak at Function(新建断点→在函数处中断)

指定函数名来为其定义来创建新断点。这可能是有用的,如果你知道函数名但不想在代码文件中搜索它

New BreakpointNew Data Breakpoint(新建断点→新建数据断点)

这个选项仅对本地的C++应用程序有效。它允许你来定义一个断点,在特定内存位置的值改变时,中断进入到IDE

Delete All Breakpoints(删除所有断点)

从解决方案中删除所有断点

Disable All Breakpoints(禁用所有断点)

在解决方案中,禁用(而不是移除)所有断点

</DIV>

当调试器正在使用并且正在进行一个调试会话时,Debug菜单的状态就会发生改变。它现在提供了比待机状态更多的选项。这些选项包括单步执行代码、重启会话以及访问更多调试相关的窗口。图10-13显示了调试会话中的Debug菜单。

10-13 调试会话中的Debug菜单

来看看Debug菜单在调试会话中提供的众多选项。表10-2展示了在这种状态下从调试菜单可找到的许多项。在浏览这个表的时候,可以参考前面的几张图来获得给定项的上下文。

调试工具栏

Debug(调试)工具栏提供了快速访问一些Debug菜单中的关键项的功能。使用这个工具栏,你可以管理调试会话。比如,可以启动或继续一个调试会话,停止一个执行会话,逐语句(调试)代码,等等。

10-14显示了活动调试会话中的Debug工具栏。在设计模式中,Continue按钮的标题是Start Debugging(启动调试),还有很多项都被禁用了。我们给每个工具栏的每个项都做了标注。可以交叉参考这些标注与表10-2来获取更多信息。

 

10-14 中断模式中的Debug工具栏

10-2 活动调试会话中的Debug菜单项

<DIV align=center>

菜 单 项

描  述

WindowsBreakpoints(窗口→断点)

允许你在调试会话过程中打开Breakpoints窗口

WindowsOutput(窗口→输出)

在活动的调试会话中打开Output窗口来读出编译器和调试器发送的输出信息

WindowsWatch窗口→监视)

IDE中打开可能有多个监视窗口中的一个。在整个调试会话中都要仔细盯着Watch窗口中展示的项和表达式

WindowsAutos窗口→自动窗口)

打开Autos窗口。这个窗口显示代码的当前行和上一行的变量(以及它们的值)

WindowsLocals窗口→局部变量)

IDE中打开Locals窗口。这个窗口显示局部作用域(函数)内的变量

WindowsImmediate(窗口→即时)

打开Immediate窗口执行命令

WindowsCall Stack窗口→调用栈)

显示栈中的函数列表。也指明了栈的当前帧(函数)。就是这个选中的项定义了LocalsWatchAuto窗口中的内容

WindowsThreads窗口→线程)

IDE中显示Threads窗口,你可以查看并控制你所调试的应用程序中的线程

WindowsModules窗口→模块)

IDE中显示Modules窗口。这个窗口列出了你的应用程序使所用的DLLEXE

WindowsProcesses窗口→进程)

IDE中显示Process窗口。这个窗口列出了调试会话中所附加的所有进程

WindowsMemory窗口→内存)

打开Memory窗口以查看你的应用程序所使用的内存。只有在Options对话框中地址级(address-level)调试启用时才有效

WindowsDisassembly(窗口→反汇编

打开Disassembly窗口。这个窗口显示与编译器指令相符合的反汇编代码。只有在Options对话框中地址级调试启用时才有效

</DIV>

(续)

<DIV align=center>

菜 单 项

描  述

WindowsRegisters(窗口→寄存器)

打开Registers窗口,这样单步执行代码时就可以看到寄存器值的变化。只有在Options对话框中地址级调试启用时才有效

Continue

中断IDE后继续执行应用程序。应用程序继续在活动的代码行(即断点,指抛出错误的代码行,或者是一个使用Set Next Statement的行设置)上运行

Break All(全部中断)

允许你在调试会话中手动地中断应用程序进入到调试器(无须命中断点)。应用程序会跳到下一行执行。这个功能在访问诸如监视窗口等调试信息时很有用。还可在你的应用程序可能处于挂起状态时用来访问调试会话

Stop Debugging(停止调试)

结束调试会话。如果这个进程由Visual Studio启动,也同时结束调试的进程

Detech All(全部分离)

将调试器与正在执行的进程分离。这允许你的应用程序在调试器结束后继续执行

Terminate All(全部终止)

停止调试并终止所有所附加的进程

Restart(重新启动)

停止调试会话并重新启动。相当于按顺序点击Stop DebuggingStart Debugging

Attach to Process(附加到进程)

允许你将活动的调试会话附加到一个或多个另外的进程,如,一个正在执行的Web服务器,或一个Windows服务

Exceptions(异常)

打开Exceptions选项对话框。这个对话框允许你选择IDE如何在.NET Framework以及其他库中中断给定的异常

Step Into(逐语句)

Step Into命令使调试器前进一行。如果你选择Step Into调试一个函数,调试器会进入这个函数一行一行的执行

Step Over(逐过程)

功能和Step Into基本相同,但是有一个主要的差别:如果你选择Step Over调试一个函数,调用这个函数的行(和这个函数)会被执行,然后调试器会将下一行(函数调用之后)设置为要调试的下一行

Step Out(跳出)

告诉调试器执行当前的函数,然后在函数执行完成后回到调试(中断)状态。在你逐语句进入一个函数后却想只执行这个函数,然后在它完成后回到调试模式时,该功能可以派上用场

Quick Watch(快速监视)

当调试器在中断模式时打开Quick Watch窗口。Quick Watch窗口显示你所监视的一个变量或者表达式及其值

Toggle Breakpoint(切换断点)

打开或关闭活动的断点

New Breakpoint(新建断点)

弹出New Breakpoint对话框(详见表10-1

Delete All Breakpoints(删除所有断点)

在解决方案中删除所有断点

Disable All Breakpoints(禁用所有断点)

在解决方案中禁用所有断点,但是不删除它们。你也可以禁用个别断点。如果你想保留这些断点以方便以后使用,但是不希望在此刻命中它时,这个功能非常有用

Enable All Breakpoints(启用所有断点)

启用所有因调用Disable All Breakpoints而被禁用的断点

</DIV><DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

注解   在图10-14右部的Breakpoints窗口图标标记为“Debug windows”,实际上是一个下拉菜单。这个下拉菜单可供开发人员访问许多调试窗口。参见图10-13关于可从该工具栏项访问的菜单的示例

</DIV>

10.2.2 调试选项

可以通过这个Options对话框控制Visual Studio的许多调试选项。可以通过选项树中的Debugging节点访问这些调试开关。图10-15显示了General调试设置。

10-15 调试选项对话框

可以通过常规设置列表打开和关闭许多调试选项。这些选项如下所示:

q   启用和禁用断点筛选器;

q   在删除所有断点时是否弹出警告对话框;

q   启用或者禁用异常助手;

q   启用或者禁用just-my-code调试;

q   要求源文件与原始版本完全匹配(或者不);

q   其他选项。

这些选项背后的特性都将在本章讲到。

通过该选项对话框可以访问更多的调试设置。举个例子,你可以控制Edit and Continue如何工作(还可以关闭这个特性)。这里还可以设置在哪种类型代码(ManagedNativeScript)上启用实时调试。还可以选择是否使用额外的调试符号文件(.pdb.dbg)。当需要调试一个特殊的库时,如果没有它的源代码,这些文件就能够为你提供一些帮助,比如Windows的源代码或者第三方组件

这里有很多选项帮助你自定义调试体验。不过,在本章中我们会使用默认设置来调试。

10.2.3 逐语句、跳出和逐过程代码调试

也许对开发人员来说,最通常的调试操作是一行行地调试代码,并检查程序和调试器输出的数据。单步调试代码就是检查一行,执行一行,然后检查结果(然后再重复这个过程)。正因为这个活动如此重要,因此掌握如何在Visual Studio中高效地执行这些操作,是节省调试时间最重要的途径。这里,我们会覆盖每个单步操作并提供相应的示例。

1. 启动一个调试会话(逐语句代码调试)

Step Into(逐语句)命令在调试菜单和工具栏上都有(还可以通过F11快捷键调用)。调用这个命令有两种相关行为。第一种是当程序不在调试模式时调用。这时,程序会被编译、启动,第一行会被显示在调试窗口准备单步调试。这也就是逐语句调试程序。图10-16显示了调试模式下的一个窗口,这是调用逐语句命令的结果。

<DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

注解   对于Web应用程序使用Step IntoStep Over不是同样的效果。在站点这种情形中你的应用程序运行在调试模式中。调试器不会在程序的第一行中断。要达到同样的效果,你必须设置断点或者使用Run To Cursor选项(参看下一节)。

</DIV>

10-16 使用Step Into启动应用程序

在程序待机的时候调用Step Over命令(通过调试菜单、工具栏或者按F10)会产生和Step Into一样的效果。换句话说程序(假设不是一个站点)会被编译并在第一行代码启动调试。

l   运行到光标处

一个更方便(而且被忽略)的调试工具是Run To Cursor(运行到光标)处。该特性的工作方式正如其名。将你的光标定位在某行代码上并调用这个命令,程序会被编译并运行直到抵达光标定位的那行代码。在这一点上,调试器中断程序并呈现这行代码,以便你能继续单步执行。这个特性非常方便,因为这正是许多开发人员的工作方式。他们观察并指定了一行(或多行)代码,并想调试这行代码。他们不需要从第一行启动,并常常不希望为断点而费心。运行到光标处的有用之处在于它可以使得调试器和你在同一页代码上[1]。图10-17显示了从上下文菜单调用这个特性的情形

即使需要用户在代码执行到光标位置之前激活部分代码,Run To Cursor仍然是可以工作的。在这种情况下,它实际上成为了一个不可见的、临时的断点。比如,在这个例子中,它会向用户显示一个默认页面。这里,他们可以选择编辑他们的资料。如果你在编辑资料页面代码中某一行设置Run To Cursor命令,调试器会仍然执行程序并等待用户(测试者或开发人员)调用给定的那行代码。

<DIV style="mso-element: footnote-list">
<DIV style="mso-element: footnote" id=ftn1>

10-17 调用Run To Cursor命令

l   开始调试

还可以通过选择Debug菜单或者工具栏(或F5)上的Start Debugging(开始调试)选项(绿色的Play箭头)启动调试,但是不会中断代码,除非发生异常或命中断点。如果开发人员正在测试自己的代码,而并不想一直单步执行或者他使用了很多的断点,这是一个非常常用的操作。

l   全部中断

最后,如果程序正在运行,但是你想进入中断模式,你可以在任何时候从Debug菜单或者工具栏(或Ctrl+Alt+Break)调用Break All(全部中断)命令。全部中断命令用一个暂停图标表示。这样允许你在任何位置停止程序的执行并从调试器获取信息。要在一个非常长的运行进程或循环(看起来已使你的应用程序停止)中暂停的时候,Break All命令尤其有用。

2. 单步执行代码

在调试过程中,执行代码有3个选项。可以逐语句执行一行或一个函数、逐过程执行一个给定函数和跳出一个函数。我们来看看这3个选项。

l   逐语句

Step Into(逐语句)命令(C#中使用F11VB中使用F8)允许你逐行调试代码。调用这个命令将会执行当前指令,并设置光标到将要执行的下一行代码。逐语句命令跟其他相似命令最大的区别是如何处理包含函数调用的行代码。如果你当前调试这样的行代码,逐语句命令将会运行到被调用函数内部的第一行代码处(如果相应的调试符号已经加载了的话)。

举个例子,如图10-18所示。这里展示了Web服务例程中调用数据访问库中的函数Customer.GetById方法。在这种情况下,两个项目都被加载到解决方案中;因此,你可以访问两个项目的调试符号。所以,使用逐语句命令,调试器将会执行到ReturnDataTable函数的第一行。

10-19展示了跟踪调试到这个函数内部的情形。需要注意的是你已经进入到逐行调试这个函数。当然,当你到达这个函数的结尾,调试器将会返回到调用函数结束后的下一行处(如图10-18Web Service中的第30行)。

10-18 逐语句调试代码

10-19 逐语句调试进入函数内部的结果

l   逐过程

Step Over(逐过程)命令(F10)帮助你保持焦点在当前过程而不进入任何调用的函数内部。也就是说,逐过程命令将会逐行执行代码而不会进入到任何被调用函数、构造函数或者属性函数的内部。

举个例子,考虑图10-18。在这里,调试器位于调用函数ReturnDataTable处。如果你调用逐过程命令,将会执行完函数ReturnDataTable而不会进入其内部。也就是说,下一个将要执行的行将会是调用函数ReturnDataTable的下一行代码(第30行)。当然,如果函数内部抛出异常,调试器仍然会进入你的代码异常处。

l   跳出

Step Out(跳出)命令(Shift+F11)是另外一个有用的工具。它允许你告诉调试器执行完当前调用的方法后立即返回到中断模式。当你调试到一个代码很长的方法中而又想跳出方法的时候,这个跳出命令将会给你带来很多方便。另外,有时候你只想调试一个函数的一部分代码的时候,调试完想快速跳出也可以使用这个命令。

举个例子,如图10-19所示。回想刚才你逐语句进入的那个函数,代码如图10-18。假设你已经逐行执行了几行代码。当你检查了代码,核实连接数据库已经成功后,你想立即完成函数的执行,并且返回调用这个函数的断点处(如图10-18中的30行)。要达到这个目的,简单地调用跳出命令即可。

3. 继续执行

当你在调试状态下,Start Debugging(或者Run)命令将改变为Continue命令。在调试器中,当你中断在某一行代码的时候,继续命令按钮将会变为可用状态。这将允许你让程序继续运行而不需要单步执行后面的每一行代码。例如,假设你已经单步走过了你感兴趣的代码行,现在你又想从一个用户的观点去检查你的程序。使用继续按钮,告诉程序和编译器继续执行,直到遇到下一个断点或者发生异常的地方。

4. 结束调试

结束调试会话有好几种方法。一种比较通常的做法是关闭当前可执行程序。对于一个Web程序你可能通过关闭浏览器结束,而对于一个Windows程序你可以单击了关闭(或者X)按钮。在你的代码中调用终止程序也可以结束一个调试会话。

在调试窗口中,还有很多其他的功能。Terminate All(全部终止)命令将终止所有被调试器附加上的进程,并且结束调试会话。还有一个命令叫做Detach All(全部分离)。图10-20在工具栏上面显示了这两个按钮。全部分离只是简单地将调试器跟被调试进程分离而不中断他们。当你只是临时附加到一个运行中的进程,调试完后想让它继续运行的时候,这种功能是很有用的。

 

10-20 从进程中分离

10.2.4 在代码中指定断点

可以通过断点和跟踪点来控制调试器。有了这些,你就可以在想要中断代码或者接收程序信息的时候通知调试器。断点允许你指明调试器何时必须在某一特定行中断。跟踪点是Visual Studio 2005第一次引入的。它是一种当调试器到达某特定行的时候执行一种动作的断点。这通常包含发送一些与应用程序相关的信息到输出窗口。掌握这类断点的用法可以减少定位和修复代码错误的时间

1. 设置断点

设置断点最通常的做法就是找到你想让调试器中断的代码行,然后单击代码编辑器的指示器边距。这样操作后指定行的指示器边距将会出现一个红色的圆圈,并且指定代码行会以红色突出显示。当然,这些都是默认颜色。你也可以在ToolsOptions对话框的Environment节点的Fonts and Colors中调整

还有一些设置断点的其他方法。例如,你可以在指定行单击右键,从右键的Breakpoints菜单中选择Insert Breakpoint。你也可以从Debug菜单中选择New Breakpoint(或者在C#中按Ctrl+DN;也可在VB中按Ctrl+B)。这个选项将会弹出一个New Breakpoint对话框,在这里你可以设置一个函数断点。

l   设置函数断点

通过New Breakpoint(新建断点)对话框设置的断点称之为函数断点function breakpoint。之所以被称为函数断点仅仅是因为断点被设置在函数的入口处而已(但是不一定要这样)。在新建断点对话框,你可以手动设置中断的函数名称和在函数的哪一行,甚至这一行的哪个字符中断。

如果光标在某个函数内部或者在调用某个函数的地方,当你调用新建断点对话框的时候,函数名称会自动填充到对话框中的中断函数名称中。你也可以手动敲入中断函数的名称。图10-21显示了使用新建断点对话框的情形。记住你可以手动设置断点所放置的行甚至该行中的特定字符。

<DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

注解   如果在新建断点对话框中指定一个重载的函数时,你必须指定需要中断的实际函数名称。可以通过设置重载函数的参数类型来指定它。例如,当前函数LoadCustomer接受一个int类型的客户ID参数。如果你有一个重载函数通过客户名称(string类型)来检索客户,必须在函数字段指定重载函数为LoadCustomerstring

</DIV>

10-21 新建断点对话框

2. 识别Visual Studio的多种断点

Visual Studio 2008有很多断点图标。通过这些图标你可以很容易地识别出某代码行的断点类型。例如,一个实心圆表示一个普通断点,反之空心圆则表示一个被禁止的普通断点。我们提供了表10-3作为参考。它展示了一些比较常用的断点类型的图标及其描述。

10-3 断点图标

<DIV align=center>

图  标

描  述

表示一个标准的、启用的断点。当调试器执行到这一行时,将会停止应用程序并进入调试状态

表示一个标准的跟踪点。当调试器执行到这一行时,它将执行与该跟踪点关联的动作

断点图标中的加号表示这是一个高级断点,它包含了条件、命中次数或者过滤器

跟踪点图标中的加号表示这是一个高级跟踪点,它包含了条件、命中次数或者过滤器

中空的断点图标表示一个禁用的断点。该断点仍然与这行代码关联,只是在它再次启用之前调试器不会识别它

中空的图标可以用于多种断点图标,比如跟踪点高级项以及错误和含有警告的断点。在这些情况下,中空的图标表示该项已经被禁用了

表示一个断点警告,因为一个暂时的原因该断点无法设置。这可能是由于没有对网站启用调试模式或者调试符号没有加载。该图标由Visual Studio设置

表示一个跟踪点警告(参见上一条描述)

</DIV>

3. 使用断点窗口

Visual StudioBreakpoints(断点)窗口提供了一个方便的途径来组织和管理那些你想中断调试器的条件。通过点击调试菜单或者工具栏(或者在C#中按Ctrl+DB;也可以在VB中按Ctrl+Alt+B)都可以弹出断点对话框。图10-22显示了Visual StudioBreakpoints菜单。

10-22 断点窗口

l   断点窗口工具栏

断点窗口有它自己的工具栏,可以使用这个工具栏管理窗口中的断点。表10-4列出了该工具栏上有效的命令。

10-4 Breakpoints窗口工具栏

<DIV align=center>

描  述

打开新建断点窗口,允许用户对一个函数设置断点

允许你删除列表中的选中断点

删除窗口中的所有断点

切换所有断点的启用状态。如果所有断点都是启用的,点击该图标你可以保留这些断点,并把它们一起禁用。如果所有断点都是禁用的,该命令则可以将它们一起启用

允许你跳到与选中断点关联的源代码中

允许你跳到与选中断点关联的反编译代码中

允许你选择你希望在断点窗口中看到哪些列。每个列提供给定断点的不同信息。例如,你可以看到和每个断点相关联的条件、文件名、函数、过滤器、进程等

</DIV>

l   管理每个独立的断点

断点窗口同时也提供了管理每个独立断点的方法。在这里你可以设置与断点相关联的各种属性。例如,你可以通过切换和列表中断点关联的复选框来禁用或启用某个断点。图10-23显示了一个被禁止的跟踪点和一个独立断点的关联菜单。

10-23 管理独立的断点

注意,通过断点的上下文菜单你可以删除这个断点或者跳转到断点所在的代码。然而,更重要的是设置断点条件和过滤器。我们将在下一节讲解这两种操作。

4. 条件断点

设置一个简单的断点经常是不够的(或者效率太低)。例如,如果你要在代码中测试一个特定条件是否为真——一个看起来会导致异常的条件,就可以使用条件断点。这将为你的调试节省很多时间,而不需要经常地中断到函数内部,然后判断是不是没有到达你指定的条件。

可以在断点中添加5种条件:位置、条件、命中次数、筛选器、命中时间。可以从Breakpoints窗口给断点添加条件。选择给定的断点,然后右击。这会弹出给定断点的上下文菜单。图10-24给出了一个示例。从中可以看到列出的断点的各种条件。后面几节重点讲解可以在断点中添加的这几种条件。

10-24 访问断点的条件特性

l   设置断点条件

当某个条件被测试为真或者改变的时候,断点条件允许你中断调试器或者执行一个动作(跟踪点)。通常,你知道你正在调查的bug只有在某一个非常特定的条件下才会出现。对于查找一个偶发的bug,断点条件是一个非常好的解决方案。

要设置条件,首先选择一个你想设置条件的断点。你可以在右键菜单中选择Condition选项,这时弹出一个Breakpoint Condition(断点条件)对话框,如图10-25所示。注意当设置一个条件的时候,你可以使用智能感知特性(通过输入一个点或者按Ctrl+Space来调用智能感知)。

当设置一个条件时,你有两种选择:Is True(为真)或者Has Changed(已改变)。Is True选项允许你设置一个布尔类型的条件;当调试器计算条件为真的时候,会自动中断到指定代码行。

比如,来看一个简单的例子。假设只有某特定客户才会发生一个错误。你可能想在Customer服务类的Get函数中设置一个断点。然后,你可能会为添加Is Ture的条件id=1234id是函数的参数)。这将告诉编译器,如果函数参数的表达式不满足,调试器就不会在断点处停止。图10-26的对话框中显示了设置的条件。对话框同时也显示了可用于条件的两个选项。

 

<DIV align=center>

10-25 设置断点条件

10-26 设置断点条件对话框

</DIV>

“已更改”(Has Changed)选项告诉调试器在调试中当表达式的值发生改变时,调试器将会在断点处停住。编译器第一次编译时,为表达式赋初值。如果在这之后,表达式的值改变了,编译器会在断点处中断。当某个字段或是某个属性有初值,同时你想跟踪这个初值什么时候被改变了,这个功能将很有用。另外,“已更改”选项对于循环和if...then关系语句很有用,因为使用这两种关系的语句,你只关心你代码的结果是不是改变了一个特定的值。

<DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

提示   断点的信息将被永久保存在调试会话中。也就是说,当你在设置断点信息后关闭Visual Studio,你的断点在下次打开Visual Studio时依然存在。这样就不会每次都在设置一些复杂的调试选项上面浪费时间。断点保存在程序中,你随时可按照需要启用或者禁用。

</DIV>

l   位置中断

你可以基于文件中特定位置来编辑断点以设置中断。大多数断点都是位置断点。也就是说,它们自动知道文件、行数和中断特征。然而,还有很多时候你可能想编辑该信息。例如,假设你的代码和运行中的生成稍微不同步。你需要编辑断点来在不同的代码行上中断。

10-27显示了文件断点窗口,该问来自断点上下文菜单的位置选项。你也许使用这项功能来快速设置断点在特定行上,而不用搜索你的源代码。

l   设置断点过滤器

Breakpoint Filter(断点过滤器)允许你指定断点在某个具体的机器、处理器或是线程上有效。例如,如果你的错误有可能只发生在某台特定的机器或是某种特定的处理器上,那么你可以通过筛选器指定这种具体情况来调试错误。筛选器在高度分布式的复杂调试环境下是极其有用的。

使用这种特性的方法是,你可以指定机器的名称、处理器的名称或是编号,或者进程的名称或编号。也可以&(与)、||(或)、和!(非)等符号来进行组合。这种指定合集的方法允许你指定特定机器的特定进程上的某个特定的线程。图10-28显示了设置断点筛选器的对话框。图中假定正在运行的进程是开发Web服务器(WebDev.WebServer.EXE),断点状态被配置为停止。

 

<DIV align=center>

10-27 文件断点位置对话框

10-28 断点筛选器对话框

</DIV>

l   使用断点命中次数

使用Hit Count(命中次数)命令,你可以告诉调试器在给定的代码行被执行过多少次之后中断。一般来说,你可以找到比基于命中次数中断更好的条件。但是,当你无法确定一个恰当的条件,但是你知道你的代码在执行了多少次之后将会出现错误时,这个特性很有用。此外,设置命中次数选项可能在使用跟踪点的情况下更有用,这种情况下你会输出与你代码中发生问题有关的数据。你可能只想定期输出这样的数据。

10-29显示了Breakpoint Hit Count(断点命中次数)对话框。注意这个屏幕截图是在调试会话运行过程中获取的。可以在活动的调试会话中为断点添加任何条件。另外,需要注意的是当前命中次数被设置为1。可以通过单击Reset(重置)按钮来设置命中次数为0,再从那一点继续调试。

10-29 设置断点命中次数

这个对话框同样提供了一些设置实际命中次数的选项。在When the breakpoint is hit下面的下拉框列表中包含这些选项:

q   Break Always(总是中断)(默认,不会调用命中次数选项);

q   Break When the hit count is eqral to(中断,条件是命中次数等于);

q   Break When the hit count is a multiple of(中断,条件是命中次数几倍于);

q   Break When the hit count is greater than or equal to(中断,条件是命中次数大于或等于)。

<DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

提示   可以将以上我们讨论过的所有的断点条件组合到一个断点上。例如,可以为给定的断点添加条件和筛选器,由此可以创建更为具体的场景来使用断点调试应用程序。

</DIV>

10.2.5 使用跟踪点

通过跟踪点可以在执行到某个断点时,向Output(输出)窗口发送数据,或者运行一段Visual Studio宏。然后你就可以选择中断调试器(比如一个常规断点),处理另外一个条件,或仅仅是继续执行应用程序。如果需要你的应用程序在调试模式下运行,并且在遇到断点的时候记录一些运行日志,该功能就非常适用。这样你就可以从这个日志中获得很多有价值的信息,比如异常抛出时的特定条件和执行顺序。

可以显式地设置跟踪点,右击一行代码,然后从Breakpoint菜单中选择Insert Tracepoint(插入跟踪点)。还应从断点(断点窗口中)的上下文菜单中选择When hit命令(见图10-24),打开跟踪点对话框,该对话框的标题为When Breakpoint Is Hit,如图10-30所示。

10-30 设置跟踪点

When Breakpoint Is Hit对话框中的选项包括向输出窗口Print a message(打印消息)、Run a macro(运行宏)和Continue execution(继续执行)。你可以任意组合这些选项。首先,通过打印消息,你可以输出有关函数的数据。你可以使用多个关键字来输出数据,比如$FUNCTION代表函数名字,$CALLER代表调用者的名字。图10-28中显示了完整的列表。你还可以输出某个变量的值,只要把变量名放在大括号中。

通过继续执行选项,你可以决定这是一个真正的跟踪点还是一个包含跟踪点的断点。如果你选择继续,你就只能使用跟踪动作(消息或者宏)。如果你不选择继续,除了能够使用跟踪动作外,调试器将会在执行到该行的时候停止,就跟一个简单的断点一样。这从本质上就是给一个标准的断点使用When Hit动作。

最后,如果你选择运行宏选项,对话框将会列出你的环境中加载的所有的宏,你可以选择自己要执行哪个。

也可以对跟踪点动作使用条件。这样,仅当满足条件时才会执行跟踪点动作。

举例来说,在Web服务GetCustomerProfile中,我们设置了一个跟踪点(如图10-30所示)。该跟踪点在执行到该行代码的时候向输出窗口打印一条消息,然后程序继续执行。我们要打印的消息如下所示:

 

Function: $FUNCTION, Thread: $TID $TNAME, Id: {id}

 

消息中包括函数名、线程ID和名字(如果有的话),以及变量customerId的值。图10-31Output(输出)窗口中显示了该跟踪点被执行过两次后的结果。

10-31 跟踪点的结果

10.2.6 在调试器中查看数据

在调试器进入中断模式后,接下来的挑战就是过滤程序中的数据了。找到正确的数据可以帮助你更快地发现问题和修复问题。Visual Studio试图在你想要查看数据的时候,把所有的数据都准备好。比如,数据提示显示了代码编辑器中变量的值。关于Visual Studio何时何地显示需要调试数据的方式有很多相似的例子,我们会在随后的几节中阐述。

1. 观察变量

在调试会话中,一个常见的活动是观察与程序中众多类型相关联的值。这有多个窗口可以带来帮助。最明显的两个是Locals(局部变量)窗口和Autos(自动)窗口。

l   局部变量窗口

局部变量窗口显示当前调试范围内的所有变量和它们的值。这就为你呈现了当前正在执行的方法中所有的值。调试器自动设置该窗口中变量的值,这些变量根据名字按照字母顺序出现在列表中。并且这里也显示了一些层级结构。例如,某个变量对应于一个对象类型,那么该对象的成员会列在该变量的下面(以树的形式)。

10-32显示了一个局部变量窗口的例子。这里你可以看到,示例程序暂停在Customer.Get服务方法中。注意,customer变量(c)处于展开状态,下面显示了和该对象相关的各个属性和字段。它们的值显示在Value列中。

10-32 局部变量窗口

<DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

提示   可以在局部变量或自动窗口中编辑变量的值。右击变量,从上下文菜单中选择Edit Value(编辑值)。然后,就可以像在即时窗口中那样修改变量的值了。

</DIV>

l   自动窗口

通常局部变量窗口中提供的变量显得太多了,尤其是给定进程或者函数中包含的变量过多的时候。为了限制你所能看到的变量数量,你可以使用自动窗口。该窗口显示当前执行行和前一行代码中的变量和表达式的值。这样你就可以专注于正在调试的变量了。

10-33显示了与图10-32中显示的同一行代码所对应的自动窗口。这里你应该注意,它们显示的内容有哪些差别。同时,你还应该注意Visual Studio甚至添加了一些与代码不同的表达式。比如,对dt.Rows[O]["contact_via_email"]的调用出现在观察项中。

10-33 自动窗口

l   监视窗口

可以将想要观察的变量和表达式添加到Visual Studio的监视窗口中。这样,你就可以决定你对哪些项目感兴趣。监视窗口的外观和行为与局部变量和自动窗口很相似。此外,你放到监视窗口中的项从一个调试会话到另一个调试会话始终保持不变的。

可以从调试菜单或者工具栏来访问每个监视窗口。4个监视窗口分别命名为Watch 1Watch 4。你可以使用4个监视窗口来构建4个自定义的观察列表。该功能尤其适用于每个自定义列表对应于应用程序中一个独立范围的情况。

可以从代码编辑器或者QuickWatch窗口向监视窗口添加变量或者表达式。如果是从代码编辑器中,首先选择一个变量或者突出显示一个表达式,单击鼠标右键,选择Add Watch(添加监视)菜单项。这将会将突出显示的变量或者表达式放到监视窗口中去。你也可以将突出显示的项拖到监视窗口

l   快速监视

QuickWatch(快速监视)窗口和其他监视窗口非常相似。你可以通过它关注一个单独的变量或者表达式。由于DataTips的存在,快速监视窗口用得非常少。从快速监视窗口,你可以写一些表达式,然后将其添加到监视窗口。在写表达式的时候,你会用到智能感知特性。图10-34显示了快速监视窗口。

在快速监视窗口中点击Reevaluate,窗口中的项会被重新求值。点击Add Watch按钮,相应的变量[1]会被添加到Watch 1窗口中。

2. 数据提示

可以使用数据提示突出显示代码编辑器中的一个变量或者表达式,并且不离开编辑器就能够获得观察信息。这个特性正是开发人员所期望的行为。例如,如果正在查看某一行代码,那么可以突出显示该行中的部分代码,对其进行求值。以前,这需要创建快速监视。但现在你也可以悬停在项之上,其数据会在数据提示中展开。

10-35提供了一个例子。这里光标位于变量customer的位置,变量类型为Customer。单击加号打开这个变量,你会看到该对象的众多成员。可以使用窗口底部的箭头滚动查看该列表。也可以右击列表中的某个成员,编辑它的值、复制或者将其添加到监视窗口。有些项旁边有一个放大镜图标,可以使用它来给给定的项选择一个特定的Visualizer(可视化工具)(马上会讲到)。

<DIV style="mso-element: footnote-list">
<DIV style="mso-element: footnote" id=ftn1></DIV><DIV style="mso-element: footnote">

10-35 数据提示窗口

也可以选择一个表达式,然后对其以数据提示的形式求值。例如,可以选择图10-35中第39booldt.RowsO["contanct_via_email"],数据提示会显示该变量,其值为true

<DIV style="BORDER-BOTTOM: #999999 1pt solid; BORDER-LEFT: medium none; PADDING-BOTTOM: 3pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #999999 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">

提示   数据提示窗口经常会挡住下面的代码。有时候,可能需要看到下面的代码,而又不希望离开当前的数据提示。这时,可以按住ControlCtrl)键,在按住该键的时间内数据提示是透明的。

</DIV>

3. 可视化数据

在观察变量的值时,你真正想要的其实是对象背后的数据。有时候对象模型本身反而使得数据看上去晦涩难懂。例如,假设要查看一个DataSet对象中的数据。要找到想要的数据,需要在监视窗口或者数据提示中挖掘很多层。仅仅为了获得对象中包含的一个数据,你不得不在对象模型内部来回地查找。如果使用过Visual Studio之前的版本一段时间,你就知道这是多么让人厌倦了。

为此,Visual Studio提供了一个快速、便捷的访问对象中包含数据的方式。这是通过一个新工具可视化工具实现的。可视化工具指的是将对象数据以一个有意义的形式展现出来

默认情况下,Visual Studio已经自带了几个可视化工具。它们包括:

q   HTML——将HTML数据以类似浏览器的对话框显示出来,就跟用户真正看到的一样;

q   XML——将XML显示为结构化的格式;

q   Text——将字符串显示为容易阅读的格式;

q   DataSet——用于显示DataSetDataViewDataTable对象的内容。

Visual Studio中还包含了一个编写和安装可视化工具的框架。可以自己写一个可视化工具,然后将其插入到调试器中。也可以下载别的可视化工具并安装到Visual Studio中。数据结构和检视数据的方式有多少种,可视化工具就有多少种可能。比如,可以以树的形式来展示层级结构的数据;以图形工具来显示图像数据结构。

可以从很多数据观测点来调用可视化工具。包括监测窗口和数据提示。可视化工具用一个放大镜图标表示。图10-35显示了使用该图标启动可视化工具的情形。现在可以从数据提示中启动可视化工具来检视DataSet,而无需为了得到数据而在一个复杂的对象层级结构中挖掘很多层。图10-36显示了在示例程序中使用可视化工具检视DataSet对象的例子。

10-36 DataSet可视化工具

10.2.7 使用编辑并继续特性

使用Edit and Continue(编辑并继续),你可以在调试的过程中修改代码,而不必停止调试会话。可以编辑一行代码,甚至修复一个bug,而不需要离开中断模式。那些用过早于.NET版本Visual Basic的开发人员应该记得这一强大的工具。.NET中对于这一特性的需求特别强烈。幸好,在VS 2005Visual BasicC#都支持编辑并继续特性。在VS 2008中,这一特性也添加到了Visual C++中。

调用编辑并继续特性没有什么技巧。只需要在一个调试对话中修改你的代码,然后使用Step命令或者Continue命令运行完你的代码就可以了。

这一特性默认是启用的。如果它被关闭了,可以从Tools菜单中的Options对话框重新启用它。

并不是所有的代码修改都可以使用编辑并继续功能,实际上它只能用于很小的修改。作为最佳实践,你不应该在调试状态下大幅地调整代码。如果你的修改在方法体内,通过编辑并继续的可能性会高得多。否则,如果修改是在方法体外,绝大多数情况下都需要重启调试器。一些常见的不适用于编辑并继续的修改包括:

q   编辑当前活动的语句;

q   修改通向当前活动语句的堆栈的调用;

q   添加新的类型、方法、域、事件或属性;

q   改变函数签名。

更详尽的列表,请在MSDN中搜索Edit and Continue(编辑并继续)。在那里你可以链接到所选语言的编辑并继续文档。然后可以选择“Supported Cole Changes”(支持的代码改动)链接。在此可得到所选语言的支持改动的列表和不支持改动的列表。

</DIV></DIV>
</DIV></DIV>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多