利用公共语言运行时 (CLR) 调试 API,工具供应商可以编写调试器来调试运行于 CLR 环境中的应用程序。 要调试的代码可为 CLR 支持的任何代码种类。CLR 调试 API 主要是使用非托管代码实现的。 因此,调试 API 呈现为一组组件对象模型 (COM) 接口。 API 由以下各项组成:

  • CLR 实现的 COM 对象和接口的集合。

  • 必须由调试器实现的 COM 回调接口的集合。

CLR 调试方案

以下各部分介绍公共语言运行时调试 API 如何处理典型的调试方案。 请注意,该运行时直接支持某些方案,并且可与当前方法进行互操作来支持其他方案。

进程外调试

在进程外调试中,调试器不在所调试的进程中,而在另一个进程中(即在调试对象外部)。 此方案减少了调试器与调试对象之间的交互。 因此,它可以更准确地描述进程。

CLR 调试 API 直接支持进程外调试。 API 将处理调试器与调试对象托管部分之间的所有通信以支持托管代码调试。

虽然 CLR 调试 API 用于进程外,但某些调试逻辑(例如线程同步)却与调试对象在同一进程内。 大多数情况下,这是应该对调试器保持透明的实现详细信息。 有关线程同步的更多信息,请参见 CLR 调试体系结构。 调试 API 的缺点是在进程外使用时无法用它来检查崩溃转储。

进程内调试

在 .NET Framework 1.0 和 1.1 版本中,CLR 调试 API 支持受限的进程内调试,在受限的进程内调试中,探查器可以使用调试 API 的检查功能。 在 .NET Framework 2.0 中,进程内调试被替换为一组与分析 API 更加一致的功能。 有关这些更改的更多信息,请参见分析概述中的堆栈快照和对象检查功能。

远程进程调试

在远程进程调试中,调试器用户界面与所调试的进程不在同一计算机上。 如果调试器和调试对象在同一计算机上运行时相互影响,此方案可能很有用。 这种影响可能由下列原因引起:

  • 有限资源。

  • 位置依赖性。

  • 影响操作系统的 Bug。

CLR 调试 API 不直接支持远程进程调试。 基于 CLR 调试 API 的调试器仍然必须存在于调试对象进程外。 因此,此解决方案需要在调试对象所在的计算机上有一个代理进程。

非托管代码调试

托管代码和非托管代码通常共存于同一进程中。 通常需要同时调试这两种代码类型。

CLR 调试 API 支持跨越托管代码和非托管代码之间的界限执行单步调试,但是不直接支持非托管代码调试。 然而,通过共享 Win32 调试功能,CLR 调试 API 可以与非托管代码调试器共存。

此外,CLR 调试 API 为调试进程提供了两种选择方案:

  • “软附加”选择方案,在此选择方案中只调试进程的托管部分。 软附加到进程的调试器随后可以从该进程中分离出来。

  • “硬附加”选择方案,在此选择方案中将调试进程的托管和非托管部分,并且通过调试 API 公开所有 Win32 调试事件。

混合语言环境

在组件式软件中,可以用不同的语言构建不同的组件。 调试器必须知道语言差异,以便能够用正确的格式显示数据、用正确的语法对表达式进行求值等等。

CLR 调试 API 不对混合语言环境提供任何直接支持,因为 CLR 没有源语言概念。 通过应用调试器的现有源映射功能,应该能够将给定的函数映射到实现该函数所采用的语言。

多个进程和分布式程序

组件式程序可以包括协作组件,这些组件可以在不同的进程上运行,甚至可以在整个网络中的不同计算机上运行。 调试器应该能够跟踪进程之间以及计算机之间的执行逻辑,以提供所发生事件的逻辑视图。

CLR 调试 API 不对多进程调试提供任何直接支持。 而使用 API 的调试器应该直接提供这类支持,并且用于执行此操作的现有方法应该继续有效。

API 类别

调试 API 包括以下三组接口,这三组接口通常都由 CLR 调试器使用,并均以非托管代码的形式实现:

  • 支持对 CLR 应用程序进行调试的接口。

  • 允许访问符号调试信息的接口,该信息通常存储在程序数据库 (PDB) 文件中。

  • 支持在计算机上查询进程和应用程序域的接口。

调试 API 依赖于另外两组接口:

  • 用于处理静态程序信息(比如类和方法类型信息)检查的元数据 API。

  • 用于为托管代码调试器支持源级别调试的符号存储区 API。

调试接口也可以划分为下表中所示的功能类别。

API 类别

说明

注册

调试器调用以向 CLR 注册并在发生特定事件时请求通知的接口。

通知

CLR 用于将各种事件通知调试器并返回请求的信息的回调接口。 这些接口必须由调试器实现。

断点

