内存转储是查明托管.NET应用程序中异常的原因的一种极好的方法,特别是在生产应用程序中发生异常时。当您在无法使用Visual Studio的应用程序中跟踪异常时,cdb和sos.dll的使用技术就变成了它们自己的技术。它们可能也不是你需要经常使用的技能,但在某些时候,它们将是无价的。Edward提供了内存转储示例,并为您提供了一个简单的介绍。

Windows的调试工具占主导地位,sos.dll将CLR内部变成了一个真正的知识源泉。具体来说,我将讨论现有的异常处理框架,讨论.Net异常,如System.exception,以及我所称的“CPU异常”,如访问冲突(C0000005):您可能知道它们是Win32或硬件异常。然后,我将解释如何创建和使用内存转储来查找和修复事后的错误;换句话说,事后调试。 最后,我将展示一个使用cdb和Visual Studio调试的示例。在第一篇文章中(不要给我任何VS),我提倡使用cdb,但是我现在意识到这是一个很大的飞跃:因此,在本文中,我将展示一个使用cdb和sos.dll的示例,然后继续讨论如何在Visual Studio中实现相同的功能,尽管在这个特定的示例中,你需要一定程度的耐心。不过,我想说清楚的是,我个人并不反对Visual Studio或微软,我认为它是一个非常好的IDE,我每天都在使用它,并且对它很满意,我只是相信,Windows的调试工具在调试时给了您更多的控制和动力,当您没有安装Visual Studio(即生产服务器)的选项,或者您没有现成的源代码时,您就无法击败cdb和sos.dll来调试托管应用程序。

什么是异常?

让我们从头开始。异常是指在正常操作下,不应在任何时间发生但可能发生的事件。如果不进行处理,它会阻止应用程序运行。一个例外类似于你的车在你开车的时候停了下来。停车有很多可能的原因;你可能踩了刹车,发动机可能卡住了,车内的乘客可能拉了手刹,或者外部有人把一些相当坚固的东西挡在你的路上。不管发生了什么,如果你处理好了,有些事情你可以恢复,有些事情你不能。
异常要么是由CPU引发的;可能是零除或访问冲突。这是因为CPU有一套操作规则:如果它不喜欢这些参数,它会大叫并问你(开发人员)你想做什么。还包括一些软异常,如用户断点,因为这些将导致相同的结果。发生异常的第二种方式是应用程序要求Windows引发异常,然后由Windows发出请求,而不是CPU。要引发异常,应用程序使用win32函数raiseexception。

如何处理异常?

为了围绕异常提供一个框架,Windows提供了结构化异常处理,CLR使用了结构化异常处理。发生的情况是,您使用SetUnhandledExceptionFilter注册一个处理程序,而Windows将按最近添加这些方法的顺序存储在链中注册的方法,依次访问每个方法,直到处理异常或终止应用程序。

.NET异常呢?

您可能想知道为什么,在.NET文章中,我已经描述了在C++ Win32应用程序中发生了什么。当然,这都是旧帽子,我们是在所有的时髦CLR世界?好吧,CLR是用c++编写的,当程序运行时,如果出现异常,它就是一个称为COMPlusFrameHandler的CLR函数。这在mscorwks.dll中。
如果您有rotor或sscli代码,并且想了解更多关于CLR如何处理异常的信息,可以在“sscli20\CLR\src\vm\i386\excepx86.cpp”中找到它,这可以做很多事情,但需要特别注意的是,它接受CPU异常(如访问冲突),并创建实际的.NET sysm.Access Violation exception或System.NullReferenceException(它将访问冲突转换为两者之一,请参阅sscli20\clr\src\vm\excepp.cpp中的MapWin32FaultToCOMPlusException)。COMPlusFrameHandler也为托管异常调用,因为MSIL代码调用RaiseException,然后导致调用结构化异常处理(SEH)。
无论是.NET异常还是CPU异常,CLR都使用JIT管理器来查找已引发异常类型的MSIL处理程序。如果找到处理程序,则在该点继续执行;如果没有,则检查SEH链以获取进一步的处理程序。处理异常或终止应用程序。

