1// Copyright 2023 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 5package load 6 7import ( 8 "errors" 9 "fmt" 10 "go/build" 11 "internal/godebugs" 12 "sort" 13 "strconv" 14 "strings" 15 16 "cmd/go/internal/gover" 17 "cmd/go/internal/modload" 18) 19 20var ErrNotGoDebug = errors.New("not //go:debug line") 21 22func ParseGoDebug(text string) (key, value string, err error) { 23 if !strings.HasPrefix(text, "//go:debug") { 24 return "", "", ErrNotGoDebug 25 } 26 i := strings.IndexAny(text, " \t") 27 if i < 0 { 28 if strings.TrimSpace(text) == "//go:debug" { 29 return "", "", fmt.Errorf("missing key=value") 30 } 31 return "", "", ErrNotGoDebug 32 } 33 k, v, ok := strings.Cut(strings.TrimSpace(text[i:]), "=") 34 if !ok { 35 return "", "", fmt.Errorf("missing key=value") 36 } 37 if err := modload.CheckGodebug("//go:debug setting", k, v); err != nil { 38 return "", "", err 39 } 40 return k, v, nil 41} 42 43// defaultGODEBUG returns the default GODEBUG setting for the main package p. 44// When building a test binary, directives, testDirectives, and xtestDirectives 45// list additional directives from the package under test. 46func defaultGODEBUG(p *Package, directives, testDirectives, xtestDirectives []build.Directive) string { 47 if p.Name != "main" { 48 return "" 49 } 50 goVersion := modload.MainModules.GoVersion() 51 if modload.RootMode == modload.NoRoot && p.Module != nil { 52 // This is go install pkg@version or go run pkg@version. 53 // Use the Go version from the package. 54 // If there isn't one, then assume Go 1.20, 55 // the last version before GODEBUGs were introduced. 56 goVersion = p.Module.GoVersion 57 if goVersion == "" { 58 goVersion = "1.20" 59 } 60 } 61 62 var m map[string]string 63 for _, g := range modload.MainModules.Godebugs() { 64 if m == nil { 65 m = make(map[string]string) 66 } 67 m[g.Key] = g.Value 68 } 69 for _, list := range [][]build.Directive{p.Internal.Build.Directives, directives, testDirectives, xtestDirectives} { 70 for _, d := range list { 71 k, v, err := ParseGoDebug(d.Text) 72 if err != nil { 73 continue 74 } 75 if m == nil { 76 m = make(map[string]string) 77 } 78 m[k] = v 79 } 80 } 81 if v, ok := m["default"]; ok { 82 delete(m, "default") 83 v = strings.TrimPrefix(v, "go") 84 if gover.IsValid(v) { 85 goVersion = v 86 } 87 } 88 89 defaults := godebugForGoVersion(goVersion) 90 if defaults != nil { 91 // Apply m on top of defaults. 92 for k, v := range m { 93 defaults[k] = v 94 } 95 m = defaults 96 } 97 98 var keys []string 99 for k := range m { 100 keys = append(keys, k) 101 } 102 sort.Strings(keys) 103 var b strings.Builder 104 for _, k := range keys { 105 if b.Len() > 0 { 106 b.WriteString(",") 107 } 108 b.WriteString(k) 109 b.WriteString("=") 110 b.WriteString(m[k]) 111 } 112 return b.String() 113} 114 115func godebugForGoVersion(v string) map[string]string { 116 if strings.Count(v, ".") >= 2 { 117 i := strings.Index(v, ".") 118 j := i + 1 + strings.Index(v[i+1:], ".") 119 v = v[:j] 120 } 121 122 if !strings.HasPrefix(v, "1.") { 123 return nil 124 } 125 n, err := strconv.Atoi(v[len("1."):]) 126 if err != nil { 127 return nil 128 } 129 130 def := make(map[string]string) 131 for _, info := range godebugs.All { 132 if n < info.Changed { 133 def[info.Name] = info.Old 134 } 135 } 136 return def 137} 138