Как правильно работать с непримитивными значениями 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. Мне еще предстоит протестировать массивы структур, поэтому мне также интересно, будет ли это выделение встроенных структур в стиле C, как с int[], или это будет массив указателей на структуры в куче. Guid[] — это один из типов, из которых у меня возникли проблемы с получением ArrayComponentType.

Объект: Исправлено (логическая ошибка) С ClrInstanceField, у которого Type из ClrElementType.Object, я получаю гораздо лучшие результаты, но все же нужно немного больше. Во-первых, после вызова GetFieldValue я получаю обратно адрес ulong(?), против которого я могу использовать ClrInstanceField.Type.Fields, так что я могу видеть имена полей и значения вложенного объекта. Тем не менее, я должен учитывать полиморфизм, поэтому я попытался использовать ClrHeap.GetObjectType по тому же адресу, и он либо возвращает NULL, либо что-то совершенно неправильное. Кажется странным, что адрес сработал в моем первом случае, но не во втором.

Строка: Исправлено (найден обходной путь) Поскольку мой реальный проект уже использует DbgEng с SOS, у меня есть другой способ легко получить значение строки по адресу, но показалось очень странным, что при попытке использовать ClrInstanceField.GetFieldValue удалось вернуть строку, но с совершенно неточными результатами (куча странных символов). Может я что-то не так делаю?


Изменить: я извлек абстракцию, которая теперь работает в LINQPad, из исходного кода. Публиковать здесь немного долго, но все вкратце . Это все еще немного запутано из-за всего копирования/вставки/рефакторинга, и я буду чистить его дальше и, вероятно, опубликую окончательный исходный код либо на CodePlex, либо на GitHub после того, как я исправлю эти проблемы.

База кода довольно велика и специфична для проекта, но если это абсолютно необходимо, я могу извлечь набор примеров. Тем не менее, любой доступ к объектам ClrMD довольно прост. Я получаю начальные адреса из команд SOS, таких как !dumpheap -stat (что прекрасно работает для корневых объектов), а затем использую ClrHeap.GetTypeByName или ClrHeap.GetObjectType. После этого он полагается исключительно на ClrType.Fields и ClrInstanceField членов Type, ElementType и GetFieldValue

В качестве дополнительного бонуса я нашел дружественный к браузеру версия документов XML, поставляемая с пакетом NuGet, хотя это та же документация, которую предоставляет IntelliSense.


person TheXenocide    schedule 03.03.2014    source источник
comment
Я создал библиотеку расширений поверх ClrMD, которая хорошо интегрируется с LINQPad, проверьте это, если вам интересно: github.com/JeffCyr/ClrMD.Extensions   -  person Jeff Cyr    schedule 20.12.2014


Ответы (1)


Будет сложно ответить очень точно, не видя, как выглядит ваш код, но в основном это выглядит так:

Первое, что вам нужно знать, чтобы иметь возможность вызывать GetFieldAddress/GetFieldValue, — является ли адрес вашего объекта обычным указателем или внутренним указателем. То есть, если он напрямую указывает на объект в куче или на внутреннюю структуру внутри реального объекта (подумайте о поле String или Struct внутри реального объекта).

Если вы получаете неправильные значения из GetFieldAddress/GetFieldValue, это обычно означает, что вы не указываете, что у вас есть внутренний указатель (или вы думали, что он у вас есть, когда это не так).

Вторая часть — это понимание того, что означают значения.

Если field.IsPrimitive() имеет значение true: GetFieldValue() даст вам фактическое примитивное значение (т.е. Int32, Byte или что-то еще)

Если field.IsValueClass() имеет значение true, то GetFieldAddress() даст вам внутренний указатель на структуру. Таким образом, любые вызовы GetFieldAddress/Value(), которые вы используете для этого адреса, должны сообщать ему, что это внутренний указатель!

Если field.ElementType является ClrElementType.String, то я, кажется, помню, что вам нужно вызвать GetFieldValue, чтобы получить фактическое содержимое строки (необходимо проверить, но это должно быть так).

В противном случае у вас есть ссылка на объект, и в этом случае GetFieldValue() даст вам обычный указатель на новый объект ссылки.

Имеет ли это смысл?

person tomasr    schedule 06.03.2014
comment
В этом есть смысл, хотя я думаю, что некоторые трудности, с которыми я столкнулся, были связаны с абстракцией в моем коде, которая не знала, является ли она внутренней или нет; мне оказалось проще думать об этом, просто используя Address и Offset для некоторых сценариев, хотя теперь, когда я очищаю свой код, я мог бы вернуться к использованию GetFieldValue. На самом деле у меня уже извлечена большая часть моего кода, он почти запущен и работает автономно в LINQPad (борется с SZArrays, которые по какой-то причине не сообщают свой ArrayComponentType). - person TheXenocide; 07.03.2014
comment
Что касается строки, GetFieldValue определенно возвращал странные значения, но я прокомментировал обнаруженный мной обходной путь, который получает длину строки, а затем просто использует ReadMemory для заполнения byte[] данными Unicode, с которыми достаточно легко работать. Как только мой автономный модуль заработает правильно, я обновлю сообщение и, возможно, попрошу еще отзывов? Спасибо любезно. - person TheXenocide; 07.03.2014
comment
Я обновил вопрос, который теперь также включает ссылку на код. - person TheXenocide; 07.03.2014
comment
Я быстро взглянул на код, и действительно кажется, что вам нужно лучше отслеживать внутренние указатели при навигации по структуре объекта. На самом деле это не сложно, но требует четкого понимания того, на что указывает адрес. - person tomasr; 07.03.2014
comment
Кроме того, что касается массивов, вам нужно иметь в виду, что будут экземпляры, в которых ArrayComponentType отсутствует, потому что в интерфейсах отладки CLR возникают проблемы, из-за которых эта информация теряется. Это действительно имеет значение только в том случае, если у вас есть массив примитивов/структур, в противном случае вы всегда должны получать тип из кучи, используя адрес элемента. - person tomasr; 07.03.2014
comment
Наконец, что касается строк: вам не нужно возиться, чтобы получить фактическое значение. Если у вас есть адрес, указывающий на объект System.String, просто сделайте что-то вроде: String val = heap.GetObjectType(address).GetValue(address) as String; - person tomasr; 07.03.2014
comment
Да, я думал, что мне нужно будет добавить параметр /etc. к абстракции структуры, чтобы он знал, находится ли он непосредственно в куче или нет. Я пробую веревку сейчас, так как это низко висящий фрукт. Увы, я хотел бы получить доступ к некоторым массивам Guid в этом конкретном дампе памяти, но пока это основная проблема, а не что-то, что я делаю неправильно, я просто учту это в классах массива. Большое спасибо за вклад! - person TheXenocide; 07.03.2014
comment
На самом деле; относительно массивов ValueType; возможно ли использовать необработанную память или какой-либо прямой доступ для проверки массива? Я понимаю, что материал ClrMD может не иметь такой возможности, но дамп все равно должен содержать данные, пока они находятся в куче, верно? Может быть, я могу использовать некоторую комбинацию адресов/смещений? Просто мысль; пока у меня есть эталонные массивы, я могу делать большую часть того, что мне сейчас нужно. - person TheXenocide; 07.03.2014
comment
Я знал, что видел кое-что об этом в отношении SOS/WinDbg. Я думаю, что могу использовать информацию здесь: blogs.msdn .com/b/shawnfa/archive/2004/04/30/124218.aspx, но это возвращает меня к вопросу, который меня интересовал: есть ли способ получить ClrType из адреса MethodTable? В моем реальном проекте используются как ClrMD , так и DbgEng + SOS. - person TheXenocide; 07.03.2014
comment
Прямо сейчас работа с массивами ValueType вполне возможна с ClrMD. По сути, для каждой записи вызовите arrayType.GetArrayElementAddress() и обработайте результат как внутренний указатель при вызове GetFieldAddress/Value позже. - person tomasr; 08.03.2014
comment
Кроме того, адреса MethodTable не отображаются в ClrMD напрямую, поскольку они имеют некоторые недостатки (но, безусловно, удобны). Какое соответствие вам нужно сделать между двумя сторонами? Большинство (если не все) того, что делает SOS, можно сделать с помощью ClrMD... - person tomasr; 08.03.2014
comment
У меня есть массивы, которые неправильно отображают тип своего компонента и имеют некоторые ошибки при использовании стандартных методов массива (как вы упомянули выше, кажется, что интерфейс имеет некоторые ограничения). У меня есть несколько сегментов Guid[] и словаря, которые должны быть доступны в дампе памяти, но для которых я не могу очень чисто получить ClrType. Я думал, что статья, на которую я ссылался (и некоторые другие), указывает на схему памяти, которая включает информацию о MT. Я просто надеялся найти надежный способ получить ClrType. Я попытался удалить [] в конце имени типа массива, но дженерики взяли верх надо мной. - person TheXenocide; 10.03.2014
comment
Если вы знаете, что такое фактический тип объекта, вам просто нужно получить ClrType из объекта ClrHeap, и он будет работать. если вы пишете только общий код, то обойти ограничение CLR сложно, потому что вы действительно не знаете, с чем имеете дело. - person tomasr; 10.03.2014
comment
Таким образом, тип самого массива кажется относительно информативным, но недостаточно хорошим, поэтому я и задумался о МТ. Массив правильно сообщает свое имя (например, System.Guid[] или System.Collections.Generic.Dictionary+Entry<System.__Canon,System.__Canon>[]), но ArrayComponentType возвращает для них null, а другие методы массива не работают. Первое, что я попытался сделать, это удалить замыкающий [] из имени и попытаться получить тип из кучи, но я не думаю, что дженерики хорошо с этим справляются. - person TheXenocide; 11.03.2014
comment
Я также могу извлечь некоторую информацию из родительского объекта, но у меня не было возможности поэкспериментировать. Словарь, из которого взяты эти записи выше, перечисляет его имя как: Из родительского объекта у меня есть System.Collections.Generic.Dictionary<System.String,System.Object> - person TheXenocide; 11.03.2014
comment
Можете ли вы поделиться образцом дампа и фрагментом кода, который показывает проблему? Я бы с удовольствием посмотрел... - person tomasr; 11.03.2014
comment
Тот, который я использую, является проприетарным, поэтому мне придется выделить некоторое время, чтобы попытаться сделать несколько репродукций, но я был бы рад попробовать его. Рабочие приоритеты были немного скорректированы, но как только это будет завершено, я надеюсь сделать из этого проект с открытым исходным кодом, поэтому нужно больше личного времени (просто немного сложнее найти). Я думаю превратить это во что-то с некоторыми общими представлениями об устранении неполадок и механизмом для запросов и копания. Вы обязательно будете зачислены за всю помощь. Огромное спасибо! - person TheXenocide; 12.03.2014
comment
Извините за задержку, у меня была очень беспокойная неделя. Я постараюсь выкроить немного свободного времени на этих выходных (при условии, что выходные будут лучше, чем последние пару недель, лол). - person TheXenocide; 28.03.2014
comment
Может ли кто-нибудь указать мне информацию о том, как и когда использовать interior=true? Пока что я не вижу никакой логики в этом логическом значении и не могу найти точной информации о том, когда оно должно быть правдой. Надеюсь, кто-нибудь сможет помочь... - person Diana; 07.03.2016