github.com/thanos-io/thanos@v0.32.5/pkg/errors/stacktrace.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package errors
     5  
     6  import (
     7  	"fmt"
     8  	"runtime"
     9  	"strings"
    10  )
    11  
    12  // stacktrace holds a snapshot of program counters.
    13  type stacktrace []uintptr
    14  
    15  // newStackTrace captures a stack trace. It skips first 3 frames to record the
    16  // snapshot of the stack trace at the origin of a particular error. It tries to
    17  // record maximum 16 frames (if available).
    18  func newStackTrace() stacktrace {
    19  	const stackDepth = 16 // record maximum 16 frames (if available).
    20  
    21  	pc := make([]uintptr, stackDepth)
    22  	// using skip=3 for not to count the program counter address of
    23  	// 1. the respective function from errors package (eg. errors.New)
    24  	// 2. newStacktrace itself
    25  	// 3. the function used in runtime.Callers
    26  	n := runtime.Callers(3, pc)
    27  
    28  	// this approach is taken to reduce long term memory footprint (obtained through escape analysis).
    29  	// We are returning a new slice by re-slicing the pc with the required length and capacity (when the
    30  	// no of returned callFrames is less that stackDepth). This uses less memory compared to pc[:n] as
    31  	// the capacity of new slice is inherited from the parent slice if not specified.
    32  	return stacktrace(pc[:n:n])
    33  }
    34  
    35  // String implements the fmt.Stringer interface to provide formatted text output.
    36  func (s stacktrace) String() string {
    37  	var buf strings.Builder
    38  
    39  	// CallersFrames takes the slice of Program Counter addresses returned by Callers to
    40  	// retrieve function/file/line information.
    41  	cf := runtime.CallersFrames(s)
    42  	for {
    43  		// more indicates if the next call will be successful or not.
    44  		frame, more := cf.Next()
    45  		// used formatting scheme <`>`space><function name><tab><filepath><:><line><newline> for example:
    46  		// > testing.tRunner	/home/go/go1.17.8/src/testing/testing.go:1259
    47  		buf.WriteString(fmt.Sprintf("> %s\t%s:%d\n", frame.Func.Name(), frame.File, frame.Line))
    48  		if !more {
    49  			break
    50  		}
    51  	}
    52  	return buf.String()
    53  }