现在让我们看看内存转储文件来验证这一点。第一个例子不能用Visual Studio完成,所以它将在cdb中。

启动命令提示符,然后运行上一篇文章中的doDebug.cmd文件,然后执行“cdb-z c:\ pathToDumpFile\sampleCrash.dmp”。它应该打开转储文件并显示如下内容:

这告诉我们它可以打开文件,并且它位于异常点(“这个转储文件中存储了一个感兴趣的异常”)。首先让我们看看SEH链:键入!exchain

这表明第一个处理程序是COMPlusFrameHandler,然后我们有许多其他的处理程序,最终以kernel32结束,如果它达到了这个程度,将终止程序。现在我们只需要理解,事实上,有一个CLR处理程序(CLR曾经被称为com+,原因我不知道)。理解COMPlusFrameHandler在内部使用CPFH_RealFirstPassHandler来实际处理异常也是很重要的。

我们现在应该加载sos.dll,也应该加载“.loadby sos mscorwks”。这只会将您返回到命令提示符,而不会出现任何错误。

让我们看看一些示例代码:

namespace SampleCrash
{
class Program
{
static void Main(string[] args)
{
throw new System.InvalidOperationException();
}
}
}

这引发了一个新的异常,那么我们为什么不看看MSIL是什么样子的呢?首先我们需要方法的描述,所以做“!Name2EE * SampleCrash.Program.Main”。这显示了我们需要的细节:

Module: 00752f2c (SampleCrash.exe)
Token: 0x06000001
MethodDesc: 007532c0
Name: SampleCrash.Program.Main(System.String[])
JITTED Code Address: 007f0070
 
现在让我们看看MSIL代码“DumpIL 007532c0”
0:000> !DumpIL 007532c0
ilAddr = 00802050
IL_0000: nop
IL_0001: newobj System.InvalidOperationException::.ctor
IL_0006: throw
 
这将创建一个新的invalidooperationexception并调用MSIL函数“throw”–现在让我们通过获取“JITTED code address”(007f0070)并运行“,来查看此操作的程序集代码!U 007f0070英寸。这里我们看到调用invalidooperationexception构造函数,然后调用“JIT_Throw”

如果使用sscli代码或反汇编JIT_THROW(sscli20\clr\src\vm\jithelpers.cpp或“uf JIT_THROW”),则可以看到它调用“raiseexceptioninternalonly”,然后调用“RaiseException”:

这显示了c#中的“throw new System.invalidooperationexception();”实际上调用了RaiseException,并显示了异常如何在SEH处理程序中结束。

CLR如何处理异常?

当从CPU异常调用CLR异常处理时,它需要创建.NET异常对象,因为托管catch块需要从System.exception派生的托管对象。CLR基于一个简单的switch语句创建一个特定的异常,该语句接受CPU异常并决定要引发哪些CLR异常。创建异常时,CPFH_FirstPassHandler会将其存储在托管线程的“throwable”属性中。然后再次调用CPFH_FirstPassHandler,此时因为“throwable”属性有一个异常对象,所以它会经过处理它的逻辑。当引发.NET异常时,已设置“throwable”属性,因此不需要随后创建异常。

调试时应该注意的一些事

在Visual Studio下调试托管进程时,始终将异常转换为.NET变量,即System.AccessViolationException,而不是cdb中的CPU异常代码,即C0000005,因此您可能认为在cdb下运行到Visual Studio的结果不同,但实际上是相同的。当您调试一个dmp文件时,因为您正在调试它,就好像它是一个非托管应用程序一样,所以它不会被翻译。

如果不处理异常会发生什么?

