Spec-Zone.ru › OCaml 4.14

Глава 23 Отслеживание выполнения с инструментированным окружением выполнения

  • 23.1 Обзор
  • 23.2 Включение инструментирования окружения выполнения
  • 23.3 Чтение трасс
  • 23.4 Управление инструментированием и ограничения

В этой главе описывается инструментированное окружение выполнения OCaml, позволяющее собирать события и метрики.

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

23.1 Обзор

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

Сгенерированные файлы трасс хранятся в формате Common Trace Format, представляющем собой универсальный двоичный формат трассировки. Полная трасса состоит из:

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

Дополнительную информацию о формате Common Trace Format см. на странице https://diamon.org/ctf/.

23.2 Включение инструментирования окружения выполнения

Для следующих примеров мы будем использовать следующую программу-пример:

module SMap = Map.Make(String)

let s i = String.make 512 (Char.chr (i mod 256))

let clear map = SMap.fold (fun k _ m -> SMap.remove k m) map map

let rec seq i =
  if i = 0 then Seq.empty else fun () -> (Seq.Cons (i, seq (i - 1)))

let () =
  seq 1_000_000
  |> Seq.fold_left (fun m i -> SMap.add (s i) i m) SMap.empty
  |> clear
  |> ignore

Следующим шагом является компиляция и компоновка программы с инструментированным окружением выполнения. Это можно сделать, используя флаг -runtime-variant:

       ocamlopt -runtime-variant i program.ml -o program

Обратите внимание, что инструментированное окружение выполнения является альтернативным окружением выполнения для программ OCaml. Оно упоминается только на этапе компоновки итогового исполняемого файла. Это означает, что этап компиляции не нуждается в изменении для включения инструментирования.

Полученную программу можно проследить, запустив её со переменной окружения OCAML_EVENTLOG_ENABLED:

        OCAML_EVENTLOG_ENABLED=1 ./program

Во время выполнения файл трассы будет создан в текущем рабочем каталоге программы.

Примеры сборки

При использовании системы сборки dune это вызов компилятора можно воспроизвести, используя секцию flags при построении исполняемого файла.

       (executable
         (name program)
         (flags "-runtime-variant=i"))

Инструментированное окружение выполнения также может использоваться с интерпретатором байткода OCaml. Это можно сделать, используя флаг -runtime-variant=i при компоновке программы с ocamlc, или запустив сгенерированный байткод через ocamlruni:

       ocamlc program.ml -o program.byte
       OCAML_EVENTLOG_ENABLED=1 ocamlruni program.byte

Для получения дополнительной информации об ocamlc и ocamlrun, см. главу 11 и главу 13.

23.3 Чтение трасс

Трассы, сгенерированные инструментированным окружением выполнения, могут быть проанализированы с помощью инструментов, доступных вне дистрибутива OCaml.

Полная трасса состоит из файла метаданных и файла трассы. Два простых способа работы с трассами — библиотеки eventlog-tools и babeltrace.

23.3.1 eventlog-tools

eventlog-tools — это библиотека, реализующая парсер, а также набор инструментов, позволяющий выполнять базовые преобразования форматов и анализ.

Дополнительную информацию о eventlog-tools см. на главной странице проекта: https://github.com/ocaml-multicore/eventlog-tools

23.3.2 babeltrace

babeltrace — это библиотека C, а также Python-связь и набор инструментов, служащих эталонной реализацией формата Common Trace Format. Утилита командной строки babeltrace позволяет выполнить базовое отображение содержимого трассы, а высокоуровневый Python-API может использоваться для декодирования трассы и обработки её программно с библиотеками, такими как numpy или Jupyter.

В отличие от eventlog-tools, имеющей специфическое знание схемы Common Trace Format OCaml, для babeltrace требуется предоставить файл метаданных OCaml.

Файл метаданных доступен в установке OCaml. Его расположение можно получить, используя следующую команду:

        ocamlc -where

Файл eventlog_metadata можно найти по этому пути и скопировать в ту же директорию, что и сгенерированный файл трассы. Однако babeltrace ожидает, что файл будет назван metadata для обработки трассы. Поэтому его нужно переименовать при копировании в директорию трассы.

Вот пример простого декодера, используя Python-библиотеку babeltrace и Python 3.8:

import subprocess
import shutil
import sys
import babeltrace as bt

def print_event(ev):
    print(ev['timestamp'])
    print(ev['pid'])
    if ev.name == "entry":
        print('entry_event')
        print(ev['phase'])
    if ev.name == "exit":
        print('exit_event')
        print(ev['phase'])
    if ev.name == "alloc":
        print(ev['count'])
        print(ev['bucket'])
    if ev.name == "counter":
        print(ev['count'])
        print(ev['kind'])
    if ev.name == "flush":
        print("flush")

