Как правильно работать с не примитивными значениями ClrInstanceField, используя ClrMD?
У меня есть несколько очень больших дампов памяти управляемого процесса, из которого я пытаюсь получить большую статистику, а также представить интерактивное представление довольно глубоких графов объектов в куче. Подумайте, что-то сопоставимое !do <address>
с prefer_dml 1
установить в WinDbg с SOS, где вы можете постоянно нажимать на свойства и видеть их значения, только в гораздо более дружественном пользовательском интерфейсе для сравнения многих объектов.
Я обнаружил, что Microsoft.Diagnostics.Runtime (ClrMD) особенно хорошо подходит для этой задачи, но мне трудно работать с полями массива, и я немного запутался в объектных полях, над которыми я немного работал лучше.
Массив: если я нацеливаю массив с адресом прямо из кучи и использую ClrType.GetArrayLength
а также ClrType.GetArrayElementValue
все работает хорошо, но как только я копаюсь в полях другого объекта, я не уверен, какое значение я получаю от ClrInstanceField.GetValue
когда ClrInstanceField.ElementType
является ClrElementType.SZArray
(Я не встречал Array
пока еще копаться в моем графе объектов, но я бы тоже хотел с этим справиться).
Изменить: я просто решил использовать ClrType
за System.UInt64
разыменовать поле массива (используя parent address + offset of the array field
чтобы вычислить адрес, где хранится указатель массива), тогда я могу работать с ним так же, как если бы я получил его от EnumerateObjects. Теперь у меня возникли трудности с некоторыми массивами, не поддерживающими ArrayComponentType
имущество. Мне еще предстоит протестировать с массивами Structs, поэтому мне также интересно, будет ли это выделение встроенных структур в стиле C, как это происходит с int[]
или если это будет массив указателей на структуры в куче. Guid[]
это один из типов, у меня есть проблема с получением ArrayComponentType
от.
Объект: Исправлено (логическая ошибка) с ClrInstanceField
это имеет Type
из ClrElementType.Object
Я получаю намного лучшие результаты, но все еще нужно немного больше. Во-первых, после звонка GetFieldValue
Я вернусь ulong
адрес (?) который я могу использовать ClrInstanceField.Type.Fields
просто отлично, так что я могу видеть имена полей и значения вложенного объекта. Тем не менее, я должен учитывать полиморфизм, поэтому я попытался использовать ClrHeap.GetObjectType
на тот же адрес, и он либо возвращает NULL, либо что-то совершенно неверное. Кажется странным, что адрес работал бы в моем первом случае использования, но не во втором.
String: Исправлено (найден обходной путь) Поскольку мой настоящий проект уже использует DbgEng с SOS, у меня есть другой способ легко получить значение строки по адресу, но мне показалось очень странным, что попытка использовать ClrInstanceField.GetFieldValue
удалось вернуть строку, но с совершенно неточными результатами (набор странных символов). Может я не так делаю?
Изменить: я извлек абстракцию, которая теперь работает в LINQPad из моего исходного кода. Это немного долго, чтобы опубликовать здесь, но это все здесь в сущности. Это все еще немного грязно от всех копий / вставок / рефакторинга, и я буду очищать его дальше, вероятно, разместив окончательный исходный код на CodePlex или GitHub после того, как я исправлю эти проблемы.
База кода достаточно велика и специфична для проекта, но, если это абсолютно необходимо, я смогу извлечь набор образцов. Тем не менее, весь доступ к объектам ClrMD довольно прост. Я получаю начальные адреса из команд SOS, таких как !dumpheap -stat
(который отлично работает для корневых объектов), а затем я использую ClrHeap.GetTypeByName
или же ClrHeap.GetObjectType
, После этого он полагается исключительно на ClrType.Fields
а также ClrInstanceField
члены Type
, ElementType
, а также GetFieldValue
В качестве дополнительного бонуса я нашел браузерную версию XML Docs, поставляемую с пакетом NuGet, хотя это та же документация, которую предоставляет IntelliSense.
1 ответ
Будет очень сложно ответить очень точно, не видя, как выглядит ваш код, но в основном это выглядит так:
Первое, что вам нужно знать, чтобы иметь возможность вызывать GetFieldAddress/GetFieldValue, это если у вас есть адрес объекта - обычный указатель или внутренний указатель. То есть, если он напрямую указывает на объект в куче или на внутреннюю структуру в реальном объекте (например, поле String vs. Struct в реальном объекте).
Если вы получаете неправильные значения из GetFieldAddress/GetFieldValue, это обычно означает, что вы не указываете, что у вас есть внутренний указатель (или вы думали, что он есть, когда его нет).
Вторая часть - это понимание значения.
Если field.IsPrimitive() имеет значение true: GetFieldValue() получит фактическое значение примитива (т. Е. Int32, Byte или что-то еще)
Если field.IsValueClass() имеет значение true, то GetFieldAddress() получит внутренний указатель на структуру. Таким образом, при любых вызовах GetFieldAddress/Value(), которые вы используете по этому адресу, вы должны сообщить ему, что это внутренний указатель!
Если field.ElementType является ClrElementType.String, то я помню, что вам нужно вызвать GetFieldValue, чтобы получить фактическое содержимое строки (нужно проверить, но это должно быть).
В противном случае у вас есть ссылка на объект, и в этом случае GetFieldValue () получит регулярный указатель на новый ссылочный объект.
Имеет ли это смысл?