这取决于应用程序的类型,所有.NET应用程序在SEH链中的处理程序都比ComPlusFrameHandler高(如果要在cdb中看到链,前面提到的命令是“!exchain“)。控制台程序显示异常的细节,然后退出。Windows应用程序和服务都将关闭,您可能会在事件日志中看到一条消息。

ASP.NET应用程序最有趣,因为通常未经处理的异常不会终止进程,而是会向web客户端写入一条消息,也就是死亡的黄色屏幕(这可以使用web.config关闭),消息被添加到事件日志中,连接被终止,但使用同一进程的所有其他web客户端都可以。某些异常(如堆栈溢出)可能会导致asp.NET工作进程崩溃,因此不要总是期望web应用继续运行。

因此,如果您发现您的应用程序或服务刚刚停止运行,那么您很有可能会遇到未处理的异常。在某些配置中的某些版本的Windows中,您可能会看到可信的“Dr Watson”进入生命,捕获内存转储,然后询问您是否要将其发送到Microsoft,以检查是否有已知的修复方法。如果您选择将转储排队到稍后(通过GUI或组策略),则可以从C:\ WINDOWS\pchealth\ERRORREP\QSIGNOFF获取它,其中可能有许多cab文件中包含转储文件。

内存转储

什么是内存转储?

内存转储是包含进程的全部或部分内存的文件。它们可能非常有用,也可能用途有限,这取决于它们是如何创建的,以及它们所包含的内存部分。

进程内存

让我们快速看一看进程是如何布局的,以及它如何使用内存来提供足够的细节,以便能够决定我们需要哪种类型的转储,以及为什么有时我们得不到所需的细节。启动进程时,从exe或dll文件读取二进制CPU指令并将其复制到内存中:然后,将字节的地址传递给CPU执行。这称为代码部分;我们需要它进行调试,以便能够知道当前的CPU命令是什么,以及它发生在哪里。如果我们有完整的符号,那么我们还可以找出源代码中当前命令的位置。

在这个过程中的某个时刻,我们得到了堆栈的空间。这些存储.NET值类型,并允许CPU和调试器跟踪我们的确切位置;我指的是指向当前位置的方法调用。如果我们不在转储中包含堆栈空间,那么我们就无法知道如何在特定位置结束以及传入了哪些参数。

最后一部分是堆。这就是创建.NET引用和大型对象的地方:没有这些,我们将知道我们拥有哪些类型的对象,但不知道这些类型的属性。

如何创建内存转储?

有一个名为MiniDumpWriteDump的API函数可以帮助创建内存转储,很多不同的应用程序都使用它来创建内存转储。在cdb中,这只是附加到进程,然后运行“.dump/ma c:.pathToDump.dmp”的情况。/ma开关会将所有堆栈空间、代码和堆添加到文件中。

其他工具是DrWtsn32(使用-p然后是进程id)和debug diag,它主要用于基于IIS的应用程序,但可以用于创建任何进程的转储。debug diag需要注意的一点是,它创建了两个文件,一个.dmp只是堆栈部分,另一个叫做.hdmp,它包含堆,因此它有更多的用途。