调试器调用以检索有关断点的信息的接口。

执行

调试器调用以控制调试对象执行和访问调用堆栈的接口。

信息

调试器调用以获取有关调试对象的信息的接口。

Enumeration

调试器调用以枚举对象的接口。

修改

调试器调用以修改所调试代码的接口。

以下各部分介绍公共语言运行时 (CLR) 调试服务提供的功能。

附加到程序或启动程序

CLR 允许您将调试器附加到正在运行的程序或启动进程。 CLR 调试服务允许您将调试器附加到引发未经处理的异常的程序,因此它支持实时 (JIT) 调试。 但是,未在可调试模式下运行的程序提供的调试信息可能较少。 程序可以始终使自身运行在可调试模式下以避免此问题。 有关可调试模式的更多信息,请参见以下内容:

  • 调试、跟踪和分析

  • 应用特性

  • DebuggableAttribute

控制执行

CLR 调试服务提供了多种控制程序执行的方式。 这些方式包括断点、单步执行、异常通知、函数求值以及其他与程序的启动和关闭相关的事件。

CLR 调试 API 只为托管代码提供执行控制。 如果要在非托管代码中实施执行控制,您必须在调试器中单独实现该功能。

断点

通过指定代码以及应进行中断的位置的 Microsoft 中间语言 (MSIL) 或本机偏移量,您可以创建断点。 随后,当遇到断点时,调试器将得到通知。 调试 API 不直接支持条件断点。 但是,通过对表达式求值以响应断点并决定是否将有关停止的信息通知用户,调试器可以实现这些断点。

单步执行

CLR 调试服务提供了各种各样的单步执行功能。 程序可以采用一次一条指令的方式单步执行代码(单一单步执行),也可以采用一次一系列指令的方式单步执行代码(范围单步执行)。 它可以跳过、进入并单步执行或者跳出函数。 如果发生了中断单步执行操作的异常,CLR 调试服务还可以通知调试器。

尽管调试服务不直接支持单步执行非托管代码,但是,当单步执行操作到达非托管代码时,调试服务将提供回调以将控制交给调试器。 调试服务还提供了允许调试器确定何时将从非托管代码进入托管代码的功能。

CLR 未直接提供源级别单步执行。 调试器可通过将范围单步执行与它自己的源映射信息结合使用来提供此功能。 您可以使用符号存储区接口来获得源级别信息。 有关这些接口的更多信息,请参见诊断符号存储区(非托管 API 参考)。

异常

利用 CLR 调试服务,当托管代码中出现首次异常和第二次异常时,调试器都会收到通知。 每次都可以使用引发的对象进行检查。

CLR 不会处理非托管代码中的本机异常,除非这些异常向上传播到了托管代码。 但是,您仍然可以使用与 CLR 调试服务共享的 Win32 调试服务来处理非托管异常。

程序事件

在发生多个程序事件时,CLR 调试服务将通知调试器。 这些事件包括进程创建和退出、线程创建和退出、应用程序域创建和退出、程序集加载和卸载、模块加载和卸载以及类加载和卸载。 为了保证良好的性能,您可以为模块禁用类加载和卸载事件。 默认情况下,类加载和卸载事件处于禁用状态。

线程控制

CLR 调试服务提供了用于挂起和继续执行个别(托管)线程的接口。

检查程序状态

当进程处于停止状态时,CLR 调试服务可以详细检查正在运行托管代码的进程的各部分。 可以对进程进行检查以获得物理线程的列表。

可以对线程进行检查以检测其调用堆栈。 可以在两个级别分解线程的调用堆栈:链级别和堆栈帧级别。 调用堆栈首先分解为链。 链是一个连续的逻辑调用堆栈段。 它包含托管或非托管堆栈帧,但不能同时包含这二者。 此外,单一链中的所有托管调用帧共享同一个 CLR 上下文。 链可以是托管链,也可以是非托管链。

每个托管链都可以另行分解为单一堆栈帧。 每个堆栈帧表示一个方法调用。 您可以查询堆栈帧来获取它正在执行的代码,或者获取它的参数、局部变量和本机寄存器。

非托管链不包含堆栈帧。 相反,它提供分配给非托管代码的堆栈地址范围。 非托管代码调试器负责对堆栈的非托管部分进行解码,并提供堆栈跟踪。

备注

CLR 调试服务不支持在源代码中出现的局部变量的概念。调试器负责将局部变量映射到它们的分配。

CLR 调试服务还提供了对全局变量、类静态变量和线程局部变量的访问。

修改程序状态

