分类 调试 下的文章

SOS.DLL、SOSEX.DLL这两个就是用来对.NET程序在Windows调试工具中起到翻译作用的调试器扩展。简单讲就是,这两个组件是.NET项目组专门开发出来用来对.NET应用程序进行方便调试用的,当然不用这两个扩展也能调试.NET程序,只不过就会很困难,会被很多细节束缚住。有了这个调试扩展之后,我们就可以让原生Windows调试器正确的翻译出.NET相关概念。

所有对.NET程序发起的调试会话都要经过.NET调试扩展组件进行翻译才行,也就是要使用.NET调试扩展的调试命令来调试.NET程序。上图中,我们如果要想调试.NET程序就需要将.NET调试扩展组件加载到Windows调试工具中去,然后才能方便在Windows调试工具中使用。

还有就是.NET  DAC概念。其实.NET DAC也就是.NET Data Access .NET数据访问层,这个是专门用来提供给SOS.DLL\SOSEXDLL或者其他调试扩展包使用的,所有的调试扩展组件必须通过这个DAC才能访问到.NET运行时的数据,所以在初次使用SOS的时候会经常碰见加载错误的mscordacwks.dll文件,此文件就是DAC的物理文件。

这个文件和SOS扩展文件一样,都有这不同的版本,当加载不同类型的.NET程序时会使用到不同版本的mscordacwks.dll文件,当然大部分情况下此文件时自动加载的,只有出现你分析的文件与生成调试文件的环境不一致时才会出现头疼的问题。

 

 当你知道这个组件是工作于此位置时,当出现跟它相关的错误提示时你就不需要担心了,无非就是文件加载的位置或者版本不匹配而已。

到目前为止,!dumpheap 是调试托管内存泄漏时最强大的命令。它将向您显示托管堆上的所有对象,并使用的不同开关你可以用你想要的任何方式显示输出。
!dumpheap是sos.dll框架安装附带的扩展(在framework目录中),如果安装了SDK,则可以在C:\Program files\ Microsoft Visual Studio.NET\2003\SDK\v1.1工具开发人员指南示例SoS中找到有关其使用的一些基本帮助。
堆上存储有两种类型的对象。以某个地方为根的对象,即应用程序中的某个对象有指向它们的指针;自上次垃圾回收后已创建或取消根目录的对象。如果您想知道泄漏在哪里,并且希望避免查看大量即将被垃圾收集的数据,那么一个好方法是运行一个压力测试,然后通过调用GC.Collect(3) ,获取一个内存转储,然后再强调一点,再次诱导GC并获取另一个内存转储并比较堆上的对象。
-stat开关(statistics)显示堆上每种类型的对象的摘要。

0:000> !dumpheap -stat
0x79c489a0 1 12System.Runtime.Remoting.Messaging.ClientContextTerminatorSink0x79bf9aec 1 12 System.IO.TextReader/NullTextReader0x79be7078 1 12System.Runtime.Remoting.Proxies.ProxyAttribute0x79bce8e0 1 12System.Runtime.InteropServices.ComVisibleAttribute0x79bce7c8 1 12System.CLSCompliantAttribute0x79bc08e0 1 12System.Empty0x0618ae68 1 12System.Web.Configuration.CustomErrorsConfigHandler0x061887f8 1 12System.Web.UI.WebControls.UnitConverter0x06180848 1 12System.Drawing.ColorConverter0x05dbfbc4 1 12System.Data.Res<… cut to save space …>

0x03f1236c 625 2,820,896System.Char[]0x04ad88f4 102,874 2,880,472System.Web.UI.ControlCollection0x0469bdf0 156,650 3,133,000System.Collections.Specialized.HybridDictionary0x04ad91bc 164,516 3,290,320System.Web.UI.Triplet0x03f134a8 7,582 3,799,704 System.Collections.Hashtable/bucket[]0x04ade5e4 47,395 4,549,920System.Web.UI.WebControls.Label0x061826bc 58,197 4,888,548System.Web.UI.DataBoundLiteralControl0x04adff44 323,119 5,169,904System.Web.UI.StateItem0x0618788c 63,437 6,089,952System.Web.UI.WebControls.TableCell0x0469c5c4 309,132 6,182,640 System.Collections.Specialized.ListDictionary/DictionaryNode0x0011cec0 305 6,240,720Free0x79ba2ee4 270,831 6,499,944System.Collections.ArrayList0x03f16d9c 222 7,703,284System.DateTime[]0x04add34c 105,502 8,018,152System.Web.UI.LiteralControl0x0615c6f4 558,019 11,160,380System.Data.DataRowView0x03f15d1c 3,783 15,447,528System.Boolean[]0x060bcc74 570,274 22,810,960System.Data.DataRow0x03f15fd4 702 50,930,472System.Decimal[]0x03f131e8 21,013 60,573,352System.Int32[]0x03f1209c 508,734 75,399,184System.Object[]0x79b94638 5,286,303 697,441,440System.String
Total
9,712,896 objects, Total size: 1,032,127,612

