วิธีการทิ้ง goroutine stacktraces?


92

ฉันมีพื้นหลัง Java และฉันชอบใช้สัญญาณ QUIT เพื่อตรวจสอบการถ่ายโอนข้อมูลเธรด Java

จะให้ Golang พิมพ์ goroutines stack trace ได้อย่างไร?


คำตอบ:


109

หากต้องการพิมพ์กองติดตามสำหรับปัจจุบัน goroutine ใช้จากPrintStack()runtime/debug

PrintStack พิมพ์ไปยังข้อผิดพลาดมาตรฐานการติดตามสแต็กที่ส่งคืนโดย Stack

ตัวอย่างเช่น:

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

หากต้องการพิมพ์กองติดตามสำหรับทุก goroutines ใช้LookupและจากWriteToruntime/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
heap ปัจจุบันทั้งหมด- การสุ่มตัวอย่างของการจัดสรร heap ทั้งหมด
threadcreate - สแต็กเทรซที่นำไปสู่การสร้าง
บล็อกเธรด OS ใหม่- สแต็กเทรซที่นำไปสู่การบล็อกบนพื้นฐานการซิงโครไนซ์

ตัวอย่างเช่น:

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

1
มันพิมพ์ stack trace ของ goroutines ทั้งหมดหรือไม่?

Stackมันควรจะเรียก "Stack ส่งคืนการติดตามสแต็กที่จัดรูปแบบแล้วของ goroutine ที่เรียกมันสำหรับแต่ละรูทีนจะมีข้อมูลรายการต้นทางและค่าพีซีจากนั้นจะพยายามค้นหาสำหรับฟังก์ชัน Go ฟังก์ชันการโทรหรือวิธีการและข้อความของบรรทัดที่มี การวิงวอน "
Intermernet

1
ขออภัยมันพิมพ์การติดตามสแต็ก goroutine ปัจจุบันเท่านั้น

4
@HowardGuo ฉันได้เพิ่มตัวอย่างโดยใช้รันไทม์ / pprof เพื่อถ่ายโอนข้อมูลสแต็กเทรซทั้งหมด
Intermernet

2
ฉันคิดว่าสิ่งนี้จะส่งออกเฉพาะ goroutine แต่ละเธรดที่กำลังทำงานอยู่ไม่ใช่goroutines ทั้งหมดเช่นplay.golang.org/p/0hVB0_LMdm
rogerdpack

42

มีส่วนหน้า 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สำหรับกองถ่าย goroutine แบบเต็ม

ยังมีเรื่องสนุก ๆ อื่น ๆ ที่คุณสามารถเรียนรู้เกี่ยวกับโค้ดที่ใช้งานได้ด้วยวิธีนี้ ดูตัวอย่างและรายละเอียดเพิ่มเติมในบล็อกโพสต์: http://blog.golang.org/profiling-go-programs


ฉันทำให้มันแสดงเฉพาะ goroutines ที่ดำเนินการเท่าที่ฉันเห็น มีวิธีใดบ้างที่ฉันสามารถดู "วิธีการ" ทั้งหมดที่ดำเนินการหลังจากที่ main.go เปิดตัวได้
Lukas Lukac

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 เวอร์ชันล่าสุดจะไม่อนุญาตให้คุณประกาศตัวแปรโดยไม่ใช้ในภายหลัง
George Armhold

@ ไบรอันคุณยินดีที่จะอนุญาตสิ่งนี้ภายใต้ BSD หรือข้อกำหนดอื่น ๆ ที่อนุญาตเพิ่มเติมใน CC-BY-SA 3.0 ที่ StackOverflow ต้องการหรือไม่
Charles Duffy

1
@CharlesDuffy คุณสามารถค้นหาสิ่งเดียวกันได้ที่นี่ภายใต้ใบอนุญาต Apache: github.com/weaveworks/weave/blob/…
ไบรอัน

37

