gotrack.go (3160B)
1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Defensive debug-only utility to track that functions run on the 6 // goroutine that they're supposed to. 7 8 package http2 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "os" 15 "runtime" 16 "strconv" 17 "sync" 18 ) 19 20 var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" 21 22 type goroutineLock uint64 23 24 func newGoroutineLock() goroutineLock { 25 if !DebugGoroutines { 26 return 0 27 } 28 return goroutineLock(curGoroutineID()) 29 } 30 31 func (g goroutineLock) check() { 32 if !DebugGoroutines { 33 return 34 } 35 if curGoroutineID() != uint64(g) { 36 panic("running on the wrong goroutine") 37 } 38 } 39 40 func (g goroutineLock) checkNotOn() { 41 if !DebugGoroutines { 42 return 43 } 44 if curGoroutineID() == uint64(g) { 45 panic("running on the wrong goroutine") 46 } 47 } 48 49 var goroutineSpace = []byte("goroutine ") 50 51 func curGoroutineID() uint64 { 52 bp := littleBuf.Get().(*[]byte) 53 defer littleBuf.Put(bp) 54 b := *bp 55 b = b[:runtime.Stack(b, false)] 56 // Parse the 4707 out of "goroutine 4707 [" 57 b = bytes.TrimPrefix(b, goroutineSpace) 58 i := bytes.IndexByte(b, ' ') 59 if i < 0 { 60 panic(fmt.Sprintf("No space found in %q", b)) 61 } 62 b = b[:i] 63 n, err := parseUintBytes(b, 10, 64) 64 if err != nil { 65 panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) 66 } 67 return n 68 } 69 70 var littleBuf = sync.Pool{ 71 New: func() interface{} { 72 buf := make([]byte, 64) 73 return &buf 74 }, 75 } 76 77 // parseUintBytes is like strconv.ParseUint, but using a []byte. 78 func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { 79 var cutoff, maxVal uint64 80 81 if bitSize == 0 { 82 bitSize = int(strconv.IntSize) 83 } 84 85 s0 := s 86 switch { 87 case len(s) < 1: 88 err = strconv.ErrSyntax 89 goto Error 90 91 case 2 <= base && base <= 36: 92 // valid base; nothing to do 93 94 case base == 0: 95 // Look for octal, hex prefix. 96 switch { 97 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 98 base = 16 99 s = s[2:] 100 if len(s) < 1 { 101 err = strconv.ErrSyntax 102 goto Error 103 } 104 case s[0] == '0': 105 base = 8 106 default: 107 base = 10 108 } 109 110 default: 111 err = errors.New("invalid base " + strconv.Itoa(base)) 112 goto Error 113 } 114 115 n = 0 116 cutoff = cutoff64(base) 117 maxVal = 1<<uint(bitSize) - 1 118 119 for i := 0; i < len(s); i++ { 120 var v byte 121 d := s[i] 122 switch { 123 case '0' <= d && d <= '9': 124 v = d - '0' 125 case 'a' <= d && d <= 'z': 126 v = d - 'a' + 10 127 case 'A' <= d && d <= 'Z': 128 v = d - 'A' + 10 129 default: 130 n = 0 131 err = strconv.ErrSyntax 132 goto Error 133 } 134 if int(v) >= base { 135 n = 0 136 err = strconv.ErrSyntax 137 goto Error 138 } 139 140 if n >= cutoff { 141 // n*base overflows 142 n = 1<<64 - 1 143 err = strconv.ErrRange 144 goto Error 145 } 146 n *= uint64(base) 147 148 n1 := n + uint64(v) 149 if n1 < n || n1 > maxVal { 150 // n+v overflows 151 n = 1<<64 - 1 152 err = strconv.ErrRange 153 goto Error 154 } 155 n = n1 156 } 157 158 return n, nil 159 160 Error: 161 return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} 162 } 163 164 // Return the first number n such that n*base >= 1<<64. 165 func cutoff64(base int) uint64 { 166 if base < 2 { 167 return 0 168 } 169 return (1<<64-1)/uint64(base) + 1 170 }