go.undefinedlabs.com/scopeagent@v0.4.2/tracer/debug.go (about) 1 package tracer 2 3 import ( 4 "bytes" 5 "fmt" 6 "runtime" 7 "strconv" 8 "sync" 9 ) 10 11 const debugGoroutineIDTag = "_initial_goroutine" 12 13 type errAssertionFailed struct { 14 span *spanImpl 15 msg string 16 } 17 18 // Error implements the error interface. 19 func (err *errAssertionFailed) Error() string { 20 return fmt.Sprintf("%s:\n%+v", err.msg, err.span) 21 } 22 23 func (s *spanImpl) Lock() { 24 s.Mutex.Lock() 25 s.maybeAssertSanityLocked() 26 } 27 28 func (s *spanImpl) maybeAssertSanityLocked() { 29 if s.tracer == nil { 30 s.Mutex.Unlock() 31 panic(&errAssertionFailed{span: s, msg: "span used after call to Finish()"}) 32 } 33 if s.tracer.options.DebugAssertSingleGoroutine { 34 startID := curGoroutineID() 35 curID, ok := s.raw.Tags[debugGoroutineIDTag].(uint64) 36 if !ok { 37 // This is likely invoked in the context of the SetTag which sets 38 // debugGoroutineTag. 39 return 40 } 41 if startID != curID { 42 s.Mutex.Unlock() 43 panic(&errAssertionFailed{ 44 span: s, 45 msg: fmt.Sprintf("span started on goroutine %d, but now running on %d", startID, curID), 46 }) 47 } 48 } 49 } 50 51 var goroutineSpace = []byte("goroutine ") 52 var littleBuf = sync.Pool{ 53 New: func() interface{} { 54 buf := make([]byte, 64) 55 return &buf 56 }, 57 } 58 59 // Credit to @bradfitz: 60 // https://github.com/golang/net/blob/master/http2/gotrack.go#L51 61 func curGoroutineID() uint64 { 62 bp := littleBuf.Get().(*[]byte) 63 defer littleBuf.Put(bp) 64 b := *bp 65 b = b[:runtime.Stack(b, false)] 66 // Parse the 4707 out of "goroutine 4707 [" 67 b = bytes.TrimPrefix(b, goroutineSpace) 68 i := bytes.IndexByte(b, ' ') 69 if i < 0 { 70 panic(fmt.Sprintf("No space found in %q", b)) 71 } 72 b = b[:i] 73 n, err := strconv.ParseUint(string(b), 10, 64) 74 if err != nil { 75 panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) 76 } 77 return n 78 }