github.com/zchee/zap-cloudlogging@v0.0.0-20220819025602-19b026d3900e/caller.go (about) 1 // Copyright 2022 The zap-cloudlogging Authors 2 // SPDX-License-Identifier: BSD-3-Clause 3 4 //go:build go1.13 5 6 package zapcloudlogging 7 8 import ( 9 "runtime" 10 "unsafe" 11 ) 12 13 // Token from https://github.com/golang/go/blob/go1.19/src/runtime/symtab.go#L799-L802 14 type funcInfo struct { 15 Func *runtime.Func // *_func 16 _ unsafe.Pointer // datap *moduledata 17 } 18 19 // Token from https://github.com/golang/go/blob/go1.19/src/runtime/symtab.go#L828 20 // 21 //go:linkname findfunc runtime.findfunc 22 //go:noescape 23 func findfunc(pc uintptr) funcInfo 24 25 // FuncForPC is a drop-in replacement for runtime.FuncForPC. 26 // 27 //go:nosplit 28 func FuncForPC(pc uintptr) *runtime.Func { 29 return findfunc(pc).Func 30 } 31 32 // frame is the information returned by Frames for each call frame. 33 // 34 // Token from: https://github.com/golang/go/blob/go1.19/src/runtime/symtab.go#L26-L61 35 type frame struct { 36 // PC is the program counter for the location in this frame. 37 // For a frame that calls another frame, this will be the 38 // program counter of a call instruction. Because of inlining, 39 // multiple frames may have the same PC value, but different 40 // symbolic information. 41 PC uintptr 42 43 // Func is the Func value of this call frame. This may be nil 44 // for non-Go code or fully inlined functions. 45 Func *runtime.Func 46 47 // Function is the package path-qualified function name of 48 // this call frame. If non-empty, this string uniquely 49 // identifies a single function in the program. 50 // This may be the empty string if not known. 51 // If Func is not nil then Function == Func.Name(). 52 Function string 53 54 // File and Line are the file name and line number of the 55 // location in this frame. For non-leaf frames, this will be 56 // the location of a call. These may be the empty string and 57 // zero, respectively, if not known. 58 File string 59 Line int 60 61 // Entry point program counter for the function; may be zero 62 // if not known. If Func is not nil then Entry == 63 // Func.Entry(). 64 Entry uintptr 65 66 // The runtime's internal view of the function. This field 67 // is set (funcInfo.valid() returns true) only for Go functions, 68 // not for C functions. 69 funcInfo funcInfo 70 } 71 72 // frames may be used to get function/file/line information for a 73 // slice of PC values returned by Callers. 74 // 75 // Token from: https://github.com/golang/go/blob/go1.19/src/runtime/symtab.go#L16-L23 76 type frames struct { 77 // callers is a slice of PCs that have not yet been expanded to frames. 78 callers []uintptr 79 80 // frames is a slice of Frames that have yet to be returned. 81 frames []frame 82 frameStore [2]frame 83 } 84 85 // CallersFrames is a drop-in replacement for runtime.CallersFrames. 86 // 87 // CallersFrames takes a slice of PC values returned by Callers and 88 // prepares to return function/file/line information. 89 // Do not change the slice until you are done with the Frames. 90 // 91 // Token from: https://github.com/golang/go/blob/go1.19/src/runtime/symtab.go#L66 92 func CallersFrames(callers []uintptr) *frames { 93 f := &frames{callers: callers} 94 f.frames = f.frameStore[:0] 95 return f 96 } 97 98 // Token from: https://github.com/golang/go/blob/go1.19/src/runtime/symtab.go#L81 99 // 100 //go:linkname next runtime.(*Frames).Next 101 //go:noescape 102 func next(*frames) (frame frame, more bool) 103 104 // Next returns a Frame representing the next call frame in the slice 105 // of PC values. If it has already returned all call frames, Next 106 // returns a zero Frame. 107 // 108 // The more result indicates whether the next call to Next will return 109 // a valid Frame. It does not necessarily indicate whether this call 110 // returned one. 111 // 112 // See the Frames example for idiomatic usage. 113 // 114 //go:nosplit 115 func (ci *frames) Next() (frame frame, more bool) { 116 return next(ci) 117 } 118 119 // Caller is a drop-in replacement for runtime.Caller. 120 // 121 // Token from: https://github.com/golang/go/blob/go1.19/src/runtime/extern.go#L217-L225 122 func Caller(skip int) (pc uintptr, file string, line int, ok bool) { 123 rpc := make([]uintptr, 1) 124 n := callers(skip+1, rpc) 125 if n < 1 { 126 return 127 } 128 frame, _ := CallersFrames(rpc).Next() 129 130 return frame.PC, frame.File, frame.Line, frame.PC != 0 131 } 132 133 // Callers is a drop-in replacement for runtime.Callers that uses frame 134 // pointers for fast and simple stack unwinding. 135 // 136 // Based by: https://github.com/golang/go/blob/go1.19/src/runtime/extern.go#L240-L248 137 // 138 //go:noinline 139 func Callers(skip int, pcs []uintptr) int { 140 return callers(skip+1, pcs) 141 } 142 143 //go:noinline 144 //go:nosplit 145 func callers(skip int, pcs []uintptr) int { 146 fp := uintptr(unsafe.Pointer(&skip)) - 16 147 148 i := 0 149 for i < len(pcs) { 150 pc := deref(fp + 8) 151 if skip == 0 { 152 pcs[i] = pc 153 i++ 154 } else { 155 skip-- 156 } 157 fp = deref(fp) 158 if fp == 0 { 159 break 160 } 161 } 162 163 return i 164 } 165 166 //go:nosplit 167 func deref(addr uintptr) uintptr { 168 return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&addr))) 169 }