def get_ocaml_dir():
    # Fetching OCaml's installation directory to extract the CTF metadata
    ocamlc_where = subprocess.run(['ocamlc', '-where'], stdout=subprocess.PIPE)
    ocaml_dir = ocamlc_where.stdout.decode('utf-8').rstrip('\n')
    return(ocaml_dir)

def main():
    trace_dir = sys.argv[1]
    ocaml_dir = get_ocaml_dir()
    metadata_path = ocaml_dir + "/eventlog_metadata"
    # copying the metadata to the trace's directory,
    # and renaming it to 'metadata'.
    shutil.copyfile(metadata_path, trace_dir + "/metadata")
    tr = bt.TraceCollection()
    tr.add_trace(trace_dir, 'ctf')
    for event in tr.events:
        print_event(event)

if __name__ == '__main__':
    main()

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

Дополнительную информацию о babeltrace см. на сайте: https://babeltrace.org/

23.4 Управление инструментированием и ограничения

23.4.1 Имя файла трассы

По умолчанию имя файла трассы — caml-{PID}.eventlog, где {PID} — идентификатор процесса отслеживаемой программы.

Это имя файла также можно указать, используя переменную окружения OCAML_EVENTLOG_PREFIX. Указанный путь будет дополнен {.PID}.eventlog.

        OCAML_EVENTLOG_PREFIX=/tmp/a_prefix OCAML_EVENTLOG_ENABLED=1 ./program

В этом примере трасса будет доступна по пути /tmp/a_prefix.{PID}.eventlog.

Обратите внимание, что это повлияет только на префикс имени файла трассы, нет возможности указать полное имя файла. Это ограничение предусмотрено для будущих улучшений инструментированного окружения выполнения, где дизайн единого файла трассы на сеанс может быть изменён.

Для скриптовых целей сопоставление с «{PID}», а также расширение файла .eventlog должно обеспечить достаточный контроль над создаваемыми файлами.

Также обратите внимание, что родительские директории в указанном пути не будут созданы при открытии трассы. Окружение выполнения предполагает, что путь доступен для создания и записи трассы. Программа не сможет запуститься, если это требование не выполнено.

23.4.2 Приостановка и возобновление отслеживания

Существуют механизмы управления сбором событий во время выполнения.

OCAML_EVENTLOG_ENABLED можно установить на флаг p, чтобы запустить программу с приостановленным сбором событий.

        OCAML_EVENTLOG_ENABLED=p ./program

Программа должна явно начать сбор событий. Запуск и остановка сбора событий программно можно выполнить, вызвав Gc.eventlog_resume и Gc.eventlog_pause) внутри программы. Дополнительную информацию см. в документации модуля Gc.

Запуск программы, приведенной выше, с OCAML_EVENTLOG_ENABLED=p даст, например, следующий результат.

$ OCAML_EVENTLOG_ENABLED=p ./program
$ ocaml-eventlog-report caml-{PID}.eventlog
==== eventlog/flush
median flush time: 58ns
total flush time: 58ns
flush count: 1

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

Однако, если в программу добавить вызов Gc.eventlog_resume, полезные нагрузки событий снова будут видны в файле трассы.

       let () =
         Gc.eventlog_resume();
         seq 1_000_000
         |> Seq.fold_left (fun m i -> SMap.add (s i) i m) SMap.empty
         |> clear
         |> ignore

Результирующая трасса будет содержать все события, произошедшие во время выполнения программы:

        $ ocaml-eventlog-report caml-{PID}.eventlog
        [..omitted..]
        ==== force_minor/alloc_small
        100.0K..200.0K: 174
        20.0K..30.0K: 1
        0..100: 1

        ==== eventlog/flush
        median flush time: 207.8us
        total flush time: 938.1us
        flush count: 5

23.4.3 Ограничения

Инструментированное окружение выполнения не поддерживает системный вызов fork. Дочерний процесс, созданный с помощью fork из инструментированной программы, не будет отслеживаться.

Инструментированное окружение выполнения предназначено для предоставления информации о работе окружения выполнения во время выполнения при сохранении низкой нагрузки. Однако эта нагрузка может стать более заметной в зависимости от того, как работает программа. В настоящее время инструментированное окружение выполнения уделяет основное внимание отслеживанию событий мусорного сборщика. Это означает, что программы с интенсивной активностью мусорного сборщика могут быть более подвержены последствиям отслеживания производительности.

Трудно дать точную оценку потенциальной потери производительности, но тесты с различными программами OCaml показали увеличение общего времени выполнения в диапазоне от 1% до 8%.

Для программы с длительным временем выполнения, когда требуется сбор только небольшой выборки событий, использование функций eventlog_resume и eventlog_pause может помочь уменьшить влияние отслеживания на производительность.

© 1995-2022 INRIA.
https://v2.ocaml.org/releases/4.14/htmlman/instrumented-runtime.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API