gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

callers.go (2161B)


      1 package errors
      2 
      3 import (
      4 	"encoding/json"
      5 	"runtime"
      6 	"strconv"
      7 	"strings"
      8 	"unsafe"
      9 )
     10 
     11 // Callers is a stacktrace of caller PCs.
     12 type Callers []uintptr
     13 
     14 // GetCallers returns a Callers slice of PCs, of at most 'depth'.
     15 func GetCallers(skip int, depth int) Callers {
     16 	rpc := make([]uintptr, depth)
     17 	n := runtime.Callers(skip+1, rpc)
     18 	return Callers(rpc[0:n])
     19 }
     20 
     21 // Frames fetches runtime frames for a slice of caller PCs.
     22 func (f Callers) Frames() []runtime.Frame {
     23 	// Allocate expected frames slice
     24 	frames := make([]runtime.Frame, 0, len(f))
     25 
     26 	// Get frames iterator for PCs
     27 	iter := runtime.CallersFrames(f)
     28 
     29 	for {
     30 		// Get next frame in iter
     31 		frame, ok := iter.Next()
     32 		if !ok {
     33 			break
     34 		}
     35 
     36 		// Append to frames slice
     37 		frames = append(frames, frame)
     38 	}
     39 
     40 	return frames
     41 }
     42 
     43 // MarshalJSON implements json.Marshaler to provide an easy, simple default.
     44 func (f Callers) MarshalJSON() ([]byte, error) {
     45 	// JSON-able frame type
     46 	type jsonFrame struct {
     47 		Func string `json:"func"`
     48 		File string `json:"file"`
     49 		Line int    `json:"line"`
     50 	}
     51 
     52 	// Convert to frames
     53 	frames := f.Frames()
     54 
     55 	// Allocate expected size jsonFrame slice
     56 	jsonFrames := make([]jsonFrame, 0, len(f))
     57 
     58 	for i := 0; i < len(frames); i++ {
     59 		frame := frames[i]
     60 
     61 		// Convert each to jsonFrame object
     62 		jsonFrames = append(jsonFrames, jsonFrame{
     63 			Func: funcname(frame.Function),
     64 			File: frame.File,
     65 			Line: frame.Line,
     66 		})
     67 	}
     68 
     69 	// marshal converted frames
     70 	return json.Marshal(frames)
     71 }
     72 
     73 // String will return a simple string representation of receiving Callers slice.
     74 func (f Callers) String() string {
     75 	// Guess-timate to reduce allocs
     76 	buf := make([]byte, 0, 64*len(f))
     77 
     78 	// Convert to frames
     79 	frames := f.Frames()
     80 
     81 	for i := 0; i < len(frames); i++ {
     82 		frame := frames[i]
     83 
     84 		// Append formatted caller info
     85 		fn := funcname(frame.Function)
     86 		buf = append(buf, fn+"()\n\t"+frame.File+":"...)
     87 		buf = strconv.AppendInt(buf, int64(frame.Line), 10)
     88 		buf = append(buf, '\n')
     89 	}
     90 
     91 	return *(*string)(unsafe.Pointer(&buf))
     92 }
     93 
     94 // funcname splits a function name with pkg from its path prefix.
     95 func funcname(name string) string {
     96 	i := strings.LastIndex(name, "/")
     97 	return name[i+1:]
     98 }