github.com/lulzWill/go-agent@v2.1.2+incompatible/internal/stacktrace.go (about)

     1  package internal
     2  
     3  import (
     4  	"bytes"
     5  	"path"
     6  	"runtime"
     7  )
     8  
     9  // StackTrace is a stack trace.
    10  type StackTrace []uintptr
    11  
    12  // GetStackTrace returns a new StackTrace.
    13  func GetStackTrace(skipFrames int) StackTrace {
    14  	skip := 2 // skips runtime.Callers and this function
    15  	skip += skipFrames
    16  
    17  	callers := make([]uintptr, maxStackTraceFrames)
    18  	written := runtime.Callers(skip, callers)
    19  	return StackTrace(callers[0:written])
    20  }
    21  
    22  func pcToFunc(pc uintptr) (*runtime.Func, uintptr) {
    23  	// The Golang runtime package documentation says "To look up the file
    24  	// and line number of the call itself, use pc[i]-1. As an exception to
    25  	// this rule, if pc[i-1] corresponds to the function runtime.sigpanic,
    26  	// then pc[i] is the program counter of a faulting instruction and
    27  	// should be used without any subtraction."
    28  	//
    29  	// TODO: Fully understand when this subtraction is necessary.
    30  	place := pc - 1
    31  	return runtime.FuncForPC(place), place
    32  }
    33  
    34  func topCallerNameBase(st StackTrace) string {
    35  	f, _ := pcToFunc(st[0])
    36  	if nil == f {
    37  		return ""
    38  	}
    39  	return path.Base(f.Name())
    40  }
    41  
    42  // WriteJSON adds the stack trace to the buffer in the JSON form expected by the
    43  // collector.
    44  func (st StackTrace) WriteJSON(buf *bytes.Buffer) {
    45  	buf.WriteByte('[')
    46  	for i, pc := range st {
    47  		// Stack traces may be provided by the customer, and therefore
    48  		// may be excessively long.  The truncation is done here to
    49  		// facilitate testing.
    50  		if i >= maxStackTraceFrames {
    51  			break
    52  		}
    53  		if i > 0 {
    54  			buf.WriteByte(',')
    55  		}
    56  		// Implements the format documented here:
    57  		// https://source.datanerd.us/agents/agent-specs/blob/master/Stack-Traces.md
    58  		buf.WriteByte('{')
    59  		if f, place := pcToFunc(pc); nil != f {
    60  			name := path.Base(f.Name())
    61  			file, line := f.FileLine(place)
    62  
    63  			w := jsonFieldsWriter{buf: buf}
    64  			w.stringField("filepath", file)
    65  			w.stringField("name", name)
    66  			w.intField("line", int64(line))
    67  		}
    68  		buf.WriteByte('}')
    69  	}
    70  	buf.WriteByte(']')
    71  }
    72  
    73  // MarshalJSON prepares JSON in the format expected by the collector.
    74  func (st StackTrace) MarshalJSON() ([]byte, error) {
    75  	estimate := 256 * len(st)
    76  	buf := bytes.NewBuffer(make([]byte, 0, estimate))
    77  
    78  	st.WriteJSON(buf)
    79  
    80  	return buf.Bytes(), nil
    81  }