1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "fmt"
10 "runtime"
11 "strings"
12 )
13
14 func init() {
15 register("TracebackAncestors", TracebackAncestors)
16 }
17
18 const numGoroutines = 3
19 const numFrames = 2
20
21 func TracebackAncestors() {
22 w := make(chan struct{})
23 recurseThenCallGo(w, numGoroutines, numFrames, true)
24 <-w
25 printStack()
26 close(w)
27 }
28
29 var ignoreGoroutines = make(map[string]bool)
30
31 func printStack() {
32 buf := make([]byte, 1024)
33 for {
34 n := runtime.Stack(buf, true)
35 if n < len(buf) {
36 all := string(buf[:n])
37 var saved string
38
39
40 for all != "" {
41 var g string
42 g, all, _ = strings.Cut(all, "\n\n")
43
44 if strings.HasPrefix(g, "goroutine ") {
45 id, _, _ := strings.Cut(strings.TrimPrefix(g, "goroutine "), " ")
46 if ignoreGoroutines[id] {
47 continue
48 }
49 }
50 if saved != "" {
51 saved += "\n\n"
52 }
53 saved += g
54 }
55
56 fmt.Print(saved)
57 return
58 }
59 buf = make([]byte, 2*len(buf))
60 }
61 }
62
63 func recurseThenCallGo(w chan struct{}, frames int, goroutines int, main bool) {
64 if frames == 0 {
65
66 w <- struct{}{}
67 <-w
68 return
69 }
70 if goroutines == 0 {
71
72
73
74 if !main {
75 ignoreGoroutines[goroutineID()] = true
76 }
77
78
79
80 go recurseThenCallGo(w, frames-1, numFrames, false)
81 return
82 }
83 recurseThenCallGo(w, frames, goroutines-1, main)
84 }
85
86 func goroutineID() string {
87 buf := make([]byte, 128)
88 runtime.Stack(buf, false)
89 prefix := []byte("goroutine ")
90 if !bytes.HasPrefix(buf, prefix) {
91 panic(fmt.Sprintf("expected %q at beginning of traceback:\n%s", prefix, buf))
92 }
93 id, _, _ := bytes.Cut(bytes.TrimPrefix(buf, prefix), []byte(" "))
94 return string(id)
95 }
96
View as plain text