]> Cypherpunks.ru repositories - gostls13.git/blob - src/internal/godebug/godebug.go
cmd/go: add check for unknown godebug setting
[gostls13.git] / src / internal / godebug / godebug.go
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 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.
30 package godebug
31
32 import (
33         "internal/godebugs"
34         "sync"
35         "sync/atomic"
36         _ "unsafe" // go:linkname
37 )
38
39 // A Setting is a single setting in the $GODEBUG environment variable.
40 type Setting struct {
41         name string
42         once sync.Once
43         *setting
44 }
45
46 type setting struct {
47         value          atomic.Pointer[string]
48         nonDefaultOnce sync.Once
49         nonDefault     atomic.Uint64
50         info           *godebugs.Info
51 }
52
53 // New returns a new Setting for the $GODEBUG setting with the given name.
54 //
55 // GODEBUGs meant for use by end users must be listed in ../godebugs/table.go,
56 // which is used for generating and checking various documentation.
57 // If the name is not listed in that table, New will succeed but calling Value
58 // on the returned Setting will panic.
59 // To disable that panic for access to an undocumented setting,
60 // prefix the name with a #, as in godebug.New("#gofsystrace").
61 // The # is a signal to New but not part of the key used in $GODEBUG.
62 func New(name string) *Setting {
63         return &Setting{name: name}
64 }
65
66 // Name returns the name of the setting.
67 func (s *Setting) Name() string {
68         if s.name != "" && s.name[0] == '#' {
69                 return s.name[1:]
70         }
71         return s.name
72 }
73
74 // Undocumented reports whether this is an undocumented setting.
75 func (s *Setting) Undocumented() bool {
76         return s.name != "" && s.name[0] == '#'
77 }
78
79 // String returns a printable form for the setting: name=value.
80 func (s *Setting) String() string {
81         return s.Name() + "=" + s.Value()
82 }
83
84 // IncNonDefault increments the non-default behavior counter
85 // associated with the given setting.
86 // This counter is exposed in the runtime/metrics value
87 // /godebug/non-default-behavior/<name>:events.
88 //
89 // Note that Value must be called at least once before IncNonDefault.
90 func (s *Setting) IncNonDefault() {
91         s.nonDefaultOnce.Do(s.register)
92         s.nonDefault.Add(1)
93 }
94
95 func (s *Setting) register() {
96         if s.info == nil || s.info.Opaque {
97                 panic("godebug: unexpected IncNonDefault of " + s.name)
98         }
99         registerMetric("/godebug/non-default-behavior/"+s.Name()+":events", s.nonDefault.Load)
100 }
101
102 // cache is a cache of all the GODEBUG settings,
103 // a locked map[string]*atomic.Pointer[string].
104 //
105 // All Settings with the same name share a single
106 // *atomic.Pointer[string], so that when GODEBUG
107 // changes only that single atomic string pointer
108 // needs to be updated.
109 //
110 // A name appears in the values map either if it is the
111 // name of a Setting for which Value has been called
112 // at least once, or if the name has ever appeared in
113 // a name=value pair in the $GODEBUG environment variable.
114 // Once entered into the map, the name is never removed.
115 var cache sync.Map // name string -> value *atomic.Pointer[string]
116
117 var empty string
118
119 // Value returns the current value for the GODEBUG setting s.
120 //
121 // Value maintains an internal cache that is synchronized
122 // with changes to the $GODEBUG environment variable,
123 // making Value efficient to call as frequently as needed.
124 // Clients should therefore typically not attempt their own
125 // caching of Value's result.
126 func (s *Setting) Value() string {
127         s.once.Do(func() {
128                 s.setting = lookup(s.Name())
129                 if s.info == nil && !s.Undocumented() {
130                         panic("godebug: Value of name not listed in godebugs.All: " + s.name)
131                 }
132         })
133         return *s.value.Load()
134 }
135
136 // lookup returns the unique *setting value for the given name.
137 func lookup(name string) *setting {
138         if v, ok := cache.Load(name); ok {
139                 return v.(*setting)
140         }
141         s := new(setting)
142         s.info = godebugs.Lookup(name)
143         s.value.Store(&empty)
144         if v, loaded := cache.LoadOrStore(name, s); loaded {
145                 // Lost race: someone else created it. Use theirs.
146                 return v.(*setting)
147         }
148
149         return s
150 }
151
152 // setUpdate is provided by package runtime.
153 // It calls update(def, env), where def is the default GODEBUG setting
154 // and env is the current value of the $GODEBUG environment variable.
155 // After that first call, the runtime calls update(def, env)
156 // again each time the environment variable changes
157 // (due to use of os.Setenv, for example).
158 //
159 //go:linkname setUpdate
160 func setUpdate(update func(string, string))
161
162 // registerMetric is provided by package runtime.
163 // It forwards registrations to runtime/metrics.
164 //
165 //go:linkname registerMetric
166 func registerMetric(name string, read func() uint64)
167
168 // setNewNonDefaultInc is provided by package runtime.
169 // The runtime can do
170 //
171 //      inc := newNonDefaultInc(name)
172 //
173 // instead of
174 //
175 //      inc := godebug.New(name).IncNonDefault
176 //
177 // since it cannot import godebug.
178 //
179 //go:linkname setNewIncNonDefault
180 func setNewIncNonDefault(newIncNonDefault func(string) func())
181
182 func init() {
183         setUpdate(update)
184         setNewIncNonDefault(newIncNonDefault)
185 }
186
187 func newIncNonDefault(name string) func() {
188         s := New(name)
189         s.Value()
190         return s.IncNonDefault
191 }
192
193 var updateMu sync.Mutex
194
195 // update records an updated GODEBUG setting.
196 // def is the default GODEBUG setting for the running binary,
197 // and env is the current value of the $GODEBUG environment variable.
198 func update(def, env string) {
199         updateMu.Lock()
200         defer updateMu.Unlock()
201
202         // Update all the cached values, creating new ones as needed.
203         // We parse the environment variable first, so that any settings it has
204         // are already locked in place (did[name] = true) before we consider
205         // the defaults.
206         did := make(map[string]bool)
207         parse(did, env)
208         parse(did, def)
209
210         // Clear any cached values that are no longer present.
211         cache.Range(func(name, s any) bool {
212                 if !did[name.(string)] {
213                         s.(*setting).value.Store(&empty)
214                 }
215                 return true
216         })
217 }
218
219 // parse parses the GODEBUG setting string s,
220 // which has the form k=v,k2=v2,k3=v3.
221 // Later settings override earlier ones.
222 // Parse only updates settings k=v for which did[k] = false.
223 // It also sets did[k] = true for settings that it updates.
224 func parse(did map[string]bool, s string) {
225         // Scan the string backward so that later settings are used
226         // and earlier settings are ignored.
227         // Note that a forward scan would cause cached values
228         // to temporarily use the ignored value before being
229         // updated to the "correct" one.
230         end := len(s)
231         eq := -1
232         for i := end - 1; i >= -1; i-- {
233                 if i == -1 || s[i] == ',' {
234                         if eq >= 0 {
235                                 name, value := s[i+1:eq], s[eq+1:end]
236                                 if !did[name] {
237                                         did[name] = true
238                                         lookup(name).value.Store(&value)
239                                 }
240                         }
241                         eq = -1
242                         end = i
243                 } else if s[i] == '=' {
244                         eq = i
245                 }
246         }
247 }