stacktrace/errors.go (68 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package stacktrace // import "go.elastic.co/apm/v2/stacktrace" import ( "reflect" "runtime" "unsafe" "github.com/pkg/errors" ) 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) ) // AppendErrorStacktrace appends at most n entries extracted from err // to frames, and returns the extended slice. If n is negative, then // all stack frames will be appended. func AppendErrorStacktrace(frames []Frame, err error, n int) []Frame { type internalStackTracer interface { StackTrace() []Frame } type errorsStackTracer interface { StackTrace() errors.StackTrace } type runtimeStackTracer interface { StackTrace() *runtime.Frames } switch stackTracer := err.(type) { case internalStackTracer: stackTrace := stackTracer.StackTrace() if n >= 0 && len(stackTrace) > n { stackTrace = stackTrace[:n] } frames = append(frames, stackTrace...) case errorsStackTracer: stackTrace := stackTracer.StackTrace() frames = appendPkgerrorsStacktrace(frames, stackTrace, n) case runtimeStackTracer: runtimeFrames := stackTracer.StackTrace() count := 0 for { if n >= 0 && count == n { break } frame, more := runtimeFrames.Next() frames = append(frames, RuntimeFrame(frame)) if !more { break } count++ } } return frames } func appendPkgerrorsStacktrace(frames []Frame, stackTrace errors.StackTrace, n int) []Frame { // 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 v2.0.0. if errorsStackTraceUintptr { pc := make([]uintptr, len(stackTrace)) for i, frame := range stackTrace { pc[i] = *(*uintptr)(unsafe.Pointer(&frame)) } frames = AppendCallerFrames(frames, pc, n) } else if errorsStackTraceFrame { if n >= 0 && len(stackTrace) > n { stackTrace = stackTrace[:n] } for _, frame := range stackTrace { rf := (*runtime.Frame)(unsafe.Pointer(&frame)) frames = append(frames, RuntimeFrame(*rf)) } } return frames }