内容简介 《.NET高级调试》内容主要包括:调试工具简介、CLR基础、基本调试任务、程序集加载器、托管堆与垃圾收集、同步、互用性以及一些高级主题,如事后调试、一些功能强大的调试工具和.NET 4.0中的新功能等。这是一本介绍如何通过非托管调试器(包括WinDBG、NTSD和CDB等)来调试.NET。应用程序的书籍。《.NET高级调试》内容翔实、条理清晰,适合软件开发人员、软件测试人员、质量保证人员和产品技术支持人员等参考。 作者简介 Mario Hewardt,是微软公司的一位资深开发经理,在WirIdows系统级开发领域拥有十余年的开发经验。他目前领导开发团队负责Microosoft在线IT管理解决方案的开发。Hewardt是《Windows高级调试》(机械工业出版社2009年5月出版)的作者之一。 目录 对本书的赞誉 译者序 序 前言 关于作者 第一部分 简介 第1章 调试工具简介 1.1 Windows调试工具集 1.2.NET 2.0可再发行组件 1.3.NET 2.0 SDK 1.4 SOS 1.5 SOSEX 1.6 CLR分析器 1.7 性能计数器 1.8 .NET反编译器 1.9 PowerDbg 1.1 0托管 调试助手 1.1 1小结 第2章 CLR基础 2.1 高层概览 2.2 CLR和Windows加载器 2.2.1 加载非托管映像 2.2.2 加载.NET。程序集 2.3 应用程序域 2.3.1 系统应用程序域 2.3.2 共享应用程序域 2.3.3 默认应用程序域 2.4 程序集简介 2.5 程序集清单 2.6 类型元数据 2.6.1 同步块表 2.6.2 类型句柄 2.6.3 方法描述符 2.6.4 模块 2.6.5 元数据标记 2.6.6 EEClss 2.7 小结 第3章 基本调试任务 3.1 调试器以及调试目标 3.2 符号 3.3 控制调试目标的执行 3.3.1 中断执行 3.3.2 恢复执行 3.3.3 单步调试代码 3.3.4 退出调试会话 3.4 加载托管代码调试的扩展命令 3.4.1 加载SOS调试器扩展 3.4.2 加载SOSEX调试器扩展 3.5 控制CLR的调试 3.6 设置断点 3.6.1 在JIT编译生成的函数上设置断点 3.6.2 在还没有被JIT编译的函数上设置断点 3.6.3 在预编译的程序集中设置断点 3.6.4 在泛型方法上设置断点 3.7 对象检查 3.7.1 内存转储 3.7.2 值类型的转储 3.7.3 转储基本的引用类型 3.7.4 数组的转储 3.7.5 栈上对象的转储 3.7.6 找出对象的大小 3.7.7 异常的转储 3.8 线程的操作 3.8.1 ClrStack 3.8.2 Threads 3.8.3 DumpStack 3.8.4 EEStack 3.8.5 COMState 3.9 代码审查 3.9.1 反汇编代码 3.9.2 从代码地址上获得方法描述符 3.9.3 显示中间语言指令 3.10 CLR内部命令 3.10.1 获得CLR的版本 3.10.2 根据名字找到方法描述符 3.10.3 对象同步块的转储 3.10.4 对象方法表的转储 3.10.5 托管堆和垃圾收集器信息的转储 3.11 诊断命令 3.11.1 找出对象的应用程序域 3.11.2 进程信息 3.12 SOSEX扩展命令 3.12.1 扩展的断点支持 3.12.2 托管元数据 3.12.3 栈回溯 3.12.4 对象检查 3.12.5 自动死锁检测 3.12.6 托管堆与垃圾收集命令 3.13崩溃转储文件 3.14小结 第二部分 调试实践 第4章 程序集加载器 4.1 CLR加载器简介 4.1.1 程序集标识 4.1.2 全局程序集缓存 4.1.3 默认加载上下文 4.1.4 指定加载上下文 4.1.5 无加载上下文 4.2 简单的程序集加载故障 4.3 加载上下文故障 4.4 互用性与DllNot Found Exception 4.5 轻量级代码生成的调试 4.6 小结 第5章 托管堆与垃圾收集 5.1 Windows内存架构简介 5.2 垃圾收集器的内部工作机制 5.2.1 代 5.2.2 根对象 5.2.3 终结操作 5.2.4 回收GC内存 5.2.5 大对象堆 5.2.6 固定 5.2.7 垃圾收集模式 5.3 调试托管堆的破坏问题 5.4 调试托管堆的碎片问题 5.5 小结 第6章 同步 6.1 同步的基础知识 6.2 线程同步原语 6.2.1 事件 6.2.2 互斥体 6.2.3 信号量 6.2.4 监视器 6.2.5 读写锁 6.2.6 线程池 6.3 同步的内部细节 6.3.1 对象头 6.3.2 同步块 6.3.3 瘦锁 6.4 同步任务 6.4.1 死锁 6.4.2 孤立锁异常 6.4.3 线程中止 6.4.4 终结器挂起 6.5 小结 第7章 互用性 7.1 平台调用 7.2 COM 7.3 P/Invoke调用的调试 7.3.1 调用约定 7.3.2 委托 7.4 互操作中内存泄漏问题的调试 7.5 COM互用性中终结操作的调试 7.6 小结 第三部分 高级主题 第8章 事后调试 8.1 转储文件基本知识 8.1.1 通过调试器来生成转储文件 8.1.2 通过ADPIus生成转储文件 8.1.3 转储文件的调试 8.1.4 数据访问层 8.1.5 转储文件分析:未处理的NET异常 8.2 Windows错误报告 8.3 小结 第9章 一些功能强大的调试工具 9.1 PowerDbg 9.1.1 安装PowerDbg 9.1.2 Analyze-PowerDbgllareads 9.1.3 Send-PowerDbgCommand 9.1.4 扩展PowerDbg的功能 9.2 Visual Studio 9.2.1 SOS的集成 9.2.2.NET框架源代码级调试 9.2.3 VisualStudio2010 9.3 CLR分析器 9.3.1 运行CLR分析器 9.3.2 Summary视图 9.3.3 Histogram视图 9.3.4 Graph视图 9.4 WinDbg和CmdTme命令 9.5 小结 第10章 CLR4.0 10.1 工具 10.1.1 Windows调试工具集 10.1.2.NET4.0可再发行组件 10.1.3 SOS 10.2 托管堆与垃圾收集 10.2.1 扩展的诊断信息 10.2.2 后台垃圾收集 10.3 同步 10.3.1 线程池与任务 10.3.2 监视器 10.3.3 栅栏 10.3.4 CountdownEvent 10.3.5 ManualResetEventSlim 10.3.6 SemaphoreSlim 10.3.7 SpinWait和Spin10ck 10.4 互用性 10.5 事后调试 10.6 小结 前言 去年,我们在微软公司庆祝了CLR发布十周年。CLR的目的是通过提供一种安全的和稳定的环境来提高开发人员的生产效率。目前,CLR在各种环境中都得到了广泛应用,例如,在性能和可伸缩性上有着极高要求的大型服务器程序,以及日常使用的桌面程序等。随着CLR的日益普及,基于CLR来开发软件的人们同样面临着越来越多的挑战,因为他们的产品必须能够在不同的机器配置和网络环境中运行;此外,随着硬件的高速发展,人们正在构建的软件功能越来越强,同时复杂性也越来越高。所有这些情形都意味着,当程序没有按照预期方式运行时,你就需要负责分析和修复程序中的问题,因此了解一些调试知识和工具就显得尤为重要。 为了提高工作效率,CLR为开发人员实现了许多基础的辅助机制,从而使开发人员能将主要精力放在关键逻辑上。事实上,人们无需花太多的时间来理解完整的CLR内部细节,而只需知道一些有助于分析问题的重要概念,这一点非常重要。然而,要想知道哪些概念是重要的却并不容易。许多人都是通过反复摸索之后才掌握这些知识,而这需要长时间的积累过程并且有时候可能得不到准确的答案。 本书对运行时的阐述恰到好处,它能帮助你理解在分析问题时遵循的思考过程以及在解决问题时采用的各种技术,此外书中还给出了从调试实际应用程序中提炼出的许多实用技术。因此,如果你希望提高调试CLR应用程序的速度,那么应该仔细阅读本书。本书涵盖了托管程序调试的许多方面——特别是对于一些难以诊断的领域,例如线程同步问题,本书给出了深入而细致的讲解。此外,本书在说明调试技术时使用了大量的示例,使得读者更容易掌握这些技术。 在本书中重点讲解的调试工具之一就是SOS调试器扩展,这个工具是由CLR小组开发和维护的。每当发布新版本的CLR时,都会对SOS进行升级,使SOS包含更多的新功能。对于分析托管进程中的问题来说,SOS是一种功能强大的工具。它提供的大部分功能都是无法从其他调试工具中获得的。例如,SOS可以找出引用托管堆中某个对象的根对象,这是托管程序开发人员经常遇到的问题之一。在熟悉了这个工具的使用后,你将可以进一步理解程序的工作流程。我还从未见过有其他的书比这本书更详细地介绍SOS。 当掌握本书介绍的知识后,在分析问题时可以付出更少的时间和精力。我希望读者在阅读这本书时获得的乐趣与我在审阅本书手稿时获得的乐趣是一样的。 精彩书摘 当加载私有程序集时,它通常只会局限于某个应用程序域中。根据之前对应用程序域的讨论,我们知道在一个.NET应用程序中通常会包含三个应用程序域。除了系统应用程序域和共享应用程序域之外,程序集要么是被加载到默认应用程序域中,要么是被加载到显式创建的应用程序域中。当程序集被加载到某个应用程序域时,它将停留在这个应用程序域中,直到这个应用程序域被销毁。由于程序集都是局限在某个应用程序域中,那么对于任何一个应用程序域,我们如何找出其中加载了哪些程序集?在本章的前面,我们使用了SOS的dumpdomain命令来转储出某个进程中所有的应用程序域。在dumpdomain命令的输出中包含了每个应用程序域中加载的所有程序集。清单2-3给出了在02simple.exe上执行扩展命令dumpdomain时输出的信息。我们可以看到,在默认的应用程序域中包含了两个已加载的程序集:02simple.exe和mscorlib.d11。此外,程序集的名字同样也是它们的地址。当使用SOS的dumpassembly命令来获取每个程序集的进一步信息时,需要用到这些地址。我们可以使用扩展命令dumpassembly,并将程序集的地址作为命令参数来获得更多的信息。 编辑推荐 这是分析.NET应用程序问题方面的一本全面且实用的参考书。《.NET高级调试》首次专门且系统地介绍了如何分析当前最复杂和最具挑战性的.NET应用程序问题。这是一本介绍如何通过非托管调试器(包括WinDBG、NTSD和CDB等)来调试.NET应用程序的书籍。作者详细阐述了如何借助这些工具找出问题的真实原因——这比使用其他任何调试器都将节省大量的调试时间。作者首先介绍了在使用.NET非托管调试器时的一些关键概念。接下来介绍了许多巧妙的调试技术,并且通过真实的示例来展示各种常见的C#编程错误。 读者在《.NET高级调试》中可以学到: ●使用事后调试技术,包括Power DBG以及其他“强大的调试工具”。 ●理解在.NET CLR 4.O中包含的新调试功能以及与之前版本的差异。 ●掌握对Windows调试工具集、SOS、SOSEX、CLR分析器以及其他调试工具的使用。 ●深入理解CLR内部工作机制,例如分析线程特定的数据、托管堆和垃圾收集器、互用层以及.NET异常等。 ●解决一些复杂的同步问题、托管堆问题、互用性问题等。 ●如何生成和分析崩溃转储。 媒体评论 “对于任何一个.NET开发人员来说,本书都具有极高的参考价值。它包含了许多调试技巧以及CLR内部工作机制的细节,这对于设计软件架构的开发人员来说是非常有益的。” ——Jeffrey Richter,Wintellect 公司顾问,培训理由和作者 “这是Mario推出的又一本好书。他之前著的《Windows高级调试》(与Daniel Pravat合著)对于非托管代码的调试来说是一本不可多得的参考书,而本书同样具有极高的质量,阐述清晰并且探讨深入,因此对于.NET’调试来说同样具有帮助作用。” ——Mark Russinovich,微软公司技术顾问 调试助手 1.1 1小结 第2章 CLR基础 2.1 高层概览 2.2 CLR和Windows加载器 2.2.1 加载非托管映像 2.2.2 加载.NET。程序集 2.3 应用程序域 2.3.1 系统应用程序域 2.3.2 共享应用程序域 2.3.3 默认应用程序域 2.4 程序集简介 2.5 程序集清单 2.6 类型元数据 2.6.1 同步块表 2.6.2 类型句柄 2.6.3 方法描述符 2.6.4 模块 2.6.5 元数据标记 2.6.6 EEClss 2.7 小结 第3章 基本调试任务 3.1 调试器以及调试目标 3.2 符号 3.3 控制调试目标的执行 3.3.1 中断执行 3.3.2 恢复执行 3.3.3 单步调试代码 3.3.4 退出调试会话 3.4 加载托管代码调试的扩展命令 3.4.1 加载SOS调试器扩展 3.4.2 加载SOSEX调试器扩展 3.5 控制CLR的调试 3.6 设置断点 3.6.1 在JIT编译生成的函数上设置断点 3.6.2 在还没有被JIT编译的函数上设置断点 3.6.3 在预编译的程序集中设置断点 3.6.4 在泛型方法上设置断点 3.7 对象检查 3.7.1 内存转储 3.7.2 值类型的转储 3.7.3 转储基本的引用类型 3.7.4 数组的转储 3.7.5 栈上对象的转储 3.7.6 找出对象的大小 3.7.7 异常的转储 3.8 线程的操作 3.8.1 ClrStack 3.8.2 Threads 3.8.3 DumpStack 3.8.4 EEStack 3.8.5 COMState 3.9 代码审查 3.9.1 反汇编代码 3.9.2 从代码地址上获得方法描述符 3.9.3 显示中间语言指令 3.10 CLR内部命令 3.10.1 获得CLR的版本 3.10.2 根据名字找到方法描述符 3.10.3 对象同步块的转储 3.10.4 对象方法表的转储 3.10.5 托管堆和垃圾收集器信息的转储 3.11 诊断命令 3.11.1 找出对象的应用程序域 3.11.2 进程信息 3.12 SOSEX扩展命令 3.12.1 扩展的断点支持 3.12.2 托管元数据 3.12.3 栈回溯 3.12.4 对象检查 3.12.5 自动死锁检测 3.12.6 托管堆与垃圾收集命令 3.13崩溃转储文件 3.14小结 第二部分 调试实践 第4章 程序集加载器 4.1 CLR加载器简介 4.1.1 程序集标识 4.1.2 全局程序集缓存 4.1.3 默认加载上下文 4.1.4 指定加载上下文 4.1.5 无加载上下文 4.2 简单的程序集加载故障 4.3 加载上下文故障 4.4 互用性与DllNot Found Exception 4.5 轻量级代码生成的调试 4.6 小结 第5章 托管堆与垃圾收集 5.1 Windows内存架构简介 5.2 垃圾收集器的内部工作机制 5.2.1 代 5.2.2 根对象 5.2.3 终结操作 5.2.4 回收GC内存 5.2.5 大对象堆 5.2.6 固定 5.2.7 垃圾收集模式 5.3 调试托管堆的破坏问题 5.4 调试托管堆的碎片问题 5.5 小结 第6章 同步 6.1 同步的基础知识 6.2 线程同步原语 6.2.1 事件 6.2.2 互斥体 6.2.3 信号量 6.2.4 监视器 6.2.5 读写锁 6.2.6 线程池 6.3 同步的内部细节 6.3.1 对象头 6.3.2 同步块 6.3.3 瘦锁 6.4 同步任务 6.4.1 死锁 6.4.2 孤立锁异常 6.4.3 线程中止 6.4.4 终结器挂起 6.5 小结 第7章 互用性 7.1 平台调用 7.2 COM 7.3 P/Invoke调用的调试 7.3.1 调用约定 7.3.2 委托 7.4 互操作中内存泄漏问题的调试 7.5 COM互用性中终结操作的调试 7.6 小结 第三部分 高级主题 第8章 事后调试 8.1 转储文件基本知识 8.1.1 通过调试器来生成转储文件 8.1.2 通过ADPIus生成转储文件 8.1.3 转储文件的调试 8.1.4 数据访问层 8.1.5 转储文件分析:未处理的NET异常 8.2 Windows错误报告 8.3 小结 第9章 一些功能强大的调试工具 9.1 PowerDbg 9.1.1 安装PowerDbg 9.1.2 Analyze-PowerDbgllareads 9.1.3 Send-PowerDbgCommand 9.1.4 扩展PowerDbg的功能 9.2 Visual Studio 9.2.1 SOS的集成 9.2.2.NET框架源代码级调试 9.2.3 VisualStudio2010 9.3 CLR分析器 9.3.1 运行CLR分析器 9.3.2 Summary视图 9.3.3 Histogram视图 9.3.4 Graph视图 9.4 WinDbg和CmdTme命令 9.5 小结 第10章 CLR4.0 10.1 工具 10.1.1 Windows调试工具集 10.1.2.NET4.0可再发行组件 10.1.3 SOS 10.2 托管堆与垃圾收集 10.2.1 扩展的诊断信息 10.2.2 后台垃圾收集 10.3 同步 10.3.1 线程池与任务 10.3.2 监视器 10.3.3 栅栏 10.3.4 CountdownEvent 10.3.5 ManualResetEventSlim 10.3.6 SemaphoreSlim 10.3.7 SpinWait和Spin10ck 10.4 互用性 10.5 事后调试 10.6 小结 前言 去年,我们在微软公司庆祝了CLR发布十周年。CLR的目的是通过提供一种安全的和稳定的环境来提高开发人员的生产效率。目前,CLR在各种环境中都得到了广泛应用,例如,在性能和可伸缩性上有着极高要求的大型服务器程序,以及日常使用的桌面程序等。随着CLR的日益普及,基于CLR来开发软件的人们同样面临着越来越多的挑战,因为他们的产品必须能够在不同的机器配置和网络环境中运行;此外,随着硬件的高速发展,人们正在构建的软件功能越来越强,同时复杂性也越来越高。所有这些情形都意味着,当程序没有按照预期方式运行时,你就需要负责分析和修复程序中的问题,因此了解一些调试知识和工具就显得尤为重要。 为了提高工作效率,CLR为开发人员实现了许多基础的辅助机制,从而使开发人员能将主要精力放在关键逻辑上。事实上,人们无需花太多的时间来理解完整的CLR内部细节,而只需知道一些有助于分析问题的重要概念,这一点非常重要。然而,要想知道哪些概念是重要的却并不容易。许多人都是通过反复摸索之后才掌握这些知识,而这需要长时间的积累过程并且有时候可能得不到准确的答案。 本书对运行时的阐述恰到好处,它能帮助你理解在分析问题时遵循的思考过程以及在解决问题时采用的各种技术,此外书中还给出了从调试实际应用程序中提炼出的许多实用技术。因此,如果你希望提高调试CLR应用程序的速度,那么应该仔细阅读本书。本书涵盖了托管程序调试的许多方面——特别是对于一些难以诊断的领域,例如线程同步问题,本书给出了深入而细致的讲解。此外,本书在说明调试技术时使用了大量的示例,使得读者更容易掌握这些技术。 在本书中重点讲解的调试工具之一就是SOS调试器扩展,这个工具是由CLR小组开发和维护的。每当发布新版本的CLR时,都会对SOS进行升级,使SOS包含更多的新功能。对于分析托管进程中的问题来说,SOS是一种功能强大的工具。它提供的大部分功能都是无法从其他调试工具中获得的。例如,SOS可以找出引用托管堆中某个对象的根对象,这是托管程序开发人员经常遇到的问题之一。在熟悉了这个工具的使用后,你将可以进一步理解程序的工作流程。我还从未见过有其他的书比这本书更详细地介绍SOS。 当掌握本书介绍的知识后,在分析问题时可以付出更少的时间和精力。我希望读者在阅读这本书时获得的乐趣与我在审阅本书手稿时获得的乐趣是一样的。 精彩书摘 当加载私有程序集时,它通常只会局限于某个应用程序域中。根据之前对应用程序域的讨论,我们知道在一个.NET应用程序中通常会包含三个应用程序域。除了系统应用程序域和共享应用程序域之外,程序集要么是被加载到默认应用程序域中,要么是被加载到显式创建的应用程序域中。当程序集被加载到某个应用程序域时,它将停留在这个应用程序域中,直到这个应用程序域被销毁。由于程序集都是局限在某个应用程序域中,那么对于任何一个应用程序域,我们如何找出其中加载了哪些程序集?在本章的前面,我们使用了SOS的dumpdomain命令来转储出某个进程中所有的应用程序域。在dumpdomain命令的输出中包含了每个应用程序域中加载的所有程序集。清单2-3给出了在02simple.exe上执行扩展命令dumpdomain时输出的信息。我们可以看到,在默认的应用程序域中包含了两个已加载的程序集:02simple.exe和mscorlib.d11。此外,程序集的名字同样也是它们的地址。当使用SOS的dumpassembly命令来获取每个程序集的进一步信息时,需要用到这些地址。我们可以使用扩展命令dumpassembly,并将程序集的地址作为命令参数来获得更多的信息。 编辑推荐 这是分析.NET应用程序问题方面的一本全面且实用的参考书。《.NET高级调试》首次专门且系统地介绍了如何分析当前最复杂和最具挑战性的.NET应用程序问题。这是一本介绍如何通过非托管调试器(包括WinDBG、NTSD和CDB等)来调试.NET应用程序的书籍。作者详细阐述了如何借助这些工具找出问题的真实原因——这比使用其他任何调试器都将节省大量的调试时间。作者首先介绍了在使用.NET非托管调试器时的一些关键概念。接下来介绍了许多巧妙的调试技术,并且通过真实的示例来展示各种常见的C#编程错误。 读者在《.NET高级调试》中可以学到: ●使用事后调试技术,包括Power DBG以及其他“强大的调试工具”。 ●理解在.NET CLR 4.O中包含的新调试功能以及与之前版本的差异。 ●掌握对Windows调试工具集、SOS、SOSEX、CLR分析器以及其他调试工具的使用。 ●深入理解CLR内部工作机制,例如分析线程特定的数据、托管堆和垃圾收集器、互用层以及.NET异常等。 ●解决一些复杂的同步问题、托管堆问题、互用性问题等。 ●如何生成和分析崩溃转储。 媒体评论 “对于任何一个.NET开发人员来说,本书都具有极高的参考价值。它包含了许多调试技巧以及CLR内部工作机制的细节,这对于设计软件架构的开发人员来说是非常有益的。” ——Jeffrey Richter,Wintellect 公司顾问,培训理由和作者 “这是Mario推出的又一本好书。他之前著的《Windows高级调试》(与Daniel Pravat合著)对于非托管代码的调试来说是一本不可多得的参考书,而本书同样具有极高的质量,阐述清晰并且探讨深入,因此对于.NET’调试来说同样具有帮助作用。” ——Mark Russinovich,微软公司技术顾问