github.com/mailgun/holster/v4@v4.20.0/callstack/callstack.go (about) 1 package callstack 2 3 import ( 4 "bytes" 5 "fmt" 6 "runtime" 7 "strconv" 8 "strings" 9 10 pkgerrors "github.com/pkg/errors" //nolint:gomodguard // Legacy code requires deprecated package. 11 ) 12 13 type FrameInfo struct { 14 CallStack string 15 Func string 16 File string 17 LineNo int 18 } 19 20 func GetCallStack(frames pkgerrors.StackTrace) string { 21 var trace []string 22 for i := len(frames) - 1; i >= 0; i-- { 23 trace = append(trace, fmt.Sprintf("%v", frames[i])) 24 } 25 return strings.Join(trace, " ") 26 } 27 28 // GetLastFrame returns Caller information on the first frame in the stack trace. 29 func GetLastFrame(frames pkgerrors.StackTrace) FrameInfo { 30 if len(frames) == 0 { 31 return FrameInfo{} 32 } 33 pc := uintptr(frames[0]) - 1 34 fn := runtime.FuncForPC(pc) 35 if fn == nil { 36 return FrameInfo{Func: fmt.Sprintf("unknown func at %v", pc)} 37 } 38 filePath, lineNo := fn.FileLine(pc) 39 return FrameInfo{ 40 CallStack: GetCallStack(frames), 41 Func: FuncName(fn), 42 File: filePath, 43 LineNo: lineNo, 44 } 45 } 46 47 // FuncName given a runtime function spec returns a short function name in 48 // format `<package name>.<function name>` or if the function has a receiver 49 // in format `<package name>.(<receiver>).<function name>`. 50 func FuncName(fn *runtime.Func) string { 51 if fn == nil { 52 return "" 53 } 54 funcPath := fn.Name() 55 idx := strings.LastIndex(funcPath, "/") 56 if idx == -1 { 57 return funcPath 58 } 59 return funcPath[idx+1:] 60 } 61 62 type HasStackTrace interface { 63 StackTrace() pkgerrors.StackTrace 64 } 65 66 // CallStack represents a stack of program counters. 67 type CallStack []uintptr 68 69 func (cs *CallStack) Format(st fmt.State, verb rune) { 70 if verb == 'v' && st.Flag('+') { 71 for _, pc := range *cs { 72 f := pkgerrors.Frame(pc) 73 _, _ = fmt.Fprintf(st, "\n%+v", f) 74 } 75 } 76 } 77 78 func (cs *CallStack) StackTrace() pkgerrors.StackTrace { 79 f := make([]pkgerrors.Frame, len(*cs)) 80 for i := 0; i < len(f); i++ { 81 f[i] = pkgerrors.Frame((*cs)[i]) 82 } 83 return f 84 } 85 86 // New creates a new CallStack struct from current stack minus 'skip' number of frames. 87 func New(skip int) *CallStack { 88 skip += 2 89 const depth = 32 90 var pcs [depth]uintptr 91 n := runtime.Callers(skip, pcs[:]) 92 var st CallStack = pcs[0:n] 93 return &st 94 } 95 96 // GoRoutineID returns the current goroutine id. 97 func GoRoutineID() uint64 { 98 b := make([]byte, 64) 99 b = b[:runtime.Stack(b, false)] 100 b = bytes.TrimPrefix(b, []byte("goroutine ")) 101 b = b[:bytes.IndexByte(b, ' ')] 102 n, _ := strconv.ParseUint(string(b), 10, 64) 103 return n 104 }