[dev.simd] all: merge master (a33bbf1) into dev.simd

Merge List:

+ 2025-12-08 a33bbf1988 weak: fix weak pointer test to correctly iterate over weak pointers after GC
+ 2025-12-08 a88a96330f cmd/cgo: use doc link for cgo.Handle
+ 2025-12-08 276cc4d3db cmd/link: fix AIX builds after recent linker changes
+ 2025-12-08 f2d96272cb runtime/trace: update TestSubscribers to dump traces
+ 2025-12-08 4837bcc92c internal/trace: skip tests for alloc/free experiment by default
+ 2025-12-08 b5f6816cea cmd/link: generate DWARF for moduledata
+ 2025-12-08 44a39c9dac runtime: only run TestNotInGoMetricCallback when building with cgo
+ 2025-12-08 3a6a034cd6 runtime: disable TestNotInGoMetricCallback on FreeBSD + race
+ 2025-12-08 4122d3e9ea runtime: use atomic C types with atomic C functions
+ 2025-12-08 34397865b1 runtime: deflake TestProfBufWakeup
+ 2025-12-08 d4972f6295 runtime: mark getfp as nosplit
+ 2025-12-05 0d0d5c9a82 test/codegen: test negation with add/sub on riscv64
+ 2025-12-05 2e509e61ef cmd/go: convert some more tests to script tests
+ 2025-12-05 c270e71835 cmd/go/internal/vet: skip -fix on pkgs from vendor or non-main mod
+ 2025-12-05 745349712e runtime: don't count nGsyscallNoP for extra Ms in C
+ 2025-12-05 f3d572d96a cmd/go: fix race applying fixes in fix and vet -fix modes
+ 2025-12-05 76345533f7 runtime: expand Pinner documentation
+ 2025-12-05 b133524c0f cmd/go/testdata/script: skip vet_cache in short mode
+ 2025-12-05 96e142ba2b runtime: skip TestArenaCollision if we run out of hints
+ 2025-12-05 fe4952f116 runtime: relax threadsSlack in TestReadMetricsSched
+ 2025-12-05 8947f092a8 runtime: skip mayMoreStackMove in goroutine leak tests
+ 2025-12-05 44cb82449e runtime/race: set missing argument frame for ppc64x atomic And/Or wrappers
+ 2025-12-05 435e61c801 runtime: reject any goroutine leak test failure that failed to execute
+ 2025-12-05 54e5540014 runtime: print output in case of segfault in goroutine leak tests
+ 2025-12-05 9616c33295 runtime: don't specify GOEXPERIMENT=greenteagc in goroutine leak tests
+ 2025-12-05 2244bd7eeb crypto/subtle: add speculation barrier after DIT
+ 2025-12-05 f84f8d86be cmd/compile: fix mis-infer bounds in slice len/cap calculations
+ 2025-12-05 a70addd3b3 all: fix some comment issues
+ 2025-12-05 93b49f773d internal/runtime/maps: clarify probeSeq doc comment
+ 2025-12-04 91267f0a70 all: update vendored x/tools
+ 2025-12-04 a753a9ed54 cmd/internal/fuzztest: move fuzz tests out of cmd/go test suite
+ 2025-12-04 1681c3b67f crypto: use rand.IsDefaultReader instead of comparing to boring.RandReader
+ 2025-12-04 7b67b68a0d cmd/compile: use isUnsignedPowerOfTwo rather than isPowerOfTwo for unsigneds
+ 2025-12-03 2b62144069 all: REVERSE MERGE dev.simd (9ac524a) into master

Change-Id: Ia0cdf06cdde89b6a4db30ed15ed8e0bcbac6ae30
This commit is contained in:
Cherry Mui 2025-12-08 17:41:04 -05:00
commit c456ab7a30
108 changed files with 1453 additions and 436 deletions

View File

@ -425,8 +425,8 @@ and of course there is nothing stopping the C code from doing anything
it likes. However, programs that break these rules are likely to fail
in unexpected and unpredictable ways.
The runtime/cgo.Handle type can be used to safely pass Go values
between Go and C. See the runtime/cgo package documentation for details.
The type [runtime/cgo.Handle] can be used to safely pass Go values
between Go and C.
Note: the current implementation has a bug. While Go code is permitted
to write nil or a C pointer (but not a Go pointer) to C memory, the

View File

