| // 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 ( | |
| "internal/bytealg" | |
| _ "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 a Frame representing the next call frame in the slice | |
| // of PC values. If it has already returned all call frames, Next | |
| // returns a zero Frame. | |
| // | |
| // The more result indicates whether the next call to Next will return | |
| // a valid Frame. It does not necessarily indicate whether this call | |
| // returned one. | |
| // | |
| // See the Frames example for idiomatic usage. | |
| 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_1pprof.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 *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) | |
| } | |
| // decodeIdentifier performs an in-place decoding on the input byte slice. | |
| // This undoes the compiler underscore mangling. | |
| // Returns the number of bytes used by the result. | |
| func decodeIdentifier(bsl []byte) int { | |
| underscoreCodes := map[byte]byte{ | |
| '_': '_', | |
| '0': '.', | |
| '1': '/', | |
| '2': '*', | |
| '3': ',', | |
| '4': '{', | |
| '5': '}', | |
| '6': '[', | |
| '7': ']', | |
| '8': '(', | |
| '9': ')', | |
| 'a': '"', | |
| 'b': ' ', | |
| 'c': ';', | |
| } | |
| j := 0 | |
| for i := 0; i < len(bsl); i++ { | |
| b := bsl[i] | |
| if b != '_' || i+1 >= len(bsl) { | |
| bsl[j] = b | |
| j++ | |
| continue | |
| } | |
| if d, ok := underscoreCodes[bsl[i+1]]; ok { | |
| i++ | |
| bsl[j] = d | |
| j++ | |
| continue | |
| } | |
| rlen := 0 | |
| switch bsl[i+1] { | |
| case 'x': | |
| rlen = 2 | |
| case 'u': | |
| rlen = 4 | |
| case 'U': | |
| rlen = 8 | |
| } | |
| if rlen > 0 && i+1+rlen < len(bsl) { | |
| r := hexDigitsToRune(bsl[i+2:], rlen) | |
| nc := encoderune(bsl[j:], r) | |
| j += nc | |
| i += rlen + 1 | |
| } else { | |
| bsl[j] = b | |
| j++ | |
| } | |
| } | |
| return j | |
| } | |
| // Demangle a function symbol. Applies the reverse of go_encode_id() | |
| // as used in the compiler. | |
| func demangleSymbol(s string) string { | |
| if bytealg.IndexByteString(s, '.') < 0 { | |
| // A symbol with no '.' is not a Go symbol. | |
| return s | |
| } | |
| 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 |