Skip to content

Commit

Permalink
apm: add support for github.com/pkg/errors@master
Browse files Browse the repository at this point in the history
github.com/pkg/errors has broken backwards compatibility
on master, which will become 0.9.0. Until we can get rid
of support for the older version (which, undoubtedly, many
people are using), we will support both by using reflection.

Fixes #408
  • Loading branch information
axw committed Jan 7, 2019
1 parent 2f96ceb commit 3884b65
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 7 deletions.
31 changes: 27 additions & 4 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ import (
"net"
"os"
"reflect"
"runtime"
"syscall"
"time"
"unsafe"

"github.com/pkg/errors"

"go.elastic.co/apm/stacktrace"
)

var (
uintptrType = reflect.TypeOf(uintptr(0))
runtimeFrameType = reflect.TypeOf(runtime.Frame{})
errorsStackTraceUintptr = uintptrType.ConvertibleTo(reflect.TypeOf(*new(errors.Frame)))
errorsStackTraceFrame = reflect.TypeOf(*new(errors.Frame)).ConvertibleTo(runtimeFrameType)
)

// Recovered creates an Error with t.NewError(err), where
// err is either v (if v implements error), or otherwise
// fmt.Errorf("%v", v). The value v is expected to have
Expand Down Expand Up @@ -355,12 +364,26 @@ func initStacktrace(e *Error, err error) {
case internalStackTracer:
e.stacktrace = append(e.stacktrace[:0], stackTracer.StackTrace()...)
case errorsStackTracer:
// github.com/pkg/errors 0.8.x and earlier represent
// stack frames as uintptr; 0.9.0 and later represent
// them as runtime.Frames.
//
// TODO(axw) drop support for older github.com/pkg/errors
// versions when we release go.elastic.co/apm v2.0.0.
stackTrace := stackTracer.StackTrace()
pc := make([]uintptr, len(stackTrace))
for i, frame := range stackTrace {
pc[i] = uintptr(frame)
if errorsStackTraceUintptr {
pc := make([]uintptr, len(stackTrace))
for i, frame := range stackTrace {
pc[i] = *(*uintptr)(unsafe.Pointer(&frame))
}
e.stacktrace = stacktrace.AppendCallerFrames(e.stacktrace[:0], pc, -1)
} else if errorsStackTraceFrame {
e.stacktrace = e.stacktrace[:0]
for _, frame := range stackTrace {
rf := (*runtime.Frame)(unsafe.Pointer(&frame))
e.stacktrace = append(e.stacktrace, stacktrace.RuntimeFrame(*rf))
}
}
e.stacktrace = stacktrace.AppendCallerFrames(e.stacktrace[:0], pc, -1)
}
}

Expand Down
31 changes: 28 additions & 3 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"path/filepath"
"reflect"
"runtime"
"testing"

Expand Down Expand Up @@ -171,9 +172,33 @@ func (e *errorsStackTracer) StackTrace() errors.StackTrace {
func newErrorsStackTrace(skip, n int) errors.StackTrace {
callers := make([]uintptr, 2)
callers = callers[:runtime.Callers(1, callers)]
frames := make([]errors.Frame, len(callers))
for i, pc := range callers {
frames[i] = errors.Frame(pc)

var (
uintptrType = reflect.TypeOf(uintptr(0))
errorsFrameType = reflect.TypeOf(*new(errors.Frame))
runtimeFrameType = reflect.TypeOf(runtime.Frame{})
)

var frames []errors.Frame
switch {
case errorsFrameType.ConvertibleTo(uintptrType):
frames = make([]errors.Frame, len(callers))
for i, pc := range callers {
reflect.ValueOf(&frames[i]).Elem().Set(reflect.ValueOf(pc).Convert(errorsFrameType))
}
case errorsFrameType.ConvertibleTo(runtimeFrameType):
fs := runtime.CallersFrames(callers)
for {
var frame errors.Frame
runtimeFrame, more := fs.Next()
reflect.ValueOf(&frame).Elem().Set(reflect.ValueOf(runtimeFrame).Convert(errorsFrameType))
frames = append(frames, frame)
if !more {
break
}
}
default:
panic(fmt.Errorf("unhandled errors.Frame type %s", errorsFrameType))
}
return errors.StackTrace(frames)
}
Expand Down

0 comments on commit 3884b65

Please sign in to comment.