tlog.app/go/loc@v0.6.2-0.20231112073106-b6382a0ac518/location.go (about)

     1  package loc
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  	"sync/atomic"
     7  	"unsafe"
     8  )
     9  
    10  type (
    11  	// PC is a program counter alias.
    12  	// Function name, file name and line can be obtained from it but only in the same binary where Caller or FuncEntry was called.
    13  	PC uintptr
    14  
    15  	// PCs is a stack trace.
    16  	// It's quiet the same as runtime.CallerFrames but more efficient.
    17  	PCs []PC
    18  )
    19  
    20  // Caller returns information about the calling goroutine's stack. The argument s is the number of frames to ascend, with 0 identifying the caller of Caller.
    21  //
    22  // It's hacked version of runtime.Caller with no allocs.
    23  func Caller(s int) (r PC) {
    24  	caller1(1+s, &r, 1, 1)
    25  
    26  	return
    27  }
    28  
    29  // FuncEntry returns information about the calling goroutine's stack. The argument s is the number of frames to ascend, with 0 identifying the caller of Caller.
    30  //
    31  // It's hacked version of runtime.Callers -> runtime.CallersFrames -> Frames.Next -> Frame.Entry with no allocs.
    32  func FuncEntry(s int) (r PC) {
    33  	caller1(1+s, &r, 1, 1)
    34  
    35  	return r.FuncEntry()
    36  }
    37  
    38  func CallerOnce(s int, pc *PC) (r PC) {
    39  	r = PC(atomic.LoadUintptr((*uintptr)(unsafe.Pointer(pc))))
    40  	if r != 0 {
    41  		return
    42  	}
    43  
    44  	caller1(1+s, &r, 1, 1)
    45  
    46  	atomic.StoreUintptr((*uintptr)(unsafe.Pointer(pc)), uintptr(r))
    47  
    48  	return
    49  }
    50  
    51  func FuncEntryOnce(s int, pc *PC) (r PC) {
    52  	r = PC(atomic.LoadUintptr((*uintptr)(unsafe.Pointer(pc))))
    53  	if r != 0 {
    54  		return
    55  	}
    56  
    57  	caller1(1+s, &r, 1, 1)
    58  
    59  	r = r.FuncEntry()
    60  
    61  	atomic.StoreUintptr((*uintptr)(unsafe.Pointer(pc)), uintptr(r))
    62  
    63  	return
    64  }
    65  
    66  // Callers returns callers stack trace.
    67  //
    68  // It's hacked version of runtime.Callers -> runtime.CallersFrames -> Frames.Next -> Frame.Entry with only one alloc (resulting slice).
    69  func Callers(skip, n int) PCs {
    70  	tr := make([]PC, n)
    71  	n = callers(1+skip, tr)
    72  	return tr[:n]
    73  }
    74  
    75  // CallersFill puts callers stack trace into provided slice.
    76  //
    77  // It's hacked version of runtime.Callers -> runtime.CallersFrames -> Frames.Next -> Frame.Entry with no allocs.
    78  func CallersFill(skip int, tr PCs) PCs {
    79  	n := callers(1+skip, tr)
    80  	return tr[:n]
    81  }
    82  
    83  func cropFilename(fn, tp string) string {
    84  	p := strings.LastIndexByte(tp, '/')
    85  	pp := strings.IndexByte(tp[p+1:], '.')
    86  	tp = tp[:p+pp] // cut type and func name
    87  
    88  	for {
    89  		if p = strings.LastIndex(fn, tp); p != -1 {
    90  			return fn[p:]
    91  		}
    92  
    93  		p = strings.IndexByte(tp, '/')
    94  		if p == -1 {
    95  			return filepath.Base(fn)
    96  		}
    97  
    98  		tp = tp[p+1:]
    99  	}
   100  }