@ -2119,7 +2119,10 @@ func (ft *factsTable) detectSliceLenRelation(v *Value) {
if bound := ow.Args[0]; (bound.Op == OpSliceLen || bound.Op == OpStringLen) && bound.Args[0] == slice {
lenOffset = ow.Args[1]
} else if bound := ow.Args[1]; (bound.Op == OpSliceLen || bound.Op == OpStringLen) && bound.Args[0] == slice {
lenOffset = ow.Args[0]
// Do not infer K - slicelen, see issue #76709.
if ow.Op == OpAdd64 {
lenOffset = ow.Args[0]
}
}
if lenOffset == nil || lenOffset.Op != OpConst64 {
continue
@ -2885,9 +2888,9 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
xl := ft.limits[x.ID]
y := v.Args[1]
yl := ft.limits[y.ID]
if xl.umin == xl.umax && isPowerOfTwo(int64(xl.umin)) ||
if xl.umin == xl.umax && isUnsignedPowerOfTwo(xl.umin) ||
xl.min == xl.max && isPowerOfTwo(xl.min) ||
yl.umin == yl.umax && isPowerOfTwo(int64(yl.umin)) ||
yl.umin == yl.umax && isUnsignedPowerOfTwo(yl.umin) ||
yl.min == yl.max && isPowerOfTwo(yl.min) {
// 0,1 * a power of two is better done as a shift
break

View File

@ -11,7 +11,7 @@ require (
golang.org/x/sys v0.38.1-0.20251125153526-08e54827f670
golang.org/x/telemetry v0.0.0-20251128220624-abf20d0e57ec
golang.org/x/term v0.37.0
golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713
golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2
)
require (

View File

@ -22,7 +22,7 @@ golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.31.1-0.20251128220601-087616b6cde9 h1:IjQf87/qLz2y0SiCc0uY3DwajALXkAgP1Pxal0mmdrM=
golang.org/x/text v0.31.1-0.20251128220601-087616b6cde9/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713 h1:i4GzAuZW4RuKXltwKyLYAfk7E1TSKQBxRAI7XKfLjSk=
golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2 h1:2Qqv605Nus9iUp3ErvEU/q92Q3HAzeROztzl9pzAno8=
golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=

View File

@ -972,19 +972,6 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after building with old release")
}
func TestPackageMainTestCompilerFlags(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("."))
tg.tempFile("src/p1/p1.go", "package main\n")
tg.tempFile("src/p1/p1_test.go", "package main\nimport \"testing\"\nfunc Test(t *testing.T){}\n")
tg.run("test", "-c", "-n", "p1")
tg.grepBothNot(`([\\/]compile|gccgo).* (-p main|-fgo-pkgpath=main).*p1\.go`, "should not have run compile -p main p1.go")
tg.grepStderr(`([\\/]compile|gccgo).* (-p p1|-fgo-pkgpath=p1).*p1\.go`, "should have run compile -p p1 p1.go")
}
// Issue 4104.
func TestGoTestWithPackageListedMultipleTimes(t *testing.T) {
tooSlow(t, "links and runs a test")
@ -1070,43 +1057,6 @@ func TestGoListDeps(t *testing.T) {
}
}
func TestGoListTest(t *testing.T) {
skipIfGccgo(t, "gccgo does not have standard packages")
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOCACHE", tg.tempdir)
tg.run("list", "-test", "-deps", "bytes")
tg.grepStdout(`^bytes.test$`, "missing test main")
tg.grepStdout(`^bytes$`, "missing real bytes")
tg.grepStdout(`^bytes \[bytes.test\]$`, "missing test copy of bytes")
tg.grepStdout(`^testing \[bytes.test\]$`, "missing test copy of testing")
tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
tg.run("list", "-test", "bytes")
tg.grepStdout(`^bytes.test$`, "missing test main")
tg.grepStdout(`^bytes$`, "missing real bytes")
tg.grepStdout(`^bytes \[bytes.test\]$`, "unexpected test copy of bytes")
tg.grepStdoutNot(`^testing \[bytes.test\]$`, "unexpected test copy of testing")
tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
tg.run("list", "-test", "cmd/buildid", "cmd/gofmt")
tg.grepStdout(`^cmd/buildid$`, "missing cmd/buildid")
tg.grepStdout(`^cmd/gofmt$`, "missing cmd/gofmt")
tg.grepStdout(`^cmd/gofmt\.test$`, "missing cmd/gofmt test")
tg.grepStdoutNot(`^cmd/buildid\.test$`, "unexpected cmd/buildid test")
tg.grepStdoutNot(`^testing`, "unexpected testing")
tg.run("list", "-test", "runtime/cgo")
tg.grepStdout(`^runtime/cgo$`, "missing runtime/cgo")
tg.run("list", "-deps", "-f", "{{if .DepOnly}}{{.ImportPath}}{{end}}", "sort")
tg.grepStdout(`^internal/reflectlite$`, "missing internal/reflectlite")
tg.grepStdoutNot(`^sort`, "unexpected sort")
}
func TestGoListCompiledCgo(t *testing.T) {
tooSlow(t, "compiles cgo files")
@ -1528,40 +1478,6 @@ func main() {}
tg.run("run", tg.path("bar.go"))
}
func TestListTemplateContextFunction(t *testing.T) {
t.Parallel()
for _, tt := range []struct {
v string
want string
}{
{"GOARCH", runtime.GOARCH},
{"GOOS", runtime.GOOS},
{"GOROOT", testGOROOT},
{"GOPATH", os.Getenv("GOPATH")},
{"CgoEnabled", ""},
{"UseAllFiles", ""},
{"Compiler", ""},
{"BuildTags", ""},
{"ReleaseTags", ""},
{"InstallSuffix", ""},
} {
tt := tt
t.Run(tt.v, func(t *testing.T) {
tg := testgo(t)
tg.parallel()
defer tg.cleanup()
tmpl := "{{context." + tt.v + "}}"
tg.run("list", "-f", tmpl)
if tt.want == "" {
return
}
if got := strings.TrimSpace(tg.getStdout()); got != tt.want {
t.Errorf("go list -f %q: got %q; want %q", tmpl, got, tt.want)
}
})
}
}
// Test that you cannot use a local import in a package
// accessed by a non-local import (found in a GOPATH/GOROOT).
// See golang.org/issue/17475.
@ -2247,23 +2163,6 @@ func TestCacheCoverage(t *testing.T) {
tg.run("test", "-cover", "-short", "math", "strings")
}
func TestIssue22588(t *testing.T) {
// Don't get confused by stderr coming from tools.
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.wantNotStale("runtime", "", "must be non-stale to compare staleness under -toolexec")
if _, err := os.Stat("/usr/bin/time"); err != nil {
t.Skip(err)
}
tg.run("list", "-f={{.Stale}}", "runtime")
tg.run("list", "-toolexec=/usr/bin/time", "-f={{.Stale}}", "runtime")
tg.grepStdout("false", "incorrectly reported runtime as stale")
}
func TestIssue22531(t *testing.T) {
tooSlow(t, "links binaries")
if gocacheverify.Value() == "1" {

View File

@ -736,7 +736,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
// Otherwise, if fuzzing identifies a failure it could corrupt checksums in
// the module cache (or permanently alter the behavior of std tests for all
// users) by writing the failing input to the package's testdata directory.
// (See https://golang.org/issue/48495 and test_fuzz_modcache.txt.)
// (See https://golang.org/issue/48495 and cmd/internal/fuzztest/test_fuzz_modcache.txt.)
mainMods := moduleLoaderState.MainModules
if m := pkgs[0].Module; m != nil && m.Path != "" {
if !mainMods.Contains(m.Path) {
@ -1371,7 +1371,7 @@ func addTestVet(loaderstate *modload.State, b *work.Builder, p *load.Package, ru
return
}
vet := b.VetAction(loaderstate, work.ModeBuild, work.ModeBuild, p)
vet := b.VetAction(loaderstate, work.ModeBuild, work.ModeBuild, false, p)
runAction.Deps = append(runAction.Deps, vet)
// Install will clean the build directory.
// Make sure vet runs first.

View File

@ -6,6 +6,8 @@
package vet
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"errors"
@ -176,6 +178,7 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
work.VetExplicit = len(toolFlags) > 0
applyFixes := false
if cmd.Name() == "fix" || *vetFixFlag {
// fix mode: 'go fix' or 'go vet -fix'
if jsonFlag {
@ -186,6 +189,8 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
toolFlags = append(toolFlags, "-fix")
if diffFlag {
toolFlags = append(toolFlags, "-diff")
} else {
applyFixes = true
}
}
if contextFlag != -1 {
@ -226,6 +231,7 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("no packages to %s", cmd.Name())
}
// Build action graph.
b := work.NewBuilder("", moduleLoaderState.VendorDirOrEmpty)
defer func() {
if err := b.Close(); err != nil {
@ -233,6 +239,13 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
}
}()
root := &work.Action{Mode: "go " + cmd.Name()}
addVetAction := func(p *load.Package) {
act := b.VetAction(moduleLoaderState, work.ModeBuild, work.ModeBuild, applyFixes, p)
root.Deps = append(root.Deps, act)
}
// To avoid file corruption from duplicate application of
// fixes (in fix mode), and duplicate reporting of diagnostics
// (in vet mode), we must run the tool only once for each
@ -248,9 +261,16 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
// We needn't worry about intermediate test variants, as they
// will only be executed in VetxOnly mode, for facts but not
// diagnostics.
root := &work.Action{Mode: "go " + cmd.Name()}
for _, p := range pkgs {
// Don't apply fixes to vendored packages, including
// the GOROOT vendor packages that are part of std,
// or to packages from non-main modules (#76479).
if applyFixes {
if p.Standard && strings.HasPrefix(p.ImportPath, "vendor/") ||
p.Module != nil && !p.Module.Main {
continue
}
}
_, ptest, pxtest, perr := load.TestPackagesFor(moduleLoaderState, ctx, pkgOpts, p, nil)
if perr != nil {
base.Errorf("%v", perr.Error)
@ -262,13 +282,65 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
}
if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 {
// The test package includes all the files of primary package.
root.Deps = append(root.Deps, b.VetAction(moduleLoaderState, work.ModeBuild, work.ModeBuild, ptest))
addVetAction(ptest)
}
if pxtest != nil {
root.Deps = append(root.Deps, b.VetAction(moduleLoaderState, work.ModeBuild, work.ModeBuild, pxtest))
addVetAction(pxtest)
}
}
b.Do(ctx, root)
// Apply fixes.
//
// We do this as a separate phase after the build to avoid
// races between source file updates and reads of those same
// files by concurrent actions of the ongoing build.
//
// If a file is fixed by multiple actions, they must be consistent.
if applyFixes {
contents := make(map[string][]byte)
// Gather the fixes.
for _, act := range root.Deps {
if act.FixArchive != "" {
if err := readZip(act.FixArchive, contents); err != nil {
base.Errorf("reading archive of fixes: %v", err)
return
}
}
}
// Apply them.
for filename, content := range contents {
if err := os.WriteFile(filename, content, 0644); err != nil {
base.Errorf("applying fix: %v", err)
}
}
}
}
// readZip reads the zipfile entries into the provided map.
// It reports an error if updating the map would change an existing entry.
func readZip(zipfile string, out map[string][]byte) error {
r, err := zip.OpenReader(zipfile)
if err != nil {
return err
}
defer r.Close() // ignore error
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
content, err := io.ReadAll(rc)
rc.Close() // ignore error
if err != nil {
return err
}
if prev, ok := out[f.Name]; ok && !bytes.Equal(prev, content) {
return fmt.Errorf("inconsistent fixes to file %v", f.Name)
}
out[f.Name] = content
}
return nil
}
// printJSONDiagnostics parses JSON (from the tool's stdout) and

View File

@ -109,11 +109,13 @@ type Action struct {
actionID cache.ActionID // cache ID of action input
buildID string // build ID of action output
VetxOnly bool // Mode=="vet": only being called to supply info about dependencies
needVet bool // Mode=="build": need to fill in vet config
needBuild bool // Mode=="build": need to do actual build (can be false if needVet is true)
vetCfg *vetConfig // vet config
output []byte // output redirect buffer (nil means use b.Print)
VetxOnly bool // Mode=="vet": only being called to supply info about dependencies
needVet bool // Mode=="build": need to fill in vet config
needBuild bool // Mode=="build": need to do actual build (can be false if needVet is true)
needFix bool // Mode=="vet": need secondary target, a .zip file containing fixes
vetCfg *vetConfig // vet config
FixArchive string // the created .zip file containing fixes (if needFix)
output []byte // output redirect buffer (nil means use b.Print)
sh *Shell // lazily created per-Action shell; see Builder.Shell
@ -869,9 +871,10 @@ func (b *Builder) cgoAction(p *load.Package, objdir string, deps []*Action, hasC
// It depends on the action for compiling p.
// If the caller may be causing p to be installed, it is up to the caller
// to make sure that the install depends on (runs after) vet.
func (b *Builder) VetAction(s *modload.State, mode, depMode BuildMode, p *load.Package) *Action {
func (b *Builder) VetAction(s *modload.State, mode, depMode BuildMode, needFix bool, p *load.Package) *Action {
a := b.vetAction(s, mode, depMode, p)
a.VetxOnly = false
a.needFix = needFix
return a
}

View File

@ -1187,6 +1187,7 @@ type vetConfig struct {
VetxOutput string // write vetx data to this output file
Stdout string // write stdout (JSON, unified diff) to this output file
GoVersion string // Go version for package
FixArchive string // write fixed files to this zip archive, if non-empty
SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
}
@ -1308,6 +1309,9 @@ func (b *Builder) vet(ctx context.Context, a *Action) error {
vcfg.VetxOnly = a.VetxOnly
vcfg.VetxOutput = a.Objdir + "vet.out"
vcfg.Stdout = a.Objdir + "vet.stdout"
if a.needFix {
vcfg.FixArchive = a.Objdir + "vet.fix.zip"
}
vcfg.PackageVetx = make(map[string]string)
h := cache.NewHash("vet " + a.Package.ImportPath)
@ -1368,31 +1372,60 @@ func (b *Builder) vet(ctx context.Context, a *Action) error {
vcfg.PackageVetx[a1.Package.ImportPath] = a1.built
}
}
vetxKey := cache.ActionID(h.Sum()) // for .vetx file
fmt.Fprintf(h, "stdout\n")
stdoutKey := cache.ActionID(h.Sum()) // for .stdout file
var (
id = cache.ActionID(h.Sum()) // for .vetx file
stdoutKey = cache.Subkey(id, "stdout") // for .stdout file
fixArchiveKey = cache.Subkey(id, "fix.zip") // for .fix.zip file
)
// Check the cache; -a forces a rebuild.
if !cfg.BuildA {
c := cache.Default()
if vcfg.VetxOnly {
if file, _, err := cache.GetFile(c, vetxKey); err == nil {
a.built = file
return nil
}
} else {
// Copy cached vet.std files to stdout.
if file, _, err := cache.GetFile(c, stdoutKey); err == nil {
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close() // ignore error (can't fail)
return VetHandleStdout(f)
}
// There may be multiple artifacts in the cache.
// We need to retrieve them all, or none:
// the effect must be transactional.
var (
vetxFile string // name of cached .vetx file
fixArchive string // name of cached .fix.zip file
stdout io.Reader = bytes.NewReader(nil) // cached stdout stream
)
// Obtain location of cached .vetx file.
vetxFile, _, err := cache.GetFile(c, id)
if err != nil {
goto cachemiss
}
// Obtain location of cached .fix.zip file (if needed).
if a.needFix {
file, _, err := cache.GetFile(c, fixArchiveKey)
if err != nil {
goto cachemiss
}
fixArchive = file
}
// Copy cached .stdout file to stdout.
if file, _, err := cache.GetFile(c, stdoutKey); err == nil {
f, err := os.Open(file)
if err != nil {
goto cachemiss
}
defer f.Close() // ignore error (can't fail)
stdout = f
}
// Cache hit: commit transaction.
a.built = vetxFile
a.FixArchive = fixArchive
if err := VetHandleStdout(stdout); err != nil {
return err // internal error (don't fall through to cachemiss)
}
return nil
}
cachemiss:
js, err := json.MarshalIndent(vcfg, "", "\t")
if err != nil {
@ -1419,13 +1452,23 @@ func (b *Builder) vet(ctx context.Context, a *Action) error {
return err
}
// Vet tool succeeded, possibly with facts and JSON stdout. Save both in cache.
// Vet tool succeeded, possibly with facts, fixes, or JSON stdout.
// Save all in cache.
// Save facts
// Save facts.
if f, err := os.Open(vcfg.VetxOutput); err == nil {
defer f.Close() // ignore error
a.built = vcfg.VetxOutput
cache.Default().Put(vetxKey, f) // ignore error
cache.Default().Put(id, f) // ignore error
}
// Save fix archive (if any).
if a.needFix {
if f, err := os.Open(vcfg.FixArchive); err == nil {
defer f.Close() // ignore error
a.FixArchive = vcfg.FixArchive
cache.Default().Put(fixArchiveKey, f) // ignore error
}
}
// Save stdout.

View File

@ -0,0 +1,44 @@
# Test that go fix skips fixes to non-main and/or vendored packages.
# (It uses the interface{} -> any modernizer.)
# Create vendor tree programmatically to avoid
# having to hardcode sums in this txtar archive.
go mod vendor
# Show fixes on two packages, one in the main module
# and one in a vendored dependency.
# Only the main one (a) is shown.
go fix -diff example.com/a example.com/b
stdout 'a[/\\]a.go'
stdout '\-var _ interface\{\}'
stdout '\+var _ any'
! stdout 'b[/\\]b.go'
# Apply fixes to the same two packages.
# Only the main module was modified.
go fix example.com/a example.com/b
grep 'var _ any' a/a.go
grep 'var _ interface{}' b/b.go
grep 'var _ interface{}' vendor/example.com/b/b.go
-- go.mod --
module example.com
go 1.26
require "example.com/b" v0.0.0
replace "example.com/b" => ./b
-- a/a.go --
package a
import _ "example.com/b"
var _ interface{}
-- b/go.mod --
module example.com/b
-- b/b.go --
package b
var _ interface{}

View File

@ -0,0 +1,15 @@
# This is a script test conversion of TestListTemplateContextFunction
# originally added in CL 20010, which fixed #14547.
# Test the ability to use the build context in the go list template.
go list -f '{{context.GOARCH}} {{context.GOOS}} {{context.GOROOT}} {{context.GOPATH}}'
cmpenv stdout want.txt
go list -f '{{context.CgoEnabled}} {{context.UseAllFiles}} {{context.Compiler}} {{context.BuildTags}} {{context.ReleaseTags}} {{context.InstallSuffix}}'
-- go.mod --
module foo
-- foo.go --
package foo
-- want.txt --
$GOARCH $GOOS $GOROOT $GOPATH

View File

@ -0,0 +1,33 @@
# This is a script test conversion of TestGoListTest which was added in
# CL 107916, which added support for go list -test.
# Test the behavior of go list -test.
[compiler:gccgo] skip 'gccgo does not have standard packages'
go list -test -deps bytes
stdout '^bytes.test$' # test main
stdout '^bytes$' # real bytes
stdout '^bytes \[bytes.test\]$' # test copy of bytes
stdout 'testing \[bytes.test\]$' # test copy of testing
! stdout ^testing$ # should not have real testing
go list -test bytes
stdout '^bytes.test$' # test main
stdout '^bytes$' # real bytes
stdout '^bytes \[bytes.test\]$' # test copy of bytes
! stdout '^testing \[bytes.test\]$' # should not have test copy of testing
! stdout '^testing$' # should not have real testing
go list -test cmd/buildid cmd/gofmt
stdout '^cmd/buildid$' # cmd/buildid
stdout '^cmd/gofmt$' # cmd/gofmt
stdout '^cmd/gofmt\.test$' # cmd/gofmt test
! stdout '^cmd/buildid\.test$' # should not have cmd/buildid test
! stdout '^testing' # should not have real testing
go list -test runtime/cgo
stdout '^runtime/cgo$' # runtime/cgo
go list -deps -f '{{if .DepOnly}}{{.ImportPath}}{{end}}' sort
stdout '^internal/reflectlite$' # internal/reflectlite
! stdout '^sort' # should not have sort

View File

@ -0,0 +1,11 @@
# This is a script test conversion of TestIssue22588 which was added in CL 76017.
# Test that the stderr of a tool run under toolexec doesn't affect caching.
# Don't get confused by stderr coming from tools.
[!exec:/usr/bin/time] skip
! stale runtime 'must be non-stale to compare staleness under -toolexec'
go list -f '{{.Stale}}' runtime
go list -toolexec /usr/bin/time -f '{{.Stale}}' runtime
stdout 'false' # runtime should not be reported as stale

View File

@ -0,0 +1,22 @@
# This is a script test conversion of TestPackageMainTestCompilerFlags
# originally added in CL 86265, which fixed #23180.
# Test that we don't pass the package name 'main' to -p when building the
# test package for a main package.
go test -c -n p1
# should not have run compile -p main p1.go
! stdout '([\\/]compile|gccgo).* (-p main|-fgo-pkgpath=main).*p1\.go'
! stderr '([\\/]compile|gccgo).* (-p main|-fgo-pkgpath=main).*p1\.go'
# should have run compile -p p1 p1.go
stderr '([\\/]compile|gccgo).* (-p p1|-fgo-pkgpath=p1).*p1\.go'
-- go.mod --
module p1
-- p1.go --
package main
-- p1_test.go --
package main
import "testing"
func Test(t *testing.T){}

View File

@ -91,6 +91,7 @@ stderr '-json and -diff cannot be used together'
# Legacy way of selecting fixers is a no-op.
go fix -fix=old1,old2 example.com/x
stderr 'go fix: the -fix=old1,old2 flag is obsolete and has no effect'
cp x.go.bak x.go
# -c=n flag shows n lines of context.
! go vet -c=2 -printf example.com/x

View File

@ -1,6 +1,8 @@
# Test that go vet's caching of vet tool actions replays
# the recorded stderr output even after a cache hit.
[short] skip 'uses a fresh build cache'
# Set up fresh GOCACHE.
env GOCACHE=$WORK/gocache

View File

@ -0,0 +1,22 @@
// Copyright 2025 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 fuzztest
import (
"cmd/internal/script/scripttest"
"flag"
"internal/testenv"
"testing"
)
//go:generate go test cmd/internal/fuzztest -v -run=TestScript/README --fixreadme
var fixReadme = flag.Bool("fixreadme", false, "if true, update README for script tests")
func TestScript(t *testing.T) {
testenv.MustHaveGoBuild(t)
testenv.SkipIfShortAndSlow(t)
scripttest.RunToolScriptTest(t, nil, "testdata/script", *fixReadme)
}

View File

@ -0,0 +1,286 @@
This file is generated by 'go generate'. DO NOT EDIT.
This directory holds test scripts *.txt run during 'go test cmd/<toolname>'.
To run a specific script foo.txt
go test cmd/<toolname> -run=Script/^foo$
In general script files should have short names: a few words,
not whole sentences.
The first word should be the general category of behavior being tested,
often the name of a go subcommand (build, link, compile, ...) or concept (vendor, pattern).
Each script is a text archive (go doc internal/txtar).
The script begins with an actual command script to run
followed by the content of zero or more supporting files to
create in the script's temporary file system before it starts executing.
As an example, run_hello.txt says:
# hello world
go run hello.go
stderr 'hello world'
! stdout .
-- hello.go --
package main
func main() { println("hello world") }
Each script runs in a fresh temporary work directory tree, available to scripts as $WORK.
Scripts also have access to other environment variables, including:
GOARCH=<target GOARCH>
GOOS=<target GOOS>
TMPDIR=$WORK/tmp
devnull=<value of os.DevNull>
goversion=<current Go version; for example, 1.12>
On Plan 9, the variables $path and $home are set instead of $PATH and $HOME.
On Windows, the variables $USERPROFILE and $TMP are set instead of
$HOME and $TMPDIR.
The lines at the top of the script are a sequence of commands to be executed by
a small script engine configured in .../cmd/internal/script/scripttest/run.go (not the system shell).
Each line of a script is parsed into a sequence of space-separated command
words, with environment variable expansion within each word and # marking
an end-of-line comment. Additional variables named ':' and '/' are expanded
within script arguments (expanding to the value of os.PathListSeparator and
os.PathSeparator respectively) but are not inherited in subprocess environments.
Adding single quotes around text keeps spaces in that text from being treated
as word separators and also disables environment variable expansion. Inside a
single-quoted block of text, a repeated single quote indicates a literal single
quote, as in:
'Don''t communicate by sharing memory.'
A line beginning with # is a comment and conventionally explains what is being
done or tested at the start of a new section of the script.
Commands are executed one at a time, and errors are checked for each command;
if any command fails unexpectedly, no subsequent commands in the script are
executed. The command prefix ! indicates that the command on the rest of the
line (typically go or a matching predicate) must fail instead of succeeding.
The command prefix ? indicates that the command may or may not succeed, but the
script should continue regardless.
The command prefix [cond] indicates that the command on the rest of the line
should only run when the condition is satisfied.
A condition can be negated: [!root] means to run the rest of the line only if
the user is not root. Multiple conditions may be given for a single command,
for example, '[linux] [amd64] skip'. The command will run if all conditions are
satisfied.
When TestScript runs a script and the script fails, by default TestScript shows
the execution of the most recent phase of the script (since the last # comment)
and only shows the # comments for earlier phases.
Note also that in reported output, the actual name of the per-script temporary directory
has been consistently replaced with the literal string $WORK.
The available commands are:
cat files...
concatenate files and print to the script's stdout buffer
cc args...
run the platform C compiler
cd dir
change the working directory
chmod perm paths...
change file mode bits
Changes the permissions of the named files or directories to
be equal to perm.
Only numerical permissions are supported.
cmp [-q] file1 file2
compare files for differences
By convention, file1 is the actual data and file2 is the
expected data.
The command succeeds if the file contents are identical.
File1 can be 'stdout' or 'stderr' to compare the stdout or
stderr buffer from the most recent command.
cmpenv [-q] file1 file2
compare files for differences, with environment expansion
By convention, file1 is the actual data and file2 is the
expected data.
The command succeeds if the file contents are identical
after substituting variables from the script environment.
File1 can be 'stdout' or 'stderr' to compare the script's
stdout or stderr buffer.
cp src... dst
copy files to a target file or directory
src can include 'stdout' or 'stderr' to copy from the
script's stdout or stderr buffer.
echo string...
display a line of text
env [key[=value]...]
set or log the values of environment variables
With no arguments, print the script environment to the log.
Otherwise, add the listed key=value pairs to the environment
or print the listed keys.
exec program [args...] [&]
run an executable program with arguments
Note that 'exec' does not terminate the script (unlike Unix
shells).
exists [-readonly] [-exec] file...
check that files exist
go [args...] [&]
run the 'go' program provided by the script host
grep [-count=N] [-q] 'pattern' file
find lines in a file that match a pattern
The command succeeds if at least one match (or the exact
count, if given) is found.
The -q flag suppresses printing of matches.
help [-v] name...
log help text for commands and conditions
To display help for a specific condition, enclose it in
brackets: 'help [amd64]'.
To display complete documentation when listing all commands,
pass the -v flag.
mkdir path...
create directories, if they do not already exist
Unlike Unix mkdir, parent directories are always created if
needed.
mv old new
rename a file or directory to a new path
OS-specific restrictions may apply when old and new are in
different directories.
replace [old new]... file
replace strings in a file
The 'old' and 'new' arguments are unquoted as if in quoted
Go strings.
rm path...
remove a file or directory
If the path is a directory, its contents are removed
recursively.
skip [msg]
skip the current test
sleep duration [&]
sleep for a specified duration
The duration must be given as a Go time.Duration string.
stderr [-count=N] [-q] 'pattern' file
find lines in the stderr buffer that match a pattern
The command succeeds if at least one match (or the exact
count, if given) is found.
The -q flag suppresses printing of matches.
stdout [-count=N] [-q] 'pattern' file
find lines in the stdout buffer that match a pattern
The command succeeds if at least one match (or the exact
count, if given) is found.
The -q flag suppresses printing of matches.
stop [msg]
stop execution of the script
The message is written to the script log, but no error is
reported from the script engine.
symlink path -> target
create a symlink
Creates path as a symlink to target.
The '->' token (like in 'ls -l' output on Unix) is required.
wait
wait for completion of background commands
Waits for all background commands to complete.
The output (and any error) from each command is printed to
the log in the order in which the commands were started.
After the call to 'wait', the script's stdout and stderr
buffers contain the concatenation of the background
commands' outputs.
The available conditions are:
[GOARCH:*]
runtime.GOARCH == <suffix>
[GODEBUG:*]
GODEBUG contains <suffix>
[GOEXPERIMENT:*]
GOEXPERIMENT <suffix> is enabled
[GOOS:*]
runtime.GOOS == <suffix>
[asan]
GOOS/GOARCH supports -asan
[buildmode:*]
go supports -buildmode=<suffix>
[cgo]
host CGO_ENABLED
[cgolinkext]
platform requires external linking for cgo
[compiler:*]
runtime.Compiler == <suffix>
[cross]
cmd/go GOOS/GOARCH != GOHOSTOS/GOHOSTARCH
[exec:*]
<suffix> names an executable in the test binary's PATH
[fuzz]
GOOS/GOARCH supports -fuzz
[fuzz-instrumented]
GOOS/GOARCH supports -fuzz with instrumentation
[go-builder]
GO_BUILDER_NAME is non-empty
[link]
testenv.HasLink()
[msan]
GOOS/GOARCH supports -msan
[mustlinkext]
platform always requires external linking
[pielinkext]
platform requires external linking for PIE
[race]
GOOS/GOARCH supports -race
[root]
os.Geteuid() == 0
[short]
testing.Short()
[symlink]
testenv.HasSymlink()
[verbose]
testing.Verbose()

View File

@ -137,6 +137,9 @@ func RunToolScriptTest(t *testing.T, repls []ToolReplacement, scriptsdir string,
env := os.Environ()
prependToPath(env, filepath.Join(tgr, "bin"))
env = setenv(env, "GOROOT", tgr)
// GOOS and GOARCH are expected to be set by the toolchain script conditions.
env = setenv(env, "GOOS", runtime.GOOS)
env = setenv(env, "GOARCH", runtime.GOARCH)
for _, repl := range repls {
// consistency check
chunks := strings.Split(repl.EnvVar, "=")

View File

@ -156,10 +156,102 @@ func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string)
if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
}
if buildmode != "c-archive" {
testModuledata(t, d)
}
})
}
}
// testModuledata makes sure that runtime.firstmoduledata exists
// and has a type. Issue #76731.
func testModuledata(t *testing.T, d *dwarf.Data) {
const symName = "runtime.firstmoduledata"
r := d.Reader()
for {
e, err := r.Next()
if err != nil {
t.Error(err)
return
}
if e == nil {
t.Errorf("did not find DWARF entry for %s", symName)
return
}
switch e.Tag {
case dwarf.TagVariable:
// carry on after switch
case dwarf.TagCompileUnit, dwarf.TagSubprogram:
continue
default:
r.SkipChildren()
continue
}
nameIdx, typeIdx := -1, -1
for i := range e.Field {
f := &e.Field[i]
switch f.Attr {
case dwarf.AttrName:
nameIdx = i
case dwarf.AttrType:
typeIdx = i
}
}
if nameIdx == -1 {
// unnamed variable?
r.SkipChildren()
continue
}
nameStr, ok := e.Field[nameIdx].Val.(string)
if !ok {
// variable name is not a string?
r.SkipChildren()
continue
}
if nameStr != symName {
r.SkipChildren()
continue
}
if typeIdx == -1 {
t.Errorf("%s has no DWARF type", symName)
return
}
off, ok := e.Field[typeIdx].Val.(dwarf.Offset)
if !ok {
t.Errorf("unexpected Go type %T for DWARF type for %s; expected %T", e.Field[typeIdx].Val, symName, dwarf.Offset(0))
return
}
typeInfo, err := d.Type(off)
if err != nil {
t.Error(err)
return
}
typeName := typeInfo.Common().Name
if want := "runtime.moduledata"; typeName != want {
t.Errorf("type of %s is %s, expected %s", symName, typeName, want)
}
for {
typedef, ok := typeInfo.(*dwarf.TypedefType)
if !ok {
break
}
typeInfo = typedef.Type
}
if _, ok := typeInfo.(*dwarf.StructType); !ok {
t.Errorf("type of %s is %T, expected %T", symName, typeInfo, dwarf.StructType{})
}
return
}
}
func TestDWARF(t *testing.T) {
testDWARF(t, "", true)
if !testing.Short() {

View File

@ -2036,7 +2036,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
t := d.ldr.SymType(idx)
switch {
case t.IsRODATA(), t.IsDATA(), t.IsNOPTRDATA(),
t == sym.STYPE, t == sym.SBSS, t == sym.SNOPTRBSS, t == sym.STLSBSS:
t == sym.STYPE, t == sym.SBSS, t == sym.SNOPTRBSS, t == sym.STLSBSS, t == sym.SMODULEDATA:
// ok
default:
continue

View File

@ -55,13 +55,20 @@ type pclntab struct {
// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
// It is the caller's responsibility to save the symbol in state.
func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym {
func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, align int32, f generatorFunc) loader.Sym {
size = Rnd(size, int64(ctxt.Arch.PtrSize))
state.size += size
s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f)
ctxt.loader.SetAttrReachable(s, true)
ctxt.loader.SetCarrierSym(s, state.carrier)
ctxt.loader.SetAttrNotInSymbolTable(s, true)
ldr := ctxt.loader
ldr.SetSymAlign(s, align)
ldr.SetAttrReachable(s, true)
ldr.SetCarrierSym(s, state.carrier)
ldr.SetAttrNotInSymbolTable(s, true)
if align > ldr.SymAlign(state.carrier) {
ldr.SetSymAlign(state.carrier, align)
}
return s
}
@ -277,7 +284,7 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
}
}
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, int32(ctxt.Arch.PtrSize), writeHeader)
}
// walkFuncs iterates over the funcs, calling a function for each unique
@ -326,7 +333,7 @@ func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[lo
size += int64(len(ctxt.loader.SymName(s)) + 1) // NULL terminate
})
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, 1, writeFuncNameTab)
return nameOffsets
}
@ -442,7 +449,7 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
}
}
}
state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), 4, writeCutab)
// Write filetab.
writeFiletab := func(ctxt *Link, s loader.Sym) {
@ -454,7 +461,7 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
}
}
state.nfiles = uint32(len(fileOffsets))
state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, 1, writeFiletab)
return cuOffsets
}
@ -518,7 +525,7 @@ func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
}
}
state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, 1, writePctab)
}
// generateFuncdata writes out the funcdata information.
@ -647,7 +654,7 @@ func (state *pclntab) generateFuncdata(ctxt *Link, funcs []loader.Sym, inlsyms m
}
}
state.funcdata = state.addGeneratedSym(ctxt, "go:func.*", size, writeFuncData)
state.funcdata = state.addGeneratedSym(ctxt, "go:func.*", size, maxAlign, writeFuncData)
// Because the funcdata previously was not in pclntab,
// we need to keep the visible symbol so that tools can find it.
@ -703,7 +710,7 @@ func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms ma
writePCToFunc(ctxt, sb, funcs, startLocations)
writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
}
state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, 4, writePcln)
}
// funcData returns the funcdata and offsets for the FuncInfo.
@ -967,6 +974,10 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
ldr.SetAttrReachable(state.carrier, true)
setCarrierSym(sym.SPCLNTAB, state.carrier)
// Aign pclntab to at least a pointer boundary,
// for pcHeader. This may be raised further by subsymbols.
ldr.SetSymAlign(state.carrier, int32(ctxt.Arch.PtrSize))
state.generatePCHeader(ctxt)
nameOffsets := state.generateFuncnametab(ctxt, funcs)
cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
@ -1076,6 +1087,7 @@ func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
}
state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SPCLNTAB, size, writeFindFuncTab)
ldr.SetSymAlign(state.findfunctab, 4)
ldr.SetAttrReachable(state.findfunctab, true)
ldr.SetAttrLocal(state.findfunctab, true)
}

