ClrMD: Nalezení statických rootů při analýze .NET memory-dumpu

Používáme-li WinDbg a jeho rozšíření SOS.dll (popř. SOSEX nebo PSSCOR4) k analýze memory-dumpu .NET procesů, pak jednou z komplikovanějších úloh je například nalezení statických rootů, tedy statických fieldů, které drží reference na prozkoumávanou instanci.

Že se jedná o zarootování instance ze statického fieldu je zpravidla zřejmé z výstupu !GCRoot, který pak vypadá například nějak takto:

0:000> !GCRoot 0000000acaf3e8f8 
HandleTable:
    0000000f159f19f8 (pinned handle)
    -> 0000000ec87f1be8 System.Object[]
    -> 0000000d11ac08c8 System.Action
    -> 00000010f5df1020 System.Object[]
    -> 0000000acaf3e8f8 System.Action

Found 1 unique roots (run '!GCRoot -all' to see all roots).

.NET si drží hodnoty statických fieldů v systémových object[] polích, nicméně dohledat, který konkrétní statický field je naším rootem, už není tak jednoduché (obvykle skončíme ve WinDbg u s-d nebo s-q prohledávání paměti a hledání další reference, atp.).

ClrMD: Microsoft.Diagnostics.Runtime

Naštěstí existuje a z NuGet se dá snadno nainstalovat .NET knihovna Microsoft.Diagnostics.Runtime (zkráceně ClrMD), která umožňuje inspekci a analýzu memory-dumpu z .NET kódu obdobně jako byste pracovali s SOS ve WinDbg.

…a jednou z věcí, které lze pomocí ClrMD snadno dostat je právě identifikace statického fieldu, který je rootovou referencí na prozkoumávaný objekt:

DataTarget dataTarget = DataTarget.LoadCrashDump(@"D:\path\to\memory-dump.DMP");
ClrRuntime runtime = dataTarget.CreateRuntime(@"D:\path\to\mscordacwks.dll");
ClrHeap heap = runtime.GetHeap();

foreach (ClrRoot root in heap.EnumerateRoots())
{
	if (root.Object == 0x0000000d11ac08c8) // adresa rootu, který chceme prozkoumat
	{
		// jen pro ukázku třeba:
		Console.WriteLine(root.Kind);
		if (root.Kind == GCRootKind.StaticVar)
			Console.WriteLine(root.Name);
	}
}

Pracujeme ideálně paralelně s WinDbg+SOS:

  • do CreateRuntime() dáváme cestu k příslušnému mscordacwks.dll, z WinDbg získáme přes .cordll -ve -u -l
  • adresu prozkoumávaného rootu známe z WinDbg (první po System.Object[], viz výše uvedený příklad z !GCRoot)
  • výše uvedený snippet stačí prsknout do ConsoleApp, do které nareferencujeme ClrMD z NuGet
        • je potřeba nastavit Build / Platform Target na x86 nebo x64, podle toho, z jaké platformy pochází memory-dump

Zanechat odpověď

Vyplňte detaily níže nebo klikněte na ikonu pro přihlášení:

Logo WordPress.com

Komentujete pomocí vašeho WordPress.com účtu. Odhlásit /  Změnit )

Facebook photo

Komentujete pomocí vašeho Facebook účtu. Odhlásit /  Změnit )

Připojování k %s