CLR 调试服务允许调试器在执行期间更改指令指针的物理位置,尽管这可能是一种危险的操作。 如果满足以下条件,则可以成功更改指令指针:

  • 当前指令指针和目标指令指针都位于序列点处。 序列点大致表示语句边界。

  • 目标指令指针没有位于异常筛选器、catch 块或 finally 块中。

  • 当前指令指针位于 catch 块内,目标指令指针不在 catch 块外部。

  • 目标指令指针处在与当前指令指针相同的帧中。

当指令指针的物理位置发生变化时,位于当前指令指针位置的变量将映射到位于目标指令指针位置的变量。 位于目标指令指针位置的垃圾回收引用将正常初始化。

指令指针更改后,CLR 调试服务会将任何缓存的堆栈信息标记为无效,并在下次需要时刷新该信息。 可缓存指向堆栈信息(如帧和链)的指针的调试器应在更改指令指针后刷新此信息。

调试器还可以在程序停止时修改程序的数据。 调试器能够采用与检查类似的方式在函数运行时更改函数的局部变量和参数。 调试器还可以更新数组和对象的字段,以及静态字段和全局变量。

使用“编辑并继续”

在调试会话期间,可以使用“编辑并继续”功能执行下列操作:

  • 编辑源代码。

  • 重新编译修改后的源代码。

  • 保留所调试的可执行文件的其余运行时状态。

  • 继续运行调试会话,而不必从头开始重新运行可执行文件。

对函数进行求值

若要对用户表达式和对象的动态属性进行求值,调试器需要能够运行所调试进程的代码。 CLR 调试服务使调试器能够进行函数或方法调用,并使之在调试对象的进程内运行。

CLR 允许调试器停止此类操作,因为这种操作可能很危险(例如,它可能会使现有代码死锁)。 如果成功停止了求值,则会将线程视为如同从未进行过求值一样,只是部分求值会对局部变量产生一些副作用。 如果函数通过某种方式调入了非托管代码或块,则可能无法结束求值。

函数求值完成后,CLR 将使用回调来通知调试器求值是否正确完成或者函数是否引发了异常。 可以使用 ICorDebugValue 和 ICorDebugValue2 方法来检查求值的结果。

要在其中进行函数求值的线程必须在托管代码中可安全进行垃圾回收的安全点处停止。 (未经处理的异常也允许函数求值。)在未优化的代码中,这些安全点非常常见;大多数断点或 MSIL 级单步执行操作将在安全点 1 完成。 但是,这些点在优化的代码中可能非常少见。 有时整个函数可能没有任何安全点。 可安全进行垃圾回收的安全点的频率因函数而异。 即使在未优化的代码中,也可能不会在位置 1 停止。 在优化或未优化的代码中,ICorDebugController::Stop 方法很少位于安全点处。

CLR 调试服务将在线程上设置一个新链,以便开始函数求值和调用请求的函数。 求值一旦开始,就可以看到调试 API 的各个方面:执行控制、检查、函数求值等等。 支持嵌套求值,并将按正常方式处理断点。

动态地注入代码

某些调试器允许用户在**“即时”**窗口中输入并执行任意语句。 CLR 调试服务支持此方案。 在合理的范围内,您可以动态注入的代码没有任何限制。 (例如,不允许非本地 goto 语句。)

动态代码注入是通过将“编辑并继续”操作与函数求值结合使用实现的。 要注入的代码包装在函数中,并通过使用“编辑并继续”注入。 然后,将对注入的函数进行求值。 如果需要,您可以向包装函数提供 ByRef 参数,使副作用产生直接并永久的效果。

支持的环境

CLR 支持的所有处理器和操作系统上都提供了 CLR 调试功能,但以下情况例外:

  • 64 位操作系统上不支持“编辑并继续”及混合模式调试。 SetIP 方法(ICorDebugILFrame::SetIP 和 ICorDebugNativeFrame::SetIP)在 64 位操作系统上有额外的限制。 其余的功能在所有处理器上都相同(但存在特定于处理器的数据表示形式,例如,指针大小、寄存器上下文等等)。

  • 基于 Win9x 的操作系统上不支持“编辑并继续”及混合模式调试。 其余的功能在所有操作系统上都应相同。 但是,仍存在个别功能的文档中说明的某些特定例外情况。

