go tool trace и escape analysis

Trace показывает временную историю событий runtime: goroutine, scheduler, GC, syscalls и blocking. Escape analysis — решение компилятора о том, может ли значение жить на stack, или ему нужна heap-аллокация; это реализация, а не API-контракт.

Зачем это на интервью

Нужно уметь выбрать trace при вопросе «почему работа ждёт», а не пытаться найти эту причину только CPU profile, и не оптимизировать escapes по выводу компилятора без измерения.

Минимум для E4

  • Записывать trace: go test ./... -run '^$' -trace=trace.out или runtime/trace в контролируемом сценарии.
  • Открывать go tool trace trace.out и искать периоды runnable/running/blocked, GC и syscalls.
  • Запускать go test -gcflags='-m=2' ./pkg/... для объяснений escape.
  • Подтверждать performance-вывод benchmark/pprof, а не только -m.
go test ./internal/queue -run '^TestBurst$' -trace=trace.out
go tool trace trace.out
go test -gcflags='-m=2' ./internal/codec

Углубление для E5/Senior

Trace полезен для scheduler latency, плохого GOMAXPROCS, долгого GC, network/syscall blocking и неравномерности goroutine. Он даёт временные связи, но артефакт может быть крупным и добавлять overhead; production-сбор согласуют, ограничивают длительностью и защищают так же, как profile. В trace сопоставляют задержку с workload, метриками и downstream-зависимостями.

Escape случается, если компилятор не может доказать безопасную stack-границу: например, адрес переживает вызов, значение помещается в interface или замыкание, размер слишком велик. Это не означает «любой pointer = heap», и конкретное решение меняется между Go releases. -m=2 даёт причины, а -gcflags следует применять к нужному пакету, избегая шума всего dependency graph. Изменение API ради одной аллокации оправдано только после профиля и оценки читаемости/безопасности.

Ключевые понятия

ИнструментСигналНе заменяет
tracetimeline runtime и goroutineагрегированный CPU/heap профиль
-m=2объяснения inline/escape компилятораизмерение allocations
benchmarkстоимость фиксированной операциисервисный load test

Типовые вопросы

  1. Когда trace лучше pprof? — При scheduler/blocking/GC timeline; pprof лучше агрегированно ранжирует CPU или allocations.
  2. Escape всегда плохо? — Нет, heap необходим для корректности и часто не является bottleneck.
  3. Pointer всегда escape? — Нет, компилятор анализирует lifetime и может оставить объект на stack.
  4. Почему вывод -m меняется? — Escape/inlining — детали оптимизатора, зависящие от версии и контекста сборки.
  5. Можно ли снять trace в production? — Только контролируемо: короткий интервал, доступ, overhead и политика хранения должны быть определены.

Практика

  • Воспроизведите goroutine blocking и снимите trace. Готово: указаны состояние, временной интервал, причина и подтверждающая метрика.
  • Найдите allocation через -m=2 и pprof. Готово: показано, совпадает ли компиляторная гипотеза с alloc_space; изменение измерено benchmark.

Частые ошибки и ловушки

  • Делать вывод о p99 по одной дорожке trace без workload и метрик.
  • Считать escape bug или обязателным поводом переписать API.
  • Снимать долгий trace в production и хранить его без контроля доступа.

Связанные темы

Инструменты Go · pprof · Производительность и память

Источники