Пакет pprof
Обзор
Пакет pprof записывает данные профилирования выполнения в формате, ожидаемом инструментом визуализации pprof.
Профилирование программы Go
Первый шаг для профилирования программы Go — это включение профилирования. Поддержка профилирования бенчмарков, созданных с помощью стандартного пакета тестирования, встроена в go test. Например, следующая команда выполняет бенчмарки в текущем каталоге и записывает профили ЦП и памяти в cpu.prof и mem.prof:
go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
Для добавления аналогичной поддержки профилирования в самостоятельную программу, добавьте код, подобный следующему, в вашу функцию main:
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close() // error handling omitted for example
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
// ... rest of the program ...
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
defer f.Close() // error handling omitted for example
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
}
}
Также существует стандартный HTTP-интерфейс для данных профилирования. Добавление следующей строки установит обработчики по URL /debug/pprof/ для скачивания текущих профилей:
import _ "net/http/pprof"
Дополнительные сведения см. в пакете net/http/pprof.
Профили можно визуализировать с помощью инструмента pprof:
go tool pprof cpu.prof
Из командной строки pprof доступно множество команд. Часто используемые команды включают "top", которая выводит сводку наиболее ресурсоёмких участков программы, и "web", которая открывает интерактивную диаграмму наиболее ресурсоёмких участков и их вызовов. Используйте "help", чтобы получить информацию обо всех командах pprof.
Для получения дополнительной информации о pprof, см. https://github.com/google/pprof/blob/main/doc/README.md.
Индекс
Файлы пакета
elf.go label.go map.go pe.go pprof.go pprof_rusage.go proto.go proto_other.go protobuf.go protomem.go runtime.go
func Do 1.9
func Do(ctx context.Context, labels LabelSet, f func(context.Context))
Do вызывает f с копией родительского контекста, к которому добавлены указанные метки в карту меток родителя. Потоки, запущенные во время выполнения f, унаследуют расширенный набор меток. Каждая пара ключ/значение в метках вставляется в карту меток в указанном порядке, перезаписывая любое предыдущее значение для того же ключа. Расширенная карта меток будет установлена на время вызова f и восстановлена после возвращения f.
func ForLabels 1.9
func ForLabels(ctx context.Context, f func(key, value string) bool)
ForLabels вызывает f с каждым набором меток в контексте. Функция f должна возвращать true, чтобы продолжить итерацию, или false, чтобы остановить итерацию досрочно.
func Label 1.9
func Label(ctx context.Context, key string) (string, bool)
Label возвращает значение метки с заданным ключом в ctx и булево значение, указывающее, существует ли эта метка.
func SetGoroutineLabels 1.9
func SetGoroutineLabels(ctx context.Context)
SetGoroutineLabels устанавливает метки текущего потока в соответствии с ctx. Новый поток наследует метки потока, который его создал. Это API более низкого уровня, чем Do, который следует использовать вместо него, когда это возможно.
func StartCPUProfile
func StartCPUProfile(w io.Writer) error
StartCPUProfile включает профилирование ЦП для текущего процесса. Во время профилирования профиль будет буферизован и записан в w. StartCPUProfile возвращает ошибку, если профилирование уже включено.
В системах Unix StartCPUProfile по умолчанию не работает для кода Go, построенного с помощью -buildmode=c-archive или -buildmode=c-shared. StartCPUProfile полагается на сигнал SIGPROF, но этот сигнал будет доставлен обработчику сигнала SIGPROF основной программы (если есть), а не обработчику, используемому Go. Для его работы вызовите os/signal.Notify для syscall.SIGPROF, но обратите внимание, что это может нарушить любое профилирование, выполняемое основной программой.
func StopCPUProfile
func StopCPUProfile()
StopCPUProfile останавливает текущий профиль ЦП, если он есть. StopCPUProfile возвращает только после завершения всех записей профиля.
func WithLabels 1.9
func WithLabels(ctx context.Context, labels LabelSet) context.Context
WithLabels возвращает новый контекст context.Context с добавленными метками. Метка перезаписывает предыдущую метку с тем же ключом.
func WriteHeapProfile
func WriteHeapProfile(w io.Writer) error
WriteHeapProfile — это сокращение для Lookup("heap").WriteTo(w, 0). Оно сохраняется для обратной совместимости.
тип LabelSet 1.9
LabelSet — это набор меток.
type LabelSet struct {
// contains filtered or unexported fields
}
func Labels 1.9
func Labels(args ...string) LabelSet
Labels принимает четное количество строк, представляющих пары ключ-значение, и создает LabelSet, содержащий их. Метка перезаписывает предыдущую метку с тем же ключом. В настоящее время только профили ЦП и потоков используют информацию о метках. Подробности см. на https://golang.org/issue/23458.
тип Profile
Profile — это набор стековых следов, показывающих последовательность вызовов, приведших к экземплярам определенного события, такого как выделение. Пакеты могут создавать и поддерживать свои собственные профили; наиболее распространенное использование — отслеживание ресурсов, которые необходимо явно закрыть, таких как файлы или сетевые соединения.
Методы Profile могут вызываться из нескольких потоков одновременно.
Каждый Profile имеет уникальное имя. Несколько профилей определены заранее:
goroutine - stack traces of all current goroutines heap - a sampling of memory allocations of live objects allocs - a sampling of all past memory allocations threadcreate - stack traces that led to the creation of new OS threads block - stack traces that led to blocking on synchronization primitives mutex - stack traces of holders of contended mutexes
Эти заранее определённые профили поддерживают себя и вызывают панику при явном вызове метода Profile.Add или Profile.Remove.
Профиль ЦП недоступен как Profile. У него есть специальный API, функции StartCPUProfile и StopCPUProfile, поскольку он передает вывод в писатель во время профилирования.
Профиль кучи
Профиль кучи сообщает статистику по состоянию на последнюю завершённую сборку мусора; он опускает более поздние выделения, чтобы избежать искажения профиля от живых данных в сторону мусора. Если сборка мусора не происходила, профиль кучи сообщает обо всех известных выделениях. Это исключение помогает в основном в программах, работающих без включённой сборки мусора, обычно в целях отладки.
Профиль кучи отслеживает место выделения всех живых объектов в памяти приложения и всех объектов, выделенных с момента запуска программы. Флаги pprof -inuse_space, -inuse_objects, -alloc_space и -alloc_objects выбирают, что отображать, по умолчанию -inuse_space (живые объекты, масштабируемые по размеру).
Профиль выделений
Профиль выделений такой же, как профиль кучи, но изменяет отображение pprof по умолчанию на -alloc_space, общее количество байтов, выделенных с момента запуска программы (включая собранные мусором).
Профиль блокировок
Профиль блокировок отслеживает время, затраченное на блокировку на синхронизирующих примитивах, таких как sync.Mutex, sync.RWMutex, sync.WaitGroup, sync.Cond и отправка/прием/выбор по каналу.
Стековые следы соответствуют месту блокировки (например, sync.Mutex.Lock).
Образцы соответствуют суммарному времени, затраченному на блокировку в этом стековом следе, с учётом выборочного измерения по времени, заданного runtime.SetBlockProfileRate.
Профиль мьютексов
Профиль мьютексов отслеживает конкуренцию за мьютексы, такие как sync.Mutex, sync.RWMutex и внутренние блокировки runtime.
Стековые следы соответствуют окончанию критической секции, вызывающей конкуренцию. Например, блокировка, удерживаемая в течение долгого времени, в то время как другие потоки ожидают её захвата, будет сообщать о конкуренции при окончательном разблокировании (то есть, в sync.Mutex.Unlock).
Образцы соответствуют приблизительному суммарному времени, затраченному другими потоками на ожидание блокировки, с учётом выборочного измерения по событиям, заданного runtime.SetMutexProfileFraction. Например, если вызывающий удерживает блокировку 1 секунду, в то время как 5 других потоков ожидают весь этот секунду, чтобы захватить блокировку, стек вызова разблокирования будет сообщать о 5 секундах конкуренции.
Внутренние блокировки runtime всегда отображаются в местоположении "runtime._LostContendedRuntimeLock". Более подробные стековые следы для внутренних блокировок runtime можно получить, установив `GODEBUG=runtimecontentionstacks=1` (см. документацию пакета runtime для замечаний).
type Profile struct {
// contains filtered or unexported fields
}
func Lookup
func Lookup(name string) *Profile
Lookup возвращает профиль с заданным именем или nil, если такой профиль не существует.
func NewProfile
func NewProfile(name string) *Profile
NewProfile создаёт новый профиль с заданным именем. Если профиль с таким именем уже существует, NewProfile вызывает панику. Для обеспечения совместимости с различными инструментами, которые считывают данные pprof, имена профилей не должны содержать пробелов.
func Profiles
func Profiles() []*Profile
Profiles возвращает срез всех известных профилей, отсортированных по имени.
func (*Profile) Add
func (p *Profile) Add(value any, skip int)
Add добавляет текущий стек вызовов в профиль, связанный со значением. Add сохраняет значение во внутренней карте, поэтому значение должно быть подходящим для использования в качестве ключа карты и не будет удалено сборщиком мусора до соответствующего вызова Profile.Remove. Add вызывает панику, если профиль уже содержит стек для значения.
Параметр skip имеет то же значение, что и skip в runtime.Caller, и определяет, с какой точки стека начинается трассировка. Передача skip=0 начинает трассировку с вызова функции Add. Например, при таком стеке вызовов:
Add called from rpc.NewClient called from mypkg.Run called from main.main
Передача skip=0 начинает трассировку с вызова Add внутри rpc.NewClient. Передача skip=1 начинает трассировку с вызова NewClient внутри mypkg.Run.
func (*Profile) Count
func (p *Profile) Count() int
Count возвращает количество стеков выполнения, текущих в профиле.
func (*Profile) Name
func (p *Profile) Name() string
Name возвращает имя этого профиля, которое можно передать в Lookup для повторного получения профиля.
func (*Profile) Remove
func (p *Profile) Remove(value any)
Remove удаляет стек выполнения, связанный со значением, из профиля. Это ничего не делает, если значение не находится в профиле.
func (*Profile) WriteTo
func (p *Profile) WriteTo(w io.Writer, debug int) error
WriteTo записывает в w дамп профиля в формате pprof. Если запись в w возвращает ошибку, WriteTo возвращает эту ошибку. В противном случае WriteTo возвращает nil.
Параметр debug включает дополнительный вывод. Передача debug=0 записывает сжатый gzip протокол, описанный в https://github.com/google/pprof/tree/main/proto#overview. Передача debug=1 записывает устаревший текстовый формат с комментариями, преобразующими адреса в имена функций и номера строк, чтобы программист мог прочитать профиль без инструментов.
Предварительно определённые профили могут назначать значения параметру debug; например, при печати профиля «goroutine», debug=2 означает печать стеков goroutine в том же формате, что и Go-программа использует при завершении из-за необработанной паники.
Ошибки
- ☞
Профили так же хороши, как и поддержка ядра, используемая для их создания. Подробности о известных проблемах можно найти в https://golang.org/issue/13841.
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/runtime/pprof/