// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime import ( _ "unsafe" // for go:linkname ) // Frames may be used to get function/file/line information for a // slice of PC values returned by Callers. type Frames struct { // callers is a slice of PCs that have not yet been expanded to frames. callers []uintptr // The last PC we saw. last uintptr // The number of times we've seen last. lastCount int } // Frame is the information returned by Frames for each call frame. type Frame struct { // PC is the program counter for the location in this frame. // For a frame that calls another frame, this will be the // program counter of a call instruction. Because of inlining, // multiple frames may have the same PC value, but different // symbolic information. PC uintptr // Func is the Func value of this call frame. This may be nil // for non-Go code or fully inlined functions. Func *Func // Function is the package path-qualified function name of // this call frame. If non-empty, this string uniquely // identifies a single function in the program. // This may be the empty string if not known. // If Func is not nil then Function == Func.Name(). Function string // File and Line are the file name and line number of the // location in this frame. For non-leaf frames, this will be // the location of a call. These may be the empty string and // zero, respectively, if not known. File string Line int // Entry point program counter for the function; may be zero // if not known. If Func is not nil then Entry == // Func.Entry(). Entry uintptr } // CallersFrames takes a slice of PC values returned by Callers and // prepares to return function/file/line information. // Do not change the slice until you are done with the Frames. func CallersFrames(callers []uintptr) *Frames { return &Frames{callers: callers} } // Next returns frame information for the next caller. // If more is false, there are no more callers (the Frame value is valid). func (ci *Frames) Next() (frame Frame, more bool) { if len(ci.callers) == 0 { return Frame{}, false } pc := ci.callers[0] ci.callers = ci.callers[1:] i := 0 if pc == ci.last { ci.lastCount++ i = ci.lastCount } else { ci.last = pc ci.lastCount = 0 } more = len(ci.callers) > 0 // Subtract 1 from PC to undo the 1 we added in callback in // go-callers.c. function, file, line, _ := funcfileline(pc-1, int32(i), more) if function == "" && file == "" { return Frame{}, more } // Demangle function name if needed. function = demangleSymbol(function) // Create entry. entry := funcentry(pc - 1) f := &Func{name: function, entry: entry} xpc := pc if xpc > entry { xpc-- } frame = Frame{ PC: xpc, Func: f, Function: function, File: file, Line: line, Entry: entry, } return frame, more } //go:noescape // pcInlineCallers is written in C. func pcInlineCallers(pc uintptr, locbuf *location, max int32) int32 // runtime_expandFinalInlineFrame expands the final pc in stk to include all // "callers" if pc is inline. // //go:linkname runtime_expandFinalInlineFrame runtime..z2fpprof.runtime_expandFinalInlineFrame func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr { if len(stk) == 0 { return stk } pc := stk[len(stk)-1] tracepc := pc - 1 var locbuf [_TracebackMaxFrames]location n := pcInlineCallers(tracepc, &locbuf[0], int32(len(locbuf))) // Returning the same PC several times causes Frame.Next to do // the right thing. for i := int32(1); i < n; i++ { stk = append(stk, pc) } return stk } // NOTE: Func does not expose the actual unexported fields, because we return *Func // values to users, and we want to keep them from being able to overwrite the data // with (say) *f = Func{}. // All code operating on a *Func must call raw() to get the *_func // or funcInfo() to get the funcInfo instead. // A Func represents a Go function in the running binary. type Func struct { name string entry uintptr } // FuncForPC returns a *Func describing the function that contains the // given program counter address, or else nil. // // If pc represents multiple functions because of inlining, it returns // the a *Func describing the innermost function, but with an entry // of the outermost function. func FuncForPC(pc uintptr) *Func { name, _, _, _ := funcfileline(pc, -1, false) if name == "" { return nil } entry := funcentry(pc) return &Func{name: name, entry: entry} } // Name returns the name of the function. func (f *Func) Name() string { if f == nil { return "" } return f.name } // Entry returns the entry address of the function. func (f *Func) Entry() uintptr { if f == nil { return 0 } return f.entry } // FileLine returns the file name and line number of the // source code corresponding to the program counter pc. // The result will not be accurate if pc is not a program // counter within f. func (f *Func) FileLine(pc uintptr) (file string, line int) { _, file, line, _ = funcfileline(pc, -1, false) return file, line } func hexval(b byte) uint { if b >= '0' && b <= '9' { return uint(b - '0') } if b >= 'a' && b <= 'f' { return uint(b-'a') + 10 } return 0 } func hexDigitsToRune(digits []byte, ndig int) rune { result := uint(0) for i := 0; i < ndig; i++ { result <<= uint(4) result |= hexval(digits[i]) } return rune(result) } // Perform an in-place decoding on the input byte slice. This looks // for "..z", "..u" and "..U" and overwrites // with the encoded bytes corresponding to the unicode in question. // Return value is the number of bytes taken by the result. func decodeIdentifier(bsl []byte) int { j := 0 for i := 0; i < len(bsl); i++ { b := bsl[i] if i+1 < len(bsl) && bsl[i] == '.' && bsl[i+1] == '.' { if i+4 < len(bsl) && bsl[i+2] == 'z' { digits := bsl[i+3:] r := hexDigitsToRune(digits, 2) nc := encoderune(bsl[j:], r) j += nc i += 4 continue } else if i+6 < len(bsl) && bsl[i+2] == 'u' { digits := bsl[i+3:] r := hexDigitsToRune(digits, 4) nc := encoderune(bsl[j:], r) j += nc i += 6 continue } else if i+10 < len(bsl) && bsl[i+2] == 'U' { digits := bsl[i+3:] r := hexDigitsToRune(digits, 8) nc := encoderune(bsl[j:], r) j += nc i += 10 continue } } bsl[j] = b j += 1 } return j } // Demangle a function symbol. Applies the reverse of go_encode_id() // as used in the compiler. func demangleSymbol(s string) string { bsl := []byte(s) nchars := decodeIdentifier(bsl) bsl = bsl[:nchars] return string(bsl) } // implemented in go-caller.c func funcfileline(uintptr, int32, bool) (string, string, int, int) func funcentry(uintptr) uintptr