github.com/waldiirawan/apm-agent-go/v2@v2.2.2/stacktrace/errors.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package stacktrace // import "github.com/waldiirawan/apm-agent-go/v2/stacktrace" 19 20 import ( 21 "reflect" 22 "runtime" 23 "unsafe" 24 25 "github.com/pkg/errors" 26 ) 27 28 var ( 29 uintptrType = reflect.TypeOf(uintptr(0)) 30 runtimeFrameType = reflect.TypeOf(runtime.Frame{}) 31 errorsStackTraceUintptr = uintptrType.ConvertibleTo(reflect.TypeOf(*new(errors.Frame))) 32 errorsStackTraceFrame = reflect.TypeOf(*new(errors.Frame)).ConvertibleTo(runtimeFrameType) 33 ) 34 35 // AppendErrorStacktrace appends at most n entries extracted from err 36 // to frames, and returns the extended slice. If n is negative, then 37 // all stack frames will be appended. 38 func AppendErrorStacktrace(frames []Frame, err error, n int) []Frame { 39 type internalStackTracer interface { 40 StackTrace() []Frame 41 } 42 type errorsStackTracer interface { 43 StackTrace() errors.StackTrace 44 } 45 type runtimeStackTracer interface { 46 StackTrace() *runtime.Frames 47 } 48 switch stackTracer := err.(type) { 49 case internalStackTracer: 50 stackTrace := stackTracer.StackTrace() 51 if n >= 0 && len(stackTrace) > n { 52 stackTrace = stackTrace[:n] 53 } 54 frames = append(frames, stackTrace...) 55 case errorsStackTracer: 56 stackTrace := stackTracer.StackTrace() 57 frames = appendPkgerrorsStacktrace(frames, stackTrace, n) 58 case runtimeStackTracer: 59 runtimeFrames := stackTracer.StackTrace() 60 count := 0 61 for { 62 if n >= 0 && count == n { 63 break 64 } 65 frame, more := runtimeFrames.Next() 66 frames = append(frames, RuntimeFrame(frame)) 67 if !more { 68 break 69 } 70 count++ 71 } 72 } 73 return frames 74 } 75 76 func appendPkgerrorsStacktrace(frames []Frame, stackTrace errors.StackTrace, n int) []Frame { 77 // github.com/pkg/errors 0.8.x and earlier represent 78 // stack frames as uintptr; 0.9.0 and later represent 79 // them as runtime.Frames. 80 // 81 // TODO(axw) drop support for older github.com/pkg/errors 82 // versions when we release github.com/waldiirawan/apm-agent-go/v2 v2.0.0. 83 if errorsStackTraceUintptr { 84 pc := make([]uintptr, len(stackTrace)) 85 for i, frame := range stackTrace { 86 pc[i] = *(*uintptr)(unsafe.Pointer(&frame)) 87 } 88 frames = AppendCallerFrames(frames, pc, n) 89 } else if errorsStackTraceFrame { 90 if n >= 0 && len(stackTrace) > n { 91 stackTrace = stackTrace[:n] 92 } 93 for _, frame := range stackTrace { 94 rf := (*runtime.Frame)(unsafe.Pointer(&frame)) 95 frames = append(frames, RuntimeFrame(*rf)) 96 } 97 } 98 return frames 99 }