| // 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<hex 2 >", "..u<hex x 4>" and "..U<hex x 8>" 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 |