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 tb := string(buf[:n])
37
38
39 pos := 0
40 for pos < len(tb) {
41 next := pos + strings.Index(tb[pos:], "\n\n")
42 if next < pos {
43 next = len(tb)
44 } else {
45 next += len("\n\n")
46 }
47
48 if strings.HasPrefix(tb[pos:], "goroutine ") {
49 id := tb[pos+len("goroutine "):]
50 id = id[:strings.IndexByte(id, ' ')]
51 if ignoreGoroutines[id] {
52 tb = tb[:pos] + tb[next:]
53 next = pos
54 }
55 }
56 pos = next
57 }
58
59 fmt.Print(tb)
60 return
61 }
62 buf = make([]byte, 2*len(buf))
63 }
64 }
65
66 func recurseThenCallGo(w chan struct{}, frames int, goroutines int, main bool) {
67 if frames == 0 {
68
69 w <- struct{}{}
70 <-w
71 return
72 }
73 if goroutines == 0 {
74
75
76
77 if !main {
78 ignoreGoroutines[goroutineID()] = true
79 }
80
81
82
83 go recurseThenCallGo(w, frames-1, numFrames, false)
84 return
85 }
86 recurseThenCallGo(w, frames, goroutines-1, main)
87 }
88
89 func goroutineID() string {
90 buf := make([]byte, 128)
91 runtime.Stack(buf, false)
92 const prefix = "goroutine "
93 if !bytes.HasPrefix(buf, []byte(prefix)) {
94 panic(fmt.Sprintf("expected %q at beginning of traceback:\n%s", prefix, buf))
95 }
96 buf = buf[len(prefix):]
97 n := bytes.IndexByte(buf, ' ')
98 return string(buf[:n])
99 }
100
View as plain text