简介

sx*命令控制调试器在正在调试的应用程序中发生异常或发生某些事件时采取的操作。

使用形式

sx

sx{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}

sx- [-c "Cmd1"] [-c2 "Cmd2"] {Exception|Event|*}

sxr

参数

  • -c " Cmd1 "
    指定在发生异常或事件时执行的命令。 当处理此异常的第一次机会发生时,无论此异常是否中断调试器,都将执行此命令。 必须将Cmd1字符串用引号引起来。 此字符串可以包含用分号分隔的多个命令。 -C 和带引号的命令字符串之间的空格是可选的。
  • -c2 " Cmd2 "
    指定在发生异常或事件并且在第一次机会未处理时执行的命令。 当第二次处理此异常时,会执行此命令,无论此异常是否中断调试器。 必须将Cmd2字符串用引号引起来。 此字符串可以包含用分号分隔的多个命令。 -C2 和带引号的命令字符串之间的空格是可选的。
  • -h
    更改指定事件的处理状态,而不是其中断状态。 如果事件cchcbpecssec,则无需使用 -h选项。
  • Exception
    以当前基数指定命令作用于的异常号。
  • Event
    指定命令所作用的事件。这些事件用简短的缩写来表示。
  • *
    影响所有未以其他方式显式命名为sx的异常。

支持环境

模块

用户模式,内核模式

目标

仅限实时调试

适用

全部

说明

sx命令显示当前进程的异常列表和所有非异常事件的列表,并显示每个异常和事件的调试器的默认行为。SXE、SXD、SXN和SXI命令控制每个异常和事件的调试器设置。sxr命令将所有异常和事件筛选器状态重置为默认设置。命令将被清除,中断和继续选项将重置为其默认设置,依此类推。sx-命令不会更改指定异常或事件的处理状态或中断状态。如果希望更改与特定事件关联的第一次机会命令或第二次机会命令,但不希望更改任何其他命令,则可以使用此命令。如果包含-h选项(或者如果指定了CC、HC、BPEC或SSEC事件),则SXE、SXD、SXN和SXI命令控制异常或事件的处理状态。在所有其他情况下,这些命令控制异常或事件的中断状态。
设置中断状态时,这些命令具有以下效果:

CommandStatus nameDescription

sxe

Break

(Enabled)

发生此异常时,目标会在激活任何其他错误处理程序之前立即中断调试器。 这种处理称为第一次机会处理。

sxd

Second chance break

(Disabled)

对于此类型的第一次异常,调试器不会中断(尽管显示了一条消息)。 如果其他错误处理程序未解决此异常,将停止执行,并将目标中断到调试器中。 这种处理称为第二次机会处理。

sxn

Output

(Notify)

发生此异常时,目标应用程序根本不会中断调试器。 但是,会显示一条消息,通知用户此异常。

sxi

Ignore

发生此异常时,目标应用程序根本不会中断调试器,而且不显示任何消息。

设置处理状态时,这些命令具有以下效果:

CommandStatus nameDescription

sxe

Handled

继续执行时,该事件被视为已处理。

sxd,sxn,sxi

Not Handled

继续执行时,该事件被视为未处理。

可以结合使用 -h选项和异常,而不是事件。 将此选项与chbpesse一起使用将分别设置hcbpecssec的处理状态。 如果对任何其他事件使用-h 选项,则该选项不起作用。-c-c2选项与hcbpecssec一起使用时,会将指定的命令分别与chbpesse关联。

在下面的示例中, sxe命令用于将访问冲突事件的中断状态设置为在第一次机会时中断,并设置将在该点执行到r eax的第一条命令。 然后使用sx命令将第一条命令更改为r ebx,而无需更改处理状态。 最后,显示sx输出的一部分,指示访问冲突事件的当前设置:

0:000> sxe -c "r eax" av

0:000> sx- -c "r ebx" av

0:000> sx
 av - Access violation - break - not handled
       Command: "r ebx"

