File size: 9,815 Bytes
e36aeda | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | // Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package godebug makes the settings in the $GODEBUG environment variable
// available to other packages. These settings are often used for compatibility
// tweaks, when we need to change a default behavior but want to let users
// opt back in to the original. For example GODEBUG=http2server=0 disables
// HTTP/2 support in the net/http server.
//
// In typical usage, code should declare a Setting as a global
// and then call Value each time the current setting value is needed:
//
// var http2server = godebug.New("http2server")
//
// func ServeConn(c net.Conn) {
// if http2server.Value() == "0" {
// disallow HTTP/2
// ...
// }
// ...
// }
//
// Each time a non-default setting causes a change in program behavior,
// code must call [Setting.IncNonDefault] to increment a counter that can
// be reported by [runtime/metrics.Read]. The call must only happen when
// the program executes a non-default behavior, not just when the setting
// is set to a non-default value. This is occasionally (but very rarely)
// infeasible, in which case the internal/godebugs table entry must set
// Opaque: true, and the documentation in doc/godebug.md should
// mention that metrics are unavailable.
//
// Conventionally, the global variable representing a godebug is named
// for the godebug itself, with no case changes:
//
// var gotypesalias = godebug.New("gotypesalias") // this
// var goTypesAlias = godebug.New("gotypesalias") // NOT THIS
//
// The test in internal/godebugs that checks for use of IncNonDefault
// requires the use of this convention.
//
// Note that counters used with IncNonDefault must be added to
// various tables in other packages. See the [Setting.IncNonDefault]
// documentation for details.
package godebug
// Note: Be careful about new imports here. Any package
// that internal/godebug imports cannot itself import internal/godebug,
// meaning it cannot introduce a GODEBUG setting of its own.
// We keep imports to the absolute bare minimum.
import (
"internal/bisect"
"internal/godebugs"
"sync"
"sync/atomic"
"unsafe"
_ "unsafe" // go:linkname
)
// A Setting is a single setting in the $GODEBUG environment variable.
type Setting struct {
name string
once sync.Once
*setting
}
type setting struct {
value atomic.Pointer[value]
nonDefaultOnce sync.Once
nonDefault atomic.Uint64
info *godebugs.Info
}
type value struct {
text string
bisect *bisect.Matcher
}
// New returns a new Setting for the $GODEBUG setting with the given name.
//
// GODEBUGs meant for use by end users must be listed in ../godebugs/table.go,
// which is used for generating and checking various documentation.
// If the name is not listed in that table, New will succeed but calling Value
// on the returned Setting will panic.
// To disable that panic for access to an undocumented setting,
// prefix the name with a #, as in godebug.New("#gofsystrace").
// The # is a signal to New but not part of the key used in $GODEBUG.
//
// Note that almost all settings should arrange to call [IncNonDefault] precisely
// when program behavior is changing from the default due to the setting
// (not just when the setting is different, but when program behavior changes).
// See the [internal/godebug] package comment for more.
func New(name string) *Setting {
return &Setting{name: name}
}
// Name returns the name of the setting.
func (s *Setting) Name() string {
if s.name != "" && s.name[0] == '#' {
return s.name[1:]
}
return s.name
}
// Undocumented reports whether this is an undocumented setting.
func (s *Setting) Undocumented() bool {
return s.name != "" && s.name[0] == '#'
}
// String returns a printable form for the setting: name=value.
func (s *Setting) String() string {
return s.Name() + "=" + s.Value()
}
// IncNonDefault increments the non-default behavior counter
// associated with the given setting.
// This counter is exposed in the runtime/metrics value
// /godebug/non-default-behavior/<name>:events.
//
// Note that Value must be called at least once before IncNonDefault.
func (s *Setting) IncNonDefault() {
s.nonDefaultOnce.Do(s.register)
s.nonDefault.Add(1)
}
func (s *Setting) register() {
if s.info == nil || s.info.Opaque {
panic("godebug: unexpected IncNonDefault of " + s.name)
}
registerMetric("/godebug/non-default-behavior/"+s.Name()+":events", s.nonDefault.Load)
}
// cache is a cache of all the GODEBUG settings,
// a locked map[string]*atomic.Pointer[string].
//
// All Settings with the same name share a single
// *atomic.Pointer[string], so that when GODEBUG
// changes only that single atomic string pointer
// needs to be updated.
//
// A name appears in the values map either if it is the
// name of a Setting for which Value has been called
// at least once, or if the name has ever appeared in
// a name=value pair in the $GODEBUG environment variable.
// Once entered into the map, the name is never removed.
var cache sync.Map // name string -> value *atomic.Pointer[string]
var empty value
// Value returns the current value for the GODEBUG setting s.
//
// Value maintains an internal cache that is synchronized
// with changes to the $GODEBUG environment variable,
// making Value efficient to call as frequently as needed.
// Clients should therefore typically not attempt their own
// caching of Value's result.
func (s *Setting) Value() string {
s.once.Do(func() {
s.setting = lookup(s.Name())
if s.info == nil && !s.Undocumented() {
panic("godebug: Value of name not listed in godebugs.All: " + s.name)
}
})
v := *s.value.Load()
if v.bisect != nil && !v.bisect.Stack(&stderr) {
return ""
}
return v.text
}
// lookup returns the unique *setting value for the given name.
func lookup(name string) *setting {
if v, ok := cache.Load(name); ok {
return v.(*setting)
}
s := new(setting)
s.info = godebugs.Lookup(name)
s.value.Store(&empty)
if v, loaded := cache.LoadOrStore(name, s); loaded {
// Lost race: someone else created it. Use theirs.
return v.(*setting)
}
return s
}
// setUpdate is provided by package runtime.
// It calls update(def, env), where def is the default GODEBUG setting
// and env is the current value of the $GODEBUG environment variable.
// After that first call, the runtime calls update(def, env)
// again each time the environment variable changes
// (due to use of os.Setenv, for example).
//
//go:linkname setUpdate
func setUpdate(update func(string, string))
// registerMetric is provided by package runtime.
// It forwards registrations to runtime/metrics.
//
//go:linkname registerMetric
func registerMetric(name string, read func() uint64)
// setNewIncNonDefault is provided by package runtime.
// The runtime can do
//
// inc := newNonDefaultInc(name)
//
// instead of
//
// inc := godebug.New(name).IncNonDefault
//
// since it cannot import godebug.
//
//go:linkname setNewIncNonDefault
func setNewIncNonDefault(newIncNonDefault func(string) func())
func init() {
setUpdate(update)
setNewIncNonDefault(newIncNonDefault)
}
func newIncNonDefault(name string) func() {
s := New(name)
s.Value()
return s.IncNonDefault
}
var updateMu sync.Mutex
// update records an updated GODEBUG setting.
// def is the default GODEBUG setting for the running binary,
// and env is the current value of the $GODEBUG environment variable.
func update(def, env string) {
updateMu.Lock()
defer updateMu.Unlock()
// Update all the cached values, creating new ones as needed.
// We parse the environment variable first, so that any settings it has
// are already locked in place (did[name] = true) before we consider
// the defaults. Existing immutable settings are always locked.
did := make(map[string]bool)
cache.Range(func(name, s any) bool {
if info := s.(*setting).info; info != nil && info.Immutable {
did[name.(string)] = true
}
return true
})
parse(did, env)
parse(did, def)
// Clear any cached values that are no longer present.
cache.Range(func(name, s any) bool {
if !did[name.(string)] {
s.(*setting).value.Store(&empty)
}
return true
})
}
// parse parses the GODEBUG setting string s,
// which has the form k=v,k2=v2,k3=v3.
// Later settings override earlier ones.
// Parse only updates settings k=v for which did[k] = false.
// It also sets did[k] = true for settings that it updates.
// Each value v can also have the form v#pattern,
// in which case the GODEBUG is only enabled for call stacks
// matching pattern, for use with golang.org/x/tools/cmd/bisect.
func parse(did map[string]bool, s string) {
// Scan the string backward so that later settings are used
// and earlier settings are ignored.
// Note that a forward scan would cause cached values
// to temporarily use the ignored value before being
// updated to the "correct" one.
end := len(s)
eq := -1
for i := end - 1; i >= -1; i-- {
if i == -1 || s[i] == ',' {
if eq >= 0 {
name, arg := s[i+1:eq], s[eq+1:end]
if !did[name] {
did[name] = true
v := &value{text: arg}
for j := 0; j < len(arg); j++ {
if arg[j] == '#' {
v.text = arg[:j]
v.bisect, _ = bisect.New(arg[j+1:])
break
}
}
lookup(name).value.Store(v)
}
}
eq = -1
end = i
} else if s[i] == '=' {
eq = i
}
}
}
type runtimeStderr struct{}
var stderr runtimeStderr
func (*runtimeStderr) Write(b []byte) (int, error) {
if len(b) > 0 {
write(2, unsafe.Pointer(&b[0]), int32(len(b)))
}
return len(b), nil
}
// Since we cannot import os or syscall, use the runtime's write function
// to print to standard error.
//
//go:linkname write runtime.write
func write(fd uintptr, p unsafe.Pointer, n int32) int32
|