查看.NET应用程序中的异常(上)的更多相关文章

  1. 查看.NET应用程序中的异常(下)

    为什么要使用内存转储进行调试? 在两种主要情况下,您可能需要使用内存转储进行调试.第一种情况是应用程序有一个未处理的异常并崩溃,而您只有一个内存转储.第二种情况是,在生产环境中出现异常或特定行为,并且 ...

  2. MVC应用程序中管理(更新)上传的文件

    实现上传文件功能,有时上传也会操作出错,能让用户有改正有机会,开发上传文件能有更新的功能. 文件上传时,如果是存储于应用程序某一目录的话,在更新时需要了解一些流程,先是删除旧文件,更新数据表相关信息, ...

  3. C++ 中的异常机制分析

    C++异常机制概述 异常处理是C++的一项语言机制,用于在程序中处理异常事件.异常事件在C++中表示为异常对象.异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统 ...

  4. dart系列之:dart语言中的异常

    目录 简介 Exception和Error Throw和catch Finally 总结 简介 Exception是程序中的异常情况,在JAVA中exception有checked Exception ...

  5. 中小研发团队架构实践之生产环境诊断工具WinDbg 三分钟学会.NET微服务之Polly 使用.Net Core+IView+Vue集成上传图片功能 Fiddler原理~知多少? ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一) C#程序中设置全局代理(Global Proxy) WCF 4.0 使用说明 如何在IIS上发布,并能正常访问

    中小研发团队架构实践之生产环境诊断工具WinDbg 生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器.调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具 ...

  6. 编写高质量代码改善C#程序的157个建议——建议66:正确捕获多线程中的异常

    建议66:正确捕获多线程中的异常 多线程的异常处理需要采用特殊的方式.一下这种方式会存在问题: try { Thread t = new Thread((ThreadStart)delegate { ...

  7. 2019.12.11 java程序中几种常见的异常以及出现此异常的原因

    1.java.lang.NullpointerException(空指针异常) 原因:这个异常经常遇到,异常的原因是程序中有空指针,即程序中调用了未经初始化的对象或者是不存在的对象. 经常出现在创建对 ...

  8. 捕捉WPF应用程序中XAML代码解析异常

    原文:捕捉WPF应用程序中XAML代码解析异常 由于WPF应用程序中XAML代码在很多时候是运行时加载处理的.比如DynamicResource,但是在编译或者运行的过程中,编写的XAML代码很可能有 ...

  9. 从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(上)

    目录 上一篇博文 下一篇博文 前置知识 回顾上文 最小3D程序完整代码地址 通用语言 将会在本文解决的不足之处 本文流程 解释本文使用的领域驱动设计的一些概念 本文的领域驱动设计选型 设计 引擎名 识 ...

随机推荐

  1. MySql5.7 json查询

    create table t1(name json); insert into t1 values(’ { “hello”: “song”, “num”: 111, “obj”: { “who”: “ ...

  2. linux服务器同时运行两个或多个tomcat

    第一步:条件:配置好jdk,如果没有配置好请看:https://www.cnblogs.com/weibanggang/p/11460537.html 第二步:下载安装tomcat 下载tomcat到 ...

  3. Linux 6 本地网络配置方法

    在Linux 6 系统安装完成后,需要对网络进行一系列的配置,有些朋友喜欢使用桌面图形化工具的配置方式,这种配置方法虽说比较方便,但是在某些时候并不问题,总是会出现各种问题.特别是作为服务器用途的时候 ...

  4. CentOS7配置网卡上网、安装wget、配置163yum源

    2019/09/12,CentOS 7 VMware 摘要:CentOS7安装完成(最小化安装)后,不能联网(已选择桥接网络),需要修改配置文件及配置yum源 修改配置文件 进入网卡配置目录 cd / ...

  5. 学习笔记之Slurm

    Slurm Workload Manager - Overview https://slurm.schedmd.com/overview.html Slurm is an open source, f ...

  6. 存储管理器 S3C2440A

    CPU通过存储管理器来控制外部设备 SDRAM存储结构 S3C2440A内存控制器 原理图 HY57V561620(L)T 4Banks x 4M x 16Bit Synchronous DRAM S ...

  7. Python爬虫系列:五、正则表达式

    1.了解正则表达式 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑. 正则表达式 ...

  8. springboot通过idea打jar包

    springboot打jar包 一.      检查pom文件 <packaging>jar</packaging> 二.      切换到maven窗口 三.      先c ...

  9. 自定义View(二),强大的Canvas

    本文转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android中使用图形处理引擎,2D部分是 ...

  10. 一、Linux_文件操作

    1.复制文件到当前目录: # 复制文件到当前目录下, ./表示当前目录 $ cp /home/book/poems.tar.gz ./ # 复制并改名,并存放在当前目录下 (cp源文件名 新文件名) ...