gotrack.go

  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
  8package http2
  9
 10import (
 11	"bytes"
 12	"errors"
 13	"fmt"
 14	"os"
 15	"runtime"
 16	"strconv"
 17	"sync"
 18)
 19
 20var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
 21
 22type goroutineLock uint64
 23
 24func newGoroutineLock() goroutineLock {
 25	if !DebugGoroutines {
 26		return 0
 27	}
 28	return goroutineLock(curGoroutineID())
 29}
 30
 31func (g goroutineLock) check() {
 32	if !DebugGoroutines {
 33		return
 34	}
 35	if curGoroutineID() != uint64(g) {
 36		panic("running on the wrong goroutine")
 37	}
 38}
 39
 40func (g goroutineLock) checkNotOn() {
 41	if !DebugGoroutines {
 42		return
 43	}
 44	if curGoroutineID() == uint64(g) {
 45		panic("running on the wrong goroutine")
 46	}
 47}
 48
 49var goroutineSpace = []byte("goroutine ")
 50
 51func 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
 70var 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.
 78func 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
160Error:
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.
165func cutoff64(base int) uint64 {
166	if base < 2 {
167		return 0
168	}
169	return (1<<64-1)/uint64(base) + 1
170}