-3e46519cee5c916a9b39480fbac13f4ffc6a93b0
+f368afbbd466941dcc6717412d7182e122b40c93
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
-a5bfd9da1d1b24f326399b6b75558ded14514f23
+a068054af141c01df5a4519844f4b77273605f4e
The first line of this file holds the git revision number of the
last merge done from the master library sources.
// Main bool // is this the main module?
// Indirect bool // is this module only an indirect dependency of main module?
// Dir string // directory holding files for this module, if any
-// GoMod string // path to go.mod file for this module, if any
+// GoMod string // path to go.mod file used when loading this module, if any
// GoVersion string // go version used in module
// Error *ModuleError // error loading module
// }
// Err string // the error itself
// }
//
+// The file GoMod refers to may be outside the module directory if the
+// module is in the module cache or if the -modfile flag is used.
+//
// The default output is to print the module path and then
// information about the version and replacement if any.
// For example, 'go list -m all' might print:
// execution. The "go mod download" command is useful mainly for pre-filling
// the local cache or to compute the answers for a Go module proxy.
//
-// By default, download reports errors to standard error but is otherwise silent.
+// By default, download writes nothing to standard output. It may print progress
+// messages and errors to standard error.
+//
// The -json flag causes download to print a sequence of JSON objects
// to standard output, describing each downloaded module (or failure),
// corresponding to this Go struct:
//
// Module support
//
-// Go 1.13 includes support for Go modules. Module-aware mode is active by default
-// whenever a go.mod file is found in, or in a parent of, the current directory.
+// The go command includes support for Go modules. Module-aware mode is active
+// by default whenever a go.mod file is found in the current directory or in
+// any parent directory.
//
// The quickest way to take advantage of module support is to check out your
// repository, create a go.mod file (described in the next section) there, and run
// go commands from within that file tree.
//
-// For more fine-grained control, Go 1.13 continues to respect
+// For more fine-grained control, the go command continues to respect
// a temporary environment variable, GO111MODULE, which can be set to one
// of three string values: off, on, or auto (the default).
// If GO111MODULE=on, then the go command requires the use of modules,
}
// grepBothNot looks for a regular expression in the test run's
-// standard output or stand error and fails, logging msg, if it is
+// standard output or standard error and fails, logging msg, if it is
// found.
func (tg *testgoData) grepBothNot(match, msg string) {
tg.t.Helper()
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
// Copy the runtime packages into a temporary GOROOT
// so that we can change files.
tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package .*internal/w not allowed`, "wrote error message for testdata/testinternal2")
}
-func TestRunInternal(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- dir := filepath.Join(tg.pwd(), "testdata")
- tg.setenv("GOPATH", dir)
- tg.run("run", filepath.Join(dir, "src/run/good.go"))
- tg.runFail("run", filepath.Join(dir, "src/run/bad.go"))
- tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package run/subdir/internal/private not allowed`, "unexpected error for run/bad.go")
-}
-
-func TestRunPkg(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- dir := filepath.Join(tg.pwd(), "testdata")
- tg.setenv("GOPATH", dir)
- tg.run("run", "hello")
- tg.grepStderr("hello, world", "did not find hello, world")
- tg.cd(filepath.Join(dir, "src/hello"))
- tg.run("run", ".")
- tg.grepStderr("hello, world", "did not find hello, world")
-}
-
func TestInternalPackageErrorsAreHandled(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.grepStderr("internal", "did not fail to build p")
}
-func TestImportCommandMatch(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom"))
- tg.run("build", "./testdata/importcom/works.go")
-}
-
-func TestImportCommentMismatch(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom"))
- tg.runFail("build", "./testdata/importcom/wrongplace.go")
- tg.grepStderr(`wrongplace expects import "my/x"`, "go build did not mention incorrect import")
-}
-
-func TestImportCommentSyntaxError(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom"))
- tg.runFail("build", "./testdata/importcom/bad.go")
- tg.grepStderr("cannot parse import comment", "go build did not mention syntax error")
-}
-
-func TestImportCommentConflict(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom"))
- tg.runFail("build", "./testdata/importcom/conflict.go")
- tg.grepStderr("found import comments", "go build did not mention comment conflict")
-}
-
-func TestImportCycle(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcycle"))
- tg.runFail("build", "selfimport")
-
- count := tg.grepCountBoth("import cycle not allowed")
- if count == 0 {
- t.Fatal("go build did not mention cyclical import")
- }
- if count > 1 {
- t.Fatal("go build mentioned import cycle more than once")
- }
-
- // Don't hang forever.
- tg.run("list", "-e", "-json", "selfimport")
-}
-
// cmd/go: custom import path checking should not apply to Go packages without import comment.
func TestIssue10952(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
}
}
-func TestErrorMessageForSyntaxErrorInTestGoFileSaysFAIL(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("test", "syntaxerror")
- tg.grepStderr("x_test.go:", "did not diagnose error")
- tg.grepStdout("FAIL", "go test did not say FAIL")
-}
-
-func TestWildcardsDoNotLookInUselessDirectories(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("list", "...")
- tg.grepBoth("badpkg", "go list ... failure does not mention badpkg")
- tg.run("list", "m...")
-}
-
func TestRelativeImportsGoTest(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
tg.setenv("GOPATH", "")
tg.tempDir("home")
tg.setenv(homeEnvName(), tg.path("home"))
func TestDefaultGOPATHPrintedSearchList(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
tg.setenv("GOPATH", "")
tg.tempDir("home")
tg.setenv(homeEnvName(), tg.path("home"))
tg.wantExecutable("myerrors.test"+exeSuffix, "go test -mutexprofile -o myerrors.test did not create myerrors.test")
}
-func TestGoBuildNonMain(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- // TODO: tg.parallel()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("build", "-buildmode=exe", "-o", "not_main"+exeSuffix, "not_main")
- tg.grepStderr("-buildmode=exe requires exactly one main package", "go build with -o and -buildmode=exe should on a non-main package should throw an error")
- tg.mustNotExist("not_main" + exeSuffix)
-}
-
func TestGoTestDashCDashOControlsBinaryLocation(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg.grepStdout("[no statements]", "expected [no statements] for pkg4")
}
-func TestCoverageImportMainLoop(t *testing.T) {
- skipIfGccgo(t, "gccgo has no cover tool")
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("test", "importmain/test")
- tg.grepStderr("not an importable package", "did not detect import main")
- tg.runFail("test", "-cover", "importmain/test")
- tg.grepStderr("not an importable package", "did not detect import main")
-}
-
-func TestCoveragePattern(t *testing.T) {
- skipIfGccgo(t, "gccgo has no cover tool")
- tooSlow(t)
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.makeTempdir()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
-
- // If coverpkg=sleepy... expands by package loading
- // (as opposed to pattern matching on deps)
- // then it will try to load sleepybad, which does not compile,
- // and the test command will fail.
- tg.run("test", "-coverprofile="+tg.path("cover.out"), "-coverpkg=sleepy...", "-run=^$", "sleepy1")
-}
-
func TestCoverageErrorLine(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg.wantExecutable(tg.path("coverdep"), "go -test -c -coverprofile did not create executable")
}
-func TestPluginNonMain(t *testing.T) {
- wd, err := os.Getwd()
- if err != nil {
- t.Fatal(err)
- }
-
- pkg := filepath.Join(wd, "testdata", "testdep", "p2")
-
- tg := testgo(t)
- defer tg.cleanup()
-
- tg.runFail("build", "-buildmode=plugin", pkg)
-}
-
func TestTestEmpty(t *testing.T) {
if !canRace {
t.Skip("no race detector")
tg.grepStderrNot(`os.Stat .* no such file or directory`, "unexpected stat of archive file")
}
-func TestCoverageWithCgo(t *testing.T) {
- skipIfGccgo(t, "gccgo has no cover tool")
- tooSlow(t)
- if !canCgo {
- t.Skip("skipping because cgo not enabled")
- }
-
- for _, dir := range []string{"cgocover", "cgocover2", "cgocover3", "cgocover4"} {
- t.Run(dir, func(t *testing.T) {
- tg := testgo(t)
- tg.parallel()
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("test", "-short", "-cover", dir)
- data := tg.getStdout() + tg.getStderr()
- checkCoverage(tg, data)
- })
- }
-}
-
-func TestCgoAsmError(t *testing.T) {
- if !canCgo {
- t.Skip("skipping because cgo not enabled")
- }
-
- tg := testgo(t)
- tg.parallel()
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("build", "cgoasm")
- tg.grepBoth("package using cgo has Go assembly file", "did not detect Go assembly file")
-}
-
func TestCgoDependsOnSyscall(t *testing.T) {
if testing.Short() {
t.Skip("skipping test that removes $GOROOT/pkg/*_race in short mode")
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
+
files, err := filepath.Glob(filepath.Join(runtime.GOROOT(), "pkg", "*_race"))
tg.must(err)
for _, file := range files {
}
}
-// cmd/go: "go test" should fail if package does not build
-func TestIssue7108(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("test", "notest")
-}
-
func TestGoBuildTestOnly(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.run("install", "./testonly...")
}
-func TestGoTestDetectsTestOnlyImportCycles(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("test", "-c", "testcycle/p3")
- tg.grepStderr("import cycle not allowed in test", "go test testcycle/p3 produced unexpected error")
-
- tg.runFail("test", "-c", "testcycle/q1")
- tg.grepStderr("import cycle not allowed in test", "go test testcycle/q1 produced unexpected error")
-}
-
func TestGoTestFooTestWorks(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.grepBoth(okPattern, "go test did not say ok")
}
-func TestGoTestMainTwice(t *testing.T) {
- if testing.Short() {
- t.Skip("Skipping in short mode")
- }
- tg := testgo(t)
- defer tg.cleanup()
- tg.makeTempdir()
- tg.setenv("GOCACHE", tg.tempdir)
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("test", "-v", "multimain")
- if strings.Count(tg.getStdout(), "notwithstanding") != 2 {
- t.Fatal("tests did not run twice")
- }
-}
-
-func TestGoTestFlagsAfterPackage(t *testing.T) {
- tooSlow(t)
- tg := testgo(t)
- defer tg.cleanup()
- tg.run("test", "testdata/flag_test.go", "-v", "-args", "-v=7") // Two distinct -v flags.
- tg.run("test", "-v", "testdata/flag_test.go", "-args", "-v=7") // Two distinct -v flags.
-}
-
func TestGoTestXtestonlyWorks(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
}
}
-func TestGoGenerateBadImports(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip("skipping because windows has no echo command")
- }
-
- // This package has an invalid import causing an import cycle,
- // but go generate is supposed to still run.
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("generate", "gencycle")
- tg.grepStdout("hello world", "go generate gencycle did not run generator")
-}
-
func TestGoGetCustomDomainWildcard(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
}
}
-func TestGoTestRaceFailures(t *testing.T) {
- tooSlow(t)
-
- if !canRace {
- t.Skip("skipping because race detector not supported")
- }
-
- tg := testgo(t)
- tg.parallel()
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
-
- tg.run("test", "testrace")
-
- tg.runFail("test", "-race", "testrace")
- tg.grepStdout("FAIL: TestRace", "TestRace did not fail")
- tg.grepBothNot("PASS", "something passed")
-
- tg.runFail("test", "-race", "testrace", "-run", "XXX", "-bench", ".")
- tg.grepStdout("FAIL: BenchmarkRace", "BenchmarkRace did not fail")
- tg.grepBothNot("PASS", "something passed")
-}
-
-func TestGoTestImportErrorStack(t *testing.T) {
- const out = `package testdep/p1 (test)
- imports testdep/p2
- imports testdep/p3: build constraints exclude all Go files `
-
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("test", "testdep/p1")
- if !strings.Contains(tg.stderr.String(), out) {
- t.Fatalf("did not give full import stack:\n\n%s", tg.stderr.String())
- }
-}
-
func TestGoGetUpdate(t *testing.T) {
// golang.org/issue/9224.
// The recursive updating was trying to walk to
tg.grepStderrNot("duplicate loads of", "did not remove old packages from cache")
}
-// Issue 17119 more duplicate load errors
-func TestIssue17119(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
-
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("build", "dupload")
- tg.grepBothNot("duplicate load|internal error", "internal error")
-}
-
-func TestFatalInBenchmarkCauseNonZeroExitStatus(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- // TODO: tg.parallel()
- tg.runFail("test", "-run", "^$", "-bench", ".", "./testdata/src/benchfatal")
- tg.grepBothNot("^ok", "test passed unexpectedly")
- tg.grepBoth("FAIL.*benchfatal", "test did not run everything")
-}
-
func TestBinaryOnlyPackages(t *testing.T) {
tooSlow(t)
tg.grepBoth(noMatchesPattern, "go test did not say [no tests to run]")
}
-func TestMatchesNoTestsDoesNotOverrideBuildFailure(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.runFail("test", "-run", "ThisWillNotMatch", "syntaxerror")
- tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
- tg.grepBoth("FAIL", "go test did not say FAIL")
-}
-
func TestMatchesNoBenchmarksIsOK(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.grepBoth(okPattern, "go test did not say ok")
}
-func TestBenchmarkLabels(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- // TODO: tg.parallel()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("test", "-run", "^$", "-bench", ".", "bench")
- tg.grepStdout(`(?m)^goos: `+runtime.GOOS, "go test did not print goos")
- tg.grepStdout(`(?m)^goarch: `+runtime.GOARCH, "go test did not print goarch")
- tg.grepStdout(`(?m)^pkg: bench`, "go test did not say pkg: bench")
- tg.grepBothNot(`(?s)pkg:.*pkg:`, "go test said pkg multiple times")
-}
-
func TestBenchmarkLabelsOutsideGOPATH(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.grepStderrNot(`"-L[^"]+c flags".*"-L[^"]+c flags"`, "found too many quoted ld flags")
}
-// Issue #20435.
-func TestGoTestRaceCoverModeFailures(t *testing.T) {
- tooSlow(t)
- if !canRace {
- t.Skip("skipping because race detector not supported")
- }
-
- tg := testgo(t)
- tg.parallel()
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
-
- tg.run("test", "testrace")
-
- tg.runFail("test", "-race", "-covermode=set", "testrace")
- tg.grepStderr(`-covermode must be "atomic", not "set", when -race is enabled`, "-race -covermode=set was allowed")
- tg.grepBothNot("PASS", "something passed")
-}
-
// Issue 9737: verify that GOARM and GO386 affect the computed build ID.
func TestBuildIDContainsArchModeEnv(t *testing.T) {
if testing.Short() {
}))
}
-func TestTestRegexps(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("test", "-cpu=1", "-run=X/Y", "-bench=X/Y", "-count=2", "-v", "testregexp")
- var lines []string
- for _, line := range strings.SplitAfter(tg.getStdout(), "\n") {
- if strings.Contains(line, "=== RUN") || strings.Contains(line, "--- BENCH") || strings.Contains(line, "LOG") {
- lines = append(lines, line)
- }
- }
-
- // Important parts:
- // TestX is run, twice
- // TestX/Y is run, twice
- // TestXX is run, twice
- // TestZ is not run
- // BenchmarkX is run but only with N=1, once
- // BenchmarkXX is run but only with N=1, once
- // BenchmarkX/Y is run in full, twice
- want := `=== RUN TestX
- TestX: x_test.go:6: LOG: X running
-=== RUN TestX/Y
- TestX/Y: x_test.go:8: LOG: Y running
-=== RUN TestXX
- TestXX: z_test.go:10: LOG: XX running
-=== RUN TestX
- TestX: x_test.go:6: LOG: X running
-=== RUN TestX/Y
- TestX/Y: x_test.go:8: LOG: Y running
-=== RUN TestXX
- TestXX: z_test.go:10: LOG: XX running
- BenchmarkX: x_test.go:13: LOG: X running N=1
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=1
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=100
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=10000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=100000000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=1
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=100
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=10000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=100000000
- BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000000
- BenchmarkXX: z_test.go:18: LOG: XX running N=1
-`
-
- have := strings.Join(lines, "")
- if have != want {
- t.Errorf("reduced output:<<<\n%s>>> want:<<<\n%s>>>", have, want)
- }
-}
-
func TestListTests(t *testing.T) {
tooSlow(t)
var tg *testgoData
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
tg.tempFile("main.go", `package main; func main() { print("hello") }`)
src := tg.path("main.go")
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
tg.tempFile("main.go", `package main; import "fmt"; func main() { fmt.Print("hello upx") }`)
src := tg.path("main.go")
tg.mustExist(p1)
}
-func TestGoTestMinusN(t *testing.T) {
- // Intent here is to verify that 'go test -n' works without crashing.
- // This reuses flag_test.go, but really any test would do.
- tg := testgo(t)
- defer tg.cleanup()
- tg.run("test", "testdata/flag_test.go", "-n", "-args", "-v=7")
-}
-
func TestGoTestJSON(t *testing.T) {
skipIfGccgo(t, "gccgo does not have standard packages")
tooSlow(t)
func TestBadCommandLines(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
tg.tempFile("src/x/x.go", "package x\n")
tg.setenv("GOPATH", tg.path("."))
func TestFilepathUnderCwdFormat(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
+ tg.parallel()
tg.run("test", "-x", "-cover", "log")
tg.grepStderrNot(`\.log\.cover\.go`, "-x output should contain correctly formatted filepath under cwd")
}
}
}
-// Issue 26242.
-func TestGoTestWithoutTests(t *testing.T) {
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("test", "testnorun")
- tg.grepStdout(`testnorun\t\[no test files\]`, "do not want test to run")
-}
-
// Issue 25579.
func TestGoBuildDashODevNull(t *testing.T) {
tooSlow(t)
}
}
if err != nil {
- base.Errorf("go clean -testcache: %v", err)
+ if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
+ base.Errorf("go clean -testcache: %v", err)
+ }
}
}
}
Main bool // is this the main module?
Indirect bool // is this module only an indirect dependency of main module?
Dir string // directory holding files for this module, if any
- GoMod string // path to go.mod file for this module, if any
+ GoMod string // path to go.mod file used when loading this module, if any
GoVersion string // go version used in module
Error *ModuleError // error loading module
}
Err string // the error itself
}
+The file GoMod refers to may be outside the module directory if the
+module is in the module cache or if the -modfile flag is used.
+
The default output is to print the module path and then
information about the version and replacement if any.
For example, 'go list -m all' might print:
modload.InitMod() // Parses go.mod and sets cfg.BuildMod.
if cfg.BuildMod == "vendor" {
+ const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"
+
+ if *listVersions {
+ base.Fatalf(actionDisabledFormat, "determine available versions")
+ }
+ if *listU {
+ base.Fatalf(actionDisabledFormat, "determine available upgrades")
+ }
+
for _, arg := range args {
// In vendor mode, the module graph is incomplete: it contains only the
// explicit module dependencies and the modules that supply packages in
// the import graph. Reject queries that imply more information than that.
if arg == "all" {
- base.Fatalf("go list -m: can't compute 'all' using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")
+ base.Fatalf(actionDisabledFormat, "compute 'all'")
}
if strings.Contains(arg, "...") {
- base.Fatalf("go list -m: can't match module patterns using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")
+ base.Fatalf(actionDisabledFormat, "match module patterns")
}
}
}
execution. The "go mod download" command is useful mainly for pre-filling
the local cache or to compute the answers for a Go module proxy.
-By default, download reports errors to standard error but is otherwise silent.
+By default, download writes nothing to standard output. It may print progress
+messages and errors to standard error.
+
The -json flag causes download to print a sequence of JSON objects
to standard output, describing each downloaded module (or failure),
corresponding to this Go struct:
"os"
"path/filepath"
"strings"
- "time"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
var PkgMod string // $GOPATH/pkg/mod; set by package modload
-const logFindingDelay = 1 * time.Second
-
func cacheDir(path string) (string, error) {
if PkgMod == "" {
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
err error
}
c := r.cache.Do("versions:"+prefix, func() interface{} {
- logTimer := time.AfterFunc(logFindingDelay, func() {
- fmt.Fprintf(os.Stderr, "go: finding versions for %s\n", r.path)
- })
- defer logTimer.Stop()
-
list, err := r.r.Versions(prefix)
return cached{list, err}
}).(cached)
return cachedInfo{info, nil}
}
- logTimer := time.AfterFunc(logFindingDelay, func() {
- fmt.Fprintf(os.Stderr, "go: finding %s %s\n", r.path, rev)
- })
- defer logTimer.Stop()
-
info, err = r.r.Stat(rev)
if err == nil {
// If we resolved, say, 1234abcde to v0.0.0-20180604122334-1234abcdef78,
func (r *cachingRepo) Latest() (*RevInfo, error) {
c := r.cache.Do("latest:", func() interface{} {
- logTimer := time.AfterFunc(logFindingDelay, func() {
- fmt.Fprintf(os.Stderr, "go: finding %s latest\n", r.path)
- })
- defer logTimer.Stop()
-
info, err := r.r.Latest()
// Save info for likely future Stat call.
semtag := line[len(prefix):]
// Consider only tags that are valid and complete (not just major.minor prefixes).
- if c := semver.Canonical(semtag); c != "" && strings.HasPrefix(semtag, c) && (major == "" || semver.Major(c) == major) {
- highest = semver.Max(highest, semtag)
+ // NOTE: Do not replace the call to semver.Compare with semver.Max.
+ // We want to return the actual tag, not a canonicalized version of it,
+ // and semver.Max currently canonicalizes (see golang.org/issue/32700).
+ if c := semver.Canonical(semtag); c != "" && strings.HasPrefix(semtag, c) && (major == "" || semver.Major(c) == major) && semver.Compare(semtag, highest) > 0 {
+ highest = semtag
}
}
return list, nil
}
- // We assume that if the latest release of any major version has a go.mod
- // file, all subsequent major versions will also have go.mod files (and thus
- // be ineligible for use as +incompatible versions).
- // If we're wrong about a major version, users will still be able to 'go get'
- // specific higher versions explicitly — they just won't affect 'latest' or
- // appear in 'go list'.
- //
- // Conversely, we assume that if the latest release of any major version lacks
- // a go.mod file, all versions also lack go.mod files. If we're wrong, we may
- // include a +incompatible version that isn't really valid, but most
- // operations won't try to use that version anyway.
- //
- // These optimizations bring
- // 'go list -versions -m github.com/openshift/origin' down from 1m58s to 0m37s.
- // That's still not great, but a substantial improvement.
-
versionHasGoMod := func(v string) (bool, error) {
_, err := r.code.ReadFile(v, "go.mod", codehost.MaxGoMod)
if err == nil {
}
}
- var lastMajor string
+ var (
+ lastMajor string
+ lastMajorHasGoMod bool
+ )
for i, v := range incompatible {
major := semver.Major(v)
- if major == lastMajor {
- list = append(list, v+"+incompatible")
- continue
- }
- rem := incompatible[i:]
- j := sort.Search(len(rem), func(j int) bool {
- return semver.Major(rem[j]) != major
- })
- latestAtMajor := rem[j-1]
-
- ok, err := versionHasGoMod(latestAtMajor)
- if err != nil {
- return nil, err
- }
- if ok {
- // This major version has a go.mod file, so it is not allowed as
- // +incompatible. Subsequent major versions are likely to also have
- // go.mod files, so stop here.
- break
+ if major != lastMajor {
+ rem := incompatible[i:]
+ j := sort.Search(len(rem), func(j int) bool {
+ return semver.Major(rem[j]) != major
+ })
+ latestAtMajor := rem[j-1]
+
+ var err error
+ lastMajor = major
+ lastMajorHasGoMod, err = versionHasGoMod(latestAtMajor)
+ if err != nil {
+ return nil, err
+ }
}
- lastMajor = major
+ if lastMajorHasGoMod {
+ // The latest release of this major version has a go.mod file, so it is
+ // not allowed as +incompatible. It would be confusing to include some
+ // minor versions of this major version as +incompatible but require
+ // semantic import versioning for others, so drop all +incompatible
+ // versions for this major version.
+ //
+ // If we're wrong about a minor version in the middle, users will still be
+ // able to 'go get' specific tags for that version explicitly — they just
+ // won't appear in 'go list' or as the results for queries with inequality
+ // bounds.
+ continue
+ }
list = append(list, v+"+incompatible")
}
return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file1, rev, err1)
}
mpath1 := modfile.ModulePath(gomod1)
- found1 := err1 == nil && isMajor(mpath1, r.pathMajor)
+ found1 := err1 == nil && (isMajor(mpath1, r.pathMajor) || r.canReplaceMismatchedVersionDueToBug(mpath1))
var file2 string
if r.pathMajor != "" && r.codeRoot != r.modPath && !strings.HasPrefix(r.pathMajor, ".") {
return pathMajor[1:] == mpathMajor[1:]
}
+// canReplaceMismatchedVersionDueToBug reports whether versions of r
+// could replace versions of mpath with otherwise-mismatched major versions
+// due to a historical bug in the Go command (golang.org/issue/34254).
+func (r *codeRepo) canReplaceMismatchedVersionDueToBug(mpath string) bool {
+ // The bug caused us to erroneously accept unversioned paths as replacements
+ // for versioned gopkg.in paths.
+ unversioned := r.pathMajor == ""
+ replacingGopkgIn := strings.HasPrefix(mpath, "gopkg.in/")
+ return unversioned && replacingGopkgIn
+}
+
func (r *codeRepo) GoMod(version string) (data []byte, err error) {
if version != module.CanonicalVersion(version) {
return nil, fmt.Errorf("version %s is not canonical", version)
}
if HasModRoot() {
info.Dir = ModRoot()
- info.GoMod = filepath.Join(info.Dir, "go.mod")
+ info.GoMod = ModFilePath()
if modFile.Go != nil {
info.GoVersion = modFile.Go.Version
}
Module support
-Go 1.13 includes support for Go modules. Module-aware mode is active by default
-whenever a go.mod file is found in, or in a parent of, the current directory.
+The go command includes support for Go modules. Module-aware mode is active
+by default whenever a go.mod file is found in the current directory or in
+any parent directory.
The quickest way to take advantage of module support is to check out your
repository, create a go.mod file (described in the next section) there, and run
go commands from within that file tree.
-For more fine-grained control, Go 1.13 continues to respect
+For more fine-grained control, the go command continues to respect
a temporary environment variable, GO111MODULE, which can be set to one
of three string values: off, on, or auto (the default).
If GO111MODULE=on, then the go command requires the use of modules,
latest := map[string]string{} // path -> version
for _, r := range modFile.Replace {
if maybeInModule(path, r.Old.Path) {
- latest[r.Old.Path] = semver.Max(r.Old.Version, latest[r.Old.Path])
+ // Don't use semver.Max here; need to preserve +incompatible suffix.
+ v := latest[r.Old.Path]
+ if semver.Compare(r.Old.Version, v) > 0 {
+ v = r.Old.Version
+ }
+ latest[r.Old.Path] = v
}
}
return module.Version{}, "", &ImportMissingError{Path: path}
}
+ fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path)
+
candidates, err := QueryPackage(path, "latest", Allowed)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
+ // Ensure that the replacement directory actually exists:
+ // dirInModule does not report errors for missing modules,
+ // so if we don't report the error now, later failures will be
+ // very mysterious.
+ if _, err := os.Stat(dir); err != nil {
+ if os.IsNotExist(err) {
+ // Semantically the module version itself “exists” — we just don't
+ // have its source code. Remove the equivalence to os.ErrNotExist,
+ // and make the message more concise while we're at it.
+ err = fmt.Errorf("replacement directory %s does not exist", r.Path)
+ } else {
+ err = fmt.Errorf("replacement directory %s: %w", r.Path, err)
+ }
+ return dir, true, module.VersionError(mod, err)
+ }
return dir, true, nil
}
mod = r
if current != "" && !semver.IsValid(current) {
return nil, fmt.Errorf("invalid previous version %q", current)
}
- if cfg.BuildMod != "" && cfg.BuildMod != "mod" {
+ if cfg.BuildMod == "vendor" {
return nil, errQueryDisabled
}
if allowed == nil {
git add go.mod
git commit -m v1 go.mod
git tag start
- for i in v0.0.0-pre1 v0.0.0 v0.0.1 v0.0.2 v0.0.3 v0.1.0 v0.1.1 v0.1.2 v0.3.0 v1.0.0 v1.1.0 v1.9.0 v1.9.9 v1.9.10-pre1 v1.9.10-pre2+metadata; do
+ for i in v0.0.0-pre1 v0.0.0 v0.0.1 v0.0.2 v0.0.3 v0.1.0 v0.1.1 v0.1.2 v0.3.0 v1.0.0 v1.1.0 v1.9.0 v1.9.9 v1.9.10-pre1 v1.9.10-pre2+metadata unversioned; do
echo before $i >status
git add status
git commit -m "before $i" status
{path: queryRepo, query: "v0.2", err: `no matching versions for query "v0.2"`},
{path: queryRepo, query: "v0.0", vers: "v0.0.3"},
{path: queryRepo, query: "v1.9.10-pre2+metadata", vers: "v1.9.10-pre2.0.20190513201126-42abcb6df8ee"},
+ {path: queryRepo, query: "ed5ffdaa", vers: "v1.9.10-pre2.0.20191220134614-ed5ffdaa1f5e"},
// golang.org/issue/29262: The major version for for a module without a suffix
// should be based on the most recent tag (v1 as appropriate, not v0
{path: queryRepoV2, query: "v2.6.0-pre1", vers: "v2.6.0-pre1"},
{path: queryRepoV2, query: "latest", vers: "v2.5.5"},
- // e0cf3de987e6 is the latest commit on the master branch, and it's actually
- // v1.19.10-pre1, not anything resembling v3: attempting to query it as such
- // should fail.
+ // Commit e0cf3de987e6 is actually v1.19.10-pre1, not anything resembling v3,
+ // and it has a go.mod file with a non-v3 module path. Attempting to query it
+ // as the v3 module should fail.
{path: queryRepoV3, query: "e0cf3de987e6", err: `vcs-test.golang.org/git/querytest.git/v3@v3.0.0-20180704024501-e0cf3de987e6: invalid version: go.mod has non-.../v3 module path "vcs-test.golang.org/git/querytest.git" (and .../v3/go.mod does not exist) at revision e0cf3de987e6`},
+
+ // The querytest repo does not have any commits tagged with major version 3,
+ // and the latest commit in the repo has a go.mod file specifying a non-v3 path.
+ // That should prevent us from resolving any version for the /v3 path.
{path: queryRepoV3, query: "latest", err: `no matching versions for query "latest"`},
{path: emptyRepo, query: "latest", vers: "v0.0.0-20180704023549-7bb914627242"},
// For "go build -trimpath", rewrite package source directory
// to a file system-independent path (just the import path).
if cfg.BuildTrimpath {
- if m := a.Package.Module; m != nil {
- rewrite += ";" + m.Dir + "=>" + m.Path + "@" + m.Version
+ if m := a.Package.Module; m != nil && m.Version != "" {
+ rewrite += ";" + a.Package.Dir + "=>" + m.Path + "@" + m.Version + strings.TrimPrefix(a.Package.ImportPath, m.Path)
} else {
rewrite += ";" + a.Package.Dir + "=>" + a.Package.ImportPath
}
+++ /dev/null
-package x
-
-import _ "appengine"
-import _ "nonexistent.rsc.io" // domain does not exist
+++ /dev/null
-package flag_test
-
-import (
- "flag"
- "log"
- "testing"
-)
-
-var v = flag.Int("v", 0, "v flag")
-
-// Run this as go test pkg -v=7
-func TestVFlagIsSet(t *testing.T) {
- if *v != 7 {
- log.Fatal("v flag not set")
- }
-}
+++ /dev/null
-package p
-
-import "bad"
+++ /dev/null
-package p
-
-import "conflict"
+++ /dev/null
-package bad // import
+++ /dev/null
-package conflict // import "a"
+++ /dev/null
-package conflict /* import "b" */
+++ /dev/null
-package x // import "works/x"
+++ /dev/null
-package x // important! not an import comment
+++ /dev/null
-package x // import "my/x"
+++ /dev/null
-package p
-
-import _ "works/x"
+++ /dev/null
-package p
-
-import "wrongplace"
+++ /dev/null
-package selfimport
-
-import "selfimport"
goversion=<current Go version; for example, 1.12>
:=<OS-specific path list separator>
-The scripts supporting files are unpacked relative to $GOPATH/src (aka $WORK/gopath/src)
+The scripts' supporting files are unpacked relative to $GOPATH/src (aka $WORK/gopath/src)
and then the script begins execution in that directory as well. Thus the example above runs
in $WORK/gopath/src with GOPATH=$WORK/gopath and $WORK/gopath/src/hello.go
containing the listed contents.
go test x_test.go
! stdout 'cached'
+# golang.org/issue/29100: 'go clean -testcache' should succeed
+# if the cache directory doesn't exist at all.
+# It should not write a testexpire.txt file, since there are no
+# test results that need to be invalidated in the first place.
+env GOCACHE=$WORK/nonexistent
+go clean -testcache
+! exists $WORK/nonexistent
-- x/x_test.go --
package x_test
"testing"
)
func TestMain(t *testing.T) {
-}
\ No newline at end of file
+}
# See: https://github.com/golang/go/issues/8912
[linux] [ppc64] skip
+# External linking is not supported on linux/riscv64.
+# See: https://github.com/golang/go/issues/36739
+[linux] [riscv64] skip
+
# External linking is not supported on darwin/386 (10.14+).
# See: https://github.com/golang/go/issues/31751
[darwin] [386] skip
# 'go get all' should consider test dependencies with or without -t.
cp go.mod.empty go.mod
-go get all
+go get -d all
grep 'rsc.io/quote v1.5.2$' go.mod
-- go.mod.empty --
module declares its path as: badchain.example.com/c
but was required as: example.com/badchain/c
-- list-missing-expected --
+go: finding module for package example.com/badchain/c
go: found example.com/badchain/c in example.com/badchain/c v1.1.0
go: m/use imports
example.com/badchain/c: example.com/badchain/c@v1.1.0: parsing go.mod:
module declares its path as: badchain.example.com/c
but was required as: example.com/badchain/c
-- list-missing-test-expected --
+go: finding module for package example.com/badchain/c
go: found example.com/badchain/c in example.com/badchain/c v1.1.0
go: m/testuse tested by
m/testuse.test imports
go clean -modcache
go list all
+# -mod=readonly must not cause 'go list -m' to fail.
+# (golang.org/issue/36478)
+go list -m all
+! stderr 'cannot query module'
+
# -mod=readonly should reject inconsistent go.mod files
# (ones that would be rewritten).
go mod edit -require rsc.io/sampler@v1.2.0
# Replacing gopkg.in/[…].vN with a repository with a root go.mod file
# specifying […].vN and a compatible version should succeed, even if
# the replacement path is not a gopkg.in path.
-cd dot-to-dot
-go list gopkg.in/src-d/go-git.v4
+cd 4-to-4
+go list -m gopkg.in/src-d/go-git.v4
--- dot-to-dot/go.mod --
+# Previous versions of the "go" command accepted v0 and v1 pseudo-versions
+# as replacements for gopkg.in/[…].v4.
+# As a special case, we continue to accept those.
+
+cd ../4-to-0
+go list -m gopkg.in/src-d/go-git.v4
+
+cd ../4-to-1
+go list -m gopkg.in/src-d/go-git.v4
+
+cd ../4-to-incompatible
+go list -m gopkg.in/src-d/go-git.v4
+
+# A mismatched gopkg.in path should not be able to replace a different major version.
+cd ../3-to-gomod-4
+! go list -m gopkg.in/src-d/go-git.v3
+stderr '^go: gopkg\.in/src-d/go-git\.v3@v3.0.0-20190801152248-0d1a009cbb60: invalid version: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$'
+
+-- 4-to-4/go.mod --
module golang.org/issue/34254
go 1.13
require gopkg.in/src-d/go-git.v4 v4.13.1
replace gopkg.in/src-d/go-git.v4 v4.13.1 => github.com/src-d/go-git/v4 v4.13.1
+-- 4-to-1/go.mod --
+module golang.org/issue/34254
+
+go 1.13
+
+require gopkg.in/src-d/go-git.v4 v4.13.1
+
+replace gopkg.in/src-d/go-git.v4 v4.13.1 => github.com/src-d/go-git v1.0.1-0.20190801152248-0d1a009cbb60
+-- 4-to-0/go.mod --
+module golang.org/issue/34254
+
+go 1.13
+
+require gopkg.in/src-d/go-git.v4 v4.13.1
+
+replace gopkg.in/src-d/go-git.v4 v4.13.1 => github.com/src-d/go-git v0.0.0-20190801152248-0d1a009cbb60
+-- 4-to-incompatible/go.mod --
+module golang.org/issue/34254
+
+go 1.13
+
+require gopkg.in/src-d/go-git.v4 v4.13.1
+
+replace gopkg.in/src-d/go-git.v4 v4.13.1 => github.com/src-d/go-git v4.6.0+incompatible
+-- 3-to-gomod-4/go.mod --
+module golang.org/issue/34254
+go 1.13
+
+require gopkg.in/src-d/go-git.v3 v3.2.0
+
+// This replacement has a go.mod file declaring its path to be
+// gopkg.in/src-d/go-git.v4, so it cannot be used as a replacement for v3.
+replace gopkg.in/src-d/go-git.v3 v3.2.0 => gopkg.in/src-d/go-git.v3 v3.0.0-20190801152248-0d1a009cbb60
cd fail
! go list all
stdout 'localhost.fail'
-stderr '^can.t load package: m.go:3:8: module w@latest found \(v0.0.0-00010101000000-000000000000, replaced by ../w\), but does not contain package w$'
+stderr '^can''t load package: m.go:4:2: module w@latest found \(v0.0.0-00010101000000-000000000000, replaced by ../w\), but does not contain package w$'
+stderr '^can''t load package: m.go:5:2: nonexist@v0.1.0: replacement directory ../nonexist does not exist$'
-- go.mod --
module example.com/m
example.com/v => ./v
)
+replace (
+ example.com/i v2.0.0+incompatible => ./i2
+)
+
-- m.go --
package main
import (
_ "example.com/x/v3"
_ "example.com/y/z/w"
_ "example.com/v"
+ _ "example.com/i"
)
func main() {}
-- v/v.go --
package v
+-- i2/go.mod --
+module example.com/i
+-- i2/i.go --
+package i
+
-- fail/m.go --
package main
-import _ "w"
+import (
+ _ "w"
+ _ "nonexist"
+)
func main() {}
replace w => ../w
+replace nonexist v0.1.0 => ../nonexist
+++ /dev/null
-env GO111MODULE=on
-
-go list -e -f '{{.Incomplete}}' runbad1.go
-stdout true
-! go run runbad1.go
-stderr 'use of internal package m/x/internal not allowed'
-
-go list -e -f '{{.Incomplete}}' runbad2.go
-stdout true
-! go run runbad2.go
-stderr 'use of internal package m/x/internal/y not allowed'
-
-go list -e -f '{{.Incomplete}}' runok.go
-stdout false
-go run runok.go
-
--- go.mod --
-module m
-
--- x/internal/internal.go --
-package internal
-
--- x/internal/y/y.go --
-package y
-
--- internal/internal.go --
-package internal
-
--- internal/z/z.go --
-package z
-
--- runbad1.go --
-package main
-import _ "m/x/internal"
-func main() {}
-
--- runbad2.go --
-package main
-import _ "m/x/internal/y"
-func main() {}
-
--- runok.go --
-package main
-import _ "m/internal"
-import _ "m/internal/z"
-func main() {}
go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m x
stdout '^v1.0.0 $'
+# -mod=vendor should cause 'go list' flags that look up versions to fail.
+! go list -mod=vendor -versions -m x
+stderr '^go list -m: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$'
+! go list -mod=vendor -u -m x
+stderr '^go list -m: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$'
+
# 'go list -mod=vendor -m' on a transitive dependency that does not
# provide vendored packages should give a helpful error rather than
# 'not a known dependency'.
go mod init example.com/m
grep example.com/m go.alt.mod
+# 'go env GOMOD' should print the path to the real file.
+# 'go env' does not recognize the '-modfile' flag.
+go env GOMOD
+stdout '^\$WORK[/\\]gopath[/\\]src[/\\]go.mod$'
+
+# 'go list -m' should print the effective go.mod file as GoMod though.
+go list -m -f '{{.GoMod}}'
+stdout '^go.alt.mod$'
+
# go mod edit should operate on the alternate file
go mod edit -require rsc.io/quote@v1.5.2
grep rsc.io/quote go.alt.mod
env GO111MODULE=off
-! go test badtest/...
+! go test badtest/badexec
! stdout ^ok
stdout ^FAIL\tbadtest/badexec
+
+! go test badtest/badsyntax
+! stdout ^ok
stdout ^FAIL\tbadtest/badsyntax
+
+! go test badtest/badvar
+! stdout ^ok
stdout ^FAIL\tbadtest/badvar
+! go test notest
+! stdout ^ok
+stderr '^notest.hello.go:6:1: .*declaration' # Exercise issue #7108
+
-- badtest/badexec/x_test.go --
package badexec
func f() {
_ = notdefined
}
+-- notest/hello.go --
+package notest
+
+func hello() {
+ println("hello world")
+}
+Hello world
+++ /dev/null
-// C code!
+++ /dev/null
-package badc
+++ /dev/null
-pkg badpkg
+++ /dev/null
-package bench
-
-import "testing"
-
-func Benchmark(b *testing.B) {
-}
+++ /dev/null
-package benchfatal
-
-import "testing"
-
-func BenchmarkThatCallsFatal(b *testing.B) {
- b.Fatal("called by benchmark")
-}
+++ /dev/null
-package p
-
-/*
-// hi
-*/
-import "C"
-
-func F() {}
+++ /dev/null
-TEXT asm(SB),$0
- RET
+++ /dev/null
-package p
-
-/*
-void
-f(void)
-{
-}
-*/
-import "C"
-
-var b bool
-
-func F() {
- if b {
- for {
- }
- }
- C.f()
-}
+++ /dev/null
-package p
-
-import "testing"
-
-func TestF(t *testing.T) {
- F()
-}
+++ /dev/null
-package p
-
-/*
-void
-f(void)
-{
-}
-*/
-import "C"
-
-var b bool
-
-func F() {
- if b {
- for {
- }
- }
- C.f()
-}
+++ /dev/null
-package p_test
-
-import (
- . "cgocover2"
- "testing"
-)
-
-func TestF(t *testing.T) {
- F()
-}
+++ /dev/null
-package p
-
-/*
-void
-f(void)
-{
-}
-*/
-import "C"
-
-var b bool
-
-func F() {
- if b {
- for {
- }
- }
- C.f()
-}
+++ /dev/null
-package p_test
-
-import (
- . "cgocover3"
- "testing"
-)
-
-func TestF(t *testing.T) {
- F()
-}
+++ /dev/null
-package p
-
-/*
-void
-f(void)
-{
-}
-*/
-import "C"
-
-var b bool
-
-func F() {
- if b {
- for {
- }
- }
- C.f()
-}
+++ /dev/null
-package p_test
-
-import (
- . "cgocover4"
- "testing"
-)
-
-func TestF(t *testing.T) {
- F()
-}
+++ /dev/null
-package main
-
-import (
- _ "dupload/p2"
- _ "p"
-)
-
-func main() {}
+++ /dev/null
-package p2
-
-import _ "dupload/vendor/p"
+++ /dev/null
-//go:generate echo hello world
-
-package gencycle
-
-import _ "gencycle"
+++ /dev/null
-package main
-
-import _ "importmain/test"
-
-func main() {}
+++ /dev/null
-package test
+++ /dev/null
-package test_test
-
-import "testing"
-import _ "importmain/ismain"
-
-func TestCase(t *testing.T) {}
+++ /dev/null
-package multimain_test
-
-import "testing"
-
-func TestMain(m *testing.M) {
- // Some users run m.Run multiple times, changing
- // some kind of global state between runs.
- // This used to work so I guess now it has to keep working.
- // See golang.org/issue/23129.
- m.Run()
- m.Run()
-}
-
-func Test(t *testing.T) {
- t.Log("notwithstanding")
-}
+++ /dev/null
-package not_main
-
-func F() {}
+++ /dev/null
-package notest
-
-func hello() {
- println("hello world")
-}
-Hello world
+++ /dev/null
-package main
-
-import _ "run/subdir/internal/private"
-
-func main() {}
+++ /dev/null
-package main
-
-import _ "run/internal"
-
-func main() {}
+++ /dev/null
-package internal
+++ /dev/null
-package private
+++ /dev/null
-package p
-
-import (
- "testing"
- "time"
-)
-
-func Test1(t *testing.T) {
- time.Sleep(200 * time.Millisecond)
-}
+++ /dev/null
-package p
-
-import (
- "testing"
- "time"
-)
-
-func Test1(t *testing.T) {
- time.Sleep(200 * time.Millisecond)
-}
+++ /dev/null
-package p
-
-// missing import
-
-var _ = io.DoesNotExist
+++ /dev/null
-package p
-
-func f() (x.y, z int) {
-}
+++ /dev/null
-package p1
-
-import _ "testcycle/p2"
-
-func init() {
- println("p1 init")
-}
+++ /dev/null
-package p1
-
-import "testing"
-
-func Test(t *testing.T) {
-}
+++ /dev/null
-package p2
-
-import _ "testcycle/p3"
-
-func init() {
- println("p2 init")
-}
+++ /dev/null
-package p3
-
-func init() {
- println("p3 init")
-}
+++ /dev/null
-package p3
-
-import (
- "testing"
-
- _ "testcycle/p1"
-)
-
-func Test(t *testing.T) {
-}
+++ /dev/null
-package q1
+++ /dev/null
-package q1
-
-import "testing"
-import _ "testcycle/q1"
-
-func Test(t *testing.T) {}
+++ /dev/null
-package p1
+++ /dev/null
-package p1
-
-import _ "testdep/p2"
+++ /dev/null
-package p2
-
-import _ "testdep/p3"
+++ /dev/null
-// +build ignore
-
-package ignored
+++ /dev/null
-package p
-
-func init() {
- panic("go test must not link and run test binaries without tests")
-}
+++ /dev/null
-package testrace
-
-import "testing"
-
-func TestRace(t *testing.T) {
- for i := 0; i < 10; i++ {
- c := make(chan int)
- x := 1
- go func() {
- x = 2
- c <- 1
- }()
- x = 3
- <-c
- _ = x
- }
-}
-
-func BenchmarkRace(b *testing.B) {
- for i := 0; i < b.N; i++ {
- c := make(chan int)
- x := 1
- go func() {
- x = 2
- c <- 1
- }()
- x = 3
- <-c
- _ = x
- }
-}
+++ /dev/null
-package x
-
-import "testing"
-
-func TestX(t *testing.T) {
- t.Logf("LOG: X running")
- t.Run("Y", func(t *testing.T) {
- t.Logf("LOG: Y running")
- })
-}
-
-func BenchmarkX(b *testing.B) {
- b.Logf("LOG: X running N=%d", b.N)
- b.Run("Y", func(b *testing.B) {
- b.Logf("LOG: Y running N=%d", b.N)
- })
-}
+++ /dev/null
-package x
-
-import "testing"
-
-func TestZ(t *testing.T) {
- t.Logf("LOG: Z running")
-}
-
-func TestXX(t *testing.T) {
- t.Logf("LOG: XX running")
-}
-
-func BenchmarkZ(b *testing.B) {
- b.Logf("LOG: Z running N=%d", b.N)
-}
-
-func BenchmarkXX(b *testing.B) {
- b.Logf("LOG: XX running N=%d", b.N)
-}
if timeout != 0 {
errChannel = make(chan error, 2)
- time.AfterFunc(timeout, func() {
+ timer := time.AfterFunc(timeout, func() {
errChannel <- timeoutError{}
})
+ defer timer.Stop()
}
rawConn, err := dialer.Dial(network, addr)
//
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
// be released (using CFRelease) after we've consumed its content.
-int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
+static int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
int i;
if (debugDarwinRoots) {
if err != nil {
return nil, err
}
+ if len(chain) < 1 {
+ return nil, errors.New("x509: internal error: system verifier returned an empty chain")
+ }
- chains = append(chains, chain)
+ // Mitigate CVE-2020-0601, where the Windows system verifier might be
+ // tricked into using custom curve parameters for a trusted root, by
+ // double-checking all ECDSA signatures. If the system was tricked into
+ // using spoofed parameters, the signature will be invalid for the correct
+ // ones we parsed. (We don't support custom curves ourselves.)
+ for i, parent := range chain[1:] {
+ if parent.PublicKeyAlgorithm != ECDSA {
+ continue
+ }
+ if err := parent.CheckSignature(chain[i].SignatureAlgorithm,
+ chain[i].RawTBSCertificate, chain[i].Signature); err != nil {
+ return nil, err
+ }
+ }
- return chains, nil
+ return [][]*Certificate{chain}, nil
}
func loadSystemRoots() (*CertPool, error) {
go func() {
rows, err := db.Query("SELECT|people|name,photo|")
if err != nil {
- t.Fatalf("Query: %v", err)
+ t.Errorf("Query: %v", err)
+ return
}
rows.Close()
saturateDone.Done()
}
saturate.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
state = 2
// Now cancel the request while it is waiting.
{"Import(full, FindOnly)", "go/build/doesnotexist", "", FindOnly},
{"Import(local, FindOnly)", "./doesnotexist", filepath.Join(ctxt.GOROOT, "src/go/build"), FindOnly},
}
- for _, test := range tests {
- p, err := ctxt.Import(test.path, test.srcDir, test.mode)
- if err == nil || !strings.HasPrefix(err.Error(), "cannot find package") {
- t.Errorf(`%s got error: %q, want "cannot find package" error`, test.label, err)
- }
- // If an error occurs, build.Import is documented to return
- // a non-nil *Package containing partial information.
- if p == nil {
- t.Fatalf(`%s got nil p, want non-nil *Package`, test.label)
- }
- // Verify partial information in p.
- if p.ImportPath != "go/build/doesnotexist" {
- t.Errorf(`%s got p.ImportPath: %q, want "go/build/doesnotexist"`, test.label, p.ImportPath)
- }
+
+ defer os.Setenv("GO111MODULE", os.Getenv("GO111MODULE"))
+
+ for _, GO111MODULE := range []string{"off", "on"} {
+ t.Run("GO111MODULE="+GO111MODULE, func(t *testing.T) {
+ os.Setenv("GO111MODULE", GO111MODULE)
+
+ for _, test := range tests {
+ p, err := ctxt.Import(test.path, test.srcDir, test.mode)
+
+ errOk := (err != nil && strings.HasPrefix(err.Error(), "cannot find package"))
+ wantErr := `"cannot find package" error`
+ if test.srcDir == "" {
+ if err != nil && strings.Contains(err.Error(), "is not in GOROOT") {
+ errOk = true
+ }
+ wantErr = `"cannot find package" or "is not in GOROOT" error`
+ }
+ if !errOk {
+ t.Errorf("%s got error: %q, want %s", test.label, err, wantErr)
+ }
+ // If an error occurs, build.Import is documented to return
+ // a non-nil *Package containing partial information.
+ if p == nil {
+ t.Fatalf(`%s got nil p, want non-nil *Package`, test.label)
+ }
+ // Verify partial information in p.
+ if p.ImportPath != "go/build/doesnotexist" {
+ t.Errorf(`%s got p.ImportPath: %q, want "go/build/doesnotexist"`, test.label, p.ImportPath)
+ }
+ }
+ })
}
}
},
"internal/cfg": {"L0"},
- "internal/poll": {"L0", "internal/oserror", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows"},
+ "internal/poll": {"L0", "internal/oserror", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows", "internal/syscall/unix"},
"internal/testlog": {"L0"},
"os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/testlog"},
"path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
if !ok || f.Recv != nil {
continue
}
- if params := f.Type.Params; params.List != nil {
+ if params := f.Type.Params; len(params.List) != 0 {
continue // function has params; not a valid example
}
numDecl++
base := derefStructPtr(x.typ)
sel := selx.Sel.Name
- obj, index, indirect := check.LookupFieldOrMethod(base, false, check.pkg, sel)
+ obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
switch obj.(type) {
case nil:
check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
goto Error
}
- obj, index, indirect = check.LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
switch {
case index != nil:
// the method's formal receiver base type, nor was the receiver addressable.
//
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
- return (*Checker)(nil).LookupFieldOrMethod(T, addressable, pkg, name)
+ return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name)
}
-// Internal use of Checker.LookupFieldOrMethod: If the obj result is a method
+// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method
// associated with a concrete (non-interface) type, the method's signature
// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
// the method's type.
// TODO(gri) Now that we provide the *Checker, we can probably remove this
-// caveat by calling Checker.objDecl from LookupFieldOrMethod. Investigate.
+// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate.
-// LookupFieldOrMethod is like the external version but completes interfaces
+// lookupFieldOrMethod is like the external version but completes interfaces
// as necessary.
-func (check *Checker) LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// Methods cannot be associated to a named pointer type
// (spec: "The type denoted by T is called the receiver base type;
// it must not be a pointer or interface type and it must be declared
// not have found it for T (see also issue 8590).
if t, _ := T.(*Named); t != nil {
if p, _ := t.underlying.(*Pointer); p != nil {
- obj, index, indirect = check.lookupFieldOrMethod(p, false, pkg, name)
+ obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
if _, ok := obj.(*Func); ok {
return nil, nil, false
}
}
}
- return check.lookupFieldOrMethod(T, addressable, pkg, name)
+ return check.rawLookupFieldOrMethod(T, addressable, pkg, name)
}
// TODO(gri) The named type consolidation and seen maps below must be
// types always have only one representation (even when imported
// indirectly via different packages.)
-// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
-func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod.
+func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// WARNING: The code in this function is extremely subtle - do not modify casually!
// This function and NewMethodSet should be kept in sync.
// A concrete type implements T if it implements all methods of T.
for _, m := range T.allMethods {
- obj, _, _ := check.lookupFieldOrMethod(V, false, m.pkg, m.name)
+ obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
// we must have a method (not a field of matching function type)
f, _ := obj.(*Func)
// It reports whether the read was successful.
func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
var bytes String
- if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 {
+ if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 ||
+ len(bytes)*8/8 != len(bytes) {
return false
}
length = headerLen + len32
}
- if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) {
+ if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) {
return false
}
if skipHeader && !out.Skip(int(headerLen)) {
// read advances a String by n bytes and returns them. If less than n bytes
// remain, it returns nil.
func (s *String) read(n int) []byte {
- if len(*s) < n {
+ if len(*s) < n || n < 0 {
return nil
}
v := (*s)[:n]
length = length << 8
length = length | uint32(b)
}
- if int(length) < 0 {
- // This currently cannot overflow because we read uint24 at most, but check
- // anyway in case that changes in the future.
- return false
- }
v := s.read(int(length))
if v == nil {
return false
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build s390x,!go1.11 !arm,!amd64,!s390x,!ppc64le gccgo appengine nacl
+// +build s390x,!go1.11 !amd64,!s390x,!ppc64le gccgo appengine nacl
package poly1305
// Package note defines the notes signed by the Go module database server.
//
-// This package is part of a DRAFT of what the Go module database server will look like.
-// Do not assume the details here are final!
-//
// A note is text signed by one or more server keys.
// The text should be ignored unless the note is signed by
// a trusted server key and the signature has been verified
--- /dev/null
+// Copyright 2019 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.
+
+// +build riscv64
+
+package cpu
asmArchMips64LE = asmArch{name: "mips64le", bigEndian: false, stack: "R29", lr: true}
asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true}
asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true}
+ asmArchRISCV64 = asmArch{name: "riscv64", bigEndian: false, stack: "SP", lr: true}
asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
asmArchWasm = asmArch{name: "wasm", bigEndian: false, stack: "SP", lr: false}
&asmArchMips64LE,
&asmArchPpc64,
&asmArchPpc64LE,
+ &asmArchRISCV64,
&asmArchS390X,
&asmArchWasm,
}
// These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding.
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
+// https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state
var replacementTable = [...]rune{
'\u20AC', // First entry is what 0x80 should be replaced with.
'\u0081',
--- /dev/null
+// Copyright 2019 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.
+
+// +build js,wasm
+
+package poll
+
+import "syscall"
+
+// fcntl not supported on js/wasm
+func fcntl(fd int, cmd int, arg int) (int, error) {
+ return 0, syscall.ENOSYS
+}
--- /dev/null
+// Copyright 2019 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.
+
+// +build aix darwin solaris
+
+package poll
+
+import (
+ "syscall"
+)
+
+// Use a helper function to call fcntl. This is defined in C in
+// libgo/runtime.
+//extern __go_fcntl_uintptr
+func libc_fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr)
+
+func fcntl(fd int, cmd int, arg int) (int, error) {
+ syscall.Entersyscall()
+ r, e := libc_fcntl(uintptr(fd), uintptr(cmd), uintptr(arg))
+ syscall.Exitsyscall()
+ if e != 0 {
+ return int(r), syscall.Errno(e)
+ }
+ return int(r), nil
+}
--- /dev/null
+// Copyright 2019 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.
+
+// +build dragonfly freebsd linux netbsd openbsd
+
+package poll
+
+import (
+ "syscall"
+)
+
+// Use a helper function to call fcntl. This is defined in C in
+// libgo/runtime.
+//extern __go_fcntl_uintptr
+func libc_fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr)
+
+func fcntl(fd int, cmd int, arg int) (int, error) {
+ syscall.Entersyscall()
+ r, e := libc_fcntl(uintptr(fd), uintptr(cmd), uintptr(arg))
+ syscall.Exitsyscall()
+ if e != 0 {
+ return int(r), syscall.Errno(e)
+ }
+ return int(r), nil
+}
package poll
-import (
- "syscall"
- _ "unsafe" // for go:linkname
-)
+import "syscall"
// Fsync invokes SYS_FCNTL with SYS_FULLFSYNC because
// on OS X, SYS_FSYNC doesn't fully flush contents to disk.
_, e1 := fcntl(fd.Sysfd, syscall.F_FULLFSYNC, 0)
return e1
}
-
-// Use a helper function to call fcntl. This is defined in C in
-// libgo/runtime.
-//extern __go_fcntl_uintptr
-func libc_fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr)
-
-func fcntl(fd int, cmd int, arg int) (int, error) {
- syscall.Entersyscall()
- r, e := libc_fcntl(uintptr(fd), uintptr(cmd), uintptr(arg))
- syscall.Exitsyscall()
- if e != 0 {
- return int(r), syscall.Errno(e)
- }
- return int(r), nil
-}
import "syscall"
-// Use a helper function to call fcntl. This is defined in C in
-// libgo/runtime.
-//extern __go_fcntl_uintptr
-func libc_fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr)
-
// Fsync wraps syscall.Fsync.
func (fd *FD) Fsync() error {
if err := fd.incref(); err != nil {
defer fd.decref()
return syscall.Fsync(fd.Sysfd)
}
-
-func fcntl(fd int, cmd int, arg int) (int, error) {
- syscall.Entersyscall()
- r, e := libc_fcntl(uintptr(fd), uintptr(cmd), uintptr(arg))
- syscall.Exitsyscall()
- if e != 0 {
- return int(r), syscall.Errno(e)
- }
- return int(r), nil
-}
// DupCloseOnExec dups fd and marks it close-on-exec.
func DupCloseOnExec(fd int) (int, string, error) {
- if atomic.LoadInt32(&tryDupCloexec) == 1 {
+ if syscall.F_DUPFD_CLOEXEC != 0 && atomic.LoadInt32(&tryDupCloexec) == 1 {
r0, e1 := fcntl(fd, syscall.F_DUPFD_CLOEXEC, 0)
if e1 == nil {
return r0, "", nil
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix dragonfly freebsd hurd linux netbsd openbsd solaris
+// +build dragonfly freebsd linux netbsd openbsd
package unix
+++ /dev/null
-// Copyright 2018 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.
-
-// +build darwin
-
-package unix
-
-import (
- "syscall"
- _ "unsafe" // for go:linkname
-)
-
-func IsNonblock(fd int) (nonblocking bool, err error) {
- flag, e1 := fcntl(fd, syscall.F_GETFL, 0)
- if e1 != nil {
- return false, e1
- }
- return flag&syscall.O_NONBLOCK != 0, nil
-}
-
-// Implemented in syscall/syscall_darwin.go.
-//go:linkname fcntl syscall.fcntl
-func fcntl(fd int, cmd int, arg int) (int, error)
--- /dev/null
+// Copyright 2018 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.
+
+// +build aix darwin solaris
+
+package unix
+
+import "syscall"
+
+//extern __go_fcntl_uintptr
+func fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr)
+
+func IsNonblock(fd int) (nonblocking bool, err error) {
+ flag, e1 := fcntl(uintptr(fd), syscall.F_GETFL, 0)
+ if e1 != 0 {
+ return false, syscall.Errno(e1)
+ }
+ return flag&syscall.O_NONBLOCK != 0, nil
+}
func ExampleReadAtLeast() {
r := strings.NewReader("some io.Reader stream to be read\n")
- buf := make([]byte, 33)
+ buf := make([]byte, 14)
if _, err := io.ReadAtLeast(r, buf, 4); err != nil {
log.Fatal(err)
}
}
// Output:
- // some io.Reader stream to be read
- //
+ // some io.Reader
// error: short buffer
- // error: EOF
+ // error: unexpected EOF
}
func ExampleReadFull() {
// license that can be found in the LICENSE file.
// +build ignore
-// +build !math_big_pure_go
+// +build !math_big_pure_go,!riscv64
package big
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// -build math_big_pure_go
+// -build math_big_pure_go riscv64
package big
// GCD sets z to the greatest common divisor of a and b and returns z.
// If x or y are not nil, GCD sets their value such that z = a*x + b*y.
+//
+// a and b may be positive, zero or negative.
// Regardless of the signs of a and b, z is always >= 0.
+//
// If a == b == 0, GCD sets z = x = y = 0.
+//
// If a == 0 and b != 0, GCD sets z = |b|, x = 0, y = sign(b) * 1.
+//
// If a != 0 and b == 0, GCD sets z = |a|, x = sign(a) * 1, y = 0.
func (z *Int) GCD(x, y, a, b *Int) *Int {
if len(a.abs) == 0 || len(b.abs) == 0 {
}
addr := l.Addr().String()
l.Close()
- // On OpenBSD, interference from TestSelfConnect is mysteriously
+ // On OpenBSD, interference from TestTCPSelfConnect is mysteriously
// causing the first attempt to hang for a few seconds, so we throw
// away the first result and keep the second.
for i := 1; ; i++ {
// Without stuff before onion/local, they're fine to
// use DNS. With a search path,
- // "onion.vegegtables.com" can use DNS. Without a
+ // "onion.vegetables.com" can use DNS. Without a
// search path (or with a trailing dot), the queries
// are just kinda useless, but don't reveal anything
// private.
--- /dev/null
+// Copyright 2011 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.
+
+// Tests a Go CGI program running under a Go CGI host process.
+// Further, the two programs are the same binary, just checking
+// their environment to figure out what mode to run in.
+
+package cgi
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "time"
+)
+
+// This test is a CGI host (testing host.go) that runs its own binary
+// as a child process testing the other half of CGI (child.go).
+func TestHostingOurselves(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ expectedMap := map[string]string{
+ "test": "Hello CGI-in-CGI",
+ "param-a": "b",
+ "param-foo": "bar",
+ "env-GATEWAY_INTERFACE": "CGI/1.1",
+ "env-HTTP_HOST": "example.com",
+ "env-PATH_INFO": "",
+ "env-QUERY_STRING": "foo=bar&a=b",
+ "env-REMOTE_ADDR": "1.2.3.4",
+ "env-REMOTE_HOST": "1.2.3.4",
+ "env-REMOTE_PORT": "1234",
+ "env-REQUEST_METHOD": "GET",
+ "env-REQUEST_URI": "/test.go?foo=bar&a=b",
+ "env-SCRIPT_FILENAME": os.Args[0],
+ "env-SCRIPT_NAME": "/test.go",
+ "env-SERVER_NAME": "example.com",
+ "env-SERVER_PORT": "80",
+ "env-SERVER_SOFTWARE": "go",
+ }
+ replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
+
+ if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
+ t.Errorf("got a Content-Type of %q; expected %q", got, expected)
+ }
+ if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
+ t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
+ }
+}
+
+type customWriterRecorder struct {
+ w io.Writer
+ *httptest.ResponseRecorder
+}
+
+func (r *customWriterRecorder) Write(p []byte) (n int, err error) {
+ return r.w.Write(p)
+}
+
+type limitWriter struct {
+ w io.Writer
+ n int
+}
+
+func (w *limitWriter) Write(p []byte) (n int, err error) {
+ if len(p) > w.n {
+ p = p[:w.n]
+ }
+ if len(p) > 0 {
+ n, err = w.w.Write(p)
+ w.n -= n
+ }
+ if w.n == 0 {
+ err = errors.New("past write limit")
+ }
+ return
+}
+
+// If there's an error copying the child's output to the parent, test
+// that we kill the child.
+func TestKillChildAfterCopyError(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ defer func() { testHookStartProcess = nil }()
+ proc := make(chan *os.Process, 1)
+ testHookStartProcess = func(p *os.Process) {
+ proc <- p
+ }
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ req, _ := http.NewRequest("GET", "http://example.com/test.cgi?write-forever=1", nil)
+ rec := httptest.NewRecorder()
+ var out bytes.Buffer
+ const writeLen = 50 << 10
+ rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}
+
+ donec := make(chan bool, 1)
+ go func() {
+ h.ServeHTTP(rw, req)
+ donec <- true
+ }()
+
+ select {
+ case <-donec:
+ if out.Len() != writeLen || out.Bytes()[0] != 'a' {
+ t.Errorf("unexpected output: %q", out.Bytes())
+ }
+ case <-time.After(5 * time.Second):
+ t.Errorf("timeout. ServeHTTP hung and didn't kill the child process?")
+ select {
+ case p := <-proc:
+ p.Kill()
+ t.Logf("killed process")
+ default:
+ t.Logf("didn't kill process")
+ }
+ }
+}
+
+// Test that a child handler writing only headers works.
+// golang.org/issue/7196
+func TestChildOnlyHeaders(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ expectedMap := map[string]string{
+ "_body": "",
+ }
+ replay := runCgiTest(t, h, "GET /test.go?no-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
+ if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
+ t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
+ }
+}
+
+// golang.org/issue/7198
+func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") }
+func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
+func Test500WithEmptyHeaders(t *testing.T) { want500Test(t, "/empty-headers") }
+
+func want500Test(t *testing.T, path string) {
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ expectedMap := map[string]string{
+ "_body": "",
+ }
+ replay := runCgiTest(t, h, "GET "+path+" HTTP/1.0\nHost: example.com\n\n", expectedMap)
+ if replay.Code != 500 {
+ t.Errorf("Got code %d; want 500", replay.Code)
+ }
+}
+
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (n int, err error) {
+ for i := range p {
+ p[i] = byte(b)
+ }
+ return len(p), nil
+}
+
+// Note: not actually a test.
+func TestBeChildCGIProcess(t *testing.T) {
+ if os.Getenv("REQUEST_METHOD") == "" {
+ // Not in a CGI environment; skipping test.
+ return
+ }
+ switch os.Getenv("REQUEST_URI") {
+ case "/immediate-disconnect":
+ os.Exit(0)
+ case "/no-content-type":
+ fmt.Printf("Content-Length: 6\n\nHello\n")
+ os.Exit(0)
+ case "/empty-headers":
+ fmt.Printf("\nHello")
+ os.Exit(0)
+ }
+ Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ rw.Header().Set("X-Test-Header", "X-Test-Value")
+ req.ParseForm()
+ if req.FormValue("no-body") == "1" {
+ return
+ }
+ if req.FormValue("write-forever") == "1" {
+ io.Copy(rw, neverEnding('a'))
+ for {
+ time.Sleep(5 * time.Second) // hang forever, until killed
+ }
+ }
+ fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
+ for k, vv := range req.Form {
+ for _, v := range vv {
+ fmt.Fprintf(rw, "param-%s=%s\n", k, v)
+ }
+ }
+ for _, kv := range os.Environ() {
+ fmt.Fprintf(rw, "env-%s\n", kv)
+ }
+ }))
+ os.Exit(0)
+}
+++ /dev/null
-// Copyright 2011 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.
-
-// Tests a Go CGI program running under a Go CGI host process.
-// Further, the two programs are the same binary, just checking
-// their environment to figure out what mode to run in.
-
-package cgi
-
-import (
- "bytes"
- "errors"
- "fmt"
- "internal/testenv"
- "io"
- "net/http"
- "net/http/httptest"
- "os"
- "testing"
- "time"
-)
-
-// This test is a CGI host (testing host.go) that runs its own binary
-// as a child process testing the other half of CGI (child.go).
-func TestHostingOurselves(t *testing.T) {
- testenv.MustHaveExec(t)
-
- h := &Handler{
- Path: os.Args[0],
- Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
- }
- expectedMap := map[string]string{
- "test": "Hello CGI-in-CGI",
- "param-a": "b",
- "param-foo": "bar",
- "env-GATEWAY_INTERFACE": "CGI/1.1",
- "env-HTTP_HOST": "example.com",
- "env-PATH_INFO": "",
- "env-QUERY_STRING": "foo=bar&a=b",
- "env-REMOTE_ADDR": "1.2.3.4",
- "env-REMOTE_HOST": "1.2.3.4",
- "env-REMOTE_PORT": "1234",
- "env-REQUEST_METHOD": "GET",
- "env-REQUEST_URI": "/test.go?foo=bar&a=b",
- "env-SCRIPT_FILENAME": os.Args[0],
- "env-SCRIPT_NAME": "/test.go",
- "env-SERVER_NAME": "example.com",
- "env-SERVER_PORT": "80",
- "env-SERVER_SOFTWARE": "go",
- }
- replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
-
- if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
- t.Errorf("got a Content-Type of %q; expected %q", got, expected)
- }
- if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
- t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
- }
-}
-
-type customWriterRecorder struct {
- w io.Writer
- *httptest.ResponseRecorder
-}
-
-func (r *customWriterRecorder) Write(p []byte) (n int, err error) {
- return r.w.Write(p)
-}
-
-type limitWriter struct {
- w io.Writer
- n int
-}
-
-func (w *limitWriter) Write(p []byte) (n int, err error) {
- if len(p) > w.n {
- p = p[:w.n]
- }
- if len(p) > 0 {
- n, err = w.w.Write(p)
- w.n -= n
- }
- if w.n == 0 {
- err = errors.New("past write limit")
- }
- return
-}
-
-// If there's an error copying the child's output to the parent, test
-// that we kill the child.
-func TestKillChildAfterCopyError(t *testing.T) {
- testenv.MustHaveExec(t)
-
- defer func() { testHookStartProcess = nil }()
- proc := make(chan *os.Process, 1)
- testHookStartProcess = func(p *os.Process) {
- proc <- p
- }
-
- h := &Handler{
- Path: os.Args[0],
- Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
- }
- req, _ := http.NewRequest("GET", "http://example.com/test.cgi?write-forever=1", nil)
- rec := httptest.NewRecorder()
- var out bytes.Buffer
- const writeLen = 50 << 10
- rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}
-
- donec := make(chan bool, 1)
- go func() {
- h.ServeHTTP(rw, req)
- donec <- true
- }()
-
- select {
- case <-donec:
- if out.Len() != writeLen || out.Bytes()[0] != 'a' {
- t.Errorf("unexpected output: %q", out.Bytes())
- }
- case <-time.After(5 * time.Second):
- t.Errorf("timeout. ServeHTTP hung and didn't kill the child process?")
- select {
- case p := <-proc:
- p.Kill()
- t.Logf("killed process")
- default:
- t.Logf("didn't kill process")
- }
- }
-}
-
-// Test that a child handler writing only headers works.
-// golang.org/issue/7196
-func TestChildOnlyHeaders(t *testing.T) {
- testenv.MustHaveExec(t)
-
- h := &Handler{
- Path: os.Args[0],
- Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
- }
- expectedMap := map[string]string{
- "_body": "",
- }
- replay := runCgiTest(t, h, "GET /test.go?no-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
- t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
- }
-}
-
-// golang.org/issue/7198
-func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") }
-func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
-func Test500WithEmptyHeaders(t *testing.T) { want500Test(t, "/empty-headers") }
-
-func want500Test(t *testing.T, path string) {
- h := &Handler{
- Path: os.Args[0],
- Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
- }
- expectedMap := map[string]string{
- "_body": "",
- }
- replay := runCgiTest(t, h, "GET "+path+" HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if replay.Code != 500 {
- t.Errorf("Got code %d; want 500", replay.Code)
- }
-}
-
-type neverEnding byte
-
-func (b neverEnding) Read(p []byte) (n int, err error) {
- for i := range p {
- p[i] = byte(b)
- }
- return len(p), nil
-}
-
-// Note: not actually a test.
-func TestBeChildCGIProcess(t *testing.T) {
- if os.Getenv("REQUEST_METHOD") == "" {
- // Not in a CGI environment; skipping test.
- return
- }
- switch os.Getenv("REQUEST_URI") {
- case "/immediate-disconnect":
- os.Exit(0)
- case "/no-content-type":
- fmt.Printf("Content-Length: 6\n\nHello\n")
- os.Exit(0)
- case "/empty-headers":
- fmt.Printf("\nHello")
- os.Exit(0)
- }
- Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
- rw.Header().Set("X-Test-Header", "X-Test-Value")
- req.ParseForm()
- if req.FormValue("no-body") == "1" {
- return
- }
- if req.FormValue("write-forever") == "1" {
- io.Copy(rw, neverEnding('a'))
- for {
- time.Sleep(5 * time.Second) // hang forever, until killed
- }
- }
- fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
- for k, vv := range req.Form {
- for _, v := range vv {
- fmt.Fprintf(rw, "param-%s=%s\n", k, v)
- }
- }
- for _, kv := range os.Environ() {
- fmt.Fprintf(rw, "env-%s\n", kv)
- }
- }))
- os.Exit(0)
-}
// knownRoundTripperImpl reports whether rt is a RoundTripper that's
// maintained by the Go team and known to implement the latest
-// optional semantics (notably contexts).
-func knownRoundTripperImpl(rt RoundTripper) bool {
- switch rt.(type) {
- case *Transport, *http2Transport:
+// optional semantics (notably contexts). The Request is used
+// to check whether this particular request is using an alternate protocol,
+// in which case we need to check the RoundTripper for that protocol.
+func knownRoundTripperImpl(rt RoundTripper, req *Request) bool {
+ switch t := rt.(type) {
+ case *Transport:
+ if altRT := t.alternateRoundTripper(req); altRT != nil {
+ return knownRoundTripperImpl(altRT, req)
+ }
+ return true
+ case *http2Transport, http2noDialH2RoundTripper:
return true
}
// There's a very minor chance of a false positive with this.
if deadline.IsZero() {
return nop, alwaysFalse
}
- knownTransport := knownRoundTripperImpl(rt)
+ knownTransport := knownRoundTripperImpl(rt, req)
oldCtx := req.Context()
if req.Cancel == nil && knownTransport {
// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
+//
+// ReverseProxy automatically sets the client IP as the value of the
+// X-Forwarded-For header.
+// If an X-Forwarded-For header already exists, the client IP is
+// appended to the existing values.
+// To prevent IP spoofing, be sure to delete any pre-existing
+// X-Forwarded-For header coming from the client or
+// an untrusted proxy.
type ReverseProxy struct {
// Director must be a function which modifies
// the request into a new request to be sent
func (http2erringRoundTripper) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) }
+type http2noDialH2RoundTripper struct{}
+
+func (http2noDialH2RoundTripper) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) }
+
type http2noDialClientConnPool struct {
http2clientConnPool http2clientConnPool
}
// For all requests, ParseForm parses the raw query from the URL and updates
// r.Form.
//
-// For POST, PUT, and PATCH requests, it also parses the request body as a form
-// and puts the results into both r.PostForm and r.Form. Request body parameters
-// take precedence over URL query string values in r.Form.
+// For POST, PUT, and PATCH requests, it also reads the request body, parses it
+// as a form and puts the results into both r.PostForm and r.Form. Request body
+// parameters take precedence over URL query string values in r.Form.
+//
+// If the request Body's size has not already been limited by MaxBytesReader,
+// the size is capped at 10MB.
//
// For other HTTP methods, or when the Content-Type is not
// application/x-www-form-urlencoded, the request Body is not read, and
// r.PostForm is initialized to a non-nil, empty value.
//
-// If the request Body's size has not already been limited by MaxBytesReader,
-// the size is capped at 10MB.
-//
// ParseMultipartForm calls ParseForm automatically.
// ParseForm is idempotent.
func (r *Request) ParseForm() error {
import (
"bufio"
"bytes"
- "compress/gzip"
"errors"
"fmt"
"io"
return nil
}
-// proxyingReadCloser is a composite type that accepts and proxies
-// io.Read and io.Close calls to its respective Reader and Closer.
-//
-// It is composed of:
-// a) a top-level reader e.g. the result of decompression
-// b) a symbolic Closer e.g. the result of decompression, the
-// original body and the connection itself.
-type proxyingReadCloser struct {
- io.Reader
- io.Closer
-}
-
-// multiCloser implements io.Closer and allows a bunch of io.Closer values
-// to all be closed once.
-// Example usage is with proxyingReadCloser if we are decompressing a response
-// body on the fly and would like to close both *gzip.Reader and underlying body.
-type multiCloser []io.Closer
-
-func (mc multiCloser) Close() error {
- var err error
- for _, c := range mc {
- if err1 := c.Close(); err1 != nil && err == nil {
- err = err1
- }
- }
- return err
-}
-
// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t := &transferReader{RequestMethod: "GET"}
// Prepare body reader. ContentLength < 0 means chunked encoding
// or close connection when finished, since multipart is not supported yet
switch {
- case chunked(t.TransferEncoding) || implicitlyChunked(t.TransferEncoding):
+ case chunked(t.TransferEncoding):
if noResponseBodyExpected(t.RequestMethod) || !bodyAllowedForStatus(t.StatusCode) {
t.Body = NoBody
} else {
}
}
- // Finally if "gzip" was one of the requested transfer-encodings,
- // we'll unzip the concatenated body/payload of the request.
- // TODO: As we support more transfer-encodings, extract
- // this code and apply the un-codings in reverse.
- if t.Body != NoBody && gzipped(t.TransferEncoding) {
- zr, err := gzip.NewReader(t.Body)
- if err != nil {
- return fmt.Errorf("http: failed to gunzip body: %v", err)
- }
- t.Body = &proxyingReadCloser{
- Reader: zr,
- Closer: multiCloser{zr, t.Body},
- }
- }
-
// Unify output
switch rr := msg.(type) {
case *Request:
return nil
}
-// Checks whether chunked is the last part of the encodings stack
-func chunked(te []string) bool { return len(te) > 0 && te[len(te)-1] == "chunked" }
-
-// implicitlyChunked is a helper to check for implicity of chunked, because
-// RFC 7230 Section 3.3.1 says that the sender MUST apply chunked as the final
-// payload body to ensure that the message is framed for both the request
-// and the body. Since "identity" is incompatible with any other transformational
-// encoding cannot co-exist, the presence of "identity" will cause implicitlyChunked
-// to return false.
-func implicitlyChunked(te []string) bool {
- if len(te) == 0 { // No transfer-encodings passed in, so not implicitly chunked.
- return false
- }
- for _, tei := range te {
- if tei == "identity" {
- return false
- }
- }
- return true
-}
-
-func isGzipTransferEncoding(tei string) bool {
- // RFC 7230 4.2.3 requests that "x-gzip" SHOULD be considered the same as "gzip".
- return tei == "gzip" || tei == "x-gzip"
-}
-
-// Checks where either of "gzip" or "x-gzip" are contained in transfer encodings.
-func gzipped(te []string) bool {
- for _, tei := range te {
- if isGzipTransferEncoding(tei) {
- return true
- }
- }
- return false
-}
+// Checks whether chunked is part of the encodings stack
+func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
// Checks whether the encoding is explicitly "identity".
func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
encodings := strings.Split(raw[0], ",")
te := make([]string, 0, len(encodings))
-
- // When adding new encodings, please maintain the invariant:
- // if chunked encoding is present, it must always
- // come last and it must be applied only once.
- // See RFC 7230 Section 3.3.1 Transfer-Encoding.
- for i, encoding := range encodings {
+ // TODO: Even though we only support "identity" and "chunked"
+ // encodings, the loop below is designed with foresight. One
+ // invariant that must be maintained is that, if present,
+ // chunked encoding must always come first.
+ for _, encoding := range encodings {
encoding = strings.ToLower(strings.TrimSpace(encoding))
-
+ // "identity" encoding is not recorded
if encoding == "identity" {
- // "identity" should not be mixed with other transfer-encodings/compressions
- // because it means "no compression, no transformation".
- if len(encodings) != 1 {
- return &badStringError{`"identity" when present must be the only transfer encoding`, strings.Join(encodings, ",")}
- }
- // "identity" is not recorded.
break
}
-
- switch {
- case encoding == "chunked":
- // "chunked" MUST ALWAYS be the last
- // encoding as per the loop invariant.
- // That is:
- // Invalid: [chunked, gzip]
- // Valid: [gzip, chunked]
- if i+1 != len(encodings) {
- return &badStringError{"chunked must be applied only once, as the last encoding", strings.Join(encodings, ",")}
- }
- // Supported otherwise.
-
- case isGzipTransferEncoding(encoding):
- // Supported
-
- default:
+ if encoding != "chunked" {
return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", encoding)}
}
-
te = te[0 : len(te)+1]
te[len(te)-1] = encoding
}
-
+ if len(te) > 1 {
+ return &badStringError{"too many transfer encodings", strings.Join(te, ",")}
+ }
if len(te) > 0 {
// RFC 7230 3.3.2 says "A sender MUST NOT send a
// Content-Length header field in any message that
import (
"bufio"
"bytes"
- "compress/gzip"
"crypto/rand"
"fmt"
"io"
buf := make([]byte, len(want))
n, err := res.Body.Read(buf)
if n != len(want) || err != io.EOF {
+ t.Logf("body = %#v", res.Body)
t.Errorf("Read = %v, %v; want %d, EOF", n, err, len(want))
}
if string(buf) != want {
},
{
hdr: Header{"Transfer-Encoding": {"chunked, chunked", "identity", "chunked"}},
- wantErr: &badStringError{"chunked must be applied only once, as the last encoding", "chunked, chunked"},
+ wantErr: &badStringError{"too many transfer encodings", "chunked,chunked"},
},
{
hdr: Header{"Transfer-Encoding": {"chunked"}},
}
}
}
-
-func gzipIt(s string) string {
- buf := new(bytes.Buffer)
- gw := gzip.NewWriter(buf)
- gw.Write([]byte(s))
- gw.Close()
- return buf.String()
-}
-
-func TestUnitTestProxyingReadCloserClosesBody(t *testing.T) {
- var checker closeChecker
- buf := new(bytes.Buffer)
- buf.WriteString("Hello, Gophers!")
- prc := &proxyingReadCloser{
- Reader: buf,
- Closer: &checker,
- }
- prc.Close()
-
- read, err := ioutil.ReadAll(prc)
- if err != nil {
- t.Fatalf("Read error: %v", err)
- }
- if g, w := string(read), "Hello, Gophers!"; g != w {
- t.Errorf("Read mismatch: got %q want %q", g, w)
- }
-
- if checker.closed != true {
- t.Fatal("closeChecker.Close was never invoked")
- }
-}
-
-func TestGzipTransferEncoding_request(t *testing.T) {
- helloWorldGzipped := gzipIt("Hello, World!")
-
- tests := []struct {
- payload string
- wantErr string
- wantBody string
- }{
-
- {
- // The case of "chunked" properly applied as the last encoding
- // and a gzipped request payload that is streamed in 3 parts.
- payload: `POST / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: gzip, chunked
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%02x\r\n%s\r\n%02x\r\n%s\r\n%02x\r\n%s\r\n0\r\n\r\n",
- 3, helloWorldGzipped[:3],
- 5, helloWorldGzipped[3:8],
- len(helloWorldGzipped)-8, helloWorldGzipped[8:]),
- wantBody: `Hello, World!`,
- },
-
- {
- // The request specifies "Transfer-Encoding: chunked" so its body must be left untouched.
- payload: `PUT / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: chunked
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- // We want that payload as it was sent.
- wantBody: helloWorldGzipped,
- },
-
- {
- // Valid request, the body doesn't have "Transfer-Encoding: chunked" but implicitly encoded
- // for chunking as per the advisory from RFC 7230 3.3.1 which advises for cases where.
- payload: `POST / HTTP/1.1
-Host: localhost
-Transfer-Encoding: gzip
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- wantBody: `Hello, World!`,
- },
-
- {
- // Invalid request, the body isn't chunked nor is the connection terminated immediately
- // hence invalid as per the advisory from RFC 7230 3.3.1 which advises for cases where
- // a Transfer-Encoding that isn't finally chunked is provided.
- payload: `PUT / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `EOF`,
- },
-
- {
- // The case of chunked applied before another encoding.
- payload: `PUT / HTTP/1.1
-Location: golang.org
-Transfer-Encoding: chunked, gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `chunked must be applied only once, as the last encoding "chunked, gzip"`,
- },
-
- {
- // The case of chunked properly applied as the
- // last encoding BUT with a bad "Content-Length".
- payload: `POST / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: gzip, chunked
-Content-Length: 10
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + "0\r\n\r\n",
- wantErr: "EOF",
- },
- }
-
- for i, tt := range tests {
- req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.payload)))
- if tt.wantErr != "" {
- if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
- t.Errorf("test %d. Error mismatch\nGot: %v\nWant: %s", i, err, tt.wantErr)
- }
- continue
- }
-
- if err != nil {
- t.Errorf("test %d. Unexpected ReadRequest error: %v\nPayload:\n%s", i, err, tt.payload)
- continue
- }
-
- got, err := ioutil.ReadAll(req.Body)
- req.Body.Close()
- if err != nil {
- t.Errorf("test %d. Failed to read response body: %v", i, err)
- }
- if g, w := string(got), tt.wantBody; g != w {
- t.Errorf("test %d. Request body mimsatch\nGot:\n%s\n\nWant:\n%s", i, g, w)
- }
- }
-}
-
-func TestGzipTransferEncoding_response(t *testing.T) {
- helloWorldGzipped := gzipIt("Hello, World!")
-
- tests := []struct {
- payload string
- wantErr string
- wantBody string
- }{
-
- {
- // The case of "chunked" properly applied as the last encoding
- // and a gzipped payload that is streamed in 3 parts.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip, chunked
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%02x\r\n%s\r\n%02x\r\n%s\r\n%02x\r\n%s\r\n0\r\n\r\n",
- 3, helloWorldGzipped[:3],
- 5, helloWorldGzipped[3:8],
- len(helloWorldGzipped)-8, helloWorldGzipped[8:]),
- wantBody: `Hello, World!`,
- },
-
- {
- // The response specifies "Transfer-Encoding: chunked" so response body must be left untouched.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: chunked
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- // We want that payload as it was sent.
- wantBody: helloWorldGzipped,
- },
-
- {
- // Valid response, the body doesn't have "Transfer-Encoding: chunked" but implicitly encoded
- // for chunking as per the advisory from RFC 7230 3.3.1 which advises for cases where.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- wantBody: `Hello, World!`,
- },
-
- {
- // Invalid response, the body isn't chunked nor is the connection terminated immediately
- // hence invalid as per the advisory from RFC 7230 3.3.1 which advises for cases where
- // a Transfer-Encoding that isn't finally chunked is provided.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `EOF`,
- },
-
- {
- // The case of chunked applied before another encoding.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: chunked, gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `chunked must be applied only once, as the last encoding "chunked, gzip"`,
- },
-
- {
- // The case of chunked properly applied as the
- // last encoding BUT with a bad "Content-Length".
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip, chunked
-Content-Length: 10
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + "0\r\n\r\n",
- wantErr: "EOF",
- },
-
- {
- // Including "identity" more than once.
- payload: `HTTP/1.1 200 OK
-Location: https://golang.org/
-Transfer-Encoding: identity, identity
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + "0\r\n\r\n",
- wantErr: `"identity" when present must be the only transfer encoding "identity, identity"`,
- },
- }
-
- for i, tt := range tests {
- res, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.payload)), nil)
- if tt.wantErr != "" {
- if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
- t.Errorf("test %d. Error mismatch\nGot: %v\nWant: %s", i, err, tt.wantErr)
- }
- continue
- }
-
- if err != nil {
- t.Errorf("test %d. Unexpected ReadResponse error: %v\nPayload:\n%s", i, err, tt.payload)
- continue
- }
-
- got, err := ioutil.ReadAll(res.Body)
- res.Body.Close()
- if err != nil {
- t.Errorf("test %d. Failed to read response body: %v", i, err)
- }
- if g, w := string(got), tt.wantBody; g != w {
- t.Errorf("test %d. Response body mimsatch\nGot:\n%s\n\nWant:\n%s", i, g, w)
- }
- }
-}
return true
}
+// alternateRoundTripper returns the alternate RoundTripper to use
+// for this request if the Request's URL scheme requires one,
+// or nil for the normal case of using the Transport.
+func (t *Transport) alternateRoundTripper(req *Request) RoundTripper {
+ if !t.useRegisteredProtocol(req) {
+ return nil
+ }
+ altProto, _ := t.altProto.Load().(map[string]RoundTripper)
+ return altProto[req.URL.Scheme]
+}
+
// roundTrip implements a RoundTripper over HTTP.
func (t *Transport) roundTrip(req *Request) (*Response, error) {
t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
}
}
- if t.useRegisteredProtocol(req) {
- altProto, _ := t.altProto.Load().(map[string]RoundTripper)
- if altRT := altProto[scheme]; altRT != nil {
- if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol {
- return resp, err
- }
+ if altRT := t.alternateRoundTripper(req); altRT != nil {
+ if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol {
+ return resp, err
}
}
if !isHTTP {
if hdr == nil {
hdr = make(Header)
}
+ if pa := cm.proxyAuth(); pa != "" {
+ hdr = hdr.Clone()
+ hdr.Set("Proxy-Authorization", pa)
+ }
connectReq := &Request{
Method: "CONNECT",
URL: &url.URL{Opaque: cm.targetAddr},
Host: cm.targetAddr,
Header: hdr,
}
- if pa := cm.proxyAuth(); pa != "" {
- connectReq.Header.Set("Proxy-Authorization", pa)
- }
// If there's no done channel (no deadline or cancellation
// from the caller possible), at least set some (long)
}
}
+// Issue 36431: calls to RoundTrip should not mutate t.ProxyConnectHeader.
+//
+// (A bug caused dialConn to instead write the per-request Proxy-Authorization
+// header through to the shared Header instance, introducing a data race.)
+func TestTransportProxyDialDoesNotMutateProxyConnectHeader(t *testing.T) {
+ setParallel(t)
+ defer afterTest(t)
+
+ proxy := httptest.NewTLSServer(NotFoundHandler())
+ defer proxy.Close()
+ c := proxy.Client()
+
+ tr := c.Transport.(*Transport)
+ tr.Proxy = func(*Request) (*url.URL, error) {
+ u, _ := url.Parse(proxy.URL)
+ u.User = url.UserPassword("aladdin", "opensesame")
+ return u, nil
+ }
+ h := tr.ProxyConnectHeader
+ if h == nil {
+ h = make(Header)
+ }
+ tr.ProxyConnectHeader = h.Clone()
+
+ req, err := NewRequest("GET", "https://golang.fake.tld/", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = c.Do(req)
+ if err == nil {
+ t.Errorf("unexpected Get success")
+ }
+
+ if !reflect.DeepEqual(tr.ProxyConnectHeader, h) {
+ t.Errorf("tr.ProxyConnectHeader = %v; want %v", tr.ProxyConnectHeader, h)
+ }
+}
+
// TestTransportGzipRecursive sends a gzip quine and checks that the
// client gets the same value back. This is more cute than anything,
// but checks that we don't recurse forever, and checks that
t.Errorf("error occurred: %v", err)
}
}
+
+// Issue 36820
+// Test that we use the older backward compatible cancellation protocol
+// when a RoundTripper is registered via RegisterProtocol.
+func TestAltProtoCancellation(t *testing.T) {
+ defer afterTest(t)
+ tr := &Transport{}
+ c := &Client{
+ Transport: tr,
+ Timeout: time.Millisecond,
+ }
+ tr.RegisterProtocol("timeout", timeoutProto{})
+ _, err := c.Get("timeout://bar.com/path")
+ if err == nil {
+ t.Error("request unexpectedly succeeded")
+ } else if !strings.Contains(err.Error(), timeoutProtoErr.Error()) {
+ t.Errorf("got error %q, does not contain expected string %q", err, timeoutProtoErr)
+ }
+}
+
+var timeoutProtoErr = errors.New("canceled as expected")
+
+type timeoutProto struct{}
+
+func (timeoutProto) RoundTrip(req *Request) (*Response, error) {
+ select {
+ case <-req.Cancel:
+ return nil, timeoutProtoErr
+ case <-time.After(5 * time.Second):
+ return nil, errors.New("request was not canceled")
+ }
+}
defer wg.Done()
_, err := r.LookupIPAddr(context.Background(), "google.com")
if err != nil {
- t.Fatalf("lookup failed for resolver %d: %q", index, err)
+ t.Errorf("lookup failed for resolver %d: %q", index, err)
}
}(resolver.Resolver, i)
}
wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
+
for i, resolver := range resolvers {
if !resolver.dialed {
t.Errorf("custom resolver %d not dialed during lookup", i)
}
}
-// Issue 31586: don't crash on null byte in name
+// Issue 31597: don't panic on null byte in name
func TestLookupNullByte(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.SkipFlakyNet(t)
- _, err := LookupHost("foo\x00bar") // used to crash on Windows
- if err == nil {
- t.Errorf("unexpected success")
- }
+ LookupHost("foo\x00bar") // check that it doesn't panic; it used to on Windows
}
Addr Addr
// Err is the error that occurred during the operation.
+ // The Error method panics if the error is nil.
Err error
}
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
+//
+// If f is a directory, the behavior of Seek varies by operating
+// system; you can seek to the beginning of the directory on Unix-like
+// operating systems, but not on Windows.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
if err := f.checkValid("seek"); err != nil {
return 0, err
if exported != test.exported {
t.Errorf("test-%d: got exported=%v want exported=%v", i, exported, test.exported)
}
+ if field.PkgPath != test.field.PkgPath {
+ t.Errorf("test-%d: got PkgPath=%q want pkgPath=%q", i, field.PkgPath, test.field.PkgPath)
+ }
})
}
}
}
}
+func TestStructOfDifferentPkgPath(t *testing.T) {
+ fields := []StructField{
+ {
+ Name: "f1",
+ PkgPath: "p1",
+ Type: TypeOf(int(0)),
+ },
+ {
+ Name: "f2",
+ PkgPath: "p2",
+ Type: TypeOf(int(0)),
+ },
+ }
+ shouldPanic(func() {
+ StructOf(fields)
+ })
+}
+
func TestChanOf(t *testing.T) {
// check construction and use of type not in binary
type T string
lastzero := uintptr(0)
repr = append(repr, "struct {"...)
+ pkgpath := ""
for i, field := range fields {
if field.Name == "" {
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name")
if field.Type == nil {
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
}
- f := runtimeStructField(field)
+ f, fpkgpath := runtimeStructField(field)
ft := f.typ
if ft.kind&kindGCProg != 0 {
hasGCProg = true
}
+ if fpkgpath != "" {
+ if pkgpath == "" {
+ pkgpath = fpkgpath
+ } else if pkgpath != fpkgpath {
+ panic("reflect.Struct: fields with different PkgPath " + pkgpath + " and " + fpkgpath)
+ }
+ }
// Update string and hash
name := *f.name
return addToCache(&typ.rtype)
}
-func runtimeStructField(field StructField) structField {
+// runtimeStructField takes a StructField value passed to StructOf and
+// returns both the corresponding internal representation, of type
+// structField, and the pkgpath value to use for this field.
+func runtimeStructField(field StructField) (structField, string) {
if field.Anonymous && field.PkgPath != "" {
panic("reflect.StructOf: field \"" + field.Name + "\" is anonymous but has PkgPath set")
}
s := field.PkgPath
pkgPath = &s
}
- return structField{
+ f := structField{
name: name,
pkgPath: pkgPath,
typ: field.Type.common(),
tag: tag,
offsetEmbed: offsetEmbed,
}
+ return f, field.PkgPath
}
// typeptrdata returns the length in bytes of the prefix of t
return add(c.buf, uintptr(i)*uintptr(c.elemsize))
}
-// full reports whether a send on c would block (that is, the channel is full).
-// It uses a single word-sized read of mutable state, so although
-// the answer is instantaneously true, the correct answer may have changed
-// by the time the calling function receives the return value.
-func full(c *hchan) bool {
- // c.dataqsiz is immutable (never written after the channel is created)
- // so it is safe to read at any time during channel operation.
- if c.dataqsiz == 0 {
- // Assumes that a pointer read is relaxed-atomic.
- return c.recvq.first == nil
- }
- // Assumes that a uint read is relaxed-atomic.
- return c.qcount == c.dataqsiz
-}
-
// entry point for c <- x from compiled code
//go:nosplit
func chansend1(c *hchan, elem unsafe.Pointer) {
//
// After observing that the channel is not closed, we observe that the channel is
// not ready for sending. Each of these observations is a single word-sized read
- // (first c.closed and second full()).
+ // (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).
// Because a closed channel cannot transition from 'ready for sending' to
// 'not ready for sending', even if the channel is closed between the two observations,
// they imply a moment between the two when the channel was both not yet closed
//
// It is okay if the reads are reordered here: if we observe that the channel is not
// ready for sending and then observe that it is not closed, that implies that the
- // channel wasn't closed during the first observation. However, nothing here
- // guarantees forward progress. We rely on the side effects of lock release in
- // chanrecv() and closechan() to update this thread's view of c.closed and full().
- if !block && c.closed == 0 && full(c) {
+ // channel wasn't closed during the first observation.
+ if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
+ (c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
return false
}
}
}
-// empty reports whether a read from c would block (that is, the channel is
-// empty). It uses a single atomic read of mutable state.
-func empty(c *hchan) bool {
- // c.dataqsiz is immutable.
- if c.dataqsiz == 0 {
- return atomic.Loadp(unsafe.Pointer(&c.sendq.first)) == nil
- }
- return atomic.Loaduint(&c.qcount) == 0
-}
-
// entry points for <- c from compiled code
//go:nosplit
func chanrecv1(c *hchan, elem unsafe.Pointer) {
}
// Fast path: check for failed non-blocking operation without acquiring the lock.
- if !block && empty(c) {
- // After observing that the channel is not ready for receiving, we observe whether the
- // channel is closed.
- //
- // Reordering of these checks could lead to incorrect behavior when racing with a close.
- // For example, if the channel was open and not empty, was closed, and then drained,
- // reordered reads could incorrectly indicate "open and empty". To prevent reordering,
- // we use atomic loads for both checks, and rely on emptying and closing to happen in
- // separate critical sections under the same lock. This assumption fails when closing
- // an unbuffered channel with a blocked send, but that is an error condition anyway.
- if atomic.Load(&c.closed) == 0 {
- // Because a channel cannot be reopened, the later observation of the channel
- // being not closed implies that it was also not closed at the moment of the
- // first observation. We behave as if we observed the channel at that moment
- // and report that the receive cannot proceed.
- return
- }
- // The channel is irreversibly closed. Re-check whether the channel has any pending data
- // to receive, which could have arrived between the empty and closed checks above.
- // Sequential consistency is also required here, when racing with such a send.
- if empty(c) {
- // The channel is irreversibly closed and empty.
- if ep != nil {
- typedmemclr(c.elemtype, ep)
- }
- return true, false
- }
+ //
+ // After observing that the channel is not ready for receiving, we observe that the
+ // channel is not closed. Each of these observations is a single word-sized read
+ // (first c.sendq.first or c.qcount, and second c.closed).
+ // Because a channel cannot be reopened, the later observation of the channel
+ // being not closed implies that it was also not closed at the moment of the
+ // first observation. We behave as if we observed the channel at that moment
+ // and report that the receive cannot proceed.
+ //
+ // The order of operations is important here: reversing the operations can lead to
+ // incorrect behavior when racing with a close.
+ if !block && (c.dataqsiz == 0 && c.sendq.first == nil ||
+ c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0) &&
+ atomic.Load(&c.closed) == 0 {
+ return
}
var t0 int64
wg.Wait()
}
-func BenchmarkChanClosed(b *testing.B) {
- c := make(chan struct{})
- close(c)
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- select {
- case <-c:
- default:
- b.Error("Unreachable")
- }
- }
- })
-}
-
var (
alwaysFalse = false
workSink = 0
import "unsafe"
-type ptrAlignError struct {
- ptr unsafe.Pointer
- elem *_type
- n uintptr
-}
-
-func (e ptrAlignError) RuntimeError() {}
-
-func (e ptrAlignError) Error() string {
- return "runtime error: unsafe pointer conversion"
-}
-
func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
// Check that (*[n]elem)(p) is appropriately aligned.
// TODO(mdempsky): What about fieldAlign?
if uintptr(p)&(uintptr(elem.align)-1) != 0 {
- panic(ptrAlignError{p, elem, n})
+ throw("checkptr: unsafe pointer conversion")
}
// Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) {
- panic(ptrAlignError{p, elem, n})
+ throw("checkptr: unsafe pointer conversion")
}
}
-type ptrArithError struct {
- ptr unsafe.Pointer
- originals []unsafe.Pointer
-}
-
-func (e ptrArithError) RuntimeError() {}
-
-func (e ptrArithError) Error() string {
- return "runtime error: unsafe pointer arithmetic"
-}
-
func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
if 0 < uintptr(p) && uintptr(p) < minLegalPointer {
- panic(ptrArithError{p, originals})
+ throw("checkptr: unsafe pointer arithmetic")
}
// Check that if the computed pointer p points into a heap
}
}
- panic(ptrArithError{p, originals})
+ throw("checkptr: unsafe pointer arithmetic")
}
// checkptrBase returns the base address for the allocation containing
--- /dev/null
+// Copyright 2020 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 runtime_test
+
+import (
+ "internal/testenv"
+ "os/exec"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestCheckPtr(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("gccgo does not have -d=checkptr")
+ }
+ t.Parallel()
+ testenv.MustHaveGoRun(t)
+
+ exe, err := buildTestProg(t, "testprog", "-gcflags=all=-d=checkptr=1")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ testCases := []struct {
+ cmd string
+ want string
+ }{
+ {"CheckPtrAlignment", "fatal error: checkptr: unsafe pointer conversion\n"},
+ {"CheckPtrArithmetic", "fatal error: checkptr: unsafe pointer arithmetic\n"},
+ {"CheckPtrSize", "fatal error: checkptr: unsafe pointer conversion\n"},
+ {"CheckPtrSmall", "fatal error: checkptr: unsafe pointer arithmetic\n"},
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.cmd, func(t *testing.T) {
+ t.Parallel()
+ got, err := testenv.CleanCmdEnv(exec.Command(exe, tc.cmd)).CombinedOutput()
+ if err != nil {
+ t.Log(err)
+ }
+ if !strings.HasPrefix(string(got), tc.want) {
+ t.Errorf("output:\n%s\n\nwant output starting with: %s", got, tc.want)
+ }
+ })
+ }
+}
return ret
}
- stopTheWorldGC("GOMAXPROCS")
+ stopTheWorld("GOMAXPROCS")
// newprocs will be processed by startTheWorld
newprocs = int32(n)
- startTheWorldGC()
+ startTheWorld()
return ret
}
var ParseRelease = parseRelease
+var Memmove = memmove
+var MemclrNoHeapPointers = memclrNoHeapPointers
+
const PreemptMSupported = preemptMSupported
type LFNode struct {
PageSize = pageSize
PallocChunkPages = pallocChunkPages
PageAlloc64Bit = pageAlloc64Bit
+ PallocSumBytes = pallocSumBytes
)
// Expose pallocSum for testing.
If the line ends with "(forced)", this GC was forced by a
runtime.GC() call.
- Setting gctrace to any value > 0 also causes the garbage collector
- to emit a summary when memory is released back to the system.
- This process of returning memory to the system is called scavenging.
- The format of this summary is subject to change.
- Currently it is:
- scvg#: # MB released printed only if non-zero
- scvg#: inuse: # idle: # sys: # released: # consumed: # (MB)
- where the fields are as follows:
- scvg# the scavenge cycle number, incremented at each scavenge
- inuse: # MB used or partially used spans
- idle: # MB spans pending scavenging
- sys: # MB mapped from the system
- released: # MB released to the system
- consumed: # MB allocated from the system
-
madvdontneed: setting madvdontneed=1 will use MADV_DONTNEED
instead of MADV_FREE on Linux when returning memory to the
kernel. This is less efficient, but causes RSS numbers to drop
scavenge: scavenge=1 enables debugging mode of heap scavenger.
+ scavtrace: setting scavtrace=1 causes the runtime to emit a single line to standard
+ error, roughly once per GC cycle, summarizing the amount of work done by the
+ scavenger as well as the total amount of memory returned to the operating system
+ and an estimate of physical memory utilization. The format of this line is subject
+ to change, but currently it is:
+ scav # KiB work, # KiB total, #% util
+ where the fields are as follows:
+ # KiB work the amount of memory returned to the OS since the last scav line
+ # KiB total how much of the heap at this point in time has been released to the OS
+ #% util the fraction of all unscavenged memory which is in-use
+ If the line ends with "(forced)", then scavenging was forced by a
+ debug.FreeOSMemory() call.
+
scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
detailed multiline info every X milliseconds, describing state of the scheduler,
processors, threads and goroutines.
typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64
typePointer, typeScalar, // i string
}
- case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x", "wasm":
+ case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "wasm":
return []byte{
typePointer, // q *int
typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
// xxhash: https://code.google.com/p/xxhash/
// cityhash: https://code.google.com/p/cityhash/
-// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x wasm alpha arm64be ia64 mips64p32 mips64p32le sparc64 riscv64
+// +build amd64 arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x wasm alpha amd64p32 arm64be ia64 mips64p32 mips64p32le sparc64
package runtime
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x wasm arm64be alpha sparc64 ia64 riscv64
+// +build amd64 arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x wasm arm64be alpha sparc64 ia64
package runtime
// allocation at 0x40 << 32 because when using 4k pages with 3-level
// translation buffers, the user address space is limited to 39 bits
// On darwin/arm64, the address space is even smaller.
+ //
// On AIX, mmaps starts at 0x0A00000000000000 for 64-bit.
// processes.
for i := 0x7f; i >= 0; i-- {
var arenaCollisionSink []*acLink
func TestArenaCollision(t *testing.T) {
- if GOOS == "darwin" && race.Enabled {
- // Skip this test on Darwin in race mode because Darwin 10.10 has
- // issues following arena hints and runs out of them in race mode, so
- // MAP_FIXED is used to ensure we keep the heap in the memory region the
- // race detector expects.
- // TODO(mknyszek): Delete this when Darwin 10.10 is no longer supported.
- t.Skip("disabled on Darwin with race mode since MAP_FIXED is used")
- }
testenv.MustHaveExec(t)
// Test that mheap.sysAlloc handles collisions with other
"internal/race"
"internal/testenv"
. "runtime"
+ "sync/atomic"
"testing"
+ "unsafe"
)
func TestMemmove(t *testing.T) {
return l
}
+// Ensure that memmove writes pointers atomically, so the GC won't
+// observe a partially updated pointer.
+func TestMemmoveAtomicity(t *testing.T) {
+ if race.Enabled {
+ t.Skip("skip under the race detector -- this test is intentionally racy")
+ }
+
+ var x int
+
+ for _, backward := range []bool{true, false} {
+ for _, n := range []int{3, 4, 5, 6, 7, 8, 9, 10, 15, 25, 49} {
+ n := n
+
+ // test copying [N]*int.
+ sz := uintptr(n * PtrSize)
+ name := fmt.Sprint(sz)
+ if backward {
+ name += "-backward"
+ } else {
+ name += "-forward"
+ }
+ t.Run(name, func(t *testing.T) {
+ // Use overlapping src and dst to force forward/backward copy.
+ var s [100]*int
+ src := s[n-1 : 2*n-1]
+ dst := s[:n]
+ if backward {
+ src, dst = dst, src
+ }
+ for i := range src {
+ src[i] = &x
+ }
+ for i := range dst {
+ dst[i] = nil
+ }
+
+ var ready uint32
+ go func() {
+ sp := unsafe.Pointer(&src[0])
+ dp := unsafe.Pointer(&dst[0])
+ atomic.StoreUint32(&ready, 1)
+ for i := 0; i < 10000; i++ {
+ Memmove(dp, sp, sz)
+ MemclrNoHeapPointers(dp, sz)
+ }
+ atomic.StoreUint32(&ready, 2)
+ }()
+
+ for atomic.LoadUint32(&ready) == 0 {
+ Gosched()
+ }
+
+ for atomic.LoadUint32(&ready) != 2 {
+ for i := range dst {
+ p := dst[i]
+ if p != nil && p != &x {
+ t.Fatalf("got partially updated pointer %p at dst[%d], want either nil or %p", p, i, &x)
+ }
+ }
+ }
+ })
+ }
+ }
+}
+
func benchmarkSizes(b *testing.B, sizes []int, fn func(b *testing.B, n int)) {
for _, n := range sizes {
b.Run(fmt.Sprint(n), func(b *testing.B) {
}
// Ok, we're doing it! Stop everybody else
- semacquire(&gcsema)
semacquire(&worldsema)
if trace.enabled {
work.pauseNS += now - work.pauseStart
work.tMark = now
})
-
- // Release the world sema before Gosched() in STW mode
- // because we will need to reacquire it later but before
- // this goroutine becomes runnable again, and we could
- // self-deadlock otherwise.
- semrelease(&worldsema)
-
// In STW mode, we could block the instant systemstack
// returns, so don't do anything important here. Make sure we
// block rather than returning to user code.
return
}
- // forEachP needs worldsema to execute, and we'll need it to
- // stop the world later, so acquire worldsema now.
- semacquire(&worldsema)
-
// Flush all local buffers and collect flushedWork flags.
gcMarkDoneFlushed = 0
systemstack(func() {
// work to do. Keep going. It's possible the
// transition condition became true again during the
// ragged barrier, so re-check it.
- semrelease(&worldsema)
goto top
}
now := startTheWorldWithSema(true)
work.pauseNS += now - work.pauseStart
})
- semrelease(&worldsema)
goto top
}
}
}
semrelease(&worldsema)
- semrelease(&gcsema)
// Careful: another GC cycle may start now.
releasem(mp)
// maxPagesPerPhysPage is the maximum number of supported runtime pages per
// physical page, based on maxPhysPageSize.
maxPagesPerPhysPage = maxPhysPageSize / pageSize
+
+ // scavengeCostRatio is the approximate ratio between the costs of using previously
+ // scavenged memory and scavenging memory.
+ //
+ // For most systems the cost of scavenging greatly outweighs the costs
+ // associated with using scavenged memory, making this constant 0. On other systems
+ // (especially ones where "sysUsed" is not just a no-op) this cost is non-trivial.
+ //
+ // This ratio is used as part of multiplicative factor to help the scavenger account
+ // for the additional costs of using scavenged memory in its pacing.
+ scavengeCostRatio = 0.7 * sys.GoosDarwin
)
// heapRetained returns an estimate of the current heap RSS.
released := uintptr(0)
// Time in scavenging critical section.
- crit := int64(0)
+ crit := float64(0)
// Run on the system stack since we grab the heap lock,
// and a stack growth with the heap lock means a deadlock.
// Scavenge one page, and measure the amount of time spent scavenging.
start := nanotime()
released = mheap_.pages.scavengeOne(physPageSize, false)
- crit = nanotime() - start
+ atomic.Xadduintptr(&mheap_.pages.scavReleased, released)
+ crit = float64(nanotime() - start)
})
- if debug.gctrace > 0 {
- if released > 0 {
- print("scvg: ", released>>10, " KB released\n")
- }
- print("scvg: inuse: ", memstats.heap_inuse>>20, ", idle: ", memstats.heap_idle>>20, ", sys: ", memstats.heap_sys>>20, ", released: ", memstats.heap_released>>20, ", consumed: ", (memstats.heap_sys-memstats.heap_released)>>20, " (MB)\n")
- }
-
if released == 0 {
lock(&scavenge.lock)
scavenge.parked = true
continue
}
+ // Multiply the critical time by 1 + the ratio of the costs of using
+ // scavenged memory vs. scavenging memory. This forces us to pay down
+ // the cost of reusing this memory eagerly by sleeping for a longer period
+ // of time and scavenging less frequently. More concretely, we avoid situations
+ // where we end up scavenging so often that we hurt allocation performance
+ // because of the additional overheads of using scavenged memory.
+ crit *= 1 + scavengeCostRatio
+
// If we spent more than 10 ms (for example, if the OS scheduled us away, or someone
// put their machine to sleep) in the critical section, bound the time we use to
// calculate at 10 ms to avoid letting the sleep time get arbitrarily high.
// much, then scavengeEMWA < idealFraction, so we'll adjust the sleep time
// down.
adjust := scavengeEWMA / idealFraction
- sleepTime := int64(adjust * float64(crit) / (scavengePercent / 100.0))
+ sleepTime := int64(adjust * crit / (scavengePercent / 100.0))
// Go to sleep.
slept := scavengeSleep(sleepTime)
// Compute the new ratio.
- fraction := float64(crit) / float64(crit+slept)
+ fraction := crit / (crit + float64(slept))
// Set a lower bound on the fraction.
// Due to OS-related anomalies we may "sleep" for an inordinate amount
return released
}
+// printScavTrace prints a scavenge trace line to standard error.
+//
+// released should be the amount of memory released since the last time this
+// was called, and forced indicates whether the scavenge was forced by the
+// application.
+func printScavTrace(released uintptr, forced bool) {
+ printlock()
+ print("scav ",
+ released>>10, " KiB work, ",
+ atomic.Load64(&memstats.heap_released)>>10, " KiB total, ",
+ (atomic.Load64(&memstats.heap_inuse)*100)/heapRetained(), "% util",
+ )
+ if forced {
+ print(" (forced)")
+ }
+ println()
+ printunlock()
+}
+
// resetScavengeAddr sets the scavenge start address to the top of the heap's
// address space. This should be called each time the scavenger's pacing
// changes.
//
// s.mheapLock must be held.
func (s *pageAlloc) resetScavengeAddr() {
+ released := atomic.Loaduintptr(&s.scavReleased)
+ if debug.scavtrace > 0 {
+ printScavTrace(released, false)
+ }
+ // Subtract from scavReleased instead of just setting it to zero because
+ // the scavenger could have increased scavReleased concurrently with the
+ // load above, and we may miss an update by just blindly zeroing the field.
+ atomic.Xadduintptr(&s.scavReleased, -released)
s.scavAddr = chunkBase(s.end) - 1
}
// Check the chunk containing the scav addr, starting at the addr
// and see if there are any free and unscavenged pages.
- if s.summary[len(s.summary)-1][ci].max() >= uint(minPages) {
+ //
+ // Only check this if s.scavAddr is covered by any address range
+ // in s.inUse, so that we know our check of the summary is safe.
+ if s.inUse.contains(s.scavAddr) && s.summary[len(s.summary)-1][ci].max() >= uint(minPages) {
// We only bother looking for a candidate if there at least
// minPages free pages at all. It's important that we only
// continue if the summary says we can because that's how
// Tests end-to-end scavenging on a pageAlloc.
func TestPageAllocScavenge(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
type test struct {
request, expect uintptr
}
if minPages < 1 {
minPages = 1
}
- tests := map[string]struct {
+ type setup struct {
beforeAlloc map[ChunkIdx][]BitRange
beforeScav map[ChunkIdx][]BitRange
expect []test
afterScav map[ChunkIdx][]BitRange
- }{
+ }
+ tests := map[string]setup{
"AllFreeUnscavExhaust": {
beforeAlloc: map[ChunkIdx][]BitRange{
BaseChunkIdx: {},
},
},
}
+ if PageAlloc64Bit != 0 {
+ tests["ScavAllVeryDiscontiguous"] = setup{
+ beforeAlloc: map[ChunkIdx][]BitRange{
+ BaseChunkIdx: {},
+ BaseChunkIdx + 0x1000: {},
+ },
+ beforeScav: map[ChunkIdx][]BitRange{
+ BaseChunkIdx: {},
+ BaseChunkIdx + 0x1000: {},
+ },
+ expect: []test{
+ {^uintptr(0), 2 * PallocChunkPages * PageSize},
+ {^uintptr(0), 0},
+ },
+ afterScav: map[ChunkIdx][]BitRange{
+ BaseChunkIdx: {{0, PallocChunkPages}},
+ BaseChunkIdx + 0x1000: {{0, PallocChunkPages}},
+ },
+ }
+ }
for name, v := range tests {
v := v
runTest := func(t *testing.T, locked bool) {
// on the swept stack.
sweepSpans [2]gcSweepBuf
- _ uint32 // align uint64 fields on 32-bit for atomics
+ // _ uint32 // align uint64 fields on 32-bit for atomics
// Proportional sweep
//
// reclaimChunk sweeps unmarked spans that start at page indexes [pageIdx, pageIdx+n).
// It returns the number of pages returned to the heap.
//
-// h.lock must be held and the caller must be non-preemptible.
+// h.lock must be held and the caller must be non-preemptible. Note: h.lock may be
+// temporarily unlocked and re-locked in order to do sweeping or if tracing is
+// enabled.
func (h *mheap) reclaimChunk(arenas []arenaIdx, pageIdx, n uintptr) uintptr {
// The heap lock must be held because this accesses the
// heapArena.spans arrays using potentially non-live pointers.
n -= uintptr(len(inUse) * 8)
}
if trace.enabled {
+ unlock(&h.lock)
// Account for pages scanned but not reclaimed.
traceGCSweepSpan((n0 - nFreed) * pageSize)
+ lock(&h.lock)
}
return nFreed
}
unlock(&h.lock)
gp.m.mallocing--
- if debug.gctrace > 0 {
- if released > 0 {
- print("forced scvg: ", released>>20, " MB released\n")
- }
- print("forced scvg: inuse: ", memstats.heap_inuse>>20, ", idle: ", memstats.heap_idle>>20, ", sys: ", memstats.heap_sys>>20, ", released: ", memstats.heap_released>>20, ", consumed: ", (memstats.heap_sys-memstats.heap_released)>>20, " (MB)\n")
+ if debug.scavtrace > 0 {
+ printScavTrace(released, true)
}
}
"mips64x": func() { genMIPS(true) },
"mipsx": func() { genMIPS(false) },
"ppc64x": genPPC64,
+ "riscv64": genRISCV64,
"s390x": genS390X,
"wasm": genWasm,
}
p("JMP (CTR)")
}
+func genRISCV64() {
+ p("// No async preemption on riscv64 - see issue 36711")
+ p("UNDEF")
+}
+
func genS390X() {
// Add integer registers R0-R12
// R13 (g), R14 (LR), R15 (SP) are special, and not saved here.
// the bitmaps align better on zero-values.
chunks [1 << pallocChunksL1Bits]*[1 << pallocChunksL2Bits]pallocData
- // The address to start an allocation search with.
+ // The address to start an allocation search with. It must never
+ // point to any memory that is not contained in inUse, i.e.
+ // inUse.contains(searchAddr) must always be true.
//
// When added with arenaBaseOffset, we guarantee that
// all valid heap addresses (when also added with
// space on architectures with segmented address spaces.
searchAddr uintptr
- // The address to start a scavenge candidate search with.
+ // The address to start a scavenge candidate search with. It
+ // need not point to memory contained in inUse.
scavAddr uintptr
+ // The amount of memory scavenged since the last scavtrace print.
+ //
+ // Read and updated atomically.
+ scavReleased uintptr
+
// start and end represent the chunk indices
// which pageAlloc knows about. It assumes
// chunks in the range [start, end) are
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 !darwin,arm64 mips64 mips64le ppc64 ppc64le s390x arm64be alpha sparc64 ia64 riscv64
+// +build amd64 !darwin,arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x arm64be alpha sparc64 ia64
// See mpagealloc_32bit.go for why darwin/arm64 is excluded here.
}
func TestPageAllocGrow(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
type test struct {
chunks []ChunkIdx
inUse []AddrRange
}
func TestPageAllocAlloc(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
type hit struct {
npages, base, scav uintptr
}
- tests := map[string]struct {
+ type test struct {
scav map[ChunkIdx][]BitRange
before map[ChunkIdx][]BitRange
after map[ChunkIdx][]BitRange
hits []hit
- }{
+ }
+ tests := map[string]test{
"AllFree1": {
before: map[ChunkIdx][]BitRange{
BaseChunkIdx: {},
BaseChunkIdx: {{0, 195}},
},
},
- // TODO(mknyszek): Add tests close to the chunk size.
"ExhaustPallocChunkPages-3": {
before: map[ChunkIdx][]BitRange{
BaseChunkIdx: {},
},
},
}
+ if PageAlloc64Bit != 0 {
+ const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
+
+ // This test attempts to trigger a bug wherein we look at unmapped summary
+ // memory that isn't just in the case where we exhaust the heap.
+ //
+ // It achieves this by placing a chunk such that its summary will be
+ // at the very end of a physical page. It then also places another chunk
+ // much further up in the address space, such that any allocations into the
+ // first chunk do not exhaust the heap and the second chunk's summary is not in the
+ // page immediately adjacent to the first chunk's summary's page.
+ // Allocating into this first chunk to exhaustion and then into the second
+ // chunk may then trigger a check in the allocator which erroneously looks at
+ // unmapped summary memory and crashes.
+
+ // Figure out how many chunks are in a physical page, then align BaseChunkIdx
+ // to a physical page in the chunk summary array. Here we only assume that
+ // each summary array is aligned to some physical page.
+ sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes)
+ baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1)
+ tests["DiscontiguousMappedSumBoundary"] = test{
+ before: map[ChunkIdx][]BitRange{
+ baseChunkIdx + sumsPerPhysPage - 1: {},
+ baseChunkIdx + chunkIdxBigJump: {},
+ },
+ scav: map[ChunkIdx][]BitRange{
+ baseChunkIdx + sumsPerPhysPage - 1: {},
+ baseChunkIdx + chunkIdxBigJump: {},
+ },
+ hits: []hit{
+ {PallocChunkPages - 1, PageBase(baseChunkIdx+sumsPerPhysPage-1, 0), 0},
+ {1, PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-1), 0},
+ {1, PageBase(baseChunkIdx+chunkIdxBigJump, 0), 0},
+ {PallocChunkPages - 1, PageBase(baseChunkIdx+chunkIdxBigJump, 1), 0},
+ {1, 0, 0},
+ },
+ after: map[ChunkIdx][]BitRange{
+ baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}},
+ baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}},
+ },
+ }
+ }
for name, v := range tests {
v := v
t.Run(name, func(t *testing.T) {
}
func TestPageAllocExhaust(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
for _, npages := range []uintptr{1, 2, 3, 4, 5, 8, 16, 64, 1024, 1025, 2048, 2049} {
npages := npages
t.Run(fmt.Sprintf("%d", npages), func(t *testing.T) {
}
func TestPageAllocFree(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
tests := map[string]struct {
before map[ChunkIdx][]BitRange
after map[ChunkIdx][]BitRange
}
func TestPageAllocAllocAndFree(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
type hit struct {
alloc bool
npages uintptr
}
func TestPageCacheFlush(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
bits64ToBitRanges := func(bits uint64, base uint) []BitRange {
var ranges []BitRange
start, size := uint(0), uint(0)
}
func TestPageAllocAllocToCache(t *testing.T) {
+ if GOOS == "openbsd" && testing.Short() {
+ t.Skip("skipping because virtual memory is limited; see #36210")
+ }
tests := map[string]struct {
before map[ChunkIdx][]BitRange
scav map[ChunkIdx][]BitRange
// If find fails to find any free space, it returns an index of ^uint(0) and
// the new searchIdx should be ignored.
//
-// The returned searchIdx is always the index of the first free page found
-// in this bitmap during the search, except if npages == 1, in which
-// case it will be the index just after the first free page, because the
-// index returned as the first result is assumed to be allocated and so
-// represents a minor optimization for that case.
+// Note that if npages == 1, the two returned values will always be identical.
func (b *pallocBits) find(npages uintptr, searchIdx uint) (uint, uint) {
if npages == 1 {
addr := b.find1(searchIdx)
- // Return a searchIdx of addr + 1 since we assume addr will be
- // allocated.
- return addr, addr + 1
+ return addr, addr
} else if npages <= 64 {
return b.findSmallN(npages, searchIdx)
}
return a.limit - a.base
}
+// contains returns whether or not the range contains a given address.
+func (a addrRange) contains(addr uintptr) bool {
+ return addr >= a.base && addr < a.limit
+}
+
// subtract takes the addrRange toPrune and cuts out any overlap with
// from, then returns the new range. subtract assumes that a and b
// either don't overlap at all, only overlap on one side, or are equal.
return len(a.ranges)
}
+// contains returns true if a covers the address addr.
+func (a *addrRanges) contains(addr uintptr) bool {
+ i := a.findSucc(addr)
+ if i == 0 {
+ return false
+ }
+ return a.ranges[i-1].contains(addr)
+}
+
// add inserts a new address range to a.
//
// r must not overlap with any address range in a.
--- /dev/null
+// Copyright 2020 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.
+
+// +build !windows
+
+package runtime
+
+//go:nosplit
+func osPreemptExtEnter(mp *m) {}
+
+//go:nosplit
+func osPreemptExtExit(mp *m) {}
// goroutines.
func stopTheWorld(reason string) {
semacquire(&worldsema)
- gp := getg()
- gp.m.preemptoff = reason
- systemstack(func() {
- // Mark the goroutine which called stopTheWorld preemptible so its
- // stack may be scanned.
- // This lets a mark worker scan us while we try to stop the world
- // since otherwise we could get in a mutual preemption deadlock.
- // We must not modify anything on the G stack because a stack shrink
- // may occur. A stack shrink is otherwise OK though because in order
- // to return from this function (and to leave the system stack) we
- // must have preempted all goroutines, including any attempting
- // to scan our stack, in which case, any stack shrinking will
- // have already completed by the time we exit.
- casgstatus(gp, _Grunning, _Gwaiting)
- stopTheWorldWithSema()
- casgstatus(gp, _Gwaiting, _Grunning)
- })
+ getg().m.preemptoff = reason
+ systemstack(stopTheWorldWithSema)
}
// startTheWorld undoes the effects of stopTheWorld.
getg().m.preemptoff = ""
}
-// stopTheWorldGC has the same effect as stopTheWorld, but blocks
-// until the GC is not running. It also blocks a GC from starting
-// until startTheWorldGC is called.
-func stopTheWorldGC(reason string) {
- semacquire(&gcsema)
- stopTheWorld(reason)
-}
-
-// startTheWorldGC undoes the effects of stopTheWorldGC.
-func startTheWorldGC() {
- startTheWorld()
- semrelease(&gcsema)
-}
-
-// Holding worldsema grants an M the right to try to stop the world.
+// Holding worldsema grants an M the right to try to stop the world
+// and prevents gomaxprocs from changing concurrently.
var worldsema uint32 = 1
-// Holding gcsema grants the M the right to block a GC, and blocks
-// until the current GC is done. In particular, it prevents gomaxprocs
-// from changing concurrently.
-//
-// TODO(mknyszek): Once gomaxprocs and the execution tracer can handle
-// being changed/enabled during a GC, remove this.
-var gcsema uint32 = 1
-
// stopTheWorldWithSema is the core implementation of stopTheWorld.
// The caller is responsible for acquiring worldsema and disabling
// preemption first and then should stopTheWorldWithSema on the system
// We pass now in and out to avoid extra calls of nanotime.
//go:yeswritebarrierrec
func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {
+ // If there are no timers to adjust, and the first timer on
+ // the heap is not yet ready to run, then there is nothing to do.
+ if atomic.Load(&pp.adjustTimers) == 0 {
+ next := int64(atomic.Load64(&pp.timer0When))
+ if next == 0 {
+ return now, 0, false
+ }
+ if now == 0 {
+ now = nanotime()
+ }
+ if now < next {
+ // Next timer is not ready to run.
+ // But keep going if we would clear deleted timers.
+ // This corresponds to the condition below where
+ // we decide whether to call clearDeletedTimers.
+ if pp != getg().m.p.ptr() || int(atomic.Load(&pp.deletedTimers)) <= int(atomic.Load(&pp.numTimers)/4) {
+ return now, next, false
+ }
+ }
+ }
+
lock(&pp.timersLock)
adjusttimers(pp)
}
}
+ // If this is the local P, and there are a lot of deleted timers,
+ // clear them out. We only do this for the local P to reduce
+ // lock contention on timersLock.
+ if pp == getg().m.p.ptr() && int(atomic.Load(&pp.deletedTimers)) > len(pp.timers)/4 {
+ clearDeletedTimers(pp)
+ }
+
unlock(&pp.timersLock)
return rnow, pollUntil, ran
}
// goyield is like Gosched, but it:
-// - does not emit a GoSched trace event
+// - emits a GoPreempt trace event instead of a GoSched trace event
// - puts the current G on the runq of the current P instead of the globrunq
func goyield() {
checkTimeouts()
}
func goyield_m(gp *g) {
+ if trace.enabled {
+ traceGoPreempt()
+ }
pp := gp.m.p.ptr()
casgstatus(gp, _Grunning, _Grunnable)
dropg()
lock(&pp.timersLock)
moveTimers(plocal, pp.timers)
pp.timers = nil
+ pp.numTimers = 0
pp.adjustTimers = 0
+ pp.deletedTimers = 0
+ atomic.Store64(&pp.timer0When, 0)
unlock(&pp.timersLock)
unlock(&plocal.timersLock)
}
}
// Maybe jump time forward for playground.
- _p_ := timejump()
- if _p_ != nil {
- for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link {
- if (*pp).ptr() == _p_ {
- *pp = _p_.link
- break
+ if faketime != 0 {
+ when, _p_ := timeSleepUntil()
+ if _p_ != nil {
+ faketime = when
+ for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link {
+ if (*pp).ptr() == _p_ {
+ *pp = _p_.link
+ break
+ }
}
+ mp := mget()
+ if mp == nil {
+ // There should always be a free M since
+ // nothing is running.
+ throw("checkdead: no m for timer")
+ }
+ mp.nextp.set(_p_)
+ notewakeup(&mp.park)
+ return
}
- mp := mget()
- if mp == nil {
- // There should always be a free M since
- // nothing is running.
- throw("checkdead: no m for timer")
- }
- mp.nextp.set(_p_)
- notewakeup(&mp.park)
- return
}
// There are no goroutines running, so we can look at the P's.
}
usleep(delay)
now := nanotime()
- next := timeSleepUntil()
+ next, _ := timeSleepUntil()
if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) {
lock(&sched.lock)
if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) {
osRelax(false)
}
now = nanotime()
- next = timeSleepUntil()
+ next, _ = timeSleepUntil()
lock(&sched.lock)
atomic.Store(&sched.sysmonwait, 0)
noteclear(&sched.sysmonnote)
madvdontneed int32 // for Linux; issue 28466
sbrk int32
scavenge int32
+ scavtrace int32
scheddetail int32
schedtrace int32
tracebackancestors int32
{"madvdontneed", &debug.madvdontneed},
{"sbrk", &debug.sbrk},
{"scavenge", &debug.scavenge},
+ {"scavtrace", &debug.scavtrace},
{"scheddetail", &debug.scheddetail},
{"schedtrace", &debug.schedtrace},
{"tracebackancestors", &debug.tracebackancestors},
_ uint32 // Alignment for atomic fields below
+ // The when field of the first entry on the timer heap.
+ // This is updated using atomic functions.
+ // This is 0 if the timer heap is empty.
+ timer0When uint64
+
// Per-P GC state
gcAssistTime int64 // Nanoseconds in assistAlloc
gcFractionalMarkTime int64 // Nanoseconds in fractional mark worker (atomic)
// Must hold timersLock to access.
timers []*timer
+ // Number of timers in P's heap.
+ // Modified using atomic instructions.
+ numTimers uint32
+
// Number of timerModifiedEarlier timers on P's heap.
// This should only be modified while holding timersLock,
// or while the timer status is in a transient state
// such as timerModifying.
adjustTimers uint32
+ // Number of timerDeleted timers in P's heap.
+ // Modified using atomic instructions.
+ deletedTimers uint32
+
// Race context used while executing timer functions.
// Not for gccgo: timerRaceCtx uintptr
// the waiter G immediately.
// Note that waiter inherits our time slice: this is desirable
// to avoid having a highly contended semaphore hog the P
- // indefinitely. goyield is like Gosched, but it does not emit a
- // GoSched trace event and, more importantly, puts the current G
- // on the local runq instead of the global one.
+ // indefinitely. goyield is like Gosched, but it emits a
+ // "preempted" trace event instead and, more importantly, puts
+ // the current G on the local runq instead of the global one.
// We only do this in the starving regime (handoff=true), as in
// the non-starving case it is possible for a different waiter
// to acquire the semaphore while we are yielding/scheduling,
sigprofNonGo(pc)
return
}
+ if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 {
+ // This is probably a signal from preemptM sent
+ // while executing Go code but received while
+ // executing non-Go code.
+ // We got past sigfwdgo, so we know that there is
+ // no non-Go signal handler for sigPreempt.
+ // The default behavior for sigPreempt is to ignore
+ // the signal, so badsignal will be a no-op anyway.
+ return
+ }
badsignal(uintptr(sig), &c)
return
}
--- /dev/null
+// Copyright 2020 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 main
+
+import "unsafe"
+
+func init() {
+ register("CheckPtrAlignment", CheckPtrAlignment)
+ register("CheckPtrArithmetic", CheckPtrArithmetic)
+ register("CheckPtrSize", CheckPtrSize)
+ register("CheckPtrSmall", CheckPtrSmall)
+}
+
+func CheckPtrAlignment() {
+ var x [2]int64
+ p := unsafe.Pointer(&x[0])
+ sink2 = (*int64)(unsafe.Pointer(uintptr(p) + 1))
+}
+
+func CheckPtrArithmetic() {
+ var x int
+ i := uintptr(unsafe.Pointer(&x))
+ sink2 = (*int)(unsafe.Pointer(i))
+}
+
+func CheckPtrSize() {
+ p := new(int64)
+ sink2 = p
+ sink2 = (*[100]int64)(unsafe.Pointer(p))
+}
+
+func CheckPtrSmall() {
+ sink2 = unsafe.Pointer(uintptr(1))
+}
// timerNoStatus -> timerWaiting
// anything else -> panic: invalid value
// deltimer:
-// timerWaiting -> timerDeleted
-// timerModifiedXX -> timerDeleted
-// timerNoStatus -> do nothing
-// timerDeleted -> do nothing
-// timerRemoving -> do nothing
-// timerRemoved -> do nothing
-// timerRunning -> wait until status changes
-// timerMoving -> wait until status changes
+// timerWaiting -> timerDeleted
+// timerModifiedEarlier -> timerModifying -> timerDeleted
+// timerModifiedLater -> timerDeleted
+// timerNoStatus -> do nothing
+// timerDeleted -> do nothing
+// timerRemoving -> do nothing
+// timerRemoved -> do nothing
+// timerRunning -> wait until status changes
+// timerMoving -> wait until status changes
// timerModifying -> panic: concurrent deltimer/modtimer calls
// modtimer:
// timerWaiting -> timerModifying -> timerModifiedXX
// maxWhen is the maximum value for timer's when field.
const maxWhen = 1<<63 - 1
+// verifyTimers can be set to true to add debugging checks that the
+// timer heaps are valid.
+const verifyTimers = false
+
// Package time APIs.
// Godoc uses the comments in package time, not these.
t.pp.set(pp)
i := len(pp.timers)
pp.timers = append(pp.timers, t)
- return siftupTimer(pp.timers, i)
+ ok := siftupTimer(pp.timers, i)
+ if t == pp.timers[0] {
+ atomic.Store64(&pp.timer0When, uint64(t.when))
+ }
+ atomic.Xadd(&pp.numTimers, 1)
+ return ok
}
// deltimer deletes the timer t. It may be on some other P, so we can't
for {
switch s := atomic.Load(&t.status); s {
case timerWaiting, timerModifiedLater:
+ tpp := t.pp.ptr()
if atomic.Cas(&t.status, s, timerDeleted) {
+ atomic.Xadd(&tpp.deletedTimers, 1)
// Timer was not yet run.
return true
}
if !atomic.Cas(&t.status, timerModifying, timerDeleted) {
badTimer()
}
+ atomic.Xadd(&tpp.deletedTimers, 1)
// Timer was not yet run.
return true
}
ok = false
}
}
+ if i == 0 {
+ updateTimer0When(pp)
+ }
+ atomic.Xadd(&pp.numTimers, -1)
return ok
}
if last > 0 {
ok = siftdownTimer(pp.timers, 0)
}
+ updateTimer0When(pp)
+ atomic.Xadd(&pp.numTimers, -1)
return ok
}
return
}
case timerDeleted:
+ tpp := t.pp.ptr()
if atomic.Cas(&t.status, s, timerModifying) {
t.nextwhen = when
newStatus := uint32(timerModifiedLater)
if !atomic.Cas(&t.status, timerModifying, newStatus) {
badTimer()
}
+ atomic.Xadd(&tpp.deletedTimers, -1)
if newStatus == timerModifiedEarlier {
wakeNetPoller(when)
}
if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
return false
}
+ atomic.Xadd(&pp.deletedTimers, -1)
case timerModifiedEarlier, timerModifiedLater:
if !atomic.Cas(&t.status, s, timerMoving) {
continue
return
}
if atomic.Load(&pp.adjustTimers) == 0 {
+ if verifyTimers {
+ verifyTimerHeap(pp)
+ }
return
}
var moved []*timer
+loop:
for i := 0; i < len(pp.timers); i++ {
t := pp.timers[i]
if t.pp.ptr() != pp {
if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
badTimer()
}
+ atomic.Xadd(&pp.deletedTimers, -1)
// Look at this heap position again.
i--
}
moved = append(moved, t)
if s == timerModifiedEarlier {
if n := atomic.Xadd(&pp.adjustTimers, -1); int32(n) <= 0 {
- addAdjustedTimers(pp, moved)
- return
+ break loop
}
}
+ // Look at this heap position again.
+ i--
}
case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving:
badTimer()
if len(moved) > 0 {
addAdjustedTimers(pp, moved)
}
+
+ if verifyTimers {
+ verifyTimerHeap(pp)
+ }
}
// addAdjustedTimers adds any timers we adjusted in adjusttimers
// The netpoller M will wake up and adjust timers before sleeping again.
//go:nowritebarrierrec
func nobarrierWakeTime(pp *p) int64 {
- lock(&pp.timersLock)
- ret := int64(0)
- if len(pp.timers) > 0 {
- if atomic.Load(&pp.adjustTimers) > 0 {
- ret = nanotime()
- } else {
- ret = pp.timers[0].when
- }
+ if atomic.Load(&pp.adjustTimers) > 0 {
+ return nanotime()
+ } else {
+ return int64(atomic.Load64(&pp.timer0When))
}
- unlock(&pp.timersLock)
- return ret
}
// runtimer examines the first timer in timers. If it is ready based on now,
if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
badTimer()
}
+ atomic.Xadd(&pp.deletedTimers, -1)
if len(pp.timers) == 0 {
return -1
}
if !atomic.Cas(&t.status, timerRunning, timerWaiting) {
badTimer()
}
+ updateTimer0When(pp)
} else {
// Remove from heap.
if !dodeltimer0(pp) {
lock(&pp.timersLock)
}
-func timejump() *p {
- if faketime == 0 {
- return nil
- }
-
- // Nothing is running, so we can look at all the P's.
- // Determine a timer bucket with minimum when.
- var (
- minT *timer
- minWhen int64
- minP *p
- )
- for _, pp := range allp {
- if pp.status != _Pidle && pp.status != _Pdead {
- throw("non-idle P in timejump")
- }
- if len(pp.timers) == 0 {
- continue
- }
- c := pp.adjustTimers
- for _, t := range pp.timers {
+// clearDeletedTimers removes all deleted timers from the P's timer heap.
+// This is used to avoid clogging up the heap if the program
+// starts a lot of long-running timers and then stops them.
+// For example, this can happen via context.WithTimeout.
+//
+// This is the only function that walks through the entire timer heap,
+// other than moveTimers which only runs when the world is stopped.
+//
+// The caller must have locked the timers for pp.
+func clearDeletedTimers(pp *p) {
+ cdel := int32(0)
+ cearlier := int32(0)
+ to := 0
+ changedHeap := false
+ timers := pp.timers
+nextTimer:
+ for _, t := range timers {
+ for {
switch s := atomic.Load(&t.status); s {
case timerWaiting:
- if minT == nil || t.when < minWhen {
- minT = t
- minWhen = t.when
- minP = pp
+ if changedHeap {
+ timers[to] = t
+ siftupTimer(timers, to)
}
+ to++
+ continue nextTimer
case timerModifiedEarlier, timerModifiedLater:
- if minT == nil || t.nextwhen < minWhen {
- minT = t
- minWhen = t.nextwhen
- minP = pp
+ if atomic.Cas(&t.status, s, timerMoving) {
+ t.when = t.nextwhen
+ timers[to] = t
+ siftupTimer(timers, to)
+ to++
+ changedHeap = true
+ if !atomic.Cas(&t.status, timerMoving, timerWaiting) {
+ badTimer()
+ }
+ if s == timerModifiedEarlier {
+ cearlier++
+ }
+ continue nextTimer
}
- if s == timerModifiedEarlier {
- c--
+ case timerDeleted:
+ if atomic.Cas(&t.status, s, timerRemoving) {
+ t.pp = 0
+ cdel++
+ if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
+ badTimer()
+ }
+ changedHeap = true
+ continue nextTimer
}
- case timerRunning, timerModifying, timerMoving:
+ case timerModifying:
+ // Loop until modification complete.
+ osyield()
+ case timerNoStatus, timerRemoved:
+ // We should not see these status values in a timer heap.
+ badTimer()
+ case timerRunning, timerRemoving, timerMoving:
+ // Some other P thinks it owns this timer,
+ // which should not happen.
+ badTimer()
+ default:
badTimer()
- }
- // The timers are sorted, so we only have to check
- // the first timer for each P, unless there are
- // some timerModifiedEarlier timers. The number
- // of timerModifiedEarlier timers is in the adjustTimers
- // field, used to initialize c, above.
- if c == 0 {
- break
}
}
}
- if minT == nil || minWhen <= faketime {
- return nil
+ // Set remaining slots in timers slice to nil,
+ // so that the timer values can be garbage collected.
+ for i := to; i < len(timers); i++ {
+ timers[i] = nil
+ }
+
+ atomic.Xadd(&pp.deletedTimers, -cdel)
+ atomic.Xadd(&pp.numTimers, -cdel)
+ atomic.Xadd(&pp.adjustTimers, -cearlier)
+
+ timers = timers[:to]
+ pp.timers = timers
+ updateTimer0When(pp)
+
+ if verifyTimers {
+ verifyTimerHeap(pp)
+ }
+}
+
+// verifyTimerHeap verifies that the timer heap is in a valid state.
+// This is only for debugging, and is only called if verifyTimers is true.
+// The caller must have locked the timers.
+func verifyTimerHeap(pp *p) {
+ for i, t := range pp.timers {
+ if i == 0 {
+ // First timer has no parent.
+ continue
+ }
+
+ // The heap is 4-ary. See siftupTimer and siftdownTimer.
+ p := (i - 1) / 4
+ if t.when < pp.timers[p].when {
+ print("bad timer heap at ", i, ": ", p, ": ", pp.timers[p].when, ", ", i, ": ", t.when, "\n")
+ throw("bad timer heap")
+ }
+ }
+ if numTimers := int(atomic.Load(&pp.numTimers)); len(pp.timers) != numTimers {
+ println("timer heap len", len(pp.timers), "!= numTimers", numTimers)
+ throw("bad timer heap len")
}
+}
- faketime = minWhen
- return minP
+// updateTimer0When sets the P's timer0When field.
+// The caller must have locked the timers for pp.
+func updateTimer0When(pp *p) {
+ if len(pp.timers) == 0 {
+ atomic.Store64(&pp.timer0When, 0)
+ } else {
+ atomic.Store64(&pp.timer0When, uint64(pp.timers[0].when))
+ }
}
-// timeSleepUntil returns the time when the next timer should fire.
-// This is only called by sysmon.
-func timeSleepUntil() int64 {
+// timeSleepUntil returns the time when the next timer should fire,
+// and the P that holds the timer heap that that timer is on.
+// This is only called by sysmon and checkdead.
+func timeSleepUntil() (int64, *p) {
next := int64(maxWhen)
+ var pret *p
// Prevent allp slice changes. This is like retake.
lock(&allpLock)
continue
}
- lock(&pp.timersLock)
c := atomic.Load(&pp.adjustTimers)
+ if c == 0 {
+ w := int64(atomic.Load64(&pp.timer0When))
+ if w != 0 && w < next {
+ next = w
+ pret = pp
+ }
+ continue
+ }
+
+ lock(&pp.timersLock)
for _, t := range pp.timers {
switch s := atomic.Load(&t.status); s {
case timerWaiting:
}
unlock(&allpLock)
- return next
+ return next, pret
}
// Heap maintenance algorithms.
// Most clients should use the runtime/trace package or the testing package's
// -test.trace flag instead of calling StartTrace directly.
func StartTrace() error {
- // Stop the world so that we can take a consistent snapshot
+ // Stop the world, so that we can take a consistent snapshot
// of all goroutines at the beginning of the trace.
- // Do not stop the world during GC so we ensure we always see
- // a consistent view of GC-related events (e.g. a start is always
- // paired with an end).
- stopTheWorldGC("start tracing")
+ stopTheWorld("start tracing")
// We are in stop-the-world, but syscalls can finish and write to trace concurrently.
// Exitsyscall could check trace.enabled long before and then suddenly wake up
if trace.enabled || trace.shutdown {
unlock(&trace.bufLock)
- startTheWorldGC()
+ startTheWorld()
return errorString("tracing is already enabled")
}
unlock(&trace.bufLock)
- startTheWorldGC()
+ startTheWorld()
return nil
}
func StopTrace() {
// Stop the world so that we can collect the trace buffers from all p's below,
// and also to avoid races with traceEvent.
- stopTheWorldGC("stop tracing")
+ stopTheWorld("stop tracing")
// See the comment in StartTrace.
lock(&trace.bufLock)
if !trace.enabled {
unlock(&trace.bufLock)
- startTheWorldGC()
+ startTheWorld()
return
}
trace.shutdown = true
unlock(&trace.bufLock)
- startTheWorldGC()
+ startTheWorld()
// The world is started but we've set trace.shutdown, so new tracing can't start.
// Wait for the trace reader to flush pending buffers and stop.
}},
{trace.EvGomaxprocs, []frame{
{"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged.
- {"runtime.startTheWorldGC", 0},
{"runtime.GOMAXPROCS", 0},
{"runtime/trace_test.TestTraceSymbolize", 0},
{"testing.tRunner", 0},
// Numbers fundamental to the encoding.
const (
runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
- runeSelf = 0x80 // characters below Runeself are represented as themselves in a single byte.
+ runeSelf = 0x80 // characters below runeSelf are represented as themselves in a single byte.
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
)
}
// QuoteToGraphic returns a double-quoted Go string literal representing s.
-// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
-// non-ASCII characters and non-printable characters as defined by IsGraphic.
+// The returned string leaves Unicode graphic characters, as defined by
+// IsGraphic, unchanged and uses Go escape sequences (\t, \n, \xFF, \u0100)
+// for non-graphic characters.
func QuoteToGraphic(s string) string {
return quoteWith(s, '"', false, true)
}
}
// QuoteRuneToGraphic returns a single-quoted Go character literal representing
-// the rune. The returned string uses Go escape sequences (\t, \n, \xFF,
-// \u0100) for non-ASCII characters and non-printable characters as defined
-// by IsGraphic.
+// the rune. If the rune is not a Unicode graphic character,
+// as defined by IsGraphic, the returned string will use a Go escape sequence
+// (\t, \n, \xFF, \u0100).
func QuoteRuneToGraphic(r rune) string {
return quoteRuneWith(r, '\'', false, true)
}
return a
}
-// Join concatenates the elements of a to create a single string. The separator string
-// sep is placed between elements in the resulting string.
-func Join(a []string, sep string) string {
- switch len(a) {
+// Join concatenates the elements of its first argument to create a single string. The separator
+// string sep is placed between elements in the resulting string.
+func Join(elems []string, sep string) string {
+ switch len(elems) {
case 0:
return ""
case 1:
- return a[0]
+ return elems[0]
}
- n := len(sep) * (len(a) - 1)
- for i := 0; i < len(a); i++ {
- n += len(a[i])
+ n := len(sep) * (len(elems) - 1)
+ for i := 0; i < len(elems); i++ {
+ n += len(elems[i])
}
var b Builder
b.Grow(n)
- b.WriteString(a[0])
- for _, s := range a[1:] {
+ b.WriteString(elems[0])
+ for _, s := range elems[1:] {
b.WriteString(sep)
b.WriteString(s)
}
package syscall
-import "unsafe"
+import (
+ "unsafe"
+)
func (ts *StTimespec) Unix() (sec int64, nsec int64) {
return int64(ts.Sec), int64(ts.Nsec)
// may be called simultaneously from multiple goroutines.
//
// Like in tests, benchmark logs are accumulated during execution
-// and dumped to standard error when done. Unlike in tests, benchmark logs
+// and dumped to standard output when done. Unlike in tests, benchmark logs
// are always printed, so as not to hide output whose existence may be
// affecting benchmark results.
type B struct {
)
var testPanicTest = flag.String("test_panic_test", "", "TestPanic: indicates which test should panic")
+var testPanicParallel = flag.Bool("test_panic_parallel", false, "TestPanic: run subtests in parallel")
+var testPanicCleanup = flag.Bool("test_panic_cleanup", false, "TestPanic: indicates whether test should call Cleanup")
+var testPanicCleanupPanic = flag.String("test_panic_cleanup_panic", "", "TestPanic: indicate whether test should call Cleanup function that panics")
func TestPanic(t *testing.T) {
testenv.MustHaveExec(t)
desc: "subtest panics",
flags: []string{"-test_panic_test=TestPanicHelper/1"},
want: `
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "subtest panics with cleanup",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "subtest panics with outer cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+`,
+ }, {
+ desc: "subtest panics with middle cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "subtest panics with inner cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "parallel subtest panics with cleanup",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "parallel subtest panics with outer cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=outer", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+`,
+ }, {
+ desc: "parallel subtest panics with middle cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=middle", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }, {
+ desc: "parallel subtest panics with inner cleanup panic",
+ flags: []string{"-test_panic_test=TestPanicHelper/1", "-test_panic_cleanup", "-test_panic_cleanup_panic=inner", "-test_panic_parallel"},
+ want: `
+ran inner cleanup 1
+ran middle cleanup 1
+ran outer cleanup
--- FAIL: TestPanicHelper (N.NNs)
panic_test.go:NNN: TestPanicHelper
--- FAIL: TestPanicHelper/1 (N.NNs)
if t.Name() == *testPanicTest {
panic("panic")
}
+ switch *testPanicCleanupPanic {
+ case "", "outer", "middle", "inner":
+ default:
+ t.Fatalf("bad -test_panic_cleanup_panic: %s", *testPanicCleanupPanic)
+ }
+ t.Cleanup(func() {
+ fmt.Println("ran outer cleanup")
+ if *testPanicCleanupPanic == "outer" {
+ panic("outer cleanup")
+ }
+ })
for i := 0; i < 3; i++ {
+ i := i
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
+ chosen := t.Name() == *testPanicTest
+ if chosen && *testPanicCleanup {
+ t.Cleanup(func() {
+ fmt.Printf("ran middle cleanup %d\n", i)
+ if *testPanicCleanupPanic == "middle" {
+ panic("middle cleanup")
+ }
+ })
+ }
+ if chosen && *testPanicParallel {
+ t.Parallel()
+ }
t.Log(t.Name())
- if t.Name() == *testPanicTest {
+ if chosen {
+ if *testPanicCleanup {
+ t.Cleanup(func() {
+ fmt.Printf("ran inner cleanup %d\n", i)
+ if *testPanicCleanupPanic == "inner" {
+ panic("inner cleanup")
+ }
+ })
+ }
panic("panic")
}
})
<-ch
t.Errorf("error")
},
+ }, {
+ // If a subtest panics we should run cleanups.
+ desc: "cleanup when subtest panics",
+ ok: false,
+ chatty: false,
+ output: `
+--- FAIL: cleanup when subtest panics (N.NNs)
+ --- FAIL: cleanup when subtest panics/sub (N.NNs)
+ sub_test.go:NNN: running cleanup`,
+ f: func(t *T) {
+ t.Cleanup(func() { t.Log("running cleanup") })
+ t.Run("sub", func(t2 *T) {
+ t2.FailNow()
+ })
+ },
}}
for _, tc := range testCases {
ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", ""))
t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
}
}
+
+func TestCleanupParallelSubtests(t *T) {
+ ranCleanup := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() { ranCleanup++ })
+ t.Run("x", func(t *T) {
+ t.Parallel()
+ if ranCleanup > 0 {
+ t.Error("outer cleanup ran before parallel subtest")
+ }
+ })
+ })
+ if ranCleanup != 1 {
+ t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
+ }
+}
c.helpers[callerName(1)] = struct{}{}
}
-// Cleanup registers a function to be called when the test finishes.
-// Cleanup functions will be called in last added, first called
-// order.
+// Cleanup registers a function to be called when the test and all its
+// subtests complete. Cleanup functions will be called in last added,
+// first called order.
func (c *common) Cleanup(f func()) {
c.mu.Lock()
defer c.mu.Unlock()
}
}
+// panicHanding is an argument to runCleanup.
+type panicHandling int
+
+const (
+ normalPanic panicHandling = iota
+ recoverAndReturnPanic
+)
+
// runCleanup is called at the end of the test.
-func (c *common) runCleanup() {
+// If catchPanic is true, this will catch panics, and return the recovered
+// value if any.
+func (c *common) runCleanup(ph panicHandling) (panicVal interface{}) {
c.mu.Lock()
cleanup := c.cleanup
c.cleanup = nil
c.mu.Unlock()
- if cleanup != nil {
- cleanup()
+ if cleanup == nil {
+ return nil
+ }
+
+ if ph == recoverAndReturnPanic {
+ defer func() {
+ panicVal = recover()
+ }()
}
+
+ cleanup()
+ return nil
}
// callerName gives the function name (qualified with a package path)
}
}
}
- if err != nil {
+
+ doPanic := func(err interface{}) {
t.Fail()
+ if r := t.runCleanup(recoverAndReturnPanic); r != nil {
+ t.Logf("cleanup panicked with %v", r)
+ }
// Flush the output log up to the root before dying.
t.mu.Lock()
root := &t.common
for ; root.parent != nil; root = root.parent {
root.duration += time.Since(root.start)
fmt.Fprintf(root.parent.w, "--- FAIL: %s (%s)\n", root.name, fmtDuration(root.duration))
+ if r := root.parent.runCleanup(recoverAndReturnPanic); r != nil {
+ fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
+ }
root.parent.mu.Lock()
io.Copy(root.parent.w, bytes.NewReader(root.output))
}
panic(err)
}
+ if err != nil {
+ doPanic(err)
+ }
t.duration += time.Since(t.start)
for _, sub := range t.sub {
<-sub.signal
}
+ cleanupStart := time.Now()
+ err := t.runCleanup(recoverAndReturnPanic)
+ t.duration += time.Since(cleanupStart)
+ if err != nil {
+ doPanic(err)
+ }
if !t.isParallel {
// Reacquire the count for sequential tests. See comment in Run.
t.context.waitParallel()
}
t.signal <- signal
}()
- defer t.runCleanup()
+ defer func() {
+ if len(t.sub) == 0 {
+ t.runCleanup(normalPanic)
+ }
+ }()
t.start = time.Now()
t.raceErrors = -race.Errors()
{"map MUI64S", "{{index .MUI64S 3}}", "ui643", tVal, true},
{"map MI8S", "{{index .MI8S 3}}", "i83", tVal, true},
{"map MUI8S", "{{index .MUI8S 2}}", "u82", tVal, true},
+ {"index of an interface field", "{{index .Empty3 0}}", "7", tVal, true},
// Slicing.
{"slice[:]", "{{slice .SI}}", "[3 4 5]", tVal, true},
{"string[1:2]", "{{slice .S 1 2}}", "y", tVal, true},
{"out of range", "{{slice .S 1 5}}", "", tVal, false},
{"3-index slice of string", "{{slice .S 1 2 2}}", "", tVal, false},
+ {"slice of an interface field", "{{slice .Empty3 0 1}}", "[7]", tVal, true},
// Len.
{"slice", "{{len .SI}}", "3", tVal, true},
{"map", "{{len .MSI }}", "3", tVal, true},
{"len of int", "{{len 3}}", "", tVal, false},
{"len of nothing", "{{len .Empty0}}", "", tVal, false},
+ {"len of an interface field", "{{len .Empty3}}", "2", tVal, true},
// With.
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[0], idx[1])
}
if len(indexes) < 3 {
- return item.Slice(idx[0], idx[1]), nil
+ return v.Slice(idx[0], idx[1]), nil
}
// given item[i:j:k], make sure i <= j <= k.
if idx[1] > idx[2] {
return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[1], idx[2])
}
- return item.Slice3(idx[0], idx[1], idx[2]), nil
+ return v.Slice3(idx[0], idx[1], idx[2]), nil
}
// Length
}
case r <= unicode.MaxASCII && unicode.IsPrint(r):
l.emit(itemChar)
- return lexInsideAction
default:
return l.errorf("unrecognized character in action: %#U", r)
}
// Years must be in the range 0000..9999. The day of the week is checked
// for syntax but it is otherwise ignored.
//
+// For layouts specifying the two-digit year 06, a value NN >= 69 will be treated
+// as 19NN and a value NN < 69 will be treated as 20NN.
+//
// In the absence of a time zone indicator, Parse returns a time in UTC.
//
// When parsing a time with a zone offset like -0700, if the offset corresponds
for i := 0; i < 100; i++ {
go func(i int) {
timer := AfterFunc(2*Second, func() {
- t.Fatalf("timer %d was not stopped", i)
+ t.Errorf("timer %d was not stopped", i)
})
Sleep(1 * Second)
timer.Stop()
// Unix returns t as a Unix time, the number of seconds elapsed
// since January 1, 1970 UTC. The result does not depend on the
// location associated with t.
+// Unix-like operating systems often record time as a 32-bit
+// count of seconds, but since the method here returns a 64-bit
+// value it is valid for billions of years into the past or future.
func (t Time) Unix() int64 {
return t.unixSec()
}
// Numbers fundamental to the encoding.
const (
RuneError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
- RuneSelf = 0x80 // characters below Runeself are represented as themselves in a single byte.
+ RuneSelf = 0x80 // characters below RuneSelf are represented as themselves in a single byte.
MaxRune = '\U0010FFFF' // Maximum valid Unicode code point.
UTFMax = 4 // maximum number of bytes of a UTF-8 encoded Unicode character.
)
(cd ${NEWDIR}/src && find . -name testdata -print) | while read d; do
skip=false
case "$d" in
- ./cmd/buildid/* | ./cmd/cgo/* | ./cmd/go/* | ./cmd/gofmt/* | ./cmd/testjson/* | ./cmd/vet/* | ./cmd/internal/browser/* | ./cmd/internal/buildid/* | ./cmd/internal/diff/* | | ./cmd/internal/edit/* | ./cmd/internal/objabi/* | ./cmd/internal/testj2on/* | ./cmd/internal/sys/* | ./cmd/vendor/golang.org/x/tools/* )
+ ./cmd/buildid/* | ./cmd/cgo/* | ./cmd/go/* | ./cmd/gofmt/* | ./cmd/testjson/* | ./cmd/vet/* | ./cmd/internal/browser/* | ./cmd/internal/buildid/* | ./cmd/internal/diff/* | ./cmd/internal/edit/* | ./cmd/internal/objabi/* | ./cmd/internal/testj2on/* | ./cmd/internal/sys/* | ./cmd/vendor/golang.org/x/tools/*)
;;
./cmd/*)
skip=true
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// We skip this test in race mode because, for unknown reasons,
-// linking against CoreFoundation on macOS 10.10 causes mmap to ignore
-// the hint address, which makes the Go allocator incompatible with
-// TSAN. See golang.org/issue/26475.
-//
-// TODO(austin): Once support for macOS 10.10 is dropped, remove the
-// race constraint (and the one in issue21897b.go). See
-// golang.org/issue/26513.
-
-// +build darwin,cgo,!internal,!race
+// +build darwin,cgo,!internal
package cgotest
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !darwin !cgo internal race
+// +build !darwin !cgo internal
package cgotest
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// See issue21897.go and golang.org/issue/26475 for why this is
-// skipped in race mode.
-//
-// TODO(austin): Once support for macOS 10.10 is dropped, remove the
-// race constraint. See golang.org/issue/26513.
-
-// +build !race
-
package cgotest
import (
#include <TargetConditionals.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
-#if TARGET_OS_IPHONE == 0 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200
+#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
typedef CFStringRef SecKeyAlgorithm;
static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;}
#define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo()
#include <TargetConditionals.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
-#if TARGET_OS_IPHONE == 0 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200
+#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
typedef CFStringRef SecKeyAlgorithm;
static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;}
#define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo()
#include <TargetConditionals.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
-#if TARGET_OS_IPHONE == 0 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200
+#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
typedef CFStringRef SecKeyAlgorithm;
static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;}
#define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo()