Gorutin yığın izleri nasıl atılır?


91

Java geçmişim var ve Java iş parçacığı dökümünü incelemek için QUIT sinyalini kullanmayı seviyorum.

Golang'ın tüm gorutinlerin yığın izini yazdırmasına nasıl izin verilir?


Bu sorunuzu yanıtlıyor mu? Bir paniğin
yığın izini

Yanıtlar:


107

Mevcut gorutinin yığın izlemesini yazdırmak için, ' PrintStack()danruntime/debug kullanın .

PrintStack, Yığın tarafından döndürülen yığın izini standart hataya yazdırır.

Örneğin:

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

İçin yığın izlemesini yazdırmak için tüm goroutines kullanım Lookupve WriteTogelen 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.

Her Profilin benzersiz bir adı vardır. Birkaç profil önceden tanımlanmıştır:

goroutine - mevcut
tüm
gorutin yığınlarının yığın izleri - tüm yığın tahsislerinin bir örneklemesi threadcreate - yeni işletim sistemi iş parçacığı
bloğunun oluşturulmasına yol açan yığın izleri - senkronizasyon ilkellerinde engellemeye yol açan yığın izleri

Örneğin:

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

1
Tüm gorutinlerin yığın izini yazdırıyor mu?

Yapmalı, çağırıyor Stack. "Yığın, onu çağıran gorutinin biçimlendirilmiş yığın izini döndürür. Her yordam için, kaynak satırı bilgilerini ve PC değerini içerir, ardından Go işlevleri için arama işlevini veya yöntemini ve şunu içeren satırın metnini keşfetmeye çalışır. çağrı. "
Intermernet

1
Maalesef, yalnızca mevcut gorutin yığın izlemesini yazdırır.

4
@HowardGuo Tüm yığın izlerini dökmek için çalışma zamanı / pprof kullanarak bir örnek ekledim.
Intermernet

1
Sanırım bu yalnızca her iş parçacığının şu anda çalışan gorutini çıktılar, tüm gorutinleri değil , örn: play.golang.org/p/0hVB0_LMdm
rogerdpack

41

runtime/pprofIntermernet'in cevabında bahsedilen paket için bir HTTP ön ucu var. Aşağıdakiler için bir HTTP işleyicisi kaydetmek için net / http / pprof paketini içe aktarın /debug/pprof:

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

Henüz bir HTTP dinleyiciniz yoksa, bir HTTP dinleyicisi başlatın:

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

Ardından http://localhost:6060/debug/pprof, bir menü http://localhost:6060/debug/pprof/goroutine?debug=2için veya tam bir grup yığını dökümü için bir tarayıcıyı işaret edin .

Koşu kodunuz hakkında bu şekilde öğrenebileceğiniz başka eğlenceli şeyler de var. Örnekler ve daha fazla ayrıntı için blog gönderisine bakın: http://blog.golang.org/profiling-go-programs


Onun tarafından çalıştırılmasını sağladım, sadece gördüğüm kadarıyla idam edilen gorutinleri gösteriyor. Main.go başlatıldıktan sonra yürütülen tüm "yöntemleri" görebilmemin bir yolu var mı?
Lukas Lukac

38

SIGQUIT'te yığın dökümünün Java davranışını taklit etmek, ancak programı yine de çalışır durumda bırakmak için:

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
Yazarın, bir kill -QUIT gönderdiğinizde Java'nın ne yaptığını gerçekten aradığı şeyin bu olduğunu düşünüyorum. Yapmam gereken küçük bir değişiklik, for () döngüsünün ilk satırını "<- sigs" olarak değiştirmekti. Başka bir deyişle, sinyali bekledikten sonra atın. Go'nun son sürümleri, bir değişkeni daha sonra kullanmadan bildirmenize izin vermez.
George Armhold

@Bryan, bunu BSD veya StackOverflow'un gerektirdiği CC-BY-SA 3.0'a ek olarak daha fazla izin verilen diğer şartlar altında lisanslamak istiyor musunuz?
Charles Duffy

1
@CharlesDuffy, burada Apache lisansı altında hemen hemen aynı şeyi bulabilirsiniz: github.com/weaveworks/weave/blob/…
Bryan

37

Java'ya benzer şekilde, SIGQUIT bir Go programının ve onun gorutinlerinin yığın izini yazdırmak için kullanılabilir.
Bununla birlikte, temel bir fark, varsayılan olarak SIGQUIT'i Java programlarına göndermenin, Go programları çıkarken onları sonlandırmamasıdır.

Bu yaklaşım, mevcut programların tüm programlarının yığın izini yazdırmak için kod değişikliği gerektirmez.

GOTRACEBACK ortam değişkeni ( çalışma zamanı paketinin belgelerine bakın ) üretilen çıktı miktarını kontrol eder. Örneğin, tüm gorutinleri dahil etmek için GOTRACEBACK = all olarak ayarlayın.

Yığın izlemenin yazdırılması, başlangıçta bu işlemde belgelenen beklenmedik bir çalışma zamanı koşulu (işlenmemiş sinyal) tarafından tetiklenir ve en azından Go 1.1'den beri kullanılabilir kılar.


Alternatif olarak, kaynak kodunu değiştirmek bir seçenekse, diğer yanıtlara bakın.


Bir Linux terminalinde SIGQUIT'in Ctrl+ tuş kombinasyonu ile rahatlıkla gönderilebileceğini unutmayın \.


5
Dokümanlara bakarken SIGABRT yerine SIGQUIT'ten bahsetmedim. Kendi testlerime göre (go 1.7 ile) ikincisi de birincisi üzerinde çalıştı.
soltysh

4
bu en iyi cevap olmalı.
Steven Soroka

