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  }