เช่นเดียวกับ Java SIGQUIT สามารถใช้เพื่อพิมพ์ stack trace ของโปรแกรม Go และ goroutines
อย่างไรก็ตามความแตกต่างที่สำคัญคือโดยค่าเริ่มต้นการส่ง SIGQUIT ไปยังโปรแกรม Java จะไม่ยุติลงในขณะที่โปรแกรม Go จะออก

วิธีนี้ไม่จำเป็นต้องเปลี่ยนรหัสเพื่อพิมพ์ stack trace ของ goroutines ทั้งหมดของโปรแกรมที่มีอยู่

ตัวแปรสภาพแวดล้อม GOTRACEBACK ( ดูเอกสารประกอบของแพ็กเกจรันไทม์ ) ควบคุมจำนวนเอาต์พุตที่สร้างขึ้น ตัวอย่างเช่นหากต้องการรวม goroutines ทั้งหมดให้ตั้งค่า GOTRACEBACK = all

การพิมพ์การติดตามสแต็กถูกทริกเกอร์โดยเงื่อนไขรันไทม์ที่ไม่คาดคิด (สัญญาณที่ไม่ได้จัดการ) ซึ่งเดิมบันทึกไว้ในคอมมิตนี้ทำให้พร้อมใช้งานตั้งแต่อย่างน้อย Go 1.1


หรือหากเป็นทางเลือกในการแก้ไขซอร์สโค้ดโปรดดูคำตอบอื่น ๆ


ทราบว่าในขั้ว Linux, SIGQUIT สามารถส่งสะดวกสบายด้วยคีย์ผสม+Ctrl\


5
ในขณะที่ดูเอกสารฉันไม่พบการกล่าวถึง SIGQUIT แต่เป็น SIGABRT จากการทดสอบของฉันเอง (ด้วยไป 1.7) หลังก็ใช้ได้ผลมากกว่าในอดีต
soltysh

4
นี่น่าจะเป็นคำตอบอันดับต้น ๆ
Steven Soroka

เอกสารนี้อ้างถึง "เมื่อโปรแกรม Go ล้มเหลวเนื่องจากความตื่นตระหนกที่ไม่ได้รับการกู้คืนหรือสภาวะรันไทม์ที่ไม่คาดคิด" สัญญาณที่ไม่ถูกจับ (SIGQUIT ฯลฯ ) เป็นหนึ่งในสัญญาณหลัง ทำไมฉันถึงพูดถึง SIGQUIT เนื่องจาก OP แสดงออกถึงความรักในการใช้ SIGQUIT กับ Java และคำตอบนี้เน้นความเหมือน ให้รางวัลคำตอบเพื่อให้ชัดเจนขึ้น
Rodolfo Carvalho

26

คุณสามารถใช้runtime.Stackเพื่อรับ stack trace ของ goroutines ทั้งหมด:

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

จากเอกสารประกอบ:

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

Stack จัดรูปแบบการติดตามสแต็กของ goroutine ที่เรียกเป็น buf และส่งคืนจำนวนไบต์ที่เขียนไปยัง buf ถ้าทั้งหมดเป็นจริง Stack จะจัดรูปแบบสแต็กเทรซของ goroutines อื่น ๆ ทั้งหมดลงใน buf หลังจากการติดตามสำหรับ goroutine ปัจจุบัน


ซึ่งรวมถึงการติดตามย้อนหลังจากทั้งหมด goroutines ดี!
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 + \

(หากคุณเรียกใช้ในเทอร์มินัลและเพียงแค่ต้องการฆ่าโปรแกรมของคุณและทิ้งกิจวัตรการไป ฯลฯ )

ฉันพบว่าคำถามนี้กำลังมองหาลำดับคีย์ แค่อยากได้วิธีที่ง่ายและรวดเร็วในการบอกว่าโปรแกรมของฉันรั่วไหลหรือไม่ :)


11

บนระบบ * NIX (รวม OSX) ส่งสัญญาณยกเลิกSIGABRT:

