godebug.mx raw

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