View File

@ -672,6 +672,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
addRef("runtime.rodata")
addRef("runtime.erodata")
addRef("runtime.epclntab")
addRef("go:func.*")
// As we use relative addressing for text symbols in functab, it is
// important that the offsets we computed stay unchanged by the external
// linker, i.e. all symbols in Textp should not be removed.

View File

@ -603,14 +603,20 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
outerSymSize["go:string.*"] = size
case sym.SGOFUNC:
if !ctxt.DynlinkingGo() {
outerSymSize["go:func.*"] = size
outerSymSize["go:funcdesc"] = size
}
case sym.SGOFUNCRELRO:
outerSymSize["go:funcrel.*"] = size
outerSymSize["go:funcdescrel"] = size
case sym.SGCBITS:
outerSymSize["runtime.gcbits.*"] = size
case sym.SPCLNTAB:
outerSymSize["runtime.pclntab"] = size
// go:func.* size must be removed from pclntab,
// as it's a real symbol. Same for runtime.findfunctab.
fsize := ldr.SymSize(ldr.Lookup("go:func.*", 0))
fft := ldr.Lookup("runtime.findfunctab", 0)
fsize = Rnd(fsize, int64(symalign(ldr, fft)))
tsize := ldr.SymSize(fft)
outerSymSize["runtime.pclntab"] = size - (fsize + tsize)
}
}

