Як скидати стеки горутину?


91

У мене фон Java, і я люблю використовувати сигнал QUIT для перевірки дампа потоку Java.

Як дозволити Golang роздрукувати всі стеки трасируючих програм?


Це відповідає на ваше запитання? Як отримати стек паніки (і зберегти як змінну)
hlin117

Відповіді:


107

Щоб надрукувати трасування стека для поточної програми , використовуйте PrintStack()зruntime/debug .

PrintStack друкує зі стандартною помилкою стек, що повертається Stack.

Наприклад:

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

Для друку трасування стека для всіх використання goroutines 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 - стеки стеку всієї поточної
купи goroutines - вибірка всіх розподілів
кучі threadcreate - стеки стеків, що призвели до створення нового
блоку потоків ОС - стеки стека, що призвели до блокування на примітивах синхронізації

Наприклад:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)

1
Чи друкує він стек усіх горутинів?

Це повинно, це кличе Stack. "Стек повертає відформатований стек програми, яка викликає його. Для кожної процедури вона включає інформацію про вихідний рядок та значення ПК, а потім намагається виявити для функцій Go функцію або метод виклику та текст рядка, що містить виклик ".
Intermernet

1
На жаль, він друкує лише поточну трасу стека програми.

4
@HowardGuo Я додав приклад використання Runtime / pprof для скидання всіх слідів стека.
Intermernet

1
Я думаю, що це виводить лише поточну програму кожного потоку, а не всі програми , наприклад: play.golang.org/p/0hVB0_LMdm
rogerdpack

39

Існує інтерфейс 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


я змусив його запустити це лише показує виконані програми, наскільки я бачу. Чи є спосіб побачити всі "методи", які виконуються після запуску 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 = все.

Друк трасування стека ініціюється несподіваною умовою виконання (необроблений сигнал), спочатку задокументованою в цьому коміті , що робить його доступним принаймні з 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. Якщо все правда, формати стеку стекують сліди всіх інших програм у buf після трасування поточної програми.


Сюди входять зворотні сліди з усіх горутин, приємно!
rogerdpack

Це формат, у якому зводиться невиправлена ​​паніка?
Ztyx

2
Не забудьте додати рядок (buf), інакше ви надрукуєте там необроблені байти.
koda

2
Можливо, я роблю щось не так, або, можливо, функціонал змінився, але це не отримує для мене нічого, крім порожнього фрагмента байтів?
17xande

1
@koda тут робити не потрібно string(buf), fmt.Printf("%s", buf)і fmt.Printf("%s", string(buf))робити точно те саме (див. документи для fmtпакету); єдина різниця тут полягає в тому, що stringверсія скопіює байти з bufнепотрібності
kbolino

20

Натисніть CTRL + \

(Якщо ви запускаєте його в терміналі і просто хочете вбити вашу програму і скинути підпрограми 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.
Девід Тутілл

Ніколи не бачив порожніх рядків.
Браян

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

Якщо ви також хочете надрукувати GoRutine GoRutine:

$ GOTRACEBACK=system go run main.go

Ось усі варіанти GOTRACEBACK:

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

Ось документація

Змінна GOTRACEBACK контролює кількість вихідних даних, що генеруються, коли програма Go не працює через невизначену паніку або несподівану умову виконання.

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

З історичних причин налаштування GOTRACEBACK 0, 1 та 2 є синонімами відповідно none, all та system.

Функція SetTraceback пакета виконання / налагодження дозволяє збільшити кількість вихідних даних під час виконання, але вона не може зменшити кількість, нижчу за вказану змінною середовища. Див. Https://golang.org/pkg/runtime/debug/#SetTraceback .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.