Как заглянуть внутрь работающего кода с помощью magic-trace
Знакомая ситуация: приложение в продакшене внезапно начинает «тупить» на одном запросе из тысячи. Вы открываете логи — там пусто. Смотрите в профайлер — он показывает среднюю температуру по больнице, размазывая аномалию по остальным вызовам. В итоге приходится гадать по кофейной гуще или обкладывать всё кодом с метриками, надеясь поймать баг за хвост.
Ребята из Jane Street (те самые, что торгуют на бирже и обожают OCaml) выложили инструмент magic-trace, который решает эту проблему иначе. Он не просто сэмплирует стек раз в миллисекунду, а записывает вообще всё, что делал процессор за мгновения до того, как вы нажали «стоп».
Что это такое и зачем оно мне
Если коротко, magic-trace — это обертка над системной утилитой perf, которая использует магию технологии Intel Processor Trace (Intel PT). В отличие от обычных профайлеров, которые спрашивают программу «где ты сейчас?» 100 или 1000 раз в секунду, Intel PT аппаратно фиксирует каждое ветвление кода.
В итоге вы получаете не статистическую вероятность того, что функция тормозит, а точную хронологию событий с разрешением около 40 наносекунд. Это позволяет увидеть даже те функции, которые выполняются настолько быстро, что обычный perf их просто промаргивает.
Проект пригодится, если нужно:
- Понять, почему конкретный запрос обрабатывается медленно, пока остальные летают.
- Увидеть реальный путь исполнения кода, а не тот, который вы нарисовали у себя в голове.
- Посмотреть, что именно делала программа за 10 мс до падения.
Как это работает на практике
Инструмент работает только на Linux и только на процессорах Intel (начиная со Skylake). Если вы запускаете код в виртуальной машине, скорее всего, ничего не заведется — Intel PT редко пробрасывают внутрь виртуалок.
Установка простая: скачиваете бинарник из релизов, даете права на исполнение и готово. Никаких изменений в код вносить не нужно, если только вы не хотите использовать триггеры (о них чуть позже).
Самый простой способ попробовать — приаттачиться к работающему процессу:
magic-trace attach -pid $(pidof my_app)
Когда почувствуете, что «пора», нажимаете Ctrl+C. Утилита выплюнет файл trace.fxt.gz. И вот тут начинается самое интересное. Вы открываете magic-trace.org (это форк гугловского Perfetto, который работает прямо в браузере) и перетаскиваете туда файл.
Перед вами разворачивается таймлайн. Можно зумиться до уровня отдельных вызовов функций. Навигация стандартная для геймеров: W/S — зум, A/D — влево-вправо.
Главные фишки
Никакого оверхеда (почти)
Разработчики заявляют о нагрузке в 2–10%. Для инструмента, который пишет каждый вызов функции, это очень мало. Достигается это за счет того, что основную работу берет на себя железо Intel.
Триггеры — киллер-фича
Просто нажимать Ctrl+C — это весело, но не всегда эффективно. В magic-trace можно настроить автоматический снимок по вызову конкретной функции.
Например, у вас есть функция handle_request. Вы запускаете:
magic-trace run -trigger handle_request -- ./my_app
Как только программа вызовет эту функцию, magic-trace сделает слепок кольцевого буфера. Это идеально для отлова редких лагов. Можно даже оставить пустую функцию-заглушку в коде специально для таких случаев. Пока magic-trace не запущен, вызов этой функции стоит копейки, зато в нужный момент она сработает как кнопка затвора на фотоаппарате.
Взгляд вглубь ядра
Если запустить утилиту от рута с флагом -trace-include-kernel, можно увидеть, как ваша программа взаимодействует с операционной системой. Часто оказывается, что «тормоза» в коде — это на самом деле page faults или ожидание мьютекса в ядре, которые раньше были невидимы.
Почему это не просто очередной профайлер
Обычные профайлеры хороши для поиска «горячих точек» — мест, где программа проводит больше всего времени в среднем. Magic-trace же создан для поиска аномалий.
Один из пользователей в отзывах к репозиторию хорошо подметил: «Я смог увидеть все вызовы внутри функции, которая длится всего 70 наносекунд». Обычный сэмплирующий профайлер даже не узнает, что такая функция существовала.
Стоит ли пробовать
Если вы пишете высоконагруженные системы на C, C++, Rust или даже OCaml под Linux, то magic-trace должен быть в вашем арсенале. Это как разница между фотографией (профайлинг) и замедленной съемкой в 4K (magic-trace).
Конечно, есть ограничения:
- Только Intel. Владельцы AMD и Apple Silicon остаются за бортом.
- Кольцевой буфер ограничен. Обычно это последние 10 мс работы. Этого достаточно, чтобы понять причину тормозов, но не хватит, чтобы изучить логику работы за минуту.
- Нужно понимать, как работает стек вызовов, иначе обилие данных в Perfetto может просто напугать.
В остальном — это отличный пример того, как сложные системные технологии можно упаковать в удобный инструмент, который не требует переписывания половины проекта ради одного замера.
Для старта советую заглянуть в demo.c в репозитории проекта. Скомпилируйте его и попробуйте отследить, как работает dlopen — это отличный способ прочувствовать мощь инструмента на простом примере.
