gopkg.in/hedzr/errors.v3@v3.3.1/stack.go (about)

     1  package errors
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"path"
     7  	"runtime"
     8  	"strings"
     9  )
    10  
    11  // Frame represents a program counter inside a Stack frame.
    12  type Frame uintptr
    13  
    14  // pc returns the program counter for this frame;
    15  // multiple frames may have the same PC value.
    16  func (f Frame) pc() uintptr { return uintptr(f) - 1 }
    17  
    18  // file returns the full path to the file that contains the
    19  // function for this Frame's pc.
    20  func (f Frame) file() string {
    21  	fn := runtime.FuncForPC(f.pc())
    22  	if fn == nil {
    23  		return "unknown"
    24  	}
    25  	file, _ := fn.FileLine(f.pc())
    26  	return file
    27  }
    28  
    29  // line returns the line number of source code of the
    30  // function for this Frame's pc.
    31  func (f Frame) line() int {
    32  	fn := runtime.FuncForPC(f.pc())
    33  	if fn == nil {
    34  		return 0
    35  	}
    36  	_, line := fn.FileLine(f.pc())
    37  	return line
    38  }
    39  
    40  // Format formats the frame according to the fmt.Formatter interface.
    41  //
    42  //	%s    source file
    43  //	%d    source line
    44  //	%n    function name
    45  //	%v    equivalent to %s:%d
    46  //
    47  // Format accepts flags that alter the printing of some verbs, as follows:
    48  //
    49  //	%+s   function name and path of source file relative to the
    50  //	      compiling time.
    51  //	      GOPATH separated by \n\t (<funcname>\n\t<path>)
    52  //	%+v   equivalent to %+s:%d
    53  func (f Frame) Format(s fmt.State, verb rune) {
    54  	switch verb {
    55  	case 's':
    56  		switch {
    57  		case s.Flag('+'):
    58  			pc := f.pc()
    59  			fn := runtime.FuncForPC(pc)
    60  			if fn == nil {
    61  				_, _ = io.WriteString(s, "unknown")
    62  			} else {
    63  				file, _ := fn.FileLine(pc)
    64  				_, _ = fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
    65  			}
    66  		default:
    67  			_, _ = io.WriteString(s, path.Base(f.file()))
    68  		}
    69  	case 'd':
    70  		_, _ = fmt.Fprintf(s, "%d", f.line())
    71  	case 'n':
    72  		name := runtime.FuncForPC(f.pc()).Name()
    73  		_, _ = io.WriteString(s, funcname(name))
    74  	case 'v':
    75  		f.Format(s, 's')
    76  		_, _ = io.WriteString(s, ":")
    77  		f.Format(s, 'd')
    78  	}
    79  }
    80  
    81  // StackTrace is Stack of Frames from innermost (newest) to outermost (oldest).
    82  type StackTrace []Frame
    83  
    84  // Format formats the Stack of Frames according to the fmt.Formatter interface.
    85  //
    86  //	%s	lists source files for each Frame in the Stack
    87  //	%v	lists the source file and line number for each Frame in the Stack
    88  //
    89  // Format accepts flags that alter the printing of some verbs, as follows:
    90  //
    91  //	%+v   Prints filename, function, and line number for each Frame in the Stack.
    92  func (st StackTrace) Format(s fmt.State, verb rune) {
    93  	switch verb {
    94  	case 'v':
    95  		switch {
    96  		case s.Flag('+'):
    97  			for _, f := range st {
    98  				_, _ = fmt.Fprintf(s, "\n%+v", f)
    99  			}
   100  		case s.Flag('#'):
   101  			_, _ = fmt.Fprintf(s, "%#v", []Frame(st))
   102  		default:
   103  			_, _ = fmt.Fprintf(s, "%v", []Frame(st))
   104  		}
   105  	case 's':
   106  		_, _ = fmt.Fprintf(s, "%s", []Frame(st))
   107  	}
   108  }
   109  
   110  // Stack represents a Stack of program counters.
   111  type Stack []uintptr
   112  
   113  // Format formats the stack of Frames according to the fmt.Formatter interface.
   114  //
   115  //	%s	lists source files for each Frame in the stack
   116  //	%v	lists the source file and line number for each Frame in the stack
   117  //
   118  // Format accepts flags that alter the printing of some verbs, as follows:
   119  //
   120  //	%+v   Prints filename, function, and line number for each Frame in the stack.
   121  func (s *Stack) Format(st fmt.State, verb rune) {
   122  	if verb == 'v' && st.Flag('+') {
   123  		for _, pc := range *s {
   124  			f := Frame(pc)
   125  			_, _ = fmt.Fprintf(st, "\n%+v", f)
   126  		}
   127  	}
   128  }
   129  
   130  // StackTrace returns the stacktrace frames
   131  func (s *Stack) StackTrace() StackTrace {
   132  	f := make([]Frame, len(*s))
   133  	for i := 0; i < len(f); i++ {
   134  		f[i] = Frame((*s)[i])
   135  	}
   136  	return f
   137  }
   138  
   139  func callers(skip int) *Stack {
   140  	const depth = 32
   141  	var pcs [depth]uintptr
   142  	n := runtime.Callers(2+skip, pcs[:]) // by default, we skip these frames: callers(), and runtime.Callers()
   143  	var st Stack = pcs[0:n]
   144  	return &st
   145  }
   146  
   147  // funcname removes the path prefix component of a function's name reported by func.Name().
   148  func funcname(name string) string {
   149  	i := strings.LastIndex(name, "/")
   150  	name = name[i+1:] //nolint:revive
   151  	i = strings.Index(name, ".")
   152  	return name[i+1:]
   153  }