View File

@ -1960,33 +1960,55 @@ func TestFuncdataPlacement(t *testing.T) {
case xf != nil:
defer xf.Close()
var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol
for _, sym := range xf.Symbols {
switch sym.Name {
case moddataSymName:
moddataAddr = sym.Value
moddataSym = sym
case gofuncSymName:
gofuncAddr = sym.Value
gofuncSym = sym
case "runtime.pclntab":
pclntabSym = sym
case "runtime.epclntab":
epclntabSym = sym
}
}
for _, sec := range xf.Sections {
if sec.Name == ".go.pclntab" {
data, err := sec.Data()
if err != nil {
t.Fatal(err)
}
pclntab = data
pclntabAddr = sec.VirtualAddress
pclntabEnd = sec.VirtualAddress + sec.Size
}
if moddataAddr >= sec.VirtualAddress && moddataAddr < sec.VirtualAddress+sec.Size {
data, err := sec.Data()
if err != nil {
t.Fatal(err)
}
moddataBytes = data[moddataAddr-sec.VirtualAddress:]
}
if moddataSym == nil {
t.Fatalf("could not find symbol %s", moddataSymName)
}
if gofuncSym == nil {
t.Fatalf("could not find symbol %s", gofuncSymName)
}
if pclntabSym == nil {
t.Fatal("could not find symbol runtime.pclntab")
}
if epclntabSym == nil {
t.Fatal("could not find symbol runtime.epclntab")
}
sec := xf.Sections[moddataSym.SectionNumber-1]
data, err := sec.Data()
if err != nil {
t.Fatal(err)
}
moddataBytes = data[moddataSym.Value:]
moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
sec = xf.Sections[gofuncSym.SectionNumber-1]
gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
}
sec = xf.Sections[pclntabSym.SectionNumber-1]
data, err = sec.Data()
if err != nil {
t.Fatal(err)
}
pclntab = data[pclntabSym.Value:epclntabSym.Value]
pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
default:
panic("can't happen")
@ -2183,31 +2205,16 @@ func TestModuledataPlacement(t *testing.T) {
}
}
case pf != nil:
defer pf.Close()
// On Windows all the Go specific sections seem to
// get stuffed into a few Windows sections,
// so there is nothing to test here.
case xf != nil:
defer xf.Close()
for _, sym := range xf.Symbols {
if sym.Name == moddataSymName {
if sym.SectionNumber == 0 {
t.Errorf("moduledata not in a section")
} else {
sec := xf.Sections[sym.SectionNumber-1]
if sec.Name != ".go.module" {
t.Errorf("moduledata in section %s, not .go.module", sec.Name)
}
if sym.Value != sec.VirtualAddress {
t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.VirtualAddress)
}
}
break
}
case pf != nil, xf != nil:
if pf != nil {
defer pf.Close()
}
if xf != nil {
defer xf.Close()
}
// On Windows and AIX all the Go specific sections
// get stuffed into a few sections,
// so there is nothing to test here.
}
}

View File

