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 }