mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
internal/godebug: define more efficient API
We have been expanding our use of GODEBUG for compatibility, and the current implementation forces a tradeoff between freshness and efficiency. It parses the environment variable in full each time it is called, which is expensive. But if clients cache the result, they won't respond to run-time GODEBUG changes, as happened with x509sha1 (#56436). This CL changes the GODEBUG API to provide efficient, up-to-date results. Instead of a single Get function, New returns a *godebug.Setting that itself has a Get method. Clients can save the result of New, which is no more expensive than errors.New, in a global variable, and then call that variable's Get method to get the value. Get costs only two atomic loads in the case where the variable hasn't changed since the last call. Unfortunately, these changes do require importing sync from godebug, which will mean that sync itself will never be able to use a GODEBUG setting. That doesn't seem like such a hardship. If it was really necessary, the runtime could pass a setting to package sync itself at startup, with the caveat that that setting, like the ones used by runtime itself, would not respond to run-time GODEBUG changes. Change-Id: I99a3acfa24fb2a692610af26a5d14bbc62c966ac Reviewed-on: https://go-review.googlesource.com/c/go/+/449504 Run-TryBot: Russ Cox <rsc@golang.org> Auto-Submit: Russ Cox <rsc@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
40bdcbb483
commit
ea4631cc0c
|
@ -2348,9 +2348,11 @@ func TestUpxCompression(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var gocacheverify = godebug.New("gocacheverify")
|
||||
|
||||
func TestCacheListStale(t *testing.T) {
|
||||
tooSlow(t)
|
||||
if godebug.Get("gocacheverify") == "1" {
|
||||
if gocacheverify.Value() == "1" {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
tg := testgo(t)
|
||||
|
@ -2373,7 +2375,7 @@ func TestCacheListStale(t *testing.T) {
|
|||
func TestCacheCoverage(t *testing.T) {
|
||||
tooSlow(t)
|
||||
|
||||
if godebug.Get("gocacheverify") == "1" {
|
||||
if gocacheverify.Value() == "1" {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
|
||||
|
@ -2407,7 +2409,7 @@ func TestIssue22588(t *testing.T) {
|
|||
|
||||
func TestIssue22531(t *testing.T) {
|
||||
tooSlow(t)
|
||||
if godebug.Get("gocacheverify") == "1" {
|
||||
if gocacheverify.Value() == "1" {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
tg := testgo(t)
|
||||
|
@ -2436,7 +2438,7 @@ func TestIssue22531(t *testing.T) {
|
|||
|
||||
func TestIssue22596(t *testing.T) {
|
||||
tooSlow(t)
|
||||
if godebug.Get("gocacheverify") == "1" {
|
||||
if gocacheverify.Value() == "1" {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
tg := testgo(t)
|
||||
|
@ -2466,7 +2468,7 @@ func TestIssue22596(t *testing.T) {
|
|||
func TestTestCache(t *testing.T) {
|
||||
tooSlow(t)
|
||||
|
||||
if godebug.Get("gocacheverify") == "1" {
|
||||
if gocacheverify.Value() == "1" {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
tg := testgo(t)
|
||||
|
|
|
@ -36,27 +36,29 @@ func Trace(op, path string) {
|
|||
traceMu.Lock()
|
||||
defer traceMu.Unlock()
|
||||
fmt.Fprintf(traceFile, "%d gofsystrace %s %s\n", os.Getpid(), op, path)
|
||||
if traceStack != "" {
|
||||
if match, _ := pathpkg.Match(traceStack, path); match {
|
||||
if pattern := gofsystracestack.Value(); pattern != "" {
|
||||
if match, _ := pathpkg.Match(pattern, path); match {
|
||||
traceFile.Write(debug.Stack())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
doTrace bool
|
||||
traceStack string
|
||||
traceFile *os.File
|
||||
traceMu sync.Mutex
|
||||
doTrace bool
|
||||
traceFile *os.File
|
||||
traceMu sync.Mutex
|
||||
|
||||
gofsystrace = godebug.New("gofsystrace")
|
||||
gofsystracelog = godebug.New("gofsystracelog")
|
||||
gofsystracestack = godebug.New("gofsystracestack")
|
||||
)
|
||||
|
||||
func init() {
|
||||
if godebug.Get("gofsystrace") != "1" {
|
||||
if gofsystrace.Value() != "1" {
|
||||
return
|
||||
}
|
||||
doTrace = true
|
||||
traceStack = godebug.Get("gofsystracestack")
|
||||
if f := godebug.Get("gofsystracelog"); f != "" {
|
||||
if f := gofsystracelog.Value(); f != "" {
|
||||
// Note: No buffering on writes to this file, so no need to worry about closing it at exit.
|
||||
var err error
|
||||
traceFile, err = os.OpenFile(f, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
|
|
|
@ -37,7 +37,7 @@ import (
|
|||
// It will be removed before the release.
|
||||
// TODO(matloob): Remove enabled once we have more confidence on the
|
||||
// module index.
|
||||
var enabled bool = godebug.Get("goindex") != "0"
|
||||
var enabled = godebug.New("goindex").Value() != "0"
|
||||
|
||||
// Module represents and encoded module index file. It is used to
|
||||
// do the equivalent of build.Import of packages in the module and answer other
|
||||
|
@ -368,6 +368,8 @@ func relPath(path, modroot string) string {
|
|||
return str.TrimFilePathPrefix(filepath.Clean(path), filepath.Clean(modroot))
|
||||
}
|
||||
|
||||
var installgorootAll = godebug.New("installgoroot").Value() == "all"
|
||||
|
||||
// Import is the equivalent of build.Import given the information in Module.
|
||||
func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *build.Package, err error) {
|
||||
defer unprotect(protect(), &err)
|
||||
|
@ -436,7 +438,7 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
|
|||
p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
|
||||
|
||||
// Set the install target if applicable.
|
||||
if !p.Goroot || (strings.EqualFold(godebug.Get("installgoroot"), "all") && p.ImportPath != "unsafe" && p.ImportPath != "builtin") {
|
||||
if !p.Goroot || (installgorootAll && p.ImportPath != "unsafe" && p.ImportPath != "builtin") {
|
||||
p.PkgObj = ctxt.joinPath(p.Root, pkga)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -813,6 +813,8 @@ func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm,
|
|||
return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey)
|
||||
}
|
||||
|
||||
var x509sha1 = godebug.New("x509sha1")
|
||||
|
||||
// checkSignature verifies that signature is a valid signature over signed from
|
||||
// a crypto.PublicKey.
|
||||
func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool) (err error) {
|
||||
|
@ -835,7 +837,7 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
|
|||
return InsecureAlgorithmError(algo)
|
||||
case crypto.SHA1:
|
||||
// SHA-1 signatures are mostly disabled. See go.dev/issue/41682.
|
||||
if !allowSHA1 && godebug.Get("x509sha1") != "1" {
|
||||
if !allowSHA1 && x509sha1.Value() != "1" {
|
||||
return InsecureAlgorithmError(algo)
|
||||
}
|
||||
fallthrough
|
||||
|
|
|
@ -521,6 +521,8 @@ func nameExt(name string) string {
|
|||
return name[i:]
|
||||
}
|
||||
|
||||
var installgoroot = godebug.New("installgoroot")
|
||||
|
||||
// Import returns details about the Go package named by the import path,
|
||||
// interpreting local import paths relative to the srcDir directory.
|
||||
// If the path is a local import path naming a package that can be imported
|
||||
|
@ -783,7 +785,7 @@ Found:
|
|||
p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
|
||||
|
||||
// Set the install target if applicable.
|
||||
if !p.Goroot || (strings.EqualFold(godebug.Get("installgoroot"), "all") && p.ImportPath != "unsafe" && p.ImportPath != "builtin") {
|
||||
if !p.Goroot || (installgoroot.Value() == "all" && p.ImportPath != "unsafe" && p.ImportPath != "builtin") {
|
||||
p.PkgObj = ctxt.joinPath(p.Root, pkga)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,13 +52,10 @@ var depsRules = `
|
|||
internal/goarch, unsafe
|
||||
< internal/abi;
|
||||
|
||||
unsafe
|
||||
< internal/godebug;
|
||||
|
||||
# RUNTIME is the core runtime group of packages, all of them very light-weight.
|
||||
internal/abi, internal/cpu, internal/goarch,
|
||||
internal/coverage/rtcov, internal/goexperiment,
|
||||
internal/goos, internal/godebug, unsafe
|
||||
internal/goos, unsafe
|
||||
< internal/bytealg
|
||||
< internal/itoa
|
||||
< internal/unsafeheader
|
||||
|
@ -70,6 +67,7 @@ var depsRules = `
|
|||
< sync/atomic
|
||||
< internal/race
|
||||
< sync
|
||||
< internal/godebug
|
||||
< internal/reflectlite
|
||||
< errors
|
||||
< internal/oserror, math/bits
|
||||
|
|
|
@ -48,7 +48,7 @@ func TestDisableAllCapabilities(t *testing.T) {
|
|||
func TestAllCapabilitiesDisabled(t *testing.T) {
|
||||
MustHaveDebugOptionsSupport(t)
|
||||
|
||||
if godebug.Get("cpu.all") != "off" {
|
||||
if godebug.New("cpu.all").Value() != "off" {
|
||||
t.Skipf("skipping test: GODEBUG=cpu.all=off not set")
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ func TestDisableSSE3(t *testing.T) {
|
|||
func TestSSE3DebugOption(t *testing.T) {
|
||||
MustHaveDebugOptionsSupport(t)
|
||||
|
||||
if godebug.Get("cpu.sse3") != "off" {
|
||||
if godebug.New("cpu.sse3").Value() != "off" {
|
||||
t.Skipf("skipping test: GODEBUG=cpu.sse3=off not set")
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -1077,14 +1076,8 @@ var zeroVals []any = []any{
|
|||
uint64(0),
|
||||
}
|
||||
|
||||
var (
|
||||
debugInfo bool
|
||||
debugInfoOnce sync.Once
|
||||
)
|
||||
var debugInfo = godebug.New("fuzzdebug").Value() == "1"
|
||||
|
||||
func shouldPrintDebugInfo() bool {
|
||||
debugInfoOnce.Do(func() {
|
||||
debugInfo = godebug.Get("fuzzdebug") == "1"
|
||||
})
|
||||
return debugInfo
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2022 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
|
||||
|
||||
var Xget = get
|
|
@ -2,36 +2,163 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package godebug parses the GODEBUG environment variable.
|
||||
// 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
|
||||
// ...
|
||||
// }
|
||||
// ...
|
||||
// }
|
||||
package godebug
|
||||
|
||||
import _ "unsafe" // go:linkname
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
_ "unsafe" // go:linkname
|
||||
)
|
||||
|
||||
//go:linkname getGODEBUG
|
||||
func getGODEBUG() string
|
||||
|
||||
// Get returns the value for the provided GODEBUG key.
|
||||
func Get(key string) string {
|
||||
return get(getGODEBUG(), key)
|
||||
// A Setting is a single setting in the $GODEBUG environment variable.
|
||||
type Setting struct {
|
||||
name string
|
||||
once sync.Once
|
||||
value *atomic.Pointer[string]
|
||||
}
|
||||
|
||||
// get returns the value part of key=value in s (a GODEBUG value).
|
||||
func get(s, key string) string {
|
||||
for i := 0; i < len(s)-len(key)-1; i++ {
|
||||
if i > 0 && s[i-1] != ',' {
|
||||
continue
|
||||
// New returns a new Setting for the $GODEBUG setting with the given name.
|
||||
func New(name string) *Setting {
|
||||
return &Setting{name: name}
|
||||
}
|
||||
|
||||
// Name returns the name of the setting.
|
||||
func (s *Setting) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
// String returns a printable form for the setting: name=value.
|
||||
func (s *Setting) String() string {
|
||||
return s.name + "=" + s.Value()
|
||||
}
|
||||
|
||||
// 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 string
|
||||
|
||||
// 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() {
|
||||
v, ok := cache.Load(s.name)
|
||||
if !ok {
|
||||
p := new(atomic.Pointer[string])
|
||||
p.Store(&empty)
|
||||
v, _ = cache.LoadOrStore(s.name, p)
|
||||
}
|
||||
afterKey := s[i+len(key):]
|
||||
if afterKey[0] != '=' || s[i:i+len(key)] != key {
|
||||
continue
|
||||
s.value = v.(*atomic.Pointer[string])
|
||||
})
|
||||
return *s.value.Load()
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
func init() {
|
||||
setUpdate(update)
|
||||
}
|
||||
|
||||
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, v any) bool {
|
||||
if !did[name.(string)] {
|
||||
v.(*atomic.Pointer[string]).Store(&empty)
|
||||
}
|
||||
val := afterKey[1:]
|
||||
for i, b := range val {
|
||||
if b == ',' {
|
||||
return val[:i]
|
||||
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.
|
||||
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, value := s[i+1:eq], s[eq+1:end]
|
||||
if !did[name] {
|
||||
did[name] = true
|
||||
v, ok := cache.Load(name)
|
||||
if !ok {
|
||||
p := new(atomic.Pointer[string])
|
||||
p.Store(&empty)
|
||||
v, _ = cache.LoadOrStore(name, p)
|
||||
}
|
||||
v.(*atomic.Pointer[string]).Store(&value)
|
||||
}
|
||||
}
|
||||
eq = -1
|
||||
end = i
|
||||
} else if s[i] == '=' {
|
||||
eq = i
|
||||
}
|
||||
return val
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -10,28 +10,30 @@ import (
|
|||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
foo := New("foo")
|
||||
tests := []struct {
|
||||
godebug string
|
||||
key string
|
||||
setting *Setting
|
||||
want string
|
||||
}{
|
||||
{"", "", ""},
|
||||
{"", "foo", ""},
|
||||
{"foo=bar", "foo", "bar"},
|
||||
{"foo=bar,after=x", "foo", "bar"},
|
||||
{"before=x,foo=bar,after=x", "foo", "bar"},
|
||||
{"before=x,foo=bar", "foo", "bar"},
|
||||
{",,,foo=bar,,,", "foo", "bar"},
|
||||
{"foodecoy=wrong,foo=bar", "foo", "bar"},
|
||||
{"foo=", "foo", ""},
|
||||
{"foo", "foo", ""},
|
||||
{",foo", "foo", ""},
|
||||
{"foo=bar,baz", "loooooooong", ""},
|
||||
{"", New(""), ""},
|
||||
{"", foo, ""},
|
||||
{"foo=bar", foo, "bar"},
|
||||
{"foo=bar,after=x", foo, "bar"},
|
||||
{"before=x,foo=bar,after=x", foo, "bar"},
|
||||
{"before=x,foo=bar", foo, "bar"},
|
||||
{",,,foo=bar,,,", foo, "bar"},
|
||||
{"foodecoy=wrong,foo=bar", foo, "bar"},
|
||||
{"foo=", foo, ""},
|
||||
{"foo", foo, ""},
|
||||
{",foo", foo, ""},
|
||||
{"foo=bar,baz", New("loooooooong"), ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := Xget(tt.godebug, tt.key)
|
||||
t.Setenv("GODEBUG", tt.godebug)
|
||||
got := tt.setting.Value()
|
||||
if got != tt.want {
|
||||
t.Errorf("get(%q, %q) = %q; want %q", tt.godebug, tt.key, got, tt.want)
|
||||
t.Errorf("get(%q, %q) = %q; want %q", tt.godebug, tt.setting.Name(), got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,10 +66,12 @@ var (
|
|||
valSafe = safeMap() // non-nil in safe+leaky mode
|
||||
)
|
||||
|
||||
var intern = godebug.New("intern")
|
||||
|
||||
// safeMap returns a non-nil map if we're in safe-but-leaky mode,
|
||||
// as controlled by GODEBUG=intern=leaky
|
||||
func safeMap() map[key]*Value {
|
||||
if godebug.Get("intern") == "leaky" {
|
||||
if intern.Value() == "leaky" {
|
||||
return map[key]*Value{}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -408,12 +408,14 @@ type lockedSource struct {
|
|||
//go:linkname fastrand64
|
||||
func fastrand64() uint64
|
||||
|
||||
var randautoseed = godebug.New("randautoseed")
|
||||
|
||||
// source returns r.s, allocating and seeding it if needed.
|
||||
// The caller must have locked r.
|
||||
func (r *lockedSource) source() *rngSource {
|
||||
if r.s == nil {
|
||||
var seed int64
|
||||
if godebug.Get("randautoseed") == "0" {
|
||||
if randautoseed.Value() == "0" {
|
||||
seed = 1
|
||||
} else {
|
||||
seed = int64(fastrand64())
|
||||
|
|
|
@ -301,6 +301,8 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde
|
|||
return fallbackOrder
|
||||
}
|
||||
|
||||
var netdns = godebug.New("netdns")
|
||||
|
||||
// goDebugNetDNS parses the value of the GODEBUG "netdns" value.
|
||||
// The netdns value can be of the form:
|
||||
//
|
||||
|
@ -314,7 +316,7 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde
|
|||
//
|
||||
// etc.
|
||||
func goDebugNetDNS() (dnsMode string, debugLevel int) {
|
||||
goDebug := godebug.Get("netdns")
|
||||
goDebug := netdns.Value()
|
||||
parsePart := func(s string) {
|
||||
if s == "" {
|
||||
return
|
||||
|
|
|
@ -3313,11 +3313,13 @@ func (srv *Server) onceSetNextProtoDefaults_Serve() {
|
|||
}
|
||||
}
|
||||
|
||||
var http2server = godebug.New("http2server")
|
||||
|
||||
// onceSetNextProtoDefaults configures HTTP/2, if the user hasn't
|
||||
// configured otherwise. (by setting srv.TLSNextProto non-nil)
|
||||
// It must only be called via srv.nextProtoOnce (use srv.setupHTTP2_*).
|
||||
func (srv *Server) onceSetNextProtoDefaults() {
|
||||
if omitBundledHTTP2 || godebug.Get("http2server") == "0" {
|
||||
if omitBundledHTTP2 || http2server.Value() == "0" {
|
||||
return
|
||||
}
|
||||
// Enable HTTP/2 by default if the user hasn't otherwise
|
||||
|
|
|
@ -362,11 +362,13 @@ func (t *Transport) hasCustomTLSDialer() bool {
|
|||
return t.DialTLS != nil || t.DialTLSContext != nil
|
||||
}
|
||||
|
||||
var http2client = godebug.New("http2client")
|
||||
|
||||
// onceSetNextProtoDefaults initializes TLSNextProto.
|
||||
// It must be called via t.nextProtoOnce.Do.
|
||||
func (t *Transport) onceSetNextProtoDefaults() {
|
||||
t.tlsNextProtoWasNil = (t.TLSNextProto == nil)
|
||||
if godebug.Get("http2client") == "0" {
|
||||
if http2client.Value() == "0" {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -348,6 +348,9 @@ type ctxResult struct {
|
|||
timer *time.Timer
|
||||
}
|
||||
|
||||
var execwait = godebug.New("execwait")
|
||||
var execerrdot = godebug.New("execerrdot")
|
||||
|
||||
// Command returns the Cmd struct to execute the named program with
|
||||
// the given arguments.
|
||||
//
|
||||
|
@ -376,8 +379,8 @@ func Command(name string, arg ...string) *Cmd {
|
|||
Args: append([]string{name}, arg...),
|
||||
}
|
||||
|
||||
if execwait := godebug.Get("execwait"); execwait != "" {
|
||||
if execwait == "2" {
|
||||
if v := execwait.Value(); v != "" {
|
||||
if v == "2" {
|
||||
// Obtain the caller stack. (This is equivalent to runtime/debug.Stack,
|
||||
// copied to avoid importing the whole package.)
|
||||
stack := make([]byte, 1024)
|
||||
|
|
|
@ -6,7 +6,6 @@ package exec
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"internal/godebug"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -54,7 +53,7 @@ func LookPath(file string) (string, error) {
|
|||
for _, dir := range filepath.SplitList(path) {
|
||||
path := filepath.Join(dir, file)
|
||||
if err := findExecutable(path); err == nil {
|
||||
if !filepath.IsAbs(path) && godebug.Get("execerrdot") != "0" {
|
||||
if !filepath.IsAbs(path) && execerrdot.Value() != "0" {
|
||||
return path, &Error{file, ErrDot}
|
||||
}
|
||||
return path, nil
|
||||
|
|
|
@ -8,7 +8,6 @@ package exec
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"internal/godebug"
|
||||
"internal/syscall/unix"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
@ -70,7 +69,7 @@ func LookPath(file string) (string, error) {
|
|||
}
|
||||
path := filepath.Join(dir, file)
|
||||
if err := findExecutable(path); err == nil {
|
||||
if !filepath.IsAbs(path) && godebug.Get("execerrdot") != "0" {
|
||||
if !filepath.IsAbs(path) && execerrdot.Value() != "0" {
|
||||
return path, &Error{file, ErrDot}
|
||||
}
|
||||
return path, nil
|
||||
|
|
|
@ -6,7 +6,6 @@ package exec
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"internal/godebug"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -103,7 +102,7 @@ func LookPath(file string) (string, error) {
|
|||
)
|
||||
if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found {
|
||||
if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
|
||||
if godebug.Get("execerrdot") == "0" {
|
||||
if execerrdot.Value() == "0" {
|
||||
return f, nil
|
||||
}
|
||||
dotf, dotErr = f, &Error{file, ErrDot}
|
||||
|
@ -128,7 +127,7 @@ func LookPath(file string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(f) && godebug.Get("execerrdot") != "0" {
|
||||
if !filepath.IsAbs(f) && execerrdot.Value() != "0" {
|
||||
return f, &Error{file, ErrDot}
|
||||
}
|
||||
return f, nil
|
||||
|
|
|
@ -66,14 +66,26 @@ func syscall_Exit(code int) {
|
|||
exit(int32(code))
|
||||
}
|
||||
|
||||
var godebugenv atomic.Pointer[string] // set by parsedebugvars
|
||||
var godebugDefault string
|
||||
var godebugUpdate atomic.Pointer[func(string, string)]
|
||||
var godebugEnv atomic.Pointer[string] // set by parsedebugvars
|
||||
|
||||
//go:linkname godebug_getGODEBUG internal/godebug.getGODEBUG
|
||||
func godebug_getGODEBUG() string {
|
||||
if p := godebugenv.Load(); p != nil {
|
||||
return *p
|
||||
//go:linkname godebug_setUpdate internal/godebug.setUpdate
|
||||
func godebug_setUpdate(update func(string, string)) {
|
||||
p := new(func(string, string))
|
||||
*p = update
|
||||
godebugUpdate.Store(p)
|
||||
godebugNotify()
|
||||
}
|
||||
|
||||
func godebugNotify() {
|
||||
if update := godebugUpdate.Load(); update != nil {
|
||||
var env string
|
||||
if p := godebugEnv.Load(); p != nil {
|
||||
env = *p
|
||||
}
|
||||
(*update)(godebugDefault, env)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//go:linkname syscall_runtimeSetenv syscall.runtimeSetenv
|
||||
|
@ -82,7 +94,8 @@ func syscall_runtimeSetenv(key, value string) {
|
|||
if key == "GODEBUG" {
|
||||
p := new(string)
|
||||
*p = value
|
||||
godebugenv.Store(p)
|
||||
godebugEnv.Store(p)
|
||||
godebugNotify()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +103,8 @@ func syscall_runtimeSetenv(key, value string) {
|
|||
func syscall_runtimeUnsetenv(key string) {
|
||||
unsetenv_c(key)
|
||||
if key == "GODEBUG" {
|
||||
godebugenv.Store(nil)
|
||||
godebugEnv.Store(nil)
|
||||
godebugNotify()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -375,7 +375,7 @@ func parsedebugvars() {
|
|||
}
|
||||
|
||||
globalGODEBUG = gogetenv("GODEBUG")
|
||||
godebugenv.StoreNoWB(&globalGODEBUG)
|
||||
godebugEnv.StoreNoWB(&globalGODEBUG)
|
||||
for p := globalGODEBUG; p != ""; {
|
||||
field := ""
|
||||
i := bytealg.IndexByteString(p, ',')
|
||||
|
|
Loading…
Reference in a new issue