|  | // 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 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_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 |