Dokümanlar, "kurtarılamayan bir panik veya beklenmedik bir çalışma zamanı koşulu nedeniyle bir Go programı başarısız olduğunda" ile ilgilidir. Yakalanmamış bir sinyal (SIGQUIT, vb.) İkincisinden biridir. Neden SIGQUIT'ten bahsettim? Çünkü OP, SIGQUIT'i Java ile kullanmaya olan sevgisini ifade ediyor ve bu cevap benzerliği vurguluyor. Daha açık hale getirmek için cevabı yeniden yazmak.
Rodolfo Carvalho

26

Tüm gorutinlerin yığın izini almak için runtime.Stack'i kullanabilirsiniz :

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

Belgelerden:

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

Yığın, çağıran gorutinin yığın izini buf'ye formatlar ve buf'a yazılan bayt sayısını döndürür. Her şey doğruysa, Stack formatları diğer tüm gorutinlerin izlerini mevcut gorutinin izinden sonra tamponda yığınlar.


Bu, tüm gorutinlerden geri izleri içerir , güzel!
rogerdpack

Kurtarılmamış bir paniğin kullanmak için indirdiği format bu mu?
Ztyx

2
String (buf) eklemeyi unutmayın, yoksa ham baytları orada yazdırırsınız.
koda

2
Belki yanlış bir şey yapıyorum ya da işlevsellik değişti, ancak bu benim için boş bir bayt dilimi dışında hiçbir şey getirmiyor mu?
17xande

1
@koda yapmaya gerek yoktur string(buf)burada, fmt.Printf("%s", buf)ve fmt.Printf("%s", string(buf))aynı şeyi (Dokümanları görüyorum fmtpakette); Buradaki tek fark, stringsürümün baytları bufgereksiz yere kopyalamasıdır
kbolino

20

CTRL + \ tuşlarına basın

(Bir terminalde çalıştırırsanız ve sadece programınızı öldürmek ve go rutinlerini atmak istiyorsanız vb.)

Bu soruyu anahtar sırasını ararken buldum. Programımın gidiş rutinlerini sızdırıp sızdırmadığını anlamanın hızlı ve kolay bir yolunu istedim :)


11

Açık * NIX sistemlerinde (OSX dahil) bir sinyal iptali gönderir SIGABRT:

pkill -SIGABRT program_name


Görünüşe göre, SIGQUIT'i bir Java sürecine göndermek, SIGABRT'ın yapacağı gibi onu sonlandırmaz .
Dave C

Bunu, orijinal soruya en basit ve en uygun çözüm olarak buldum. Genellikle kodunuzu değiştirmeden hemen bir yığın izine ihtiyacınız olur.
jotrocken

5

runtime.Stack()Yığın izlemenizden sonra bir dizi boş satır yazdırmaktan kaçınmak için döndürülen uzunluğu kullanmak gerekir . Aşağıdaki kurtarma işlevi, güzel biçimlendirilmiş bir iz yazdırır:

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]))
}

Yok runtime.Trace; runtime.Stack zaten bir buçuk yıl önce bahsedildi .
Dave C

Bunu hiç görmedim; hangi platformda koşuyorsun
Bryan

Görmediğin şey nedir? Kod tüm platformlarda çalışmalıdır; Windows 7, Ubuntu 14.04 ve Mac'te test ettim.
David Tootill

Hiç boş çizgiler görmedim.
Bryan

4

Varsayılan olarak, tüm grupların yığın izlerini dökmek için ^\tuşlarına ( CTRL + \ ) basın .


Aksi takdirde, daha ayrıntılı kontrol için kullanabilirsiniz panic. Go 1.6+ sürümünden itibaren basit yol :

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

Ardından, programınızı şu şekilde çalıştırın:

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

Ayrıca go çalışma zamanı goroutinlerini yazdırmak istiyorsanız:

$ GOTRACEBACK=system go run main.go

İşte tüm GOTRACEBACK seçenekleri:

  • GOTRACEBACK=none gorutin yığın izlerini tamamen atlar.
  • GOTRACEBACK=single (varsayılan) yukarıda açıklandığı gibi davranır.
  • GOTRACEBACK=all tüm kullanıcı tarafından oluşturulan gorutinler için yığın izleri ekler.
  • GOTRACEBACK=system `` tümü '' gibidir ancak çalışma zamanı işlevleri için yığın çerçeveleri ekler ve çalışma zamanı tarafından dahili olarak oluşturulan gorutinleri gösterir.
  • GOTRACEBACK=crash`` sistem '' gibidir ancak çıkmak yerine işletim sistemine özel bir şekilde çöker. Örneğin, Unix sistemlerinde, kilitlenme SIGABRTbir çekirdek dökümü tetiklemek için yükselir .

İşte belgeler

GOTRACEBACK değişkeni, bir Go programı kurtarılmamış bir panik veya beklenmedik bir çalışma zamanı durumu nedeniyle başarısız olduğunda üretilen çıktı miktarını kontrol eder.

Varsayılan olarak, bir başarısızlık, mevcut program için bir yığın izini yazdırır, çalışma zamanı sisteminin içindeki işlevleri ortadan kaldırır ve ardından çıkış kodu 2 ile çıkar. çalışma zamanına dahil.

Geçmiş nedenlerden dolayı, GOTRACEBACK ayarları 0, 1 ve 2 sırasıyla none, all ve system için eşanlamlıdır.

Çalışma zamanı / hata ayıklama paketinin SetTraceback işlevi, çalışma zamanında çıktı miktarını artırmaya izin verir, ancak miktarı ortam değişkeni tarafından belirtilenin altına indiremez. Bkz https://golang.org/pkg/runtime/debug/#SetTraceback .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.