github.com/nikandfor/loc@v0.5.1/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] 87 88 for { 89 if p = strings.Index(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 }