Мониторинг .NET приложений

от автора

.NET – управляемая среда выполнения. Это означает, что в ней представлены высокоуровневые функции, которые управляют вашей программой за вас (из Introduction to the Common Language Runtime (CLR), 2007 г.):

Среда выполнения предусматривает множество функций, поэтому их удобно разделить по следующим категориям:

  1. Основные функции, которые влияют на устройство других. К ним относятся:
    1. сборка мусора;
    2. обеспечение безопасности доступа к памяти и безопасности системы типов;
    3. высокоуровневая поддержка языков программирования.
  2. Дополнительные функции– работают на базе основных. Многие полезные программы обходятся без них. К таким функциям относятся:
    1. изолирование приложений с помощью AppDomains;
    2. защита приложений и изолирование в песочнице.
  3. Другие функции – нужны всем средам выполнения, но при этом они не используют основные функции CLR. Такие функции отражают стремление создать полноценную среду программирования. К ним относятся:
    1. управление версиями;
    2. отладка/профилирование;
    3. обеспечение взаимодействия.

Видно, что хотя отладка и профилирование не являются основными или дополнительными функциями, они находятся в списке из-за ‘стремления создать полноценную среду программирования’.

Оставшаяся часть поста описывает, какие функции мониторинга, обеспечения наблюдаемости и интроспекции существуют в Core CLR, почему они полезны и каким образом среда предоставляет их

Диагностика

Для начала взглянем на диагностическую информацию, которой нас обеспечивает CLR. Традиционно для этого используется отслеживание событий для Windows (ETW).
Событий, о которых CLR предоставляет информацию, достаточно много. Они связаны со:

  • сбором мусора (GC);
  • JIT-компиляцией;
  • модулями и доменами приложений;
  • работой с тредами и конфликтами при блокировках;
  • а также многим другим.

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

Perf View

Если вы хотите увидеть события в системе трассировки (ETW), связанные с вашими .NET приложениями, я рекомендую использовать великолепный инструмент PerfView и начать с этих обучающих видео или этой презентации PerfView: The Ultimate .NET Performance Tool. PerfView получил широкое признание за предоставляемую бесценную информацию. Например, инженеры Microsoft регулярно используют его для анализа производительности.

Общая инфраструктура

Если вдруг непонятно из названия, трассировка событий в ETW доступна только под Windows, что не очень вписывается в кроссплатформенный мир .NET Core. Можно использовать PerfView для анализа производительности под Linux (с помощью LTTng). Однако этот инструмент с командной строкой, под названием PerfCollect, только собирает данные. Возможности анализа и богатый пользовательский интерфейс (в том числе flamegraphs) в настоящий момент доступны только в решениях для Windows.

Но если вы всё-таки хотите проанализировать производительность .NET под Linux, есть и другие подходы:

Вторая ссылка сверху ведёт на обсуждение новой инфраструктуры EventPipe, над которой работают в .NET Core (помимо EventSources & EventListeners). Цели её разработки можно посмотреть в документе Cross-Platform Performance Monitoring Design. На высоком уровне эта инфраструктура позволит создать единое место, куда CLR будет отсылать события, связанные с диагностикой и производительностью. Затем эти события будут перенаправляться к одному или более логерам, которые, например, могут включать ETW, LTTng и BPF. Необходимый логер будет определяться, в зависимости от ОС или платформы, на которой запущена CLR. Подробное объяснение плюсов и минусов различных технологий логирования см. в .NET Cross-Plat Performance and Eventing Design.

Ход работы по EventPipes отслеживается в рамках проекта Performance Monitoring и связанных с ним ‘EventPipe’ Issues.

Планы на будущее

Наконец, существуют планы по созданию контроллера профилирования производительности Performance Profiling Controller, перед которым стоят следующие задачи:

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

Согласно замыслу контроллер должен обеспечивать следующие функциональные возможности через HTTP-сервер, получая все необходимые данные из инфраструктуры EventPipes:

REST APIs

  • Принцип 1: простое профилирование: профилировать среду выполнения в течение периода времени X и возвращать трассировку.
  • Принцип 1: продвинутое профилирование: начинать отслеживание (вместе с конфигурацией)
  • Принцип 1: продвинутое профилирование: завершать отслеживание (ответом на этот вызов будет сама трассировка).
  • Принцип 2: получать статистику, связанную со всеми счётчиками EventCounters или определённым EventCounter.

HTML-страницы, просматриваемые через браузер

  • Принцип 1: текстовое представление всех стеков управляемого кода в процессе.
    • Создаёт моментальные снимки запущенных процессов для использования в качестве простого диагностического отчёта.
  • Принцип 2: отображение текущего состояния (возможно с историей) счётчиков EventCounters.
    • Обеспечивает обзор существующих счётчиков и их значений.
    • НЕРЕШЁННАЯ ПРОБЛЕМА: не думаю, что существуют нужные публичные API, чтобы подсчитывать EventCounters.

