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  }