// 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/: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. did := make(map[string]bool) 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