|
2 | 2 | // Use of this source code is governed by a BSD-style
|
3 | 3 | // license that can be found in the LICENSE file.
|
4 | 4 |
|
5 |
| -// Package godebug parses the GODEBUG environment variable. |
| 5 | +// Package godebug makes the settings in the $GODEBUG environment variable |
| 6 | +// available to other packages. These settings are often used for compatibility |
| 7 | +// tweaks, when we need to change a default behavior but want to let users |
| 8 | +// opt back in to the original. For example GODEBUG=http2server=0 disables |
| 9 | +// HTTP/2 support in the net/http server. |
| 10 | +// |
| 11 | +// In typical usage, code should declare a Setting as a global |
| 12 | +// and then call Value each time the current setting value is needed: |
| 13 | +// |
| 14 | +// var http2server = godebug.New("http2server") |
| 15 | +// |
| 16 | +// func ServeConn(c net.Conn) { |
| 17 | +// if http2server.Value() == "0" { |
| 18 | +// disallow HTTP/2 |
| 19 | +// ... |
| 20 | +// } |
| 21 | +// ... |
| 22 | +// } |
| 23 | +// |
| 24 | +// Each time a non-default setting causes a change in program behavior, |
| 25 | +// code should call [Setting.IncNonDefault] to increment a counter that can |
| 26 | +// be reported by [runtime/metrics.Read]. |
| 27 | +// Note that counters used with IncNonDefault must be added to |
| 28 | +// various tables in other packages. See the [Setting.IncNonDefault] |
| 29 | +// documentation for details. |
6 | 30 | package godebug
|
7 | 31 |
|
8 |
| -import "os" |
| 32 | +// Note: Be careful about new imports here. Any package |
| 33 | +// that internal/godebug imports cannot itself import internal/godebug, |
| 34 | +// meaning it cannot introduce a GODEBUG setting of its own. |
| 35 | +// We keep imports to the absolute bare minimum. |
| 36 | +import ( |
| 37 | + "sync" |
| 38 | + "sync/atomic" |
| 39 | + _ "unsafe" // go:linkname |
9 | 40 |
|
10 |
| -// Get returns the value for the provided GODEBUG key. |
11 |
| -func Get(key string) string { |
12 |
| - return get(os.Getenv("GODEBUG"), key) |
| 41 | + "github.com/imroc/req/v3/internal/bisect" |
| 42 | + "github.com/imroc/req/v3/internal/godebugs" |
| 43 | +) |
| 44 | + |
| 45 | +// A Setting is a single setting in the $GODEBUG environment variable. |
| 46 | +type Setting struct { |
| 47 | + name string |
| 48 | + once sync.Once |
| 49 | + *setting |
| 50 | +} |
| 51 | + |
| 52 | +type setting struct { |
| 53 | + value atomic.Pointer[value] |
| 54 | + nonDefaultOnce sync.Once |
| 55 | + nonDefault atomic.Uint64 |
| 56 | + info *godebugs.Info |
| 57 | +} |
| 58 | + |
| 59 | +type value struct { |
| 60 | + text string |
| 61 | + bisect *bisect.Matcher |
| 62 | +} |
| 63 | + |
| 64 | +// New returns a new Setting for the $GODEBUG setting with the given name. |
| 65 | +// |
| 66 | +// GODEBUGs meant for use by end users must be listed in ../godebugs/table.go, |
| 67 | +// which is used for generating and checking various documentation. |
| 68 | +// If the name is not listed in that table, New will succeed but calling Value |
| 69 | +// on the returned Setting will panic. |
| 70 | +// To disable that panic for access to an undocumented setting, |
| 71 | +// prefix the name with a #, as in godebug.New("#gofsystrace"). |
| 72 | +// The # is a signal to New but not part of the key used in $GODEBUG. |
| 73 | +func New(name string) *Setting { |
| 74 | + return &Setting{name: name} |
| 75 | +} |
| 76 | + |
| 77 | +// Name returns the name of the setting. |
| 78 | +func (s *Setting) Name() string { |
| 79 | + if s.name != "" && s.name[0] == '#' { |
| 80 | + return s.name[1:] |
| 81 | + } |
| 82 | + return s.name |
13 | 83 | }
|
14 | 84 |
|
15 |
| -// get returns the value part of key=value in s (a GODEBUG value). |
16 |
| -func get(s, key string) string { |
17 |
| - for i := 0; i < len(s)-len(key)-1; i++ { |
18 |
| - if i > 0 && s[i-1] != ',' { |
19 |
| - continue |
| 85 | +// Undocumented reports whether this is an undocumented setting. |
| 86 | +func (s *Setting) Undocumented() bool { |
| 87 | + return s.name != "" && s.name[0] == '#' |
| 88 | +} |
| 89 | + |
| 90 | +// String returns a printable form for the setting: name=value. |
| 91 | +func (s *Setting) String() string { |
| 92 | + return s.Name() + "=" + s.Value() |
| 93 | +} |
| 94 | + |
| 95 | +// IncNonDefault increments the non-default behavior counter |
| 96 | +// associated with the given setting. |
| 97 | +// This counter is exposed in the runtime/metrics value |
| 98 | +// /godebug/non-default-behavior/<name>:events. |
| 99 | +// |
| 100 | +// Note that Value must be called at least once before IncNonDefault. |
| 101 | +func (s *Setting) IncNonDefault() { |
| 102 | + s.nonDefaultOnce.Do(s.register) |
| 103 | + s.nonDefault.Add(1) |
| 104 | +} |
| 105 | + |
| 106 | +func (s *Setting) register() { |
| 107 | + if s.info == nil || s.info.Opaque { |
| 108 | + panic("godebug: unexpected IncNonDefault of " + s.name) |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +// cache is a cache of all the GODEBUG settings, |
| 113 | +// a locked map[string]*atomic.Pointer[string]. |
| 114 | +// |
| 115 | +// All Settings with the same name share a single |
| 116 | +// *atomic.Pointer[string], so that when GODEBUG |
| 117 | +// changes only that single atomic string pointer |
| 118 | +// needs to be updated. |
| 119 | +// |
| 120 | +// A name appears in the values map either if it is the |
| 121 | +// name of a Setting for which Value has been called |
| 122 | +// at least once, or if the name has ever appeared in |
| 123 | +// a name=value pair in the $GODEBUG environment variable. |
| 124 | +// Once entered into the map, the name is never removed. |
| 125 | +var cache sync.Map // name string -> value *atomic.Pointer[string] |
| 126 | + |
| 127 | +var empty value |
| 128 | + |
| 129 | +// Value returns the current value for the GODEBUG setting s. |
| 130 | +// |
| 131 | +// Value maintains an internal cache that is synchronized |
| 132 | +// with changes to the $GODEBUG environment variable, |
| 133 | +// making Value efficient to call as frequently as needed. |
| 134 | +// Clients should therefore typically not attempt their own |
| 135 | +// caching of Value's result. |
| 136 | +func (s *Setting) Value() string { |
| 137 | + s.once.Do(func() { |
| 138 | + s.setting = lookup(s.Name()) |
| 139 | + if s.info == nil && !s.Undocumented() { |
| 140 | + panic("godebug: Value of name not listed in godebugs.All: " + s.name) |
20 | 141 | }
|
21 |
| - afterKey := s[i+len(key):] |
22 |
| - if afterKey[0] != '=' || s[i:i+len(key)] != key { |
23 |
| - continue |
| 142 | + }) |
| 143 | + v := *s.value.Load() |
| 144 | + if v.bisect != nil && !v.bisect.Stack(&stderr) { |
| 145 | + return "" |
| 146 | + } |
| 147 | + return v.text |
| 148 | +} |
| 149 | + |
| 150 | +// lookup returns the unique *setting value for the given name. |
| 151 | +func lookup(name string) *setting { |
| 152 | + if v, ok := cache.Load(name); ok { |
| 153 | + return v.(*setting) |
| 154 | + } |
| 155 | + s := new(setting) |
| 156 | + s.info = godebugs.Lookup(name) |
| 157 | + s.value.Store(&empty) |
| 158 | + if v, loaded := cache.LoadOrStore(name, s); loaded { |
| 159 | + // Lost race: someone else created it. Use theirs. |
| 160 | + return v.(*setting) |
| 161 | + } |
| 162 | + |
| 163 | + return s |
| 164 | +} |
| 165 | + |
| 166 | +func newIncNonDefault(name string) func() { |
| 167 | + s := New(name) |
| 168 | + s.Value() |
| 169 | + return s.IncNonDefault |
| 170 | +} |
| 171 | + |
| 172 | +var updateMu sync.Mutex |
| 173 | + |
| 174 | +// update records an updated GODEBUG setting. |
| 175 | +// def is the default GODEBUG setting for the running binary, |
| 176 | +// and env is the current value of the $GODEBUG environment variable. |
| 177 | +func update(def, env string) { |
| 178 | + updateMu.Lock() |
| 179 | + defer updateMu.Unlock() |
| 180 | + |
| 181 | + // Update all the cached values, creating new ones as needed. |
| 182 | + // We parse the environment variable first, so that any settings it has |
| 183 | + // are already locked in place (did[name] = true) before we consider |
| 184 | + // the defaults. |
| 185 | + did := make(map[string]bool) |
| 186 | + parse(did, env) |
| 187 | + parse(did, def) |
| 188 | + |
| 189 | + // Clear any cached values that are no longer present. |
| 190 | + cache.Range(func(name, s any) bool { |
| 191 | + if !did[name.(string)] { |
| 192 | + s.(*setting).value.Store(&empty) |
24 | 193 | }
|
25 |
| - val := afterKey[1:] |
26 |
| - for i, b := range val { |
27 |
| - if b == ',' { |
28 |
| - return val[:i] |
| 194 | + return true |
| 195 | + }) |
| 196 | +} |
| 197 | + |
| 198 | +// parse parses the GODEBUG setting string s, |
| 199 | +// which has the form k=v,k2=v2,k3=v3. |
| 200 | +// Later settings override earlier ones. |
| 201 | +// Parse only updates settings k=v for which did[k] = false. |
| 202 | +// It also sets did[k] = true for settings that it updates. |
| 203 | +// Each value v can also have the form v#pattern, |
| 204 | +// in which case the GODEBUG is only enabled for call stacks |
| 205 | +// matching pattern, for use with golang.org/x/tools/cmd/bisect. |
| 206 | +func parse(did map[string]bool, s string) { |
| 207 | + // Scan the string backward so that later settings are used |
| 208 | + // and earlier settings are ignored. |
| 209 | + // Note that a forward scan would cause cached values |
| 210 | + // to temporarily use the ignored value before being |
| 211 | + // updated to the "correct" one. |
| 212 | + end := len(s) |
| 213 | + eq := -1 |
| 214 | + for i := end - 1; i >= -1; i-- { |
| 215 | + if i == -1 || s[i] == ',' { |
| 216 | + if eq >= 0 { |
| 217 | + name, arg := s[i+1:eq], s[eq+1:end] |
| 218 | + if !did[name] { |
| 219 | + did[name] = true |
| 220 | + v := &value{text: arg} |
| 221 | + for j := 0; j < len(arg); j++ { |
| 222 | + if arg[j] == '#' { |
| 223 | + v.text = arg[:j] |
| 224 | + v.bisect, _ = bisect.New(arg[j+1:]) |
| 225 | + break |
| 226 | + } |
| 227 | + } |
| 228 | + lookup(name).value.Store(v) |
| 229 | + } |
29 | 230 | }
|
| 231 | + eq = -1 |
| 232 | + end = i |
| 233 | + } else if s[i] == '=' { |
| 234 | + eq = i |
30 | 235 | }
|
31 |
| - return val |
32 | 236 | }
|
33 |
| - return "" |
| 237 | +} |
| 238 | + |
| 239 | +type runtimeStderr struct{} |
| 240 | + |
| 241 | +var stderr runtimeStderr |
| 242 | + |
| 243 | +func (*runtimeStderr) Write(b []byte) (int, error) { |
| 244 | + return len(b), nil |
34 | 245 | }
|
0 commit comments