CLR 调试概述的更多相关文章

  1. CLR调试时的sos.dll/clr.dll/mscorwks.dll/mscordacwks.dll等动态库的版本对应

    大家都知道,在调试托管代码时,一定会加载到sos/clr/mscorwks/mscordacwks这些动态库,才能够很好的完成我们的调试工作,那么他们的版本对应关系是怎样的呢,特别是clr.dll/m ...

  2. WinDbg常用命令系列---.cordll (控制CLR调试)

    .cordll (控制CLR调试) 简介 .cordell命令控制托管代码调试和Microsoft.NET公共语言运行库(CLR). 使用形式 .cordll [Options] 参数 Options ...

  3. CLR 调试体系结构

    公共语言运行时 (CLR) 调试 API 专门用作操作系统内核的一部分. 在非托管代码中,当程序生成异常时,内核将暂停执行进程,并使用 Win32 调试 API 将异常信息传递给调试器. CLR 调试 ...

  4. CLR调试报错“Visual Studio远程调试监视器 (MSVSMON.EXE) 的 64 位版本无法调试 32 位进程或 32 位转储。请改用 32 位版本”的解决

    Win7 64位电脑上进行visual studio的数据库项目的CLR存储过程进行调试时,报错: ---------------------------Microsoft Visual Studio ...

  5. clr调试扩展和DAC

    SOS.DLL.SOSEX.DLL这两个就是用来对.NET程序在Windows调试工具中起到翻译作用的调试器扩展.简单讲就是,这两个组件是.NET项目组专门开发出来用来对.NET应用程序进行方便调试用 ...

  6. windbg调试托管代码 .Net clr

    现在很多的程序都是多语言混合编程的,比如我司的产品,就是用C++/.net clr混合编制的.那么当我们调试这样的程序时,一定要注意,比如有时我们只看到c++的栈和名称,而.net clr的代码确看不 ...

  7. chrome inspect 远程调测:Chrome on Android之一 普通调试

    本文PC环境: Chrome: 版本 33.0.1750.22 dev MAC OS:OS X 10.9.1 特别注意:Chrome DevToolsl使用时会联接到appspot.com,而此网址被 ...

  8. Android上的远程调试

    来源: http://www.seejs.com/archives/296 目录 远程调试概述 使用 Chrome 的 ADB 扩展进行远程调试 1. 安装 ADB 扩展 2. 启用你的移动设备上的 ...

  9. 《k8s-1.13版本源码分析》-源码调试

    源码分析系列文章已经开源到github,地址如下: github:https://github.com/farmer-hutao/k8s-source-code-analysis gitbook:ht ...

随机推荐

  1. Selenium+Java(八)Selenium下拉框处理

    Selenium定位下拉框中的元素与普通元素定位有所不同,下面介绍三种定位下拉框元素的方法. 下拉款HTML代码如图所示: 一.通过text定位 //获取下拉框对象 Select city = new ...

  2. 在Java中如何设置一个定时任务,在每天的一个时间点自动执行一个特定的程序

    Quartz定时机制 首先导入jar包到程序内 quartz-all-1.6.0.jar 然后创建一个XML TimeConfig.xml 名字可以自己定义 <?xml version=&quo ...

  3. 自学Python编程的第\七天----------来自苦逼的转行人

    2019-09-17-23:09:48 今天学的内容是有关小数据池的,学的有点懵逼,感觉越来越难学了,但是得坚持下去 明天学习下一个课程时,感觉要跟不上,看来明天得先看好几遍今天的内容 不然肯定会听的 ...

  4. Python进阶(十三)----面向对象

    Python进阶(十三)----面向对象 一丶面向过程编程vs函数式编程vs面向对象编程 面向过程: ​ 简而言之,step by step 一步一步完成功能,就是分析出解决问题所需要的步骤,然后用函 ...

  5. kali之nmap

    nmap简介 Nmap,也就是Network Mapper,最早是Linux下的网络扫描和嗅探工具包.可以扫描主机.端口.并且识别端口所对应的协议,以及猜测操作系统 Ping扫描(-sP参数) TCP ...

  6. 【重大更新】Qlik Sense September 2018重磅发布(附下载)

    作为数据分析领域领导者,Qlik,始终致力于通过产品创新来帮助企业客户撬动数据力量.近日,Qlik Sense September 2018如期而至,不仅对原有版本进行了众多优化,还发布了一系列能够提 ...

  7. php在虚拟机和windows上的应用

    学习目标:linux+apache+php合在一起的应用                在windows中三者的的关联及应用 php是apache的一个外挂程序,必须依靠web服务器才可以运行.当客户 ...

  8. C/C++ 关于数组和指针的总结

    1.数组的声明形如a[d],其中a是数组的名字,d是数组的维度,编译的时候数组的维度应该是已知的,所以维度d必须是一个常量.如果要定义一个不知道元素个数的以为数组,那么请使用vector容器: uns ...

  9. day 09作业 预科

    作业 1.简述定义函数的三种方式. 定义函数的三种方式为空函数,有参函数和无参函数 2.简述函数的返回值. 如果函数没有返回值,会直接返回到None: 函数可以通过return返回出返回值: retu ...

  10. jquery.widget开发(1)

    jquery.widget是挂件,通过挂件模式挂载在jquery对象上,其实本质上也就是用了$.fn.extend和$.extend的扩展. http://blog.sina.com.cn/s/blo ...