Получение глобальных корней из.NET программ

Недавно я начал использовать инструменты профилирования ANTS для производственных работ. Помимо удивления их удивительностью, я не мог не задаться вопросом, как они работают. Например, одна из наиболее полезных функций позволяет визуализировать глобальные корни работающей программы с указанием количества ссылок на значения различных типов.

Как этот инструмент получает эту информацию?

2 ответа

Решение

(Полное раскрытие: я в команде Visual Studio Profiler, но информация ниже является общедоступной)

Вы можете сделать это, написав профилировщик CLR, который выполняется внутри целевого процесса. Профилировщики CLR - это C++ COM-объекты, которые создаются во время выполнения, когда COR_PROFILER а также COR_PROFILING_ENABLED переменные окружения установлены (см. здесь). Существует два основных интерфейса профилирования CLR, а именно: ICorProfilerCallback а также ICorProfilerInfo, ICorProfilerCallback это то, что CLR использует для уведомления о конкретных событиях, на которые вы подписаны (загрузка модулей, компиляция функций JIT, создание потоков, события GC), в то время как ICorProfilerInfo может использоваться вашим профилировщиком для получения дополнительной информации о потоках, модулях, типах, методах и метаданных для загруженных сборок. Этот интерфейс - то, что вы могли бы использовать для получения символьной информации о выделенных типах.

С вашим профилировщиком в процессе, вы можете заставить GC через ICorProfilerInfo::ForceGC, После завершения GC ваш профайлер получит уведомление через ICorProfilerCallback2::GarbageCollectionFinished и вы получите корневые ссылки через ICorProfilerCallback2::RootReferences2, Когда вы объединяете корневую справочную информацию с ICorProfilerCallback::ObjectReferences, вы можете получить полный граф ссылок на объект для вашего приложения.NET.

Вы можете получить больше информации в реальном времени, используя ICorProfilerCallback::ObjectAllocated обратный вызов, чтобы определить, когда создаются отдельные объекты CLR. Это может быть дорогостоящим, поскольку вы выполняете как минимум дополнительный вызов функции для каждого выделенного объекта. Вы можете отслеживать отдельные объекты, сопоставляя назначенные CLR ObjectID на ваш собственный внутренний идентификатор. ObjectID для данного объекта это эфемерный указатель, поскольку он может меняться по мере сбора мусора, что может привести к перемещению объектов во время сжатия. Этот процесс иллюстрируется здесь. Вы можете использовать информацию из ICorProfilerCallback::MovedReferences отслеживать движущиеся объекты.

Чтобы активировать обратные вызовы, упомянутые выше, вам нужно сообщить API профилирования CLR, что вы заинтересованы в них. Вы можете сделать это, указав COR_PRF_MONITOR_GC а также COR_PRF_MONITOR_OBJECT_ALLOCATED как часть ваших флагов событий при звонке ICorProfilingInfo::SetEventMask,

Дэвид Броман (David Broman) является разработчиком профилировщика CLR, и в его блоге содержится масса полезной информации о профилировании в целом, включая все сумасшедшие ловушки и проблемы, с которыми вы можете столкнуться.

Профилировщики, такие как ANTS, используют "API профилирования", представленный самим CLR, который просто может сказать вам, что происходит внутри CLR. Например, существует метод обратного вызова API, который происходит при выделении объекта, метко названный ObjectAllocated (). Также есть события, когда вводятся методы, когда создаются потоки и т. Д. И т. Д.

Исходный API профилирования называется ICorProfilerCallback. Более поздние версии называются CoreProfilerCallback2 и CoreProfilerCallback3. Если вы гуглите эти имена, вы найдете именно те ответы, которые ищете. На codeproject вы можете увидеть практический пример: создание собственного.NET Profiler

Последнее замечание: API нельзя использовать из управляемого кода, такого как C# и VB.NET. Он доступен только из неуправляемого кода, например, C или C++. Поэтому приложение C# не может использовать этот API, например, для проверки своего поведения и объектов.

Другие вопросы по тегам