Как сбрасывать трассировки стека горутин?

91

У меня есть опыт работы с Java, и я люблю использовать сигнал QUIT для проверки дампа потока Java.

Как позволить Golang распечатать трассировку стека всех горутин?

Роджердпак
источник

Ответы:

107

Чтобы распечатать трассировку стека для текущей горутины, используйте PrintStack()fromruntime/debug .

PrintStack печатает до стандартной ошибки трассировку стека, возвращаемую Stack.

Например:

import(
   "runtime/debug"
)
...    
debug.PrintStack()

Чтобы распечатать трассировку стека для всех горутин, используйте Lookupи WriteToиз runtime/pprof.

func Lookup(name string) *Profile
// Lookup returns the profile with the given name,
// or nil if no such profile exists.

func (p *Profile) WriteTo(w io.Writer, debug int) error
// WriteTo writes a pprof-formatted snapshot of the profile to w.
// If a write to w returns an error, WriteTo returns that error.
// Otherwise, WriteTo returns nil.

Каждый профиль имеет уникальное имя. Несколько профилей предопределены:

goroutine - трассировки стека всех текущих горутин,
куча - выборка всех распределений кучи
threadcreate - трассировки стека, которые привели к созданию нового
блока потоков ОС - трассировки стека, которые привели к блокировке примитивов синхронизации

Например:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
Интермернет
источник
1
Печатает ли он трассировку стека всех горутин?
Он должен, зовет Stack. "Стек возвращает отформатированную трассировку стека горутины, которая его вызывает. Для каждой процедуры он включает информацию о строке источника и значение ПК, а затем пытается обнаружить для функций Go вызывающую функцию или метод и текст строки, содержащей призыв ".
Intermernet
1
Извините, он печатает только текущую трассировку стека горутины.
4
@HowardGuo Я добавил пример использования runtime / pprof для выгрузки всех трассировок стека.
Intermernet
1
Я думаю, что это выводит только горутины,
выполняемые
41

Существует HTTP-интерфейс для runtime/pprofпакета, упомянутого в ответе Intermernet. Импортируйте пакет net / http / pprof , чтобы зарегистрировать обработчик HTTP для /debug/pprof:

import _ "net/http/pprof"
import _ "net/http"

Запустите прослушиватель HTTP, если у вас его еще нет:

go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

Затем укажите в браузере http://localhost:6060/debug/pprofменю или http://localhost:6060/debug/pprof/goroutine?debug=2полный дамп стека горутин.

Таким образом вы также можете узнать о своем работающем коде и другие интересные вещи. Ознакомьтесь с сообщениями в блоге для примеров и более подробной информации: http://blog.golang.org/profiling-go-programs

lnmx
источник
Я запустил его, он показывает только выполненные горутины, насколько я понимаю. Могу ли я увидеть все «методы», которые выполняются после запуска main.go?
Лукас Лукач
38

Чтобы имитировать поведение Java при выводе дампа стека при SIGQUIT, но при этом оставив программу работающей:

go func() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGQUIT)
    buf := make([]byte, 1<<20)
    for {
        <-sigs
        stacklen := runtime.Stack(buf, true)
        log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
    }
}()
Брайан
источник
4
Я думаю, это то, что действительно искал автор - имитирует то, что делает Java, когда вы отправляете kill -QUIT. Одно небольшое изменение, которое мне пришлось сделать, - это изменить первую строку цикла for () на «<- sigs». Другими словами, просто сбросьте сигнал, дождавшись его. Последние версии Go не позволяют объявлять переменную без ее последующего использования.
Джордж Армхолд
@Bryan, готовы ли вы лицензировать это в соответствии с BSD или другими более разрешительными условиями в дополнение к CC-BY-SA 3.0, требуемому StackOverflow?
Чарльз Даффи
1
@CharlesDuffy, вы можете найти то же самое здесь под лицензией Apache: github.com/weaveworks/weave/blob/ ...
Брайан
37

Подобно Java, SIGQUIT можно использовать для печати трассировки стека программы Go и ее горутин.
Однако ключевое отличие состоит в том, что по умолчанию отправка SIGQUIT программам Java не завершает их, в то время как программы Go завершаются.

Этот подход не требует изменения кода для печати трассировки стека всех горутин существующих программ.

Переменная среды GOTRACEBACK ( см. Документацию к пакету времени выполнения ) контролирует объем создаваемого вывода. Например, чтобы включить все горутины, установите GOTRACEBACK = all.

Печать трассировки стека запускается неожиданным условием выполнения (необработанный сигнал), изначально задокументированным в этом коммите , что делает его доступным по крайней мере с версии Go 1.1.


В качестве альтернативы, если можно изменить исходный код, см. Другие ответы.


Обратите внимание, что в терминале Linux SIGQUIT можно удобно отправить с помощью комбинации клавиш Ctrl+ \.

