Source File
godebug.go
Belonging Package
internal/godebug
// 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 (
_ // 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 ( string) *Setting {
return &Setting{name: }
}
// Name returns the name of the setting.
func ( *Setting) () string {
if .name != "" && .name[0] == '#' {
return .name[1:]
}
return .name
}
// Undocumented reports whether this is an undocumented setting.
func ( *Setting) () bool {
return .name != "" && .name[0] == '#'
}
// String returns a printable form for the setting: name=value.
func ( *Setting) () string {
return .Name() + "=" + .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 ( *Setting) () {
.nonDefaultOnce.Do(.register)
.nonDefault.Add(1)
}
func ( *Setting) () {
if .info == nil || .info.Opaque {
panic("godebug: unexpected IncNonDefault of " + .name)
}
registerMetric("/godebug/non-default-behavior/"+.Name()+":events", .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 ( *Setting) () string {
.once.Do(func() {
.setting = lookup(.Name())
if .info == nil && !.Undocumented() {
panic("godebug: Value of name not listed in godebugs.All: " + .name)
}
})
:= *.value.Load()
if .bisect != nil && !.bisect.Stack(&stderr) {
return ""
}
return .text
}
// lookup returns the unique *setting value for the given name.
func lookup( string) *setting {
if , := cache.Load(); {
return .(*setting)
}
:= new(setting)
.info = godebugs.Lookup()
.value.Store(&empty)
if , := cache.LoadOrStore(, ); {
// Lost race: someone else created it. Use theirs.
return .(*setting)
}
return
}
// 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( func(string, string))
// registerMetric is provided by package runtime.
// It forwards registrations to runtime/metrics.
//
//go:linkname registerMetric
func registerMetric( string, 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( func(string) func())
func init() {
setUpdate(update)
setNewIncNonDefault(newIncNonDefault)
}
func newIncNonDefault( string) func() {
:= New()
.Value()
return .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(, 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.
:= make(map[string]bool)
parse(, )
parse(, )
// Clear any cached values that are no longer present.
cache.Range(func(, any) bool {
if ![.(string)] {
.(*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( map[string]bool, 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.
:= len()
:= -1
for := - 1; >= -1; -- {
if == -1 || [] == ',' {
if >= 0 {
, := [+1:], [+1:]
if ![] {
[] = true
:= &value{text: }
for := 0; < len(); ++ {
if [] == '#' {
.text = [:]
.bisect, _ = bisect.New([+1:])
break
}
}
lookup().value.Store()
}
}
= -1
=
} else if [] == '=' {
=
}
}
}
type runtimeStderr struct{}
var stderr runtimeStderr
func (*runtimeStderr) ( []byte) (int, error) {
if len() > 0 {
write(2, unsafe.Pointer(&[0]), int32(len()))
}
return len(), 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( uintptr, unsafe.Pointer, int32) int32
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |