24 November, 2009

Поддержка .NET Framework 4.0 в dotTrace 4.0 (Шаг №2)

Неожиданно быстро (всего за один рабочий день) получилось сделать нормальную поддержку .NET Framework 4.0 (реализован ICorProfilerCallback3 и используется ICorProfilerInfo3 + Enter3/Leave3/Tailcall3) в dotTrace 4.0, правда пока не совсем полноценную:

  • нет поддержки нескольких .NET Framework'ов
  • не реализован режим attach to process
Все остальное воркает в лучшем виде.

22 November, 2009

Поддержка .NET Framework 4.0 в dotTrace 4.0

По запросам дружественного проекта ReSharper был наконец вылизан режим совместимости с .NET Framework 4.0. Это конечно не есть полная поддержка нового фреймворка, но для начала это тоже неплохой шаг.

Аккуратность прежде всего

Есть такой режим совместимости в .NET Framework 4.0 - в этом режиме можно запустить профайлер для .NET Framework 2.0. Устанавливается он таким образом - переменной окружения COMPLUS_ProfAPI_ProfilerCompatibilitySetting присваиваем значение EnableV2Profiler. Так вот, если ненароком добавить пробел в конце присваиваемого значения, то режим совместимости просто не включается. А заметить это пробел очень трудно, если конечно не знать где искать.

07 November, 2009

GC и выгрузка модулей

Обнаружил очередную приколку в .NET Profiling API - есть событие ObjectsAllocatedByClass, где в качестве одно из аргументов передается массив ClassID. Так вот, если вызвать GetClassIDInfo/GetClassIDInfo2 для этого массива, то можно получить ModuleID для которого тоже можно вызвать GetModuleInfo. Все будет хорошо до тех пор пока GC не произойдет во время выгрузки application domain. Вот тут-то и появится проблема - GetModuleInfo может вернуть CORPROF_E_DATAINCOMPLETE. Так в чем же дело?

В MSDN сказано, что при выгрузке домена все классы и функции этого домена становятся недействительными - мы уже знаем что это ложь: во время выгрузки происходят компиляции функций и выделения объектов из самого выгружаемого домена до получения первого ModuleUnloadStarted. Сбор же мусора начинается как раз после получения всех ModuleUnloadStarted. Соответственно объекты из уже выгруженных модулей попадают в статистику GC. Занавес!

03 November, 2009

Ода гордости

Как это однако приятно увидеть, что результатом твоего труда пользуются в компании Microsoft. Смотреть тут! Для тех кто не в теме: стектрейсы сделаны моим детищем - JetBrains dotTrace 3.1.

23 October, 2009

Сюрприз

Ну вот, облизанный со всех сторон код рушится на моих глазах: RuntimeThreadSuspended и RuntimeThreadResumed оказывается могут вызываться из произвольного треда, а не только из того, что указан в аргументе. Получается, что повсеместно используемый TlsGetValue() именно в этих функциях со свистом пролетает мимо кассы. До сего момента я считал, что этим страдала только ThreadDestroyed, но там я выкрутился...

P.S. Иногда полезно быть параноиком и маниакально проверять в коде казалось бы очевидные вещи.

P.P.S. Если вы пользуетесь остановкой треда посредством DoStackSnapshot, то будьте готовы также получать и корректно обрабатывать RuntimeThreadSuspended и RuntimeThreadResumed.

21 October, 2009

Полезный тул

Каждый раз с завидным постоянством забываю название утилиты, которая показывает установленные .NET Compact Framework'и. Имя ей - cgacutil.exe. Надеюсь, что теперь не забуду.

23 September, 2009

Структура на стеке инструментируемой функции

Занимаясь инструментацией я пришел к выводу, что очень полезным является создание некой области на стеке функции для внутренних нужд профайлера. Для этого категорически необходимо создать структуру нужного размера. Увы, stackalloc не подходит так как требует довольно много накладных расходов и особенно под x64. Попытка использовать свой модуль с уже скомпилированными структурами во время работы профайлера приводит к зависанию во время загрузки оного. Поэтому пришлось в каждый модуль профилируемой программы эмитировать необходимую структуру.

Теперь о важном. Первое, что необходимо сделать - это найти mdAssemblyRef для mscorlob.dll и найти/создать mdTypeRef для System.ValueType - это необходимо, так как любая структура обязана иметь в качестве базового класса System.ValueType. Важно также чтобы наша структура имела атрибуты tdNotPublic, tdSequentialLayout, tdClass, tdSealed, tdBeforeFieldInit. И не забудьте установить необходимой вам class layout. Результатом всех этих действий будет mdTypeDef, который нужно засунуть в LocalVarSig.

Кстати в эту же структуру можно записывать (опять же с помощью той же инструментации) информацию о самой функции для профайлера - ну там statement count или еще что-нибудь нужное.