这并不是说它很重要,而是因为它是我的一个大麻烦,我想区分真正的内存泄漏和高内存使用率。
内存泄漏是指您使用了一些内存并且丢失了指向分配的指针,因此您无法再取消分配该内存。如果你仍然有一个指向它的指针,你的内存使用率很高,这可能与进程发生的情况一样糟糕,但仍然不同。为了简单起见,我将从现在开始对这两个问题使用术语内存泄漏…
我喜欢考虑解决性能和内存泄漏问题,比如剥洋葱皮。从最明显的一个层开始,一次去掉一个层,然后定义一个限制,确定问题已解决。
有两种类型的内存泄漏,一种是逐渐的内存泄漏(内存以大致相同的速率持续增长)和突然的内存跳跃。您可以用大致相同的方式对它们进行故障排除,但在后一种情况下,您还可以尝试确定在跳转时是否发生了异常情况,例如服务器上的极端负载等。
进程的内存空间包含一些不同类型的“对象”,如线程、管理的对象堆、托管加载程序堆、本机堆、dll和虚拟分配,因此,一个好的开始是运行带有以下计数器的性能监视器日志。

  • Process/Virtual Bytes
  •   Process/Private Bytes
  •   .net CLR Memory/# Bytes in all Heaps
  •    .net CLR Memory/% Time in GC
  •    .net CLR Memory/Large Object Heap size
  •    .net CLR Loading/Bytes in Loader Heap
  •    .net CLR Loading/Current Assemblies

主要要寻找的是私有字节的增长率是否与虚拟字节的增长率大致相同,以及所有堆中的字节是否都遵循相同的曲线。
如果私有字节持续增加,但是所有堆中的字节没有增加,那么很可能会看到本机内存泄漏(即,在COM组件或类似组件中泄漏),但如果所有堆中的字节以与私有字节相同的速率增加,则托管代码中可能会出现泄漏。
同样,如果您看到虚拟字节稳步增加,但是您的私有字节保持相当稳定,那么您的应用程序可能存在一个问题,即它保留了大量未使用的虚拟内存。
一旦进程启动并且加载了所有应用程序域,加载程序堆和当前程序集中的字节应该保持相当恒定。如果此值持续增加,则很可能存在总成泄漏。

所以现在你大概知道你在哪里泄露了,下一步就是找出原因

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

clr是通用语言运行时库,对应的就是clr.dll/mscorwks.dll,在clr发展过程中,文件名发生了变化,他们的版本对应关系如下:

.NET framework 版本CLR 版本CLR 文件名
1.1 1.1 mscorwks.dll
2.0 2.0 mscorwks.dll
3.0 2.0 mscorwks.dll
3.5 2.0 mscorwks.dll
4.0 4.0 clr.dll
4.5 4.0 clr.dll

我们在进行托管代码调试时,还会使用到sos.dll和mscordacwks.dll这两个动态库:

  • SOS.dll
    SOS 调试扩展 (SOS.dll) 通过提供有关内部公共语言运行时 (CLR) 环境的信息,帮助你在 Visual Studio 和 Windows 调试器 (WinDbg.exe) 中调试托管程序。
  • mscordacwks.dll
    这是进行调试的数据访问组件 (DAC)。SOS和CLR调试器使用 Data Access Component (DAC)来实现进程外调试,DAC原则上可以视做CLR执行引擎的子集。它能用在转储文件上,甚至是在CLR未安装的机器上面工作,其实现主要包括一组宏和模板,结合执行引擎代码的条件编译。当编译runtime时,clr.dll和mscordacwks.dll同时生成。为了检索对象,DAC可以读取其内存,获取mscordacwks中VM代码的输入。 然后,它可以在宿主机中运行相应的函数来计算有关托管结构所需的信息,并将最终结果返回给调试器。
    请注意。DAC需要读取对象进程的内存。调试进程和调试对象进程是独立的,地址空间也是独立的。因此需要清楚的区分对象内存和宿主(调试器)内存。在宿主进程中使用目标地址结果无法预料,通常情况下是错误的结果。当使用DAC检索目标内存时,在正确的地址空间中使用目标地址时十分重要的,此外,有时目标地址严格用作数据,在这种情况下,使用主机地址同样不正确,比如,要显示一个托管函数的信息,可能需要列出开始的地址,地址大小。当在VM文件夹下编辑DAC可能运行的代码时,需要正确的选择宿主地址或者目标进程地址。

请注意  对于所有版本的.NET Framework 中,该 DAC 的文件名是 mscordacwks.dll,,SOS 调试扩展的文件名为 sos.dll。

若要验证 mscordacwks.dll 版本与应用程序使用的 clr 的版本相匹配,请输入以下命令以显示有关已加载的 CLR 模块信息之一:

  • lmv mclr (适用于 CLR 版本 4.0)
  • lmv mscorwks (适用于版本 1.0 或 2.0 CLR)