Я очень хочу увидеть, что в итоге получится с контроллером профилирования производительности (КПП?). Думаю, если его встроят в CLR, он принесёт много пользы .NET. Такой функционал есть в других средах выполнения.

Профилирование

Ещё одно эффективное средство, которое есть в CLR – API профилирования. Его (в основном) используют сторонние инструменты для подключения к среде выполнения на низком уровне. Подробнее про API можно узнать из этого обзора, но на высоком уровне с его помощью можно выполнять обратные вызовы, которые активируются, если:

  • происходят события, связанные со сборщиком мусора;
  • выбрасываются исключения;
  • загружаются/выгружаются сборки;
  • и многое другое.

Изображение со страницы BOTR Profiling API – Overview

Кроме того, у него есть другие эффективные функции. Во-первых, вы можете установить обработчики, которые вызываются каждый раз, когда выполняется метод .NET, будь то в самой среде или из пользовательского кода. Эти обратные вызовы известны как обработчики Enter/Leave. Вот здесь есть хороший пример, как их использовать. Однако для этого нужно понять конвенции вызовов для разных ОС и архитектур центрального процессора, что не всегда просто. Также не забывайте, что API профилирования это COM-компонент, доступ к которому можно получить только из кода C/C++, но не из C#/F#/VB.NET.

Во-вторых, профилировщик может переписать IL-код любого .NET-метода перед JIT-компиляцией с помощью SetILFunctionBody() API. Этот API действительно эффективен. Он лежит в основе многих инструментов APM .NET. Подробнее о его использовании можно узнать из моего поста How to mock sealed classes and static methods и сопутствующего кода.

ICorProfiler API

Оказывается, чтобы API профилирования работал, в среде выполнения должны быть всяческие ухищрения. Просто посмотрите на обсуждение на странице Allow rejit on attach (подробную информацию о ReJIT см. в ReJIT: A How-To Guide).

Полное определение всех интерфейсов и обратных вызовов API профилирования можно найти в \vm\inc\corprof.idl (см. Interface description language). Оно разбивается на 2 логические части. Одна часть – это интерфейс Профилировщик -> Среда выполнения (EE), известный как ICorProfilerInfo:

// Объявление класса, который реализует интерфейсы ICorProfilerInfo*, позволяющие  // профилировщику взаимодействовать со средой выполнения. Таким образом, библиотека DLL профилировщика получает // доступ к частным структурам данных среды выполнения и другим вещам, которые никогда не должны // экспортироваться за пределы среды.

Это реализуется в следующих файлах:

Другая основная часть – обратные вызовы Среда выполнения -> Профилировщик, которые группируются под интерфейсом ICorProfilerCallback:

// Этот модуль использует обёртки вокруг вызовов // интерфейсов ICorProfilerCallaback* профилировщика. Если коду в среде нужно вызвать // профилировщик, он должен пройти через EEToProfInterfaceImpl.

Эти обратные вызовы реализуются в следующих файлах:

Наконец, стоит заметить, что API профилирования могут не работать на всех ОС и архитектурах, на которых работает .NET Core. Вот один из примеров: ELT call stub issues on Linux. Подробную информацию см. в Status of CoreCLR Profiler APIs.

Profiling v. Debugging

В качестве небольшого отступления нужно сказать, что профилирование и отладка всё-таки немного пересекаются. Поэтому полезно понимать, что предоставляют различные API в контексте .NET Runtime (взято из CLR Debugging vs. CLR Profiling).
Разница между отладкой и профилированием в CLR

Отладка Профилирование
Предназначена для поиска проблем с корректностью кода Предназначено для диагностики и поиска проблем с производительностью
Может иметь очень высокий уровень вмешательства Как правило, имеет низкий уровень вмешательства. Хотя профилировщик поддерживает изменение IL-кода или установку обработчиков enter/leave, всё это предназначено для инструментирования, а не для радикального изменения кода.
Основная задача – полный контроль цели. Это включает инспекцию, контроль выполнения (например команда set-next-statement) и внесение модификаций (функция Edit-and-Continue). Основная задача – инспекция цели. Для этого предусмотрено инструментирование (изменение IL-кода, установка обработчиков enter/leave)
Обширный API и толстая модель объекта, заполненная абстракциями. Небольшой API. Абстракций мало или отсутствуют.
Высокий уровень интерактивности: действия отладчика контролируются пользователем (или алгоритмом). Фактически редакторы и отладчики часто интегрированы (IDE). Интерактивность отсутствует: данные обычно собираются без вмешательства пользователя, после чего анализируются.
Небольшое количество критических изменений, если нужна обратная совместимость. Мы думаем, что миграция с версии 1.1 на версию 2.0 отладчика, будет простой или не очень сложной задачей. Большое количество критических изменений, если нужна обратная совместимость. Мы думаем, что миграция с версии 1.1 на версию 2.0 отладчика, будет тяжёлой задачей, идентичной его полному переписыванию.


ссылка на оригинал статьи https://habr.com/ru/company/clrium/blog/466097/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *