github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/hack/curgoroutineid.go (about) 1 package hack 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "runtime" 8 "strconv" 9 "sync" 10 ) 11 12 // CurGoroutineID get current goroutine id by walk stack. 13 // from https://github.com/golang/net/blob/master/http2/gotrack.go 14 // This func get goroutine id by parse current stack string, the performance is not so good. 15 // asm version, https://github.com/petermattis/goid 16 func CurGoroutineID() uint64 { 17 bp := littleBuf.Get().(*[]byte) 18 defer littleBuf.Put(bp) 19 20 b := *bp 21 b = b[:runtime.Stack(b, false)] 22 // Parse the 4707 out of "goroutine 4707 [" 23 b = bytes.TrimPrefix(b, goroutineSpace) 24 i := bytes.IndexByte(b, ' ') 25 if i < 0 { 26 panic(fmt.Sprintf("No space found in %q", b)) 27 } 28 b = b[:i] 29 n, err := ParseUintBytes(b, 10, 64) 30 if err != nil { 31 panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) 32 } 33 return n 34 } 35 36 var ( 37 goroutineSpace = []byte("goroutine ") 38 littleBuf = sync.Pool{ 39 New: func() interface{} { 40 buf := make([]byte, 64) 41 return &buf 42 }, 43 } 44 ) 45 46 // ParseUintBytes is like strconv.ParseUint, but using a []byte. 47 func ParseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { 48 var cutoff, maxVal uint64 49 50 if bitSize == 0 { 51 bitSize = strconv.IntSize 52 } 53 54 s0 := s 55 switch { 56 case len(s) < 1: 57 err = strconv.ErrSyntax 58 goto Error 59 60 case 2 <= base && base <= 36: 61 // valid base; nothing to do 62 63 case base == 0: 64 // Look for octal, hex prefix. 65 switch { 66 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 67 base = 16 68 s = s[2:] 69 if len(s) < 1 { 70 err = strconv.ErrSyntax 71 goto Error 72 } 73 case s[0] == '0': 74 base = 8 75 default: 76 base = 10 77 } 78 79 default: 80 err = errors.New("invalid base " + strconv.Itoa(base)) 81 goto Error 82 } 83 84 n = 0 85 cutoff = cutoff64(base) 86 maxVal = 1<<uint(bitSize) - 1 87 88 for i := 0; i < len(s); i++ { 89 var v byte 90 d := s[i] 91 switch { 92 case '0' <= d && d <= '9': 93 v = d - '0' 94 case 'a' <= d && d <= 'z': 95 v = d - 'a' + 10 96 case 'A' <= d && d <= 'Z': 97 v = d - 'A' + 10 98 default: 99 n = 0 100 err = strconv.ErrSyntax 101 goto Error 102 } 103 if int(v) >= base { 104 n = 0 105 err = strconv.ErrSyntax 106 goto Error 107 } 108 109 if n >= cutoff { 110 // n*base overflows 111 n = 1<<64 - 1 112 err = strconv.ErrRange 113 goto Error 114 } 115 n *= uint64(base) 116 117 n1 := n + uint64(v) 118 if n1 < n || n1 > maxVal { 119 // n+v overflows 120 n = 1<<64 - 1 121 err = strconv.ErrRange 122 goto Error 123 } 124 n = n1 125 } 126 127 return n, nil 128 129 Error: 130 return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} 131 } 132 133 // Return the first number n such that n*base >= 1<<64. 134 func cutoff64(base int) uint64 { 135 if base < 2 { 136 return 0 137 } 138 return (1<<64-1)/uint64(base) + 1 139 }