Глава 25 Отслеживание во время выполнения с помощью событий во время выполнения
В этой главе описана система отслеживания событий во время выполнения, которая позволяет непрерывно извлекать информацию о производительности из среды выполнения OCaml с очень низкими накладными расходами. Система и интерфейсы являются низкоуровневыми и тесно связаны с реализацией среды выполнения, поэтому предполагается, что конечные пользователи будут полагаться на инструменты для потребления и визуализации интересующих данных.
Выводимые данные включают:
- Временные метки событий сборщика мусора и фаз среды выполнения
- Размеры и использование кучи при малых и больших сборках
- Скорость выделения и продвижения между кучами
25.1 Обзор
Существует три основных класса событий, генерируемых системой событий во время выполнения:
- Отрезки
- События, охватывающие определенный период времени. Например, система отслеживания событий во время выполнения генерирует событие отрезка, которое начинается, когда в сборщике мусора OCaml начинается малая сборка, и заканчивается, когда сборка завершается. Отрезки могут содержать другие отрезки, т.е. могут быть сгенерированы другие события отрезков, которые начинаются после начала малой сборки и заканчиваются до ее завершения.
- События жизненного цикла
- События, происходящие в определенный момент времени. Например, при завершении домена генерируется соответствующее событие жизненного цикла.
- Счетчики
- События, включающие измерение некоторой интересующей величины. Например, количество слов, переданных из малой кучи в большую кучу во время последней малой сборки мусора, генерируется как событие счетчика.
Система отслеживания событий во время выполнения предназначена для использования в различных контекстах:
- Самоконтроль
- Программы и библиотеки OCaml могут устанавливать свои собственные обратные вызовы для прослушивания событий во время выполнения и реагирования на них программно, например, для вывода событий в файл или через сеть.
- Внешний мониторинг
- Внешний процесс может потреблять события во время выполнения программы OCaml, для которой система отслеживания событий во время выполнения была включена путем установки соответствующей переменной окружения.
Система отслеживания событий во время выполнения записывает события в кольцевой буфер. В результате старые события перезаписываются новыми. Потребители могут либо непрерывно потреблять события, либо выбрать потребление только в ответ на определенные обстоятельства, например, если выполнение определенного запроса или операции занимает больше времени, чем ожидалось.
25.2 Архитектура
Система отслеживания во время выполнения концептуально состоит из двух частей: 1) зондов, которые генерируют события, и 2) транспорт событий, который обрабатывает и передает эти события.
25.2.1 Зонды
Зонды собирают события из системы выполнения. Они далее подразделяются на две группы: 1) зонды, которые всегда доступны, и 2) зонды, доступные только в инструментированной среде выполнения. Зонды в инструментированной среде выполнения в первую очередь представляют интерес для разработчиков среды выполнения OCaml и сборщика мусора и в настоящее время включают только события счетчика размера выделения большой кучи.
Полный набор генерируемых зондами событий и их документация доступны в Модуль Runtime_events.
25.2.2 Транспорт событий
Часть системы транспорта событий обрабатывает события, генерируемые зондами, и делает их доступными для потребителей.
Кольцевые буферы
События передаются с помощью структуры данных, известной как кольцевой буфер. Эта структура данных состоит из двух указателей в линейном массиве-основе, указатель хвоста указывает на место, куда можно записать новые события, а указатель головы указывает на самое старое событие в буфере, которое можно прочитать. Когда в массиве-основе недостаточно места для записи новых событий, указатель головы перемещается вперед, и самые старые события перезаписываются новыми.
Реализация кольцевого буфера, используемая в событиях во время выполнения, может быть записана не более чем одним производителем за раз, но может одновременно читаться несколькими потребителями без координации от производителя. Для каждого выполняющегося домена существует уникальный кольцевой буфер, а при завершении домена кольцевые буферы могут быть повторно использованы для вновь запущенных доменов. Сами кольцевые буферы хранятся в файле с отображением памяти с идентификатором процесса в качестве имени и расширением .events, что позволяет их читать извне основной программы OCaml. Дополнительная информация см. в Runtime_events.
Интерфейсы потребления
Система отслеживания событий во время выполнения предоставляет как OCaml, так и C интерфейсы, основанные на курсорах и работе с опросом. Общий процесс потребления событий следующий:
- Создается курсор с помощью Runtime_events.create_cursor для текущего процесса или внешнего процесса (указанного путем и PID).
- Runtime_events.Callbacks.create вызывается для регистрации функции обратного вызова для приема событий.
- Курсор опрашивается с помощью Runtime_events.read_poll с использованием созданных на предыдущем шаге обратных вызовов. Для каждого соответствующего события в кольцевых буферах вызываются предоставленные функции обратного вызова.
25.3 Использование
25.3.1 С помощью OCaml интерфейсов
Начнем с простого примера, который выводит имя, начальное и конечное время событий, генерируемых системой отслеживания событий во время выполнения:
let runtime_begin _ ts phase =
Printf.printf "Begin\t%s\t%Ld\n"
(Runtime_events.runtime_phase_name phase)
(Runtime_events.Timestamp.to_int64 ts)
let runtime_end _ ts phase =
Printf.printf "End\t%s\t%Ld\n"
(Runtime_events.runtime_phase_name phase)
(Runtime_events.Timestamp.to_int64 ts)
let () =
Runtime_events.start ();
let cursor = Runtime_events.create_cursor None in
let callbacks = Runtime_events.Callbacks.create ~runtime_begin ~runtime_end ()
in
while true do
let list_ref = ref [] in (* for later fake GC work *)
for _ = 1 to 100 do
(* here we do some fake GC work *)
list_ref := [];
for _ = 1 to 10 do
list_ref := (Sys.opaque_identity(ref 42)) :: !list_ref
done;
Gc.full_major ();
done;
ignore(Runtime_events.read_poll cursor callbacks None);
Unix.sleep 1
done
Следующим шагом является компиляция и компоновка программы с библиотекой runtime_events. Это можно сделать следующим образом:
ocamlopt -I +runtime_events -I +unix unix.cmxa runtime_events.cmxa
example.ml -o example
При использовании системы сборки dune этот пример можно собрать следующим образом:
(executable (name example) (modules example) (libraries unix runtime_events))
Запуск скомпилированного двоичного файла примера даст вывод, подобный:
Begin explicit_gc_full_major 24086187297852 Begin stw_leader 24086187298594 Begin minor 24086187299404 Begin minor_global_roots 24086187299807 End minor_global_roots 24086187331461 Begin minor_remembered_set 24086187331631 Begin minor_finalizers_oldify 24086187544312 End minor_finalizers_oldify 24086187544704 Begin minor_remembered_set_promote 24086187544879 End minor_remembered_set_promote 24086187606414 End minor_remembered_set 24086187606584 Begin minor_finalizers_admin 24086187606854 End minor_finalizers_admin 24086187607152 Begin minor_local_roots 24086187607329 Begin minor_local_roots_promote 24086187609699 End minor_local_roots_promote 24086187610539 End minor_local_roots 24086187610709 End minor 24086187611746 Begin minor_clear 24086187612238 End minor_clear 24086187612580 End stw_leader 24086187613209 ...
Это пример самоконтроля, когда программа явно начинает прослушивать события во время выполнения и отслеживать себя.
Для внешнего мониторинга программа не должна знать о существовании событий во время выполнения. События во время выполнения можно управлять через переменную окружения OCAML_RUNTIME_EVENTS_START, которая при установке заставит систему отслеживания событий во время выполнения стартовать во время инициализации среды выполнения.
Мы могли бы удалить Runtime_events.start (); из предыдущего примера и, вместо этого, вызвать программу следующим образом, чтобы получить тот же результат:
OCAML_RUNTIME_EVENTS_START=1 ./example
Переменные окружения
Переменные окружения можно использовать для управления различными аспектами системы отслеживания событий во время выполнения. Доступны следующие переменные окружения:
- OCAML_RUNTIME_EVENTS_START, если установлена, запустит систему событий во время выполнения в качестве части инициализации среды выполнения OCaml.
- OCAML_RUNTIME_EVENTS_DIR устанавливает каталог, где будут находиться файлы .events, содержащие кольцевые буферы системы отслеживания событий во время выполнения. Если ее нет, будет использоваться рабочий каталог программы.
- OCAML_RUNTIME_EVENTS_PRESERVE, если установлена, заставит среду выполнения OCaml сохранить файлы кольцевых буферов событий во время выполнения после завершения программы OCaml. Это может быть полезно для мониторинга программ с очень коротким временем выполнения. Если она не установлена, файлы .events программы OCaml будут удалены при завершении программы.
Размер кольцевых буферов событий во время выполнения можно настроить через OCAMLRUNPARAM, см. раздел 15.2 для получения дополнительной информации.
Компиляция с инструментированной средой выполнения
Чтобы получать события, доступные только в инструментированной среде выполнения, программу OCaml необходимо скомпилировать и связать с инструментированной средой выполнения. Для нашей примера программы из предыдущего примера это достигается следующим образом:
ocamlopt -runtime-variant i -I +runtime_events -I +unix unix.cmxa runtime_events.cmxa example.ml -o example
А для dune:
(executable (name example) (modules example) (flags "-runtime-variant=i") (libraries unix runtime_events))
25.3.2 С помощью инструментов
Программный доступ к событиям предназначен в первую очередь для разработчиков библиотек наблюдаемости и инструментов, используемых конечными пользователями. Гибкий API позволяет использовать данные производительности из событий во время выполнения для целей ведения журнала и мониторинга.
В этом разделе мы рассмотрим несколько утилит в пакете runtime_events_tools, которые предоставляют простые способы извлечения и обобщения данных из событий во время выполнения. В частности, утилита трассировки генерирует данные, аналогичные данным системы инструментирования «eventlog», доступной в OCaml 4.12–4.14.
Сначала установите runtime_events_tools в OCaml 5.0+ opam switch:
opam install runtime_events_tools
Это должно установить инструмент olly в вашу среду. Теперь вы можете генерировать трассировки во время выполнения для программ, скомпилированных с OCaml 5.0+, используя подкоманду trace:
olly trace trace.json 'your_program.exe .. args ..'
Данные трассировки во время выполнения будут сгенерированы в формате JSON Trace Event Format в trace.json. Затем их можно загрузить в Chrome Tracing Viewer или в Perfetto для визуализации собранной трассировки.
Измерение задержки GC
Утилита olly также включает подкоманду задержки, которая потребляет данные событий выполнения и при завершении программы генерирует анализируемую гистограмму сводки длительностей пауз. Она может быть запущена следующим образом:
olly latency 'your_program.exe .. args ..'
Это должно вывести вывод, аналогичный следующему:
GC latency profile: #[Mean (ms): 2.46, Stddev (ms): 3.87] #[Min (ms): 0.01, max (ms): 9.17] Percentile Latency (ms) 25.0000 0.01 50.0000 0.23 60.0000 0.23 70.0000 0.45 75.0000 0.45 80.0000 0.45 85.0000 0.45 90.0000 9.17 95.0000 9.17 96.0000 9.17 97.0000 9.17 98.0000 9.17 99.0000 9.17 99.9000 9.17 99.9900 9.17 99.9990 9.17 99.9999 9.17 100.0000 9.17
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/5.0/htmlman/runtime-tracing.html