Перейти к основному содержимому

profiler

Обзор

Встроенный профайлер показывает, на что во время прогона Testplane ушло фактическое время (wall time) и процессорное время, выделяет вероятные узкие места и предлагает изменения, которые стоит попробовать. Измерения и рекомендации разделены: каждая рекомендация содержит подтверждающие данные и уровень уверенности high, medium или low.

Профилирование поддерживается для запусков через CLI и программных API run и readTests.

Настройка

testplane.config.js
module.exports = {
profiler: {
level: 2,
output: "profiler-result.json",
},
};
ПараметрТипПо умолчаниюОписание
level0 | 1 | 2 | 30Определяет совокупную детализацию сбора данных. 0 отключает профайлер.
outputstring | nullnullНеобязательный путь к JSON-отчёту, который записывается атомарно и разрешается относительно текущей рабочей директории.

level

Уровни являются накопительными:

  • 0 не собирает данные, ничего не выводит в консоль профайлера и не создаёт событие или файл;
  • 1 записывает весь прогон и основные фазы жизненного цикла, процессорное время процесса, метрики цикла событий, память и образцы загрузки CPU хоста;
  • 2 добавляет обработчики событий, загрузку тестовых файлов и работу кэша, тесты и сгруппированные хуки, загрузку воркеров, очереди пула браузеров и повторное использование сессий;
  • 3 добавляет отдельные хуки, браузерные команды, границы загрузки CommonJS/ESM, активное время и время ожидания отдельных асинхронных областей, а также частичную телеметрию браузерного окружения.

Используйте уровень 1 для поиска медленной фазы, уровень 2 для обычного исследования, а уровень 3 — только когда нужна дополнительная детализация. Значение level должно быть целым числом от 0 до 3.

output

Если параметр задан, output должен быть непустым путём с расширением .json. Testplane записывает отчёт во временный файл, а затем атомарно переименовывает его. Если output не задан, сводка в консоли и событие PROFILER_RESULT остаются доступными.

Пользователи TypeScript могут импортировать публичный тип результата из корня пакета:

import type { ProfilerResultV1 } from "testplane";

Чтение результата

Отчёт содержит следующие секции верхнего уровня:

  • run: операция, результат, общая длительность и причины частичного результата;
  • environment и capabilities: сведения о среде выполнения и доступных измерениях;
  • timeline: сохранённые операции с контекстом процесса и корреляции;
  • aggregates: полная потоковая статистика, даже если подробные операции были усечены;
  • findings: наблюдения, подтверждённые данными, и предлагаемые эксперименты;
  • dataQuality: покрытие сборщиков, погрешность синхронизации часов и предупреждения;
  • profiler: ограниченный набор ошибок сбора, сведения об усечении и измеренные накладные расходы профайлера во время прогона.

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

[profiler] Test run profile
________________________________________________________________________________________

Total time: 452ms

Execution breakdown

Phase Time Time % Bar
____________________________ _____ ______ __________
Initialize Testplane 351ms 77.8% ██████████
Discover and load test files 85ms 18.7% ██
Load configuration 7ms 1.5%
Unattributed 7ms 1.5%
Load plugins 2ms 0.4%
Set up transforms 0ms <0.1%

Performance findings

1. MEDIUM • Slow event listener • init:acceptanceSlowInit

init:acceptanceSlowInit used 351ms across 1 call(s). Slowest retained call at
/path/to/project/.profiler-acceptance/acceptance-plugin.cjs
(.profiler-acceptance/acceptance-plugin.cjs:5:19) took 351ms.

Slowest call breakdown
Activity Time Call % Bar
_________ _____ ______ _____________
Active JS 0ms <0.1%
Waiting 350ms 99.9% █████████████

Suggested action:
init:acceptanceSlowInit (acceptance-plugin.cjs:5:19): waiting dominates the slowest
retained call; inspect awaited I/O or timers and remove avoidable serial waits.

________________________________________________________________________________________
[profiler] 1 finding: 1 medium

В JSON измерения и рекомендации хранятся отдельно:

{
"schemaVersion": 1,
"run": {
"level": 2,
"profileStatus": "complete",
"runOutcome": "passed",
"durationMs": 133000
},
"timeline": [],
"aggregates": {},
"findings": [
{
"category": "event-listener",
"confidence": "high",
"evidence": [{ "metric": "wall", "value": 30000, "unit": "ms" }],
"action": "Inspect this listener's source and reduce synchronous work."
}
]
}

Пример сокращён. Для обработки полного содержимого используйте публичный тип ProfilerResultV1 и поле schemaVersion.

Каждая операция в timeline разделяет фактическое время, суммарную работу, перекрытие, влияние на критический путь и доступную оценку CPU. Для параллельных операций суммарная работа может превышать фактическое время всего прогона. Это ожидаемое поведение, и такое значение нельзя интерпретировать как прошедшее время прогона.

processCpuMs измеряется для временного окна процесса и не является эксклюзивным при перекрытии операций. На уровне 3 дополнительно могут быть доступны процессорное время потока и оценки синхронной активности JavaScript в сравнении с асинхронным ожиданием. Задержка цикла событий измеряется для всего процесса. В версии 1 атрибуция CPU браузера недоступна. Перед использованием любого необязательного поля проверяйте capabilities и dataQuality.coverage.

Сведения о сущностях сохраняются с детерминированными ограничениями top-K и сериализованного размера. Поле profiler.truncation показывает, сколько данных было обнаружено и сохранено, при этом агрегаты по-прежнему включают все наблюдения. Внутренние ошибки сборщиков переводят результат в состояние partial, но не изменяют результат тестов.

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

Граница жизненного цикла

Профиль начинается на входе CLI/API и включает конфигурацию, плагины, инициализацию, обнаружение и загрузку тестов, запуск master-процесса и воркеров, сессии, выполнение тестов, репортеры и штатное завершение работы. Этапы, которые команда не выполняет, отсутствуют, а не отображаются с нулевой длительностью. Непокрытое время представлено как unattributed.

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

END → RUNNER_END → сброс данных и завершение воркеров → afterAll и очистка
→ заморозка ProfilerResultV1 → консоль + PROFILER_RESULT + необязательный JSON

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

Обработка события

module.exports = testplane => {
testplane.on(testplane.events.PROFILER_RESULT, async result => {
await sendToTelemetry(result);
});
};

Асинхронное событие получает тот же неизменяемый объект, который используется для вывода в консоль и JSON. Его обработчики находятся за пределами замороженного профиля и не могут рекурсивно добавлять в него интервалы.