#.net #debugging #windbg
#.net #отладка #windbg
Вопрос:
Я устраняю утечку памяти и обнаружил, что объект хранится в памяти, потому что на него ссылается словарь. Когда я выполняю !gcroot в экземпляре dictionary, единственным закрепленным дескриптором является массив System .Объект [], который сам не имеет корня:
0:025> !gcroot -nostacks 38ad01f8
DOMAIN(0000000002287D80):HANDLE(Pinned):11e15c0:Root: 00000000123c5018(System.Object[])->
0000000002f2ab20(System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[MyApp.MyObject, MyApp]])->
000000004223e6e0(System.Collections.Generic.Dictionary`2 Entry[[System.String, mscorlib],[MyApp.MyObject, MyApp]][])->
0000000038ad01f8(MyApp.MyObject)
Это приводит меня к выводу, что экземпляр словаря хранится в статическом поле в некотором классе (.NET хранит все ссылки на статические поля в массиве объектов).
Однако теперь я застрял, потому что !gcroot и !refs (из sosex) не видят ссылки на статические поля.
Я могу искать в куче указатель на адрес 2f2ab20:
0:025> s-q 0 L?0xbfffffff 2f2ab20
00000000`123c76f8 00000000`02f2ab20 00000000`02f2ab78
Итак, я вижу, что какая-то структура рядом с адресом 123c76f8 ссылается на мой словарь. Но куда мне идти дальше? На структуру около 123c76f8 должна указывать структура EEClass, но sos / sosex, похоже, не предоставляет способа определить, какой EEClass является релевантным.
Как я могу без этой информации определить, какой объект содержит статический словарь?
Ответ №1:
Одним из вариантов выполнения такого рода анализа является использование библиотеки ClrMD на NuGet для написания небольшой программы для запуска поверх дампа или процесса.
Программа будет выглядеть следующим образом:
DataTarget dataTarget = DataTarget.LoadCrashDump(@"c:pathtocrash.dmp");
ClrRuntime runtime = dt.CreateRuntime(@"c:pathtomscordacwks.dll");
ClrHeap heap = runtime.GetHeap();
foreach (ClrRoot root in heap.EnumerateRoots())
{
if (root.Object == 0x0000000002f2ab20)
{
Console.WriteLine(root.Kind);
if (root.Kind == GCRootKind.StaticVar)
Console.WriteLine(root.Name);
}
}
Вы можете прочитать больше о библиотеке ClrMD здесь, в блоге DotNet
Ответ №2:
Я запускаю код, который публикуется в этом блоге.
версия: Win7 X64 .NET 4
DOMAIN(000000000007E9D0):HANDLE(Pinned):1a17f8:Root: 0000000012381018(System.Object[])->
0000000002391dc0(System.Collections.ArrayList)->
0000000002391e78(System.Object[])->
0000000002391e60(StaticRootedWhere.Fruit)
0:000> s-d 0000000012381018 L?0x2000 0000000002391dc0
00000000`12382fd8 02391dc0 00000000 0238fb30 00000000 ..9.....0.8.....
0:000> s-d 0 L?0xffffffffffffffff 00000000`12382fd8
00000000`00319680 12382fd8 00000000 02009443 00000000 ./8.....C.......
000007ff`000337d8 12382fd8 00000000 00000001 00000000 ./8.............
000007ff`00150160 12382fd8 00000000 24548b48 278ee828 ./8.....H.T$(..'
0:000> !u 000007ff`00150160
Normal JIT generated code
StaticRootedWhere.Program..cctor()
Begin 000007ff00150120, size 59
E:..........StaticRootedWhereProgram.cs @ 11:
000007ff`00150120 4883ec38 sub rsp,38h
000007ff`00150124 48b8f0340300ff070000 mov rax,7FF000334F0h
000007ff`0015012e 8b00 mov eax,dword ptr [rax]
000007ff`00150130 85c0 test eax,eax
000007ff`00150132 7405 je StaticRootedWhere!StaticRootedWhere.Program..cctor() 0x19 (000007ff`00150139)
000007ff`00150134 e817ac51ee call clr!JIT_DbgIsJustMyCode (000007fe`ee66ad50)
000007ff`00150139 488d0d58ff07ec lea rcx,[mscorlib_ni 0x4d0098 (000007fe`ec1d0098)]
000007ff`00150140 e81b2d0bee call clr!JIT_TrialAllocSFastMP_InlineGetThread (000007fe`ee202e60)
000007ff`00150145 4889442420 mov qword ptr [rsp 20h],rax
000007ff`0015014a 488b442420 mov rax,qword ptr [rsp 20h]
000007ff`0015014f 4889442428 mov qword ptr [rsp 28h],rax
000007ff`00150154 488b4c2428 mov rcx,qword ptr [rsp 28h]
000007ff`00150159 e802b7f5eb call mscorlib_ni!System.Collections.ArrayList..ctor() (000007fe`ec0ab860)
000007ff`0015015e 48b9d82f381200000000 mov rcx,12382FD8h
000007ff`00150168 488b542428 mov rdx,qword ptr [rsp 28h]
000007ff`0015016d e88e270bee call clr!JIT_WriteBarrier_Fast (000007fe`ee202900)
000007ff`00150172 eb00 jmp StaticRootedWhere!StaticRootedWhere.Program..cctor() 0x54 (000007ff`00150174)
000007ff`00150174 4883c438 add rsp,38h
000007ff`00150178 c3 ret
Я публикую свой результат здесь только для справки. это мой второй раз, чтобы проверить результат после прочтения блога. При первой проверке не получен ожидаемый результат.
Ответ №3:
Один из методов, описанных в этом блоге. Краткое объяснение метода заключается в том, что вы хотите найти фрагмент кода, который добавил новый элемент в массив объектов. Этот массив объектов представляет собой массив статических полей (00000000123c5018 в вашем случае). Предполагается, что имя метода должно быть AnOffendingType..ctor() , который является конструктором статического типа, который вы ищете.
Комментарии:
1. Я наткнулся на эту запись в блоге и попытался выполнить шаги, но, к сожалению, этот блог был написан в 2005 году для .NET 1.1. В .NET 2.0 способ доступа MSIL к статическим полям, очевидно, изменился, потому что, когда я ищу ссылки на 123c5018, я получаю пустую.
2. Согласно блогу, вам необходимо выполнить поиск ссылок на местоположение элемента внутри массива, которое в вашем случае равно 123c76f8.
3. Правильно, я сделал это, но вставил неправильный адрес в свой комментарий. «s-q 0 L?0xbfffffff 123c76f8» также появляется пустым, потому что .net 2.0, похоже, использует вспомогательную функцию для поиска статических полей вместо простого встраивания их в MSIL (например, CORINFO_HELP_ASSIGN_REF). Вы можете проверить это, разобрав метод, который присваивает ссылку статическому полю.
4. Можете ли вы попробовать еще одну вещь — поиск 123c76f8 с использованием выравнивания байтов? Команда «s-b 0 L?0xbfffffff f8 76 3c 12». Поскольку адрес является частью инструкции по сборке, он не всегда выравнивается по Dword (в 32-битном случае) или Qword (на 64 бита).
5. Пробовал, ноль попаданий. Я не уверен, что в .NET 4 дела обстоят лучше, но если нет, я думаю, я мог бы сообщить об этом пограничном случае в Microsoft. Если !EEClass может отображать значения статических полей, должна быть какая-то команда, которая отслеживает ссылку на статическое поле обратно к своей структуре EEClass.