Возможно, вы знаете, что у браузере Google Chrome есть встроенный профилировщик. Но даже из тех людей, кто его видел, большинство считает, что использовать его можно только для отладки Javascript или отрисовки кадров в браузере. Но на самом деле его весьма просто можно прикрутить в качестве средства визуализации данных профилирования в вашем проекте.
Я не открою здесь каких-то уникальных секретов, например, Colt McAnlis писал о подобном применении профилировщика Chrome в игровых проектах ещё в 2012 году. Всё, написанное там, всё ещё является правдой, а я напишу ещё один материал — просто для лучшего распространения знаний о столь полезном инструменте.
Предыстория
Для некоторой части нашей системы сборки кода мы когда-то написали простенький профилировщик (называется TinyProfiler). Он достаточно тривиален — замеряет время выполнения определенных блоков кода и создаёт набор HTML+SVG файлов, которые визуализируют эти данные в стиле flame-графов:
Это, в принципе, неплохо работало, но полученный HTML был не очень интерактивным. Можно было подвести мышку к определенному блоку и увидеть его название во всплывающей подсказке, но на этом все удобства и заканчивались. Не было ни зума, ни фильтрации, ни скрола, ни поиска — в общем ничего, чего хотелось бы получить от более-менее профессионального инструмента. Всё это можно было, конечно, сесть и написать, но… зачем же это делать, если можно этого не делать? Ведь уже есть кто-то (разработчики Chrome), кто всё это уже сделал.
Использование Chrome Tracing
Всё, что нам нужно сделать, это:
- Создать JSON-файл определенного формата, содержащий данные о профилируемых событиях
- Открыть в Chrome страницу chrome://tracing
- Перетащить JSON-файл на эту страницу
Результат визуально достаточно похож на картинку нашего мини-профилировщика:
Преимущества данного подхода:
- Значительно улучшенный UI: зум, скрол, фильтрация, статистика и т.д.
- Нам больше не нужно писать свой (пусть даже простой) код для UI
- Значительно упростившийся код по сбору и обработке данных (JSON создать проще, чем SVG)
Но есть и несколько недостатков:
- Нет простого способа «кликнуть на файле отчёта для его открытия». Chrome не позволяет открывать файлы с trace-данными из командной строки. Приходится каждый раз открывать страницу chrome://tracing и уже только с неё открывать нужный файл.
- Это можно исправить, если взять утилиту trace2html из репозитория Chrome — она умеет создавать готовый HTML со всем необходимым. Но для этого придётся утянуть к себе, хранить и собирать 1 ГБ исходников (там ведь не одна эта утилита). А это как-то слишком много для подобной задачи. Возможно, получилось бы вырезать из репозитория только то, что необходимо для сборки данной утилиты — но на это ушло бы время, да и с дальнейшей поддержкой совместимости возникли бы проблемы.
- Мы завязываемся на ещё один сторонний инструмент. Однако он, по всей видимости, достаточно стабилен (например, упомянутая выше статья 2012 года всё ещё дословно верна сейчас в 2017 году), открыт и его уже используют некоторые серьёзные инструменты (вроде Facebook Buck).
Мне кажется, что преимущества перевешивают недостатки.
Формат JSON-файла
Этот формат достаточно прост сам по себе, а также хорошо документирован. Базовая структура выглядит вот так:
{
"traceEvents": [
{ "pid":1, "tid":1, "ts":87705, "dur":956189, "ph":"X", "name":"Jambase", "args":{ "ms":956.2 } },
{ "pid":1, "tid":1, "ts":128154, "dur":75867, "ph":"X", "name":"SyncTargets", "args":{ "ms":75.9 } },
{ "pid":1, "tid":1, "ts":546867, "dur":121564, "ph":"X", "name":"DoThings", "args":{ "ms":121.6 } }
],
"meta_user": "aras",
"meta_cpu_count": "8"
}
traceEvents — это события, которые будут показаны в UI. Всё, что не является событиями, считается «метаданными», которые будут показаны в диалоге метаданных (в данном случае это meta_user и meta_cpu_count). Вышеуказанный JSON-файл будет выглядеть в Chrome Tracing вот так:
События, которые описаны в нём, достаточно просты. Они называются «завершенными» (тип ph:X) — для их описания нам потребуется указать время начала и длительность в микросекундах (аттрибуты ts и dur). Ещё указываются аттрибуты ID процесса и ID потока, а также имя события. Необходимости указывать отношение родитель-ребёнок нет — UI определит его сам на основе времени начала и длительности каждого события.
К событиям также можно прикреплять какие-небудь данные (аттрибут args), которые при выборе данного события будут отображаться на нижней панели. Есть небольшой нюанс — чтобы событие вообще можно было выбрать, у него должен быть непустой args. То есть хотя бы что-нибудь (пусть даже ненужное) туда лучше положить. Не можете придумать что — продублируйте там длительность события (возможно, преобразовав его из микросекунд в милисекунды или секунды).
Вот и всё, что я хотел рассказать по базовому использованию этого инструмента. Возможно, статья получилась несколько простоватой — но это потому, что и сам инструмент простой и удобный. Для продвинутых вариантов его использования читайте документацию. Удачного профилирования.
Автор: Владимир