Bagaimana cara membuang stacktraces goroutine?

Jawaban:

107

Untuk mencetak jejak tumpukan untuk goroutine saat ini , gunakan PrintStack()fromruntime/debug .

PrintStack mencetak ke kesalahan standar jejak tumpukan yang dikembalikan oleh Stack.

Sebagai contoh:

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

Untuk mencetak jejak tumpukan untuk semua penggunaan Lookupdan WriteTodari goroutine 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.

Setiap Profil memiliki nama yang unik. Beberapa profil sudah ditentukan sebelumnya:

goroutine - tumpukan jejak dari semua
tumpukan goroutine saat ini - sebuah contoh dari semua alokasi tumpukan
threadcreate - jejak tumpukan yang mengarah ke pembuatan
blok utas OS baru - jejak tumpukan yang menyebabkan pemblokiran pada sinkronisasi primitif

Sebagai contoh:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
Intermernet
sumber
1
Apakah itu mencetak jejak tumpukan semua goroutine?
Seharusnya, itu panggilan Stack. "Stack mengembalikan pelacakan tumpukan yang diformat dari goroutine yang memanggilnya. Untuk setiap rutinitas, ini menyertakan informasi baris sumber dan nilai PC, kemudian mencoba menemukan, untuk fungsi Go, fungsi atau metode pemanggil dan teks dari baris yang berisi doa."
Intermernet
1
Maaf, ini hanya mencetak jejak tumpukan goroutine.
4
@ HowardGuo Saya telah menambahkan contoh menggunakan runtime / pprof untuk membuang semua jejak tumpukan.
Intermernet
1
Saya pikir ini hanya mengeluarkan setiap utas goroutine yang sedang berjalan, tidak semua goroutine, mis .: play.golang.org/p/0hVB0_LMdm
rogerdpack
41

Ada frontend HTTP untuk runtime/pprofpaket yang disebutkan dalam jawaban Intermernet. Impor paket net / http / pprof untuk mendaftarkan penangan HTTP untuk /debug/pprof:

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

Mulai pendengar HTTP jika Anda belum memilikinya:

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

Kemudian arahkan browser ke http://localhost:6060/debug/pprofmenu, atau http://localhost:6060/debug/pprof/goroutine?debug=2untuk tumpukan dump goroutine lengkap.

Ada hal menyenangkan lainnya yang dapat Anda pelajari tentang kode yang sedang berjalan dengan cara ini juga. Lihat entri blog untuk mengetahui contoh dan detail lebih lanjut: http://blog.golang.org/profiling-go-programs

lnmx
sumber
saya membuatnya berjalan dengan itu hanya menunjukkan goroutine yang dieksekusi sejauh yang saya lihat. Apakah ada cara agar saya bisa melihat semua "metode" yang dijalankan setelah main.go diluncurkan?
Lukas Lukac
38

Untuk meniru perilaku Java stack-dump di SIGQUIT tetapi tetap membiarkan program berjalan:

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])
    }
}()
Bryan
sumber
4
Saya rasa inilah yang sebenarnya dicari oleh penulis- meniru apa yang dilakukan Java ketika Anda mengirim kill -QUIT. Satu perubahan kecil yang harus saya lakukan adalah mengubah baris pertama loop for () menjadi: "<- sigs". Dengan kata lain, buang saja sinyal setelah menunggu. Versi terbaru Go tidak mengizinkan Anda mendeklarasikan variabel tanpa menggunakannya nanti.
George Armhold
@ Bryan, apakah Anda bersedia melisensikan ini di bawah BSD atau persyaratan lain yang lebih permisif tambahan untuk CC-BY-SA 3.0 yang disyaratkan oleh StackOverflow?
Charles Duffy
1
@CharlesDuffy Anda dapat menemukan banyak hal yang sama di sini di bawah lisensi Apache: github.com/weaveworks/weave/blob/…
Bryan
37

Mirip dengan Java, SIGQUIT dapat digunakan untuk mencetak jejak tumpukan program Go dan goroutine-nya.
Perbedaan utama, bagaimanapun, adalah bahwa secara default mengirim SIGQUIT ke program Java tidak menghentikannya, sementara program Go keluar.

Pendekatan ini tidak memerlukan perubahan kode untuk mencetak jejak tumpukan semua goroutine dari program yang ada.

Variabel lingkungan GOTRACEBACK ( lihat dokumentasi paket runtime ) mengontrol jumlah keluaran yang dihasilkan. Misalnya, untuk menyertakan semua goroutine, setel GOTRACEBACK = all.

Pencetakan pelacakan tumpukan dipicu oleh kondisi waktu proses yang tidak terduga (sinyal tidak tertangani), yang awalnya didokumentasikan dalam pengubahan ini , membuatnya tersedia setidaknya sejak Go 1.1.


Atau, jika memodifikasi kode sumber adalah sebuah pilihan, lihat jawaban lain.


Perhatikan bahwa di terminal Linux, SIGQUIT dapat dengan mudah dikirim dengan kombinasi tombol Ctrl+ \.

Rodolfo Carvalho
sumber
5
Saat melihat-lihat dokumen, saya tidak menemukan menyebutkan SIGQUIT, melainkan SIGABRT. Dari pengujian saya sendiri (dengan go 1.7) yang terakhir juga bekerja di atas yang pertama.
soltysh
4
ini harus menjadi jawaban teratas.
Steven Soroka
Dokumen mengacu pada "ketika program Go gagal karena kepanikan yang belum pulih atau kondisi runtime yang tidak terduga". Sinyal yang tidak tertangkap (SIGQUIT, dll) adalah salah satu yang terakhir. Mengapa saya menyebutkan SIGQUIT? Karena OP mengungkapkan kecintaannya pada penggunaan SIGQUIT dengan Java, dan jawaban ini menekankan kesamaan. Mengucapkan ulang jawaban agar lebih jelas.
Rodolfo Carvalho
26