@ -118,11 +118,6 @@ func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
"runtime.noptrdata": "D",
}
if runtime.GOOS == "aix" && iscgo {
// pclntab is moved to .data section on AIX.
runtimeSyms["runtime.epclntab"] = "D"
}
out, err = testenv.Command(t, testenv.Executable(t), exe).CombinedOutput()
if err != nil {
t.Fatalf("go tool nm: %v\n%s", err, string(out))

View File

@ -199,12 +199,19 @@ are often used to express optionality.
omitzero: suggest replacing omitempty with omitzero for struct fields
The omitzero analyzer identifies uses of the `omitempty` JSON struct tag on
fields that are themselves structs. The `omitempty` tag has no effect on
struct-typed fields. The analyzer offers two suggestions: either remove the
The omitzero analyzer identifies uses of the `omitempty` JSON struct
tag on fields that are themselves structs. For struct-typed fields,
the `omitempty` tag has no effect on the behavior of json.Marshal and
json.Unmarshal. The analyzer offers two suggestions: either remove the
tag, or replace it with `omitzero` (added in Go 1.24), which correctly
omits the field if the struct value is zero.
However, some other serialization packages (notably kubebuilder, see
https://book.kubebuilder.io/reference/markers.html) may have their own
interpretation of the `json:",omitzero"` tag, so removing it may affect
program behavior. For this reason, the omitzero modernizer will not
make changes in any package that contains +kubebuilder annotations.
Replacing `omitempty` with `omitzero` is a change in behavior. The
original code would always encode the struct field, whereas the
modified code will omit it if it is a zero-value.

View File

@ -9,6 +9,8 @@ import (
"go/types"
"reflect"
"strconv"
"strings"
"sync"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
@ -25,82 +27,106 @@ var OmitZeroAnalyzer = &analysis.Analyzer{
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#omitzero",
}
func checkOmitEmptyField(pass *analysis.Pass, info *types.Info, curField *ast.Field) {
typ := info.TypeOf(curField.Type)
_, ok := typ.Underlying().(*types.Struct)
if !ok {
// Not a struct
return
}
tag := curField.Tag
if tag == nil {
// No tag to check
return
}
// The omitempty tag may be used by other packages besides json, but we should only modify its use with json
tagconv, _ := strconv.Unquote(tag.Value)
match := omitemptyRegex.FindStringSubmatchIndex(tagconv)
if match == nil {
// No omitempty in json tag
return
}
omitEmpty, err := astutil.RangeInStringLiteral(curField.Tag, match[2], match[3])
if err != nil {
return
}
var remove analysis.Range = omitEmpty
jsonTag := reflect.StructTag(tagconv).Get("json")
if jsonTag == ",omitempty" {
// Remove the entire struct tag if json is the only package used
if match[1]-match[0] == len(tagconv) {
remove = curField.Tag
} else {
// Remove the json tag if omitempty is the only field
remove, err = astutil.RangeInStringLiteral(curField.Tag, match[0], match[1])
if err != nil {
return
}
}
}
pass.Report(analysis.Diagnostic{
Pos: curField.Tag.Pos(),
End: curField.Tag.End(),
Message: "Omitempty has no effect on nested struct fields",
SuggestedFixes: []analysis.SuggestedFix{
{
Message: "Remove redundant omitempty tag",
TextEdits: []analysis.TextEdit{
{
Pos: remove.Pos(),
End: remove.End(),
},
},
},
{
Message: "Replace omitempty with omitzero (behavior change)",
TextEdits: []analysis.TextEdit{
{
Pos: omitEmpty.Pos(),
End: omitEmpty.End(),
NewText: []byte(",omitzero"),
},
},
},
}})
}
// The omitzero pass searches for instances of "omitempty" in a json field tag on a
// struct. Since "omitfilesUsingGoVersions not have any effect when applied to a struct field,
// it suggests either deleting "omitempty" or replacing it with "omitzero", which
// correctly excludes structs from a json encoding.
func omitzero(pass *analysis.Pass) (any, error) {
// usesKubebuilder reports whether "+kubebuilder:" appears in
// any comment in the package, since it has its own
// interpretation of what omitzero means; see go.dev/issue/76649.
// It is computed once, on demand.
usesKubebuilder := sync.OnceValue[bool](func() bool {
for _, file := range pass.Files {
for _, comment := range file.Comments {
if strings.Contains(comment.Text(), "+kubebuilder:") {
return true
}
}
}
return false
})
checkField := func(field *ast.Field) {
typ := pass.TypesInfo.TypeOf(field.Type)
_, ok := typ.Underlying().(*types.Struct)
if !ok {
// Not a struct
return
}
tag := field.Tag
if tag == nil {
// No tag to check
return
}
// The omitempty tag may be used by other packages besides json, but we should only modify its use with json
tagconv, _ := strconv.Unquote(tag.Value)
match := omitemptyRegex.FindStringSubmatchIndex(tagconv)
if match == nil {
// No omitempty in json tag
return
}
omitEmpty, err := astutil.RangeInStringLiteral(field.Tag, match[2], match[3])
if err != nil {
return
}
var remove analysis.Range = omitEmpty
jsonTag := reflect.StructTag(tagconv).Get("json")
if jsonTag == ",omitempty" {
// Remove the entire struct tag if json is the only package used
if match[1]-match[0] == len(tagconv) {
remove = field.Tag
} else {
// Remove the json tag if omitempty is the only field
remove, err = astutil.RangeInStringLiteral(field.Tag, match[0], match[1])
if err != nil {
return
}
}
}
// Don't offer a fix if the package seems to use kubebuilder,
// as it has its own intepretation of "omitzero" tags.
// https://book.kubebuilder.io/reference/markers.html
if usesKubebuilder() {
return
}
pass.Report(analysis.Diagnostic{
Pos: field.Tag.Pos(),
End: field.Tag.End(),
Message: "Omitempty has no effect on nested struct fields",
SuggestedFixes: []analysis.SuggestedFix{
{
Message: "Remove redundant omitempty tag",
TextEdits: []analysis.TextEdit{
{
Pos: remove.Pos(),
End: remove.End(),
},
},
},
{
Message: "Replace omitempty with omitzero (behavior change)",
TextEdits: []analysis.TextEdit{
{
Pos: omitEmpty.Pos(),
End: omitEmpty.End(),
NewText: []byte(",omitzero"),
},
},
},
}})
}
for curFile := range filesUsingGoVersion(pass, versions.Go1_24) {
for curStruct := range curFile.Preorder((*ast.StructType)(nil)) {
for _, curField := range curStruct.Node().(*ast.StructType).Fields.List {
checkOmitEmptyField(pass, pass.TypesInfo, curField)
checkField(curField)
}
}
}
return nil, nil
}

View File

@ -50,9 +50,14 @@ func init() {
// The following must hold for a replacement to occur:
//
// 1. All instances of i and s must be in one of these forms.
// Binary expressions:
// (a): establishing that i < 0: e.g.: i < 0, 0 > i, i == -1, -1 == i
// (b): establishing that i > -1: e.g.: i >= 0, 0 <= i, i == 0, 0 == i
//
// Binary expressions must be inequalities equivalent to
// "Index failed" (e.g. i < 0) or "Index succeeded" (i >= 0),
// or identities such as these (and their negations):
//
// 0 > i (flips left and right)
// i <= -1, -1 >= i (replace strict inequality by non-strict)
// i == -1, -1 == i (Index() guarantees i < 0 => i == -1)
//
// Slice expressions:
// a: s[:i], s[0:i]
@ -86,9 +91,9 @@ func init() {
// use(before, after)
// }
//
// If the condition involving `i` establishes that i > -1, then we replace it with
// `if ok“. Variants listed above include i >= 0, i > 0, and i == 0.
// If the condition is negated (e.g. establishes `i < 0`), we use `if !ok` instead.
// If the condition involving `i` is equivalent to i >= 0, then we replace it with
// `if ok“.
// If the condition is negated (e.g. equivalent to `i < 0`), we use `if !ok` instead.
// If the slices of `s` match `s[:i]` or `s[i+len(substr):]` or their variants listed above,
// then we replace them with before and after.
//
@ -178,16 +183,16 @@ func stringscut(pass *analysis.Pass) (any, error) {
// len(substr)]), then we can replace the call to Index()
// with a call to Cut() and use the returned ok, before,
// and after variables accordingly.
lessZero, greaterNegOne, beforeSlice, afterSlice := checkIdxUses(pass.TypesInfo, index.Uses(iObj), s, substr)
negative, nonnegative, beforeSlice, afterSlice := checkIdxUses(pass.TypesInfo, index.Uses(iObj), s, substr)
// Either there are no uses of before, after, or ok, or some use
// of i does not match our criteria - don't suggest a fix.
if lessZero == nil && greaterNegOne == nil && beforeSlice == nil && afterSlice == nil {
if negative == nil && nonnegative == nil && beforeSlice == nil && afterSlice == nil {
continue
}
// If the only uses are ok and !ok, don't suggest a Cut() fix - these should be using Contains()
isContains := (len(lessZero) > 0 || len(greaterNegOne) > 0) && len(beforeSlice) == 0 && len(afterSlice) == 0
isContains := (len(negative) > 0 || len(nonnegative) > 0) && len(beforeSlice) == 0 && len(afterSlice) == 0
scope := iObj.Parent()
var (
@ -200,7 +205,7 @@ func stringscut(pass *analysis.Pass) (any, error) {
// If there will be no uses of ok, before, or after, use the
// blank identifier instead.
if len(lessZero) == 0 && len(greaterNegOne) == 0 {
if len(negative) == 0 && len(nonnegative) == 0 {
okVarName = "_"
}
if len(beforeSlice) == 0 {
@ -226,8 +231,8 @@ func stringscut(pass *analysis.Pass) (any, error) {
replacedFunc := "Cut"
if isContains {
replacedFunc = "Contains"
replace(lessZero, "!"+foundVarName) // idx < 0 -> !found
replace(greaterNegOne, foundVarName) // idx > -1 -> found
replace(negative, "!"+foundVarName) // idx < 0 -> !found
replace(nonnegative, foundVarName) // idx > -1 -> found
// Replace the assignment with found, and replace the call to
// Index or IndexByte with a call to Contains.
@ -244,8 +249,8 @@ func stringscut(pass *analysis.Pass) (any, error) {
NewText: []byte("Contains"),
})
} else {
replace(lessZero, "!"+okVarName) // idx < 0 -> !ok
replace(greaterNegOne, okVarName) // idx > -1 -> ok
replace(negative, "!"+okVarName) // idx < 0 -> !ok
replace(nonnegative, okVarName) // idx > -1 -> ok
replace(beforeSlice, beforeVarName) // s[:idx] -> before
replace(afterSlice, afterVarName) // s[idx+k:] -> after
@ -364,11 +369,11 @@ func indexArgValid(info *types.Info, index *typeindex.Index, expr ast.Expr, afte
// one of the following four valid formats, it returns a list of occurrences for
// each format. If any of the uses do not match one of the formats, return nil
// for all values, since we should not offer a replacement.
// 1. lessZero - a condition involving i establishing that i is negative (e.g. i < 0, 0 > i, i == -1, -1 == i)
// 2. greaterNegOne - a condition involving i establishing that i is non-negative (e.g. i >= 0, 0 <= i, i == 0, 0 == i)
// 1. negative - a condition equivalent to i < 0
// 2. nonnegative - a condition equivalent to i >= 0
// 3. beforeSlice - a slice of `s` that matches either s[:i], s[0:i]
// 4. afterSlice - a slice of `s` that matches one of: s[i+len(substr):], s[len(substr) + i:], s[i + const], s[k + i] (where k = len(substr))
func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr ast.Expr) (lessZero, greaterNegOne, beforeSlice, afterSlice []ast.Expr) {
func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr ast.Expr) (negative, nonnegative, beforeSlice, afterSlice []ast.Expr) {
use := func(cur inspector.Cursor) bool {
ek, _ := cur.ParentEdge()
n := cur.Parent().Node()
@ -377,13 +382,13 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a
check := n.(*ast.BinaryExpr)
switch checkIdxComparison(info, check) {
case -1:
lessZero = append(lessZero, check)
negative = append(negative, check)
return true
case 1:
greaterNegOne = append(greaterNegOne, check)
nonnegative = append(nonnegative, check)
return true
}
// Check does not establish that i < 0 or i > -1.
// Check is not equivalent to that i < 0 or i >= 0.
// Might be part of an outer slice expression like s[i + k]
// which requires a different check.
// Check that the thing being sliced is s and that the slice
@ -421,7 +426,7 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a
return nil, nil, nil, nil
}
}
return lessZero, greaterNegOne, beforeSlice, afterSlice
return negative, nonnegative, beforeSlice, afterSlice
}
// hasModifyingUses reports whether any of the uses involve potential
@ -451,52 +456,57 @@ func hasModifyingUses(info *types.Info, uses iter.Seq[inspector.Cursor], afterPo
return false
}
// checkIdxComparison reports whether the check establishes that i is negative
// or non-negative. It returns -1 in the first case, 1 in the second, and 0 if
// we can confirm neither condition. We assume that a check passed to
// checkIdxComparison has i as one of its operands.
// checkIdxComparison reports whether the check is equivalent to i < 0 or its negation, or neither.
// For equivalent to i >= 0, we only accept this exact BinaryExpr since
// expressions like i > 0 or i >= 1 make a stronger statement about the value of i.
// We avoid suggesting a fix in this case since it may result in an invalid
// transformation (See golang/go#76687).
// Since strings.Index returns exactly -1 if the substring is not found, we
// don't need to handle expressions like i <= -3.
// We return 0 if the expression does not match any of these options.
// We assume that a check passed to checkIdxComparison has i as one of its operands.
func checkIdxComparison(info *types.Info, check *ast.BinaryExpr) int {
// Check establishes that i is negative.
// e.g.: i < 0, 0 > i, i == -1, -1 == i
if check.Op == token.LSS && (isNegativeConst(info, check.Y) || isZeroIntConst(info, check.Y)) || //i < (0 or neg)
check.Op == token.GTR && (isNegativeConst(info, check.X) || isZeroIntConst(info, check.X)) || // (0 or neg) > i
check.Op == token.LEQ && (isNegativeConst(info, check.Y)) || //i <= (neg)
check.Op == token.GEQ && (isNegativeConst(info, check.X)) || // (neg) >= i
check.Op == token.EQL &&
(isNegativeConst(info, check.X) || isNegativeConst(info, check.Y)) { // i == neg; neg == i
return -1
// Ensure that the constant (if any) is on the right.
x, op, y := check.X, check.Op, check.Y
if info.Types[x].Value != nil {
x, op, y = y, flip(op), x
}
// Check establishes that i is non-negative.
// e.g.: i >= 0, 0 <= i, i == 0, 0 == i
if check.Op == token.GTR && (isNonNegativeConst(info, check.Y) || isIntLiteral(info, check.Y, -1)) || // i > (non-neg or -1)
check.Op == token.LSS && (isNonNegativeConst(info, check.X) || isIntLiteral(info, check.X, -1)) || // (non-neg or -1) < i
check.Op == token.GEQ && isNonNegativeConst(info, check.Y) || // i >= (non-neg)
check.Op == token.LEQ && isNonNegativeConst(info, check.X) || // (non-neg) <= i
check.Op == token.EQL &&
(isNonNegativeConst(info, check.X) || isNonNegativeConst(info, check.Y)) { // i == non-neg; non-neg == i
return 1
yIsInt := func(k int64) bool {
return isIntLiteral(info, y, k)
}
return 0
if op == token.LSS && yIsInt(0) || // i < 0
op == token.EQL && yIsInt(-1) || // i == -1
op == token.LEQ && yIsInt(-1) { // i <= -1
return -1 // check <=> i is negative
}
if op == token.GEQ && yIsInt(0) || // i >= 0
op == token.NEQ && yIsInt(-1) || // i != -1
op == token.GTR && yIsInt(-1) { // i > -1
return +1 // check <=> i is non-negative
}
return 0 // unknown
}
// isNegativeConst returns true if the expr is a const int with value < zero.
func isNegativeConst(info *types.Info, expr ast.Expr) bool {
if tv, ok := info.Types[expr]; ok && tv.Value != nil && tv.Value.Kind() == constant.Int {
if v, ok := constant.Int64Val(tv.Value); ok {
return v < 0
}
// flip changes the comparison token as if the operands were flipped.
// It is defined only for == and the four inequalities.
func flip(op token.Token) token.Token {
switch op {
case token.EQL:
return token.EQL // (same)
case token.GEQ:
return token.LEQ
case token.GTR:
return token.LSS
case token.LEQ:
return token.GEQ
case token.LSS:
return token.GTR
}
return false
}
// isNonNegativeConst returns true if the expr is a const int with value >= zero.
func isNonNegativeConst(info *types.Info, expr ast.Expr) bool {
if tv, ok := info.Types[expr]; ok && tv.Value != nil && tv.Value.Kind() == constant.Int {
if v, ok := constant.Int64Val(tv.Value); ok {
return v >= 0
}
}
return false
return op
}
// isBeforeSlice reports whether the SliceExpr is of the form s[:i] or s[0:i].

View File

@ -27,6 +27,7 @@ package unitchecker
// printf checker.
import (
"archive/zip"
"encoding/gob"
"encoding/json"
"flag"
@ -74,6 +75,7 @@ type Config struct {
VetxOnly bool // run analysis only for facts, not diagnostics
VetxOutput string // where to write file of fact information
Stdout string // write stdout (e.g. JSON, unified diff) to this file
FixArchive string // write fixed files to this zip archive, if non-empty
SucceedOnTypecheckFailure bool // obsolete awful hack; see #18395 and below
}
@ -153,7 +155,7 @@ func Run(configFile string, analyzers []*analysis.Analyzer) {
// In VetxOnly mode, the analysis is run only for facts.
if !cfg.VetxOnly {
code = processResults(fset, cfg.ID, results)
code = processResults(fset, cfg.ID, cfg.FixArchive, results)
}
os.Exit(code)
@ -177,7 +179,7 @@ func readConfig(filename string) (*Config, error) {
return cfg, nil
}
func processResults(fset *token.FileSet, id string, results []result) (exit int) {
func processResults(fset *token.FileSet, id, fixArchive string, results []result) (exit int) {
if analysisflags.Fix {
// Don't print the diagnostics,
// but apply all fixes from the root actions.
@ -194,7 +196,40 @@ func processResults(fset *token.FileSet, id string, results []result) (exit int)
Diagnostics: res.diagnostics,
}
}
if err := driverutil.ApplyFixes(fixActions, analysisflags.Diff, false); err != nil {
// By default, fixes overwrite the original file.
// With the -diff flag, print the diffs to stdout.
// If "go fix" provides a fix archive, we write files
// into it so that mutations happen after the build.
write := func(filename string, content []byte) error {
return os.WriteFile(filename, content, 0644)
}
if fixArchive != "" {
f, err := os.Create(fixArchive)
if err != nil {
log.Fatalf("can't create -fix archive: %v", err)
}
zw := zip.NewWriter(f)
zw.SetComment(id) // ignore error
defer func() {
if err := zw.Close(); err != nil {
log.Fatalf("closing -fix archive zip writer: %v", err)
}
if err := f.Close(); err != nil {
log.Fatalf("closing -fix archive file: %v", err)
}
}()
write = func(filename string, content []byte) error {
f, err := zw.Create(filename)
if err != nil {
return err
}
_, err = f.Write(content)
return err
}
}
if err := driverutil.ApplyFixes(fixActions, write, analysisflags.Diff, false); err != nil {
// Fail when applying fixes failed.
log.Print(err)
exit = 1

View File

@ -93,11 +93,13 @@ type FixAction struct {
//
// If printDiff (from the -diff flag) is set, instead of updating the
// files it display the final patch composed of all the cleanly merged
// fixes.
// fixes. (It is tempting to factor printDiff as just a variant of
// writeFile that is provided the old and new content, but it's hard
// to generate a good summary that way.)
//
// TODO(adonovan): handle file-system level aliases such as symbolic
// links using robustio.FileID.
func ApplyFixes(actions []FixAction, printDiff, verbose bool) error {
func ApplyFixes(actions []FixAction, writeFile func(filename string, content []byte) error, printDiff, verbose bool) error {
generated := make(map[*token.File]bool)
// Select fixes to apply.
@ -264,12 +266,11 @@ fixloop:
os.Stdout.WriteString(unified)
} else {
// write
// write file
totalFiles++
// TODO(adonovan): abstract the I/O.
if err := os.WriteFile(file, final, 0644); err != nil {
if err := writeFile(file, final); err != nil {
log.Println(err)
continue
continue // (causes ApplyFix to return an error)
}
filesUpdated++
}

View File

@ -725,8 +725,48 @@ func analyzeAssignment(info *types.Info, stack []ast.Node) (assignable, ifaceAss
paramType := paramTypeAtIndex(sig, call, i)
ifaceAssign := paramType == nil || types.IsInterface(paramType)
affectsInference := false
if fn := typeutil.StaticCallee(info, call); fn != nil {
if sig2 := fn.Type().(*types.Signature); sig2.Recv() == nil {
switch callee := typeutil.Callee(info, call).(type) {
case *types.Builtin:
// Consider this litmus test:
//
// func f(x int64) any { return max(x) }
// func main() { fmt.Printf("%T", f(42)) }
//
// If we lose the implicit conversion from untyped int
// to int64, the type inferred for the max(x) call changes,
// resulting in a different dynamic behavior: it prints
// int, not int64.
//
// Inferred result type affected:
// new
// complex, real, imag
// min, max
//
// Dynamic behavior change:
// append -- dynamic type of append([]any(nil), x)[0]
// delete(m, x) -- dynamic key type where m is map[any]unit
// panic -- dynamic type of panic value
//
// Unaffected:
// recover
// make
// len, cap
// clear
// close
// copy
// print, println -- only uses underlying types (?)
//
// The dynamic type cases are all covered by
// the ifaceAssign logic.
switch callee.Name() {
case "new", "complex", "real", "imag", "min", "max":
affectsInference = true
}
case *types.Func:
// Only standalone (non-method) functions have type
// parameters affected by the call arguments.
if sig2 := callee.Signature(); sig2.Recv() == nil {
originParamType := paramTypeAtIndex(sig2, call, i)
affectsInference = originParamType == nil || new(typeparams.Free).Has(originParamType)
}

View File

@ -73,7 +73,7 @@ golang.org/x/text/internal/tag
golang.org/x/text/language
golang.org/x/text/transform
golang.org/x/text/unicode/norm
# golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713
# golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2
## explicit; go 1.24.0
golang.org/x/tools/cmd/bisect
golang.org/x/tools/cover

View File

@ -27,7 +27,7 @@ func (c *nistCurve) String() string {
}
func (c *nistCurve) GenerateKey(r io.Reader) (*PrivateKey, error) {
if boring.Enabled && r == boring.RandReader {
if boring.Enabled && rand.IsDefaultReader(r) {
key, bytes, err := boring.GenerateKeyECDH(c.name)
if err != nil {
return nil, err

View File

@ -334,7 +334,7 @@ func (priv *PrivateKey) Sign(random io.Reader, digest []byte, opts crypto.Signer
// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed
// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].
func GenerateKey(c elliptic.Curve, r io.Reader) (*PrivateKey, error) {
if boring.Enabled && r == boring.RandReader {
if boring.Enabled && rand.IsDefaultReader(r) {
x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name)
if err != nil {
return nil, err
@ -380,7 +380,7 @@ func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], ran
// is set. This setting will be removed in a future Go release. Instead, use
// [testing/cryptotest.SetGlobalRandom].
func SignASN1(r io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
if boring.Enabled && r == boring.RandReader {
if boring.Enabled && rand.IsDefaultReader(r) {
b, err := boringPrivateKey(priv)
if err != nil {
return nil, err

View File

@ -17,7 +17,6 @@ package ed25519
import (
"crypto"
"crypto/internal/fips140/drbg"
"crypto/internal/fips140/ed25519"
"crypto/internal/fips140cache"
"crypto/internal/fips140only"
@ -153,7 +152,7 @@ func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) {
if random == nil {
if cryptocustomrand.Value() == "1" {
random = cryptorand.Reader
if _, ok := random.(drbg.DefaultReader); !ok {
if !rand.IsDefaultReader(random) {
cryptocustomrand.IncNonDefault()
}
} else {

View File

@ -55,7 +55,7 @@ var cryptocustomrand = godebug.New("cryptocustomrand")
// If returning a non-default Reader, it calls [randutil.MaybeReadByte] on it.
func CustomReader(r io.Reader) io.Reader {
if cryptocustomrand.Value() == "1" {
if _, ok := r.(drbg.DefaultReader); !ok {
if !IsDefaultReader(r) {
randutil.MaybeReadByte(r)
cryptocustomrand.IncNonDefault()
}
@ -63,3 +63,11 @@ func CustomReader(r io.Reader) io.Reader {
}
return Reader
}
// IsDefaultReader reports whether r is the default [crypto/rand.Reader].
//
// If true, the Read method of r can be assumed to call [drbg.Read].
func IsDefaultReader(r io.Reader) bool {
_, ok := r.(drbg.DefaultReader)
return ok
}

View File

@ -153,7 +153,7 @@ var data = Data{
0x8d2a4c8a,
},
Table3: []uint32{
// round3
// round 3
0xfffa3942,
0x8771f681,
0x6d9d6122,

View File

@ -31,15 +31,7 @@ import (
//
// In FIPS 140-3 mode, the output passes through an SP 800-90A Rev. 1
// Deterministric Random Bit Generator (DRBG).
var Reader io.Reader
func init() {
if boring.Enabled {
Reader = boring.RandReader
return
}
Reader = rand.Reader
}
var Reader io.Reader = rand.Reader
// fatal is [runtime.fatal], pushed via linkname.
//
@ -57,9 +49,12 @@ func Read(b []byte) (n int, err error) {
// through a potentially overridden Reader, so we special-case the default
// case which we can keep non-escaping, and in the general case we read into
// a heap buffer and copy from it.
if _, ok := Reader.(drbg.DefaultReader); ok {
boring.Unreachable()
drbg.Read(b)
if rand.IsDefaultReader(Reader) {
if boring.Enabled {
_, err = io.ReadFull(boring.RandReader, b)
} else {
drbg.Read(b)
}
} else {
bb := make([]byte, len(b))
_, err = io.ReadFull(Reader, bb)

View File

@ -10,6 +10,7 @@ import (
"crypto/internal/fips140/rsa"
"crypto/internal/fips140hash"
"crypto/internal/fips140only"
"crypto/internal/rand"
"errors"
"hash"
"io"
@ -59,9 +60,9 @@ func (opts *PSSOptions) saltLength() int {
// used. If opts.Hash is set, it overrides hash.
//
// The signature is randomized depending on the message, key, and salt size,
// using bytes from rand. Most applications should use [crypto/rand.Reader] as
// rand.
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, opts *PSSOptions) ([]byte, error) {
// using bytes from random. Most applications should use [crypto/rand.Reader] as
// random.
func SignPSS(random io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, opts *PSSOptions) ([]byte, error) {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return nil, err
}
@ -70,7 +71,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte,
hash = opts.Hash
}
if boring.Enabled && rand == boring.RandReader {
if boring.Enabled && rand.IsDefaultReader(random) {
bkey, err := boringPrivateKey(priv)
if err != nil {
return nil, err
@ -87,7 +88,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte,
if fips140only.Enforced() && !fips140only.ApprovedHash(h) {
return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode")
}
if fips140only.Enforced() && !fips140only.ApprovedRandomReader(rand) {
if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) {
return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
}
@ -116,7 +117,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte,
}
}
return fipsError2(rsa.SignPSS(rand, k, h, digest, saltLength))
return fipsError2(rsa.SignPSS(random, k, h, digest, saltLength))
}
// VerifyPSS verifies a PSS signature.
@ -216,7 +217,7 @@ func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *Publi
defer hash.Reset()
defer mgfHash.Reset()
if boring.Enabled && random == boring.RandReader {
if boring.Enabled && rand.IsDefaultReader(random) {
k := pub.Size()
if len(msg) > k-2*hash.Size()-2 {
return nil, ErrMessageTooLong

View File

@ -61,7 +61,7 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro
return nil, ErrMessageTooLong
}
if boring.Enabled && random == boring.RandReader {
if boring.Enabled && rand.IsDefaultReader(random) {
bkey, err := boringPublicKey(pub)
if err != nil {
return nil, err

View File

@ -314,7 +314,7 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
return nil, err
}
if boring.Enabled && random == boring.RandReader &&
if boring.Enabled && rand.IsDefaultReader(random) &&
(bits == 2048 || bits == 3072 || bits == 4096) {
bN, bE, bD, bP, bQ, bDp, bDq, bQinv, err := boring.GenerateKeyRSA(bits)
if err != nil {

View File

@ -81,7 +81,7 @@ type SessionState struct {
version uint16
isClient bool
cipherSuite uint16
// createdAt is the generation time of the secret on the sever (which for
// createdAt is the generation time of the secret on the server (which for
// TLS 1.01.2 might be earlier than the current session) and the time at
// which the ticket was received on the client.
createdAt uint64 // seconds since UNIX epoch

View File

@ -4,8 +4,8 @@
//go:build ignore
// encgen writes the helper functions for encoding. Intended to be
// used with go generate; see the invocation in encode.go.
// decgen writes the helper functions for decoding. Intended to be
// used with go generate; see the invocation in decode.go.
// TODO: We could do more by being unsafe. Add a -unsafe flag?

View File

@ -370,7 +370,7 @@ func (f inspector) Visit(node Node) Visitor {
// call of f(nil).
//
// In many cases it may be more convenient to use [Preorder], which
// returns an iterator over the sqeuence of nodes, or [PreorderStack],
// returns an iterator over the sequence of nodes, or [PreorderStack],
// which (like [Inspect]) provides control over descent into subtrees,
// but additionally reports the stack of enclosing nodes.
func Inspect(node Node, f func(Node) bool) {

View File

@ -1260,8 +1260,10 @@ func (t *table) grow(typ *abi.MapType, m *Map, newCapacity uint16) {
// probeSeq maintains the state for a probe sequence that iterates through the
// groups in a table. The sequence is a triangular progression of the form
// hash, hash + 1, hash + 1 + 2, hash + 1 + 2 + 3, ..., modulo mask + 1.
// The i-th term of the sequence is
//
// p(i) := (i^2 + i)/2 + hash (mod mask+1)
// p(i) := hash + (i^2 + i)/2 (mod mask+1)
//
// The sequence effectively outputs the indexes of *groups*. The group
// machinery allows us to check an entire group with minimal branching.

View File

@ -9,6 +9,11 @@ TEXT ·EnableDIT(SB),$0-1
UBFX $24, R0, $1, R1
MOVB R1, ret+0(FP)
MSR $1, DIT
// TODO(roland): the SB instruction is significantly more
// performant when available. We should detect its availability
// and use it when we can.
DSB $7 // nsh
ISB $15 // sy
RET
TEXT ·DITEnabled(SB),$0-1

View File

@ -20,6 +20,7 @@ import (
var (
logEvents = flag.Bool("log-events", false, "whether to log high-level events; significantly slows down tests")
dumpTraces = flag.Bool("dump-traces", false, "dump traces even on success")
allocFree = flag.Bool("alloc-free", false, "run alloc/free trace experiment tests")
)
func TestReaderGolden(t *testing.T) {

View File

@ -697,6 +697,9 @@ func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace
runTest(t, true, "")
})
t.Run("AllocFree", func(t *testing.T) {
if !*allocFree {
t.Skip("skipping trace alloc/free tests by default; too flaky (see go.dev/issue/70838)")
}
if testing.Short() {
t.Skip("skipping trace alloc/free tests in short mode")
}

View File

@ -443,7 +443,8 @@ func isCookieDomainName(s string) bool {
}
if s[0] == '.' {
// A cookie a domain attribute may start with a leading dot.
// A cookie domain attribute may start with a leading dot.
// Per RFC 6265 section 5.2.3, a leading dot is ignored.
s = s[1:]
}
last := byte('.')

View File

@ -97,6 +97,13 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
t.Helper()
out, _ := runBuiltTestProgErr(t, exe, name, env...)
return out
}
func runBuiltTestProgErr(t *testing.T, exe, name string, env ...string) (string, error) {
t.Helper()
if *flagQuick {
t.Skip("-quick")
}
@ -120,7 +127,7 @@ func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
t.Fatalf("%v failed to start: %v", cmd, err)
}
}
return string(out)
return string(out), err
}
var serializeBuild = make(chan bool, 2)

View File

@ -551,8 +551,11 @@ func MapNextArenaHint() (start, end uintptr, ok bool) {
return
}
func GetNextArenaHint() uintptr {
return mheap_.arenaHints.addr
func NextArenaHint() (uintptr, bool) {
if mheap_.arenaHints == nil {
return 0, false
}
return mheap_.arenaHints.addr, true
}
type G = g

View File

@ -14,10 +14,13 @@ import (
)
func TestGoroutineLeakProfile(t *testing.T) {
if strings.Contains(os.Getenv("GOFLAGS"), "mayMoreStackPreempt") {
// Some tests have false negatives under mayMoreStackPreempt. This may be a test-only issue,
// but needs more investigation.
testenv.SkipFlaky(t, 75729)
// Some tests have false negatives under mayMoreStackPreempt and mayMoreStackMove.
// This may be a test-only issue in that they're just sensitive to scheduling, but it
// needs more investigation.
for _, cfg := range []string{"mayMoreStackPreempt", "mayMoreStackMove"} {
if strings.Contains(os.Getenv("GOFLAGS"), cfg) {
testenv.SkipFlaky(t, 75729)
}
}
// Goroutine leak test case.
@ -486,10 +489,7 @@ func TestGoroutineLeakProfile(t *testing.T) {
testCases := append(microTests, stressTestCases...)
testCases = append(testCases, patternTestCases...)
// Test cases must not panic or cause fatal exceptions.
failStates := regexp.MustCompile(`fatal|panic|DATA RACE`)
testApp := func(exepath string, testCases []testCase) {
runTests := func(exepath string, testCases []testCase) {
// Build the test program once.
exe, err := buildTestProg(t, exepath)
@ -503,7 +503,7 @@ func TestGoroutineLeakProfile(t *testing.T) {
cmdEnv := []string{
"GODEBUG=asyncpreemptoff=1",
"GOEXPERIMENT=greenteagc,goroutineleakprofile",
"GOEXPERIMENT=goroutineleakprofile",
}
if tcase.simple {
@ -515,14 +515,14 @@ func TestGoroutineLeakProfile(t *testing.T) {
var output string
for i := 0; i < tcase.repetitions; i++ {
// Run program for one repetition and get runOutput trace.
runOutput := runBuiltTestProg(t, exe, tcase.name, cmdEnv...)
runOutput, err := runBuiltTestProgErr(t, exe, tcase.name, cmdEnv...)
if len(runOutput) == 0 {
t.Errorf("Test %s produced no output. Is the goroutine leak profile collected?", tcase.name)
}
// Zero tolerance policy for fatal exceptions, panics, or data races.
if failStates.MatchString(runOutput) {
t.Errorf("unexpected fatal exception or panic\noutput:\n%s\n\n", runOutput)
// Test cases must not end in a non-zero exit code, or otherwise experience a failure to
// actually execute.
if err != nil {
t.Errorf("unexpected failure\noutput:\n%s\n\n", runOutput)
}
output += runOutput + "\n\n"
@ -598,6 +598,6 @@ func TestGoroutineLeakProfile(t *testing.T) {
}
}
testApp("testgoroutineleakprofile", testCases)
testApp("testgoroutineleakprofile/goker", gokerTestCases)
runTests("testgoroutineleakprofile", testCases)
runTests("testgoroutineleakprofile/goker", gokerTestCases)
}

View File

@ -664,10 +664,24 @@ func TestArenaCollision(t *testing.T) {
}
t.Logf("reserved [%#x, %#x)", start, end)
disallowed = append(disallowed, [2]uintptr{start, end})
hint, ok := NextArenaHint()
if !ok {
// We're out of arena hints. There's not much we can do now except give up.
// This might happen for a number of reasons, like if there's just something
// else already mapped in the address space where we put our hints. This is
// a bit more common than it used to be thanks to heap base randomization.
t.Skip("ran out of arena hints")
}
// Allocate until the runtime tries to use the hint we
// just mapped over.
hint := GetNextArenaHint()
for GetNextArenaHint() == hint {
for {
if next, ok := NextArenaHint(); !ok {
t.Skip("ran out of arena hints")
} else if next != hint {
break
}
ac := new(acLink)
arenaCollisionSink = append(arenaCollisionSink, ac)
// The allocation must not have fallen into

View File

@ -0,0 +1,32 @@
// Copyright 2025 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.
//go:build cgo
package runtime_test
import (
"internal/race"
"runtime"
"testing"
)
func TestNotInGoMetricCallback(t *testing.T) {
switch runtime.GOOS {
case "windows", "plan9":
t.Skip("unsupported on Windows and Plan9")
case "freebsd":
if race.Enabled {
t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
}
}
// This test is run in a subprocess to prevent other tests from polluting the metrics
// and because we need to make some cgo callbacks.
output := runTestProg(t, "testprogcgo", "NotInGoMetricCallback")
want := "OK\n"
if output != want {
t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
}
}

View File

@ -5,7 +5,7 @@
// Garbage collector (GC).
//
// The GC runs concurrently with mutator threads, is type accurate (aka precise), allows multiple
// GC thread to run in parallel. It is a concurrent mark and sweep that uses a write barrier. It is
// GC threads to run in parallel. It is a concurrent mark and sweep that uses a write barrier. It is
// non-generational and non-compacting. Allocation is done using size segregated per P allocation
// areas to minimize fragmentation while eliminating locks in the common case.
//

View File

@ -142,6 +142,8 @@ func preemptM(mp *m) {
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }
func setProcessCPUProfiler(hz int32) {}

View File

@ -12,7 +12,25 @@ import (
// A Pinner is a set of Go objects each pinned to a fixed location in memory. The
// [Pinner.Pin] method pins one object, while [Pinner.Unpin] unpins all pinned
// objects. See their comments for more information.
// objects.
//
// The purpose of a Pinner is two-fold.
// First, it allows C code to safely use Go pointers that have not been passed
// explicitly to the C code via a cgo call.
// For example, for safely interacting with a pointer stored inside of a struct
// whose pointer is passed to a C function.
// Second, it allows C memory to safely retain that Go pointer even after the
// cgo call returns, provided the object remains pinned.
//
// A Pinner arranges for its objects to be automatically unpinned some time after
// it becomes unreachable, so its referents will not leak. However, this means the
// Pinner itself must be kept alive across a cgo call, or as long as C retains a
// reference to the pinned Go pointers.
//
// Reusing a Pinner is safe, and in fact encouraged, to avoid the cost of
// initializing new Pinners on first use.
//
// The zero value of Pinner is ready to use.
type Pinner struct {
*pinner
}
@ -26,6 +44,7 @@ type Pinner struct {
// are going to be accessed from C code.
//
// The argument must be a pointer of any type or an [unsafe.Pointer].
//
// It's safe to call Pin on non-Go pointers, in which case Pin will do nothing.
func (p *Pinner) Pin(pointer any) {
if p.pinner == nil {
@ -63,6 +82,7 @@ func (p *Pinner) Pin(pointer any) {
}
// Unpin unpins all pinned objects of the [Pinner].
// It's safe and encouraged to reuse a Pinner after calling Unpin.
func (p *Pinner) Unpin() {
p.pinner.unpin()

View File

@ -2433,7 +2433,7 @@ func needm(signal bool) {
sp := sys.GetCallerSP()
callbackUpdateSystemStack(mp, sp, signal)
// Should mark we are already in Go now.
// We must mark that we are already in Go now.
// Otherwise, we may call needm again when we get a signal, before cgocallbackg1,
// which means the extram list may be empty, that will cause a deadlock.
mp.isExtraInC = false
@ -2455,7 +2455,8 @@ func needm(signal bool) {
// mp.curg is now a real goroutine.
casgstatus(mp.curg, _Gdeadextra, _Gsyscall)
sched.ngsys.Add(-1)
sched.nGsyscallNoP.Add(1)
// N.B. We do not update nGsyscallNoP, because isExtraInC threads are not
// counted as real goroutines while they're in C.
if !signal {
if trace.ok() {
@ -2590,7 +2591,7 @@ func dropm() {
casgstatus(mp.curg, _Gsyscall, _Gdeadextra)
mp.curg.preemptStop = false
sched.ngsys.Add(1)
sched.nGsyscallNoP.Add(-1)
decGSyscallNoP(mp)
if !mp.isExtraInSig {
if trace.ok() {
@ -4732,7 +4733,7 @@ func entersyscallHandleGCWait(trace traceLocker) {
if trace.ok() {
trace.ProcStop(pp)
}
sched.nGsyscallNoP.Add(1)
addGSyscallNoP(gp.m) // We gave up our P voluntarily.
pp.gcStopTime = nanotime()
pp.syscalltick++
if sched.stopwait--; sched.stopwait == 0 {
@ -4763,7 +4764,7 @@ func entersyscallblock() {
gp.m.syscalltick = gp.m.p.ptr().syscalltick
gp.m.p.ptr().syscalltick++
sched.nGsyscallNoP.Add(1)
addGSyscallNoP(gp.m) // We're going to give up our P.
// Leave SP around for GC and traceback.
pc := sys.GetCallerPC()
@ -5001,8 +5002,8 @@ func exitsyscallTryGetP(oldp *p) *p {
if oldp != nil {
if thread, ok := setBlockOnExitSyscall(oldp); ok {
thread.takeP()
addGSyscallNoP(thread.mp) // takeP does the opposite, but this is a net zero change.
thread.resume()
sched.nGsyscallNoP.Add(-1) // takeP adds 1.
return oldp
}
}
@ -5017,7 +5018,7 @@ func exitsyscallTryGetP(oldp *p) *p {
}
unlock(&sched.lock)
if pp != nil {
sched.nGsyscallNoP.Add(-1)
decGSyscallNoP(getg().m) // We got a P for ourselves.
return pp
}
}
@ -5043,7 +5044,7 @@ func exitsyscallNoP(gp *g) {
trace.GoSysExit(true)
traceRelease(trace)
}
sched.nGsyscallNoP.Add(-1)
decGSyscallNoP(getg().m)
dropg()
lock(&sched.lock)
var pp *p
@ -5081,6 +5082,41 @@ func exitsyscallNoP(gp *g) {
schedule() // Never returns.
}
// addGSyscallNoP must be called when a goroutine in a syscall loses its P.
// This function updates all relevant accounting.
//
// nosplit because it's called on the syscall paths.
//
//go:nosplit
func addGSyscallNoP(mp *m) {
// It's safe to read isExtraInC here because it's only mutated
// outside of _Gsyscall, and we know this thread is attached
// to a goroutine in _Gsyscall and blocked from exiting.
if !mp.isExtraInC {
// Increment nGsyscallNoP since we're taking away a P
// from a _Gsyscall goroutine, but only if isExtraInC
// is not set on the M. If it is, then this thread is
// back to being a full C thread, and will just inflate
// the count of not-in-go goroutines. See go.dev/issue/76435.
sched.nGsyscallNoP.Add(1)
}
}
// decGSsyscallNoP must be called whenever a goroutine in a syscall without
// a P exits the system call. This function updates all relevant accounting.
//
// nosplit because it's called from dropm.
//
//go:nosplit
func decGSyscallNoP(mp *m) {
// Update nGsyscallNoP, but only if this is not a thread coming
// out of C. See the comment in addGSyscallNoP. This logic must match,
// to avoid unmatched increments and decrements.
if !mp.isExtraInC {
sched.nGsyscallNoP.Add(-1)
}
}
// Called from syscall package before fork.
//
// syscall_runtime_BeforeFork is for package syscall,
@ -6758,7 +6794,7 @@ func (s syscallingThread) releaseP(state uint32) {
trace.ProcSteal(s.pp)
traceRelease(trace)
}
sched.nGsyscallNoP.Add(1)
addGSyscallNoP(s.mp)
s.pp.syscalltick++
}

View File

@ -232,11 +232,14 @@ func TestProfBufWakeup(t *testing.T) {
// The reader shouldn't wake up for this
b.Write(nil, 1, []uint64{1, 2}, []uintptr{3, 4})
// The reader should still be blocked
//
// TODO(nick): this is racy. We could Gosched and still have the reader
// blocked in a buggy implementation because it just didn't get a chance
// to run
// The reader should still be blocked. The awaitBlockedGoroutine here
// checks that and also gives a buggy implementation a chance to
// actually wake up (it calls Gosched) before the next write. There is a
// small chance that a buggy implementation would have woken up but
// doesn't get scheduled by the time we do the next write. In that case
// the reader will see a more-than-half-full buffer and the test will
// pass. But if the implementation is broken, this test should fail
// regularly, even if not 100% of the time.
awaitBlockedGoroutine(waitStatus, "TestProfBufWakeup.func1")
b.Write(nil, 1, []uint64{5, 6}, []uintptr{7, 8})
b.Close()
@ -247,7 +250,8 @@ func TestProfBufWakeup(t *testing.T) {
// see also runtime/pprof tests
func awaitBlockedGoroutine(state, fName string) {
re := fmt.Sprintf(`(?m)^goroutine \d+ \[%s\]:\n(?:.+\n\t.+\n)*runtime_test\.%s`, regexp.QuoteMeta(state), fName)
// NB: this matches [state] as well as [state, n minutes]
re := fmt.Sprintf(`(?m)^goroutine \d+ \[%s.*\]:\n(?:.+\n\t.+\n)*runtime_test\.%s`, regexp.QuoteMeta(state), fName)
r := regexp.MustCompile(re)
buf := make([]byte, 64<<10)

View File

@ -329,11 +329,13 @@ TEXT syncatomic·AddUintptr(SB), NOSPLIT, $0-24
TEXT syncatomic·AndInt32(SB), NOSPLIT, $0-20
GO_ARGS
MOVD $__tsan_go_atomic32_fetch_and(SB), R8
ADD $32, R1, R6
BR racecallatomic<>(SB)
TEXT syncatomic·AndInt64(SB), NOSPLIT, $0-24
GO_ARGS
MOVD $__tsan_go_atomic64_fetch_and(SB), R8
ADD $32, R1, R6
BR racecallatomic<>(SB)
TEXT syncatomic·AndUint32(SB), NOSPLIT, $0-20
@ -352,11 +354,13 @@ TEXT syncatomic·AndUintptr(SB), NOSPLIT, $0-24
TEXT syncatomic·OrInt32(SB), NOSPLIT, $0-20
GO_ARGS
MOVD $__tsan_go_atomic32_fetch_or(SB), R8
ADD $32, R1, R6
BR racecallatomic<>(SB)
TEXT syncatomic·OrInt64(SB), NOSPLIT, $0-24
GO_ARGS
MOVD $__tsan_go_atomic64_fetch_or(SB), R8
ADD $32, R1, R6
BR racecallatomic<>(SB)
TEXT syncatomic·OrUint32(SB), NOSPLIT, $0-20

View File

@ -945,7 +945,7 @@ type schedt struct {
nmfreed int64 // cumulative number of freed m's
ngsys atomic.Int32 // number of system goroutines
nGsyscallNoP atomic.Int32 // number of goroutines in syscalls without a P
nGsyscallNoP atomic.Int32 // number of goroutines in syscalls without a P but whose M is not isExtraInC
pidle puintptr // idle p's
npidle atomic.Int32

View File

@ -21,4 +21,6 @@ func asmcgocall_no_g(fn, arg unsafe.Pointer)
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

View File

@ -26,4 +26,6 @@ func asmcgocall_no_g(fn, arg unsafe.Pointer)
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

View File

@ -19,4 +19,6 @@ func unspillArgs()
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

View File

@ -17,4 +17,6 @@ func asmcgocall_no_g(fn, arg unsafe.Pointer)
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

View File

@ -12,4 +12,6 @@ func save_g()
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

View File

@ -23,4 +23,6 @@ func unspillArgs()
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

View File

@ -22,4 +22,6 @@ func unspillArgs()
// getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic
//
//go:nosplit
func getfp() uintptr { return 0 }

Some files were not shown because too many files have changed in this diff Show More