Родольфо Карвалью
источник
5
Просматривая документы, я не нашел упоминаний о SIGQUIT, точнее о SIGABRT. Судя по моим собственным тестам (с go 1.7), последний также работал над первым.
солтыш
4
это должен быть главный ответ.
Стивен Сорока
В документации говорится о «сбое программы Go из-за неисправной паники или непредвиденного состояния выполнения». Неперехваченный сигнал (SIGQUIT и т. Д.) Является одним из последних. Почему я упомянул SIGQUIT? Поскольку OP выражает свою любовь к использованию SIGQUIT с Java, и этот ответ подчеркивает сходство. Перефразируем ответ, чтобы было понятнее.
Родольфо Карвалью
26

Вы можете использовать runtime.Stack, чтобы получить трассировку стека всех горутин:

buf := make([]byte, 1<<16)
runtime.Stack(buf, true)
fmt.Printf("%s", buf)

Из документации:

func Stack(buf []byte, all bool) int

Стек форматирует трассировку стека вызывающей горутины в buf и возвращает количество байтов, записанных в buf. Если все верно, Stack форматирует трассировки стека всех других горутин в buf после трассировки для текущей горутины.

Роберт Каджич
источник
Сюда входят следы от всех горутин, хорошо!
rogerdpack
Неужели это тот формат, который сводится к использованию невосстановленной паники?
Ztyx
2
Не забудьте добавить строку (buf), иначе вы напечатаете туда необработанные байты.
koda
2
Может быть, я делаю что-то не так, или, возможно, изменилась функциональность, но это не возвращает мне ничего, кроме пустого фрагмента байтов?
17xande 03
1
@koda нет необходимости делать string(buf)здесь, fmt.Printf("%s", buf)и fmt.Printf("%s", string(buf))сделать то же самое (см документации для fmtпакета); единственная разница здесь в том, что stringверсия будет копировать байты bufбез нужды
kbolino
20

Нажмите CTRL + \

(Если вы запускаете его в терминале и просто хотите убить свою программу и сбросить подпрограммы go и т. Д.)

Я нашел этот вопрос в поисках ключевой последовательности. Просто хотел быстрый и простой способ узнать, не утекает ли моя программа подпрограммы go :)

Эйвинд Скаар
источник
11

В системах * NIX (включая OSX) отправьте сигнал прерывания SIGABRT:

pkill -SIGABRT program_name

Кайл Клёппер
источник
Очевидно, отправка SIGQUIT процессу Java не завершает его, как SIGABRT.
Dave C
Я обнаружил, что это самое простое и наиболее подходящее решение исходного вопроса. Часто вам нужна трассировка стека сразу, без изменения кода.
jotrocken
5

Необходимо использовать длину, возвращаемую до, runtime.Stack()чтобы не печатать кучу пустых строк после трассировки стека. Следующая функция восстановления распечатывает красиво отформатированную трассировку:

if r := recover(); r != nil {
    log.Printf("Internal error: %v", r))
    buf := make([]byte, 1<<16)
    stackSize := runtime.Stack(buf, true)
    log.Printf("%s\n", string(buf[0:stackSize]))
}
Дэвид Тутилл
источник
Нет runtime.Trace; runtime.Stack уже упоминалось полтора года назад .
Dave C
Я такого никогда не видел; на какой платформе вы работаете?
Брайан
Что вы еще не видели? Код должен работать на всех платформах; Я тестировал его на Windows 7, Ubuntu 14.04 и Mac.
Дэвид Тотилл
Никогда не видел пустых строк.
Bryan
4

По умолчанию нажмите ^\клавиши ( CTRL + \ ), чтобы выгрузить трассировку стека всех горутин.


В противном случае для более детального контроля вы можете использовать panic. Простой способ для Go 1.6+ :

go func() {
    s := make(chan os.Signal, 1)
    signal.Notify(s, syscall.SIGQUIT)
    <-s
    panic("give me the stack")
}()

Затем запустите вашу программу так:

# Press ^\ to dump the stack traces of all the user-created goroutines
$ GOTRACEBACK=all go run main.go

Если вы также хотите распечатать горутины времени выполнения:

$ GOTRACEBACK=system go run main.go

Вот все варианты GOTRACEBACK:

  • GOTRACEBACK=none полностью исключает трассировку стека горутин.
  • GOTRACEBACK=single (по умолчанию) ведет себя, как описано выше.
  • GOTRACEBACK=all добавляет трассировки стека для всех горутин, созданных пользователем.
  • GOTRACEBACK=system похож на `` все '', но добавляет фреймы стека для функций времени выполнения и показывает горутины, созданные внутри среды выполнения.
  • GOTRACEBACK=crashпохоже на `` систему '', но вместо выхода происходит сбой в зависимости от операционной системы. Например, в системах Unix возникает сбой, вызывающий SIGABRTдамп ядра.

Вот документация

Переменная GOTRACEBACK контролирует количество выходных данных, генерируемых при сбое программы Go из-за невосстановленной паники или неожиданного условия выполнения.

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

По историческим причинам настройки GOTRACEBACK 0, 1 и 2 являются синонимами для none, all и system соответственно.

Функция SetTraceback пакета среды выполнения / отладки позволяет увеличить объем вывода во время выполнения, но не может уменьшить объем ниже указанного в переменной среды. См. Https://golang.org/pkg/runtime/debug/#SetTraceback .

Inanc Gumus
источник