Source file src/cmd/go/internal/load/godebug.go

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

View as plain text