Anda bisa menggunakan runtime.Stack untuk mendapatkan pelacakan tumpukan semua goroutine:

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

Dari dokumentasi:

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

Stack memformat pelacakan tumpukan dari pemanggilan goroutine ke dalam buf dan mengembalikan jumlah byte yang ditulis ke buf. Jika semuanya benar, format Stack menumpuk jejak dari semua goroutine lain ke dalam buf setelah jejak goroutine saat ini.

Robert Kajic
sumber
Ini termasuk jejak belakang dari semua goroutine, bagus!
rogerdpack
Apakah ini format yang digunakan oleh kepanikan yang belum pulih?
Ztyx
2
Jangan lupa untuk menambahkan string (buf) atau Anda akan mencetak byte mentah di sana.
koda
2
Mungkin saya melakukan sesuatu yang salah, atau mungkin fungsinya telah berubah, tetapi ini tidak mengambil apa pun untuk saya kecuali sepotong byte kosong?
17xande
1
@koda tidak perlu melakukan di string(buf)sini, fmt.Printf("%s", buf)dan fmt.Printf("%s", string(buf))melakukan hal yang persis sama (lihat dokumen untuk fmtpaket); satu-satunya perbedaan di sini adalah bahwa stringversi akan menyalin byte dari yang buftidak perlu
kbolino
20

Tekan CTRL + \

(Jika Anda menjalankannya di terminal dan hanya ingin mematikan program Anda dan membuang rutinitas go, dll.)

Saya menemukan pertanyaan ini mencari urutan kuncinya. Hanya ingin cara cepat dan mudah untuk mengetahui apakah program saya membocorkan rutinitas go :)

Øyvind Skaar
sumber
11

Pada sistem * NIX (termasuk OSX) mengirim sinyal batal SIGABRT:

pkill -SIGABRT program_name

Kyle Kloepper
sumber
Rupanya, mengirim SIGQUIT ke proses Java tidak menghentikannya seperti yang akan dilakukan SIGABRT.
Dave C
Saya menemukan ini sebagai solusi paling sederhana dan paling cocok dengan pertanyaan asli. Seringkali Anda membutuhkan stacktrace segera, tanpa mengubah kode Anda.
jotrocken
5

Anda perlu menggunakan panjang yang dikembalikan oleh runtime.Stack()untuk menghindari mencetak banyak garis kosong setelah jejak tumpukan Anda. Fungsi pemulihan berikut mencetak jejak yang diformat dengan baik:

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]))
}
David Tootill
sumber
Tidak ada runtime.Trace; runtime.Stack sudah disebutkan satu setengah tahun yang lalu .
Dave C
Saya belum pernah melihat itu; Anda menjalankan platform apa?
Bryan
Apa yang belum kamu lihat? Kode harus berjalan di semua platform; Saya telah mengujinya di Windows 7, Ubuntu 14.04, dan Mac.
David Tootill
Tidak pernah melihat garis kosong.
Bryan
4

Secara default, tekan ^\tombol ( CTRL + \ ) untuk membuang jejak tumpukan semua goroutine.


Jika tidak, untuk kontrol yang lebih terperinci, Anda dapat menggunakan panic. Cara sederhana mulai Go 1.6+ :

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

Kemudian, jalankan program Anda seperti ini:

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

Jika Anda juga ingin mencetak go runtime goroutine:

$ GOTRACEBACK=system go run main.go

Berikut semua opsi GOTRACEBACK:

  • GOTRACEBACK=none menghilangkan jejak tumpukan goroutine seluruhnya.
  • GOTRACEBACK=single (default) berperilaku seperti yang dijelaskan di atas.
  • GOTRACEBACK=all menambahkan pelacakan tumpukan untuk semua goroutine buatan pengguna.
  • GOTRACEBACK=system seperti "semua '' tetapi menambahkan frame stack untuk fungsi run-time dan menampilkan goroutine yang dibuat secara internal oleh run-time.
  • GOTRACEBACK=crashseperti `` sistem '' tetapi macet dengan cara khusus sistem operasi alih-alih keluar. Misalnya, pada sistem Unix, crash muncul SIGABRTuntuk memicu dump inti.

Berikut dokumentasinya

Variabel GOTRACEBACK mengontrol jumlah output yang dihasilkan saat program Go gagal karena kepanikan yang belum pulih atau kondisi waktu proses yang tidak terduga.

Secara default, kegagalan mencetak pelacakan tumpukan untuk goroutine saat ini, memilih fungsi internal ke sistem run-time, lalu keluar dengan kode keluar 2. Kegagalan mencetak jejak tumpukan untuk semua goroutine jika tidak ada goroutine saat ini atau kegagalannya internal ke run-time.

Untuk alasan historis, setelan GOTRACEBACK 0, 1, dan 2 adalah sinonim untuk masing-masing tidak ada, semua, dan sistem.

Fungsi SetTraceback paket runtime / debug memungkinkan peningkatan jumlah output pada waktu proses, tetapi tidak dapat mengurangi jumlah di bawah yang ditentukan oleh variabel lingkungan. Lihat https://golang.org/pkg/runtime/debug/#SetTraceback .

Inanc Gumus
sumber