.net - 如何判断数组类型?

标签 .net windbg il sos sosex

我遇到了 OutOfMemoryException,我想分析要创建的数组的大小和类型。

我已经针对这种情况创建了一个演示目的转储,并且我能够获得以下信息:

0:000> !pe
Exception object: 023f389c
Exception type: System.OutOfMemoryException
Message: <none>
InnerException: <none>
StackTrace (generated):
    SP       IP       Function
    0015EE44 0099007F OOM2!OOM2.Program.Main()+0xf

StackTraceString: <none>
HResult: 8007000e

0:000> !u 0099007F 
Normal JIT generated code
OOM2.Program.Main()
Begin 00990070, size 22
00990070 baffffff7f      mov     edx,7FFFFFFFh
00990075 b90241a478      mov     ecx,offset mscorlib_ni+0x4102 (78a44102)
0099007a e8192194ff      call    002d2198 (JitHelp: CORINFO_HELP_NEWARR_1_VC)
>>> 0099007f 8bc8            mov     ecx,eax
...

所以我可以看到创建了一个新数组,大小为 7FFFFFFF,即 20 亿个项目。 (请忽略这样一个事实:您甚至无法在 32 位 .NET 应用程序中创建该大小的 byte[],因此在此示例中,类型可能根本不重要。)

我现在已经了解到数组的类型位于 ECX 寄存器中,但不幸的是 mscorlib_ni+0x4102 (78a44102) 并不是很有帮助。

我尝试过 !mln!mdt 甚至不切实际的 !ip2mt,但它们都没有显示预期的 bytebyte[] 输出。有没有办法从 mscorlib 的 native 镜像获取类型?

最佳答案

一种方法是转储与创建数组的方法相对应的 IL。

首先,使用 !clrstack 获取您所在的方法。在我的例子中,我位于 Main 方法中的虚拟应用程序中:

0:000> !clrstack
OS Thread Id: 0x7d0 (0)
Child SP       IP Call Site
0016f094 758dc42d [HelperMethodFrame: 0016f094] 
0016f120 003200af ConsoleApplication4.Program.Main(System.String[]) [c:\Users\smt\Documents\Visual Studio 2012\Projects\ConsoleApplication4\Program.cs @ 215]
0016f2bc 743b3de2 [GCFrame: 0016f2bc] 

接下来使用!name2ee获取该方法的方法描述(下一个命令需要它):

0:000> !name2ee ConsoleApplication4!ConsoleApplication4.Program.Main
Module:      00282eac
Assembly:    ConsoleApplication4.exe
Token:       0600013c
MethodDesc:  00283dbc
Name:        ConsoleApplication4.Program.Main(System.String[])
JITTED Code Address: 00320050

现在,转储方法的 IL:

0:000> !dumpil 00283dbc
ilAddr = 002d4bc4
IL_0000: nop 
IL_0001: newobj class [mscorlib]System.Collections.Generic.List`1<??????  ??????n?.::.ctor 
IL_0006: stloc.0 
IL_0007: br.s IL_001e
IL_0009: nop 
IL_000a: ldc.i4 2147483647
IL_000f: newarr System.Single
IL_0014: stloc.1 
IL_0015: ldloc.0 
IL_0016: ldloc.1 
IL_0017: callvirt class [mscorlib]System.Collections.Generic.List`1<??????  ??????N?.::Add 
IL_001c: nop 
IL_001d: nop 
IL_001e: ldc.i4.1 
IL_001f: stloc.2 
IL_0020: br.s IL_0109

作为比较,这是我在 C# 中的方法:

static void Main(string[] args)
{
    List<float[]> arrays = new List<float[]>();
    while (true)
    {
        float[] f = new float[Int32.MaxValue];
        arrays.Add(f);
    }

    Console.ReadKey();
}

您可以在 IL_000f 行看到正在创建一个 System.Single 类型的新数组。

<小时/>

我走这条路是因为我无法破译传递给创建数组的实际 native 方法的参数。如果您在抛出异常时运行 kb:

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0016efa4 74502a42 e0434352 00000001 00000005 KERNELBASE!RaiseException+0x58
0016f048 745d55ef 00000000 bc1d0b3d 0016f10c clr!RaiseTheExceptionInternalOnly+0x276
0016f078 7464ae51 bc1d0a7d 0016f150 00000000 clr!UnwindAndContinueRethrowHelperAfterCatch+0x83
0016f118 003200af 00000000 02202480 00000000 clr!JIT_NewArr1+0x1af
0016f138 743b3de2 00720198 0016f198 743c3315 0x3200af
0016f144 743c3315 0016f1dc 0016f188 74502c66 clr!CallDescrWorkerInternal+0x34
...

您可以看到正在调用 clr!JIT_NewArr1,这会创建一个一维数组。为此,it needs the type and the size 。这些参数分别复制到 ecxedx 中:

0:000> !u 003200AF 
...
003200a0 b9e2302a73      mov     ecx,offset mscorlib_ni+0x30e2 (732a30e2)
003200a5 baffffff7f      mov     edx,7FFFFFFFh
003200aa e89121f5ff      call    00272240 (JitHelp: CORINFO_HELP_NEWARR_1_VC)
>>> 003200af 8945e8          mov     dword ptr [ebp-18h],eax
003200b2 8b45e8          mov     eax,dword ptr [ebp-18h]
003200b5 8945f0          mov     dword ptr [ebp-10h],eax
...

如您所见,ecx 获取 732a30e2,它以某种方式映射到 System.Single 的类型信息,但我不能弄清楚如何...

关于.net - 如何判断数组类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25878615/

相关文章:

C# 编译优化 : null coalescing operator - UPDATED - Reflector's bug?

c# - 分析 C# 生成的 MSIL 代码

c# - 为什么在 WCF 应用程序的 Web.config 文件中添加 httpRuntime targetFramework 可以解决与 TLS 相关的连接问题?

.net - 将 exe 引用为 .NET 程序集并使用 ClickOnce 进行部署

c# - 通过 C++ 桥从 .NET 调用 Python

c# - WCF - 客户端和 > 服务绑定(bind)可能不匹配

c++ - 从外部源向 Windows 调试器发送调试事件

memory - MEM_RESERVE 和 MEM_COMMIT 状态之间的确切区别是什么?

.net - 任何学习 CIL 的好资源?

c# - .NET 线程如何等待不属于任何线程的 syncblk?