pkill -SIGABRT program_name


เห็นได้ชัดว่าการส่ง SIGQUIT ไปยังกระบวนการ Java ไม่ได้ยุติลงเหมือนที่ SIGABRT จะทำ
Dave C

ฉันพบว่านี่เป็นวิธีแก้ปัญหาที่ง่ายและตรงกับคำถามเดิมมากที่สุด บ่อยครั้งที่คุณต้องใช้ stacktrace ทันทีโดยไม่ต้องเปลี่ยนรหัส
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

ฉันไม่เคยเห็นแบบนั้นมาก่อน คุณใช้แพลตฟอร์มอะไร
Bryan

คุณไม่ได้เห็นอะไร โค้ดควรทำงานบนทุกแพลตฟอร์ม ฉันได้ทดสอบบน Windows 7, Ubuntu 14.04 และ Mac
David Tootill

ไม่เคยเห็นเส้นว่าง
Bryan

5

โดยค่าเริ่มต้นให้กด^\แป้น ( CTRL + \ ) เพื่อดัมพ์สแต็กเทรซของ goroutines ทั้งหมด


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

หากคุณต้องการพิมพ์ go runtime goroutines ด้วย:

$ GOTRACEBACK=system go run main.go

นี่คือตัวเลือก GOTRACEBACK ทั้งหมด:

  • GOTRACEBACK=none ละเว้นการติดตามกองซ้อน goroutine โดยสิ้นเชิง
  • GOTRACEBACK=single (ค่าเริ่มต้น)ทำงานตามที่อธิบายไว้ข้างต้น
  • GOTRACEBACK=all เพิ่มสแต็กเทรซสำหรับ goroutines ที่ผู้ใช้สร้างขึ้นทั้งหมด
  • GOTRACEBACK=system เหมือนกับ `` ทั้งหมด '' แต่จะเพิ่มสแต็กเฟรมสำหรับฟังก์ชันรันไทม์และแสดง goroutines ที่สร้างขึ้นภายในโดยรันไทม์
  • GOTRACEBACK=crashเป็นเหมือน `` ระบบ '' แต่ขัดข้องในลักษณะเฉพาะของระบบปฏิบัติการแทนที่จะออก ตัวอย่างเช่นในระบบ Unix ความผิดพลาดจะเพิ่มขึ้นSIGABRTเพื่อทริกเกอร์ดัมพ์หลัก

นี่คือเอกสาร

ตัวแปร GOTRACEBACK ควบคุมจำนวนเอาต์พุตที่สร้างขึ้นเมื่อโปรแกรม Go ล้มเหลวเนื่องจากความตื่นตระหนกที่ไม่ได้รับการกู้คืนหรือสภาวะรันไทม์ที่ไม่คาดคิด

ตามค่าเริ่มต้นความล้มเหลวจะพิมพ์สแต็กแทร็กสำหรับ goroutine ปัจจุบันโดยกำหนดฟังก์ชันภายในระบบรันไทม์จากนั้นออกด้วยโค้ด exit 2 ความล้มเหลวจะพิมพ์สแต็กเทรซสำหรับ goroutines ทั้งหมดหากไม่มี goroutine ในปัจจุบันหรือความล้มเหลวคือ ภายในรันไทม์

ด้วยเหตุผลทางประวัติศาสตร์การตั้งค่า GOTRACEBACK 0, 1 และ 2 จึงเป็นคำพ้องความหมายของ none ทั้งหมดและระบบตามลำดับ

ฟังก์ชัน SetTraceback ของแพ็กเกจรันไทม์ / ดีบักอนุญาตให้เพิ่มจำนวนเอาต์พุตในขณะรัน แต่ไม่สามารถลดจำนวนที่ต่ำกว่าที่ระบุโดยตัวแปรสภาพแวดล้อมได้ ดูhttps://golang.org/pkg/runtime/debug/#SetTraceback

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.