![Gopher image](doc/gopher/fiveyears.jpg)
-For documentation about how to install and use Go,
-visit https://golang.org/ or load doc/install-source.html
-in your web browser.
-
Our canonical Git repository is located at https://go.googlesource.com/go.
There is a mirror of the repository at https://github.com/golang/go.
-Go is the work of hundreds of contributors. We appreciate your help!
+Unless otherwise noted, the Go source files are distributed under the
+BSD-style license found in the LICENSE file.
-To contribute, please read the contribution guidelines:
- https://golang.org/doc/contribute.html
+### Download and Install
-##### Note that we do not accept pull requests and that we use the issue tracker for bug reports and proposals only. Please ask questions on https://forum.golangbridge.org or https://groups.google.com/forum/#!forum/golang-nuts.
+#### Binary Distributions
-Unless otherwise noted, the Go source files are distributed
-under the BSD-style license found in the LICENSE file.
+Official binary distributions are available at https://golang.org/dl/.
---
+After downloading a binary release, visit https://golang.org/doc/install
+or load doc/install.html in your web browser for installation
+instructions.
-## Binary Distribution Notes
+#### Install From Source
-If you have just untarred a binary Go distribution, you need to set
-the environment variable $GOROOT to the full path of the go
-directory (the one containing this file). You can omit the
-variable if you unpack it into /usr/local/go, or if you rebuild
-from sources by running all.bash (see doc/install-source.html).
-You should also add the Go binary directory $GOROOT/bin
-to your shell's path.
+If a binary distribution is not available for your combination of
+operating system and architecture, visit
+https://golang.org/doc/install/source or load doc/install-source.html
+in your web browser for source installation instructions.
-For example, if you extracted the tar file into $HOME/go, you might
-put the following in your .profile:
+### Contributing
- export GOROOT=$HOME/go
- export PATH=$PATH:$GOROOT/bin
+Go is the work of hundreds of contributors. We appreciate your help!
+
+To contribute, please read the contribution guidelines:
+ https://golang.org/doc/contribute.html
-See https://golang.org/doc/install or doc/install.html for more details.
+Note that the Go project does not use GitHub pull requests, and that
+we use the issue tracker for bug reports and proposals only. See
+https://golang.org/wiki/Questions for a list of places to ask
+questions about the Go language.
<p>Code that you contribute should use the standard copyright header:</p>
<pre>
-// Copyright 2016 The Go Authors. All rights reserved.
+// Copyright 2017 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.
</pre>
}-->
<p><i>
-This applies to the <code>gc</code> toolchain. Gccgo has native gdb support.
+This applies to the standard toolchain (the <code>gc</code> Go
+compiler and tools). Gccgo has native gdb support.
Besides this overview you might want to consult the
<a href="http://sourceware.org/gdb/current/onlinedocs/gdb/">GDB manual</a>.
</i></p>
debugged.
</p>
+<p>
+If you want to use gdb to inspect a core dump, you can trigger a dump
+on a program crash, on systems that permit it, by setting
+<code>GOTRACEBACK=crash</code> in the environment (see the
+<a href="/pkg/runtime/#hdr-Environment_Variables"> runtime package
+documentation</a> for more info).
+</p>
+
<h3 id="Common_Operations">Common Operations</h3>
<ul>
<p>
If you're interested in what the debugging information looks like, run
-'<code>objdump -W 6.out</code>' and browse through the <code>.debug_*</code>
+'<code>objdump -W a.out</code>' and browse through the <code>.debug_*</code>
sections.
</p>
</pre>
<p>
-That <code>struct hchan<*testing.T></code> is the runtime-internal representation of a channel. It is currently empty, or gdb would have pretty-printed it's contents.
+That <code>struct hchan<*testing.T></code> is the
+runtime-internal representation of a channel. It is currently empty,
+or gdb would have pretty-printed its contents.
</p>
<p>
1.7.4 milestone</a> on our issue tracker for details.
</p>
+<p>
+go1.7.5 (released 2017/01/26) includes fixes to the compiler, runtime,
+and the <code>crypto/x509</code> and <code>time</code> packages.
+See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.5">Go
+1.7.5 milestone</a> on our issue tracker for details.
+</p>
+
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
<p>
should not be visible to Go programs.
</p>
+<p>
+The GCC 6 releases include a complete implementation of the Go 1.6.1
+user libraries. The Go 1.6 runtime is not fully merged, but that
+should not be visible to Go programs.
+</p>
+
+<p>
+The GCC 7 releases are expected to include a complete implementation
+of the Go 1.8 user libraries. As with earlier releases, the Go 1.8
+runtime is not fully merged, but that should not be visible to Go
+programs.
+</p>
+
<h2 id="Source_code">Source code</h2>
<p>
make install
</pre>
-<h3 id="Ubuntu">A note on Ubuntu</h3>
-
-<p>
-Current versions of Ubuntu and versions of GCC before 4.8 disagree on
-where system libraries and header files are found. This is not a
-gccgo issue. When building older versions of GCC, setting these
-environment variables while configuring and building gccgo may fix the
-problem.
-</p>
-
-<pre>
-LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
-C_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
-CPLUS_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
-export LIBRARY_PATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH
-</pre>
-
<h2 id="Using_gccgo">Using gccgo</h2>
<p>
<h3 id="Types">Types</h3>
<p>
-Basic types map directly: an <code>int</code> in Go is an <code>int</code>
-in C, an <code>int32</code> is an <code>int32_t</code>,
-etc. Go <code>byte</code> is equivalent to C <code>unsigned
-char</code>.
-Pointers in Go are pointers in C. A Go <code>struct</code> is the same as C
-<code>struct</code> with the same fields and types.
+Basic types map directly: an <code>int32</code> in Go is
+an <code>int32_t</code> in C, an <code>int64</code> is
+an <code>int64_t</code>, etc.
+The Go type <code>int</code> is an integer that is the same size as a
+pointer, and as such corresponds to the C type <code>intptr_t</code>.
+Go <code>byte</code> is equivalent to C <code>unsigned char</code>.
+Pointers in Go are pointers in C.
+A Go <code>struct</code> is the same as C <code>struct</code> with the
+same fields and types.
</p>
<p>
<pre>
struct __go_string {
const unsigned char *__data;
- int __length;
+ intptr_t __length;
};
</pre>
<pre>
struct __go_slice {
void *__values;
- int __count;
- int __capacity;
+ intptr_t __count;
+ intptr_t __capacity;
};
</pre>
guarantee that it will not change in the future. It is more useful as a
starting point for real Go code than as a regular procedure.
</p>
-
-<h2 id="RTEMS_Port">RTEMS Port</h2>
-<p>
-The gccgo compiler has been ported to <a href="http://www.rtems.com/">
-<code>RTEMS</code></a>. <code>RTEMS</code> is a real-time executive
-that provides a high performance environment for embedded applications
-on a range of processors and embedded hardware. The current gccgo
-port is for x86. The goal is to extend the port to most of the
-<a href="http://www.rtems.org/wiki/index.php/SupportedCPUs">
-architectures supported by <code>RTEMS</code></a>. For more information on the port,
-as well as instructions on how to install it, please see this
-<a href="http://www.rtems.org/wiki/index.php/GCCGoRTEMS"><code>RTEMS</code> Wiki page</a>.
<p>
The Plan 9 port's networking support is now much more complete
and matches the behavior of Unix and Windows with respect to deadlines
-and cancelation.
+and cancelation. For Plan 9 kernel requirements, see the
+<a href="https://golang.org/wiki/Plan9">Plan 9 wiki page</a>.
</p>
<p>
<h3 id="plugin">Plugins</h3>
<p>
- Go now supports a “<code>plugin</code>” build mode for generating
- plugins written in Go, and a
+ Go now provides early support for plugins with a “<code>plugin</code>”
+ build mode for generating plugins written in Go, and a
new <a href="/pkg/plugin/"><code>plugin</code></a> package for
- loading such plugins at run time. Plugin support is only currently
- available on Linux.
+ loading such plugins at run time. Plugin support is currently only
+ available on Linux. Please report any issues.
</p>
<h2 id="runtime">Runtime</h2>
<dl id="crypto_x509"><dt><a href="/pkg/crypto/x509/">crypto/x509</a></dt>
<dd>
- <p> <!-- CL 30578 -->
- <a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>
- is now implemented on Windows.
- </p>
-
<p> <!-- CL 24743 -->
PSS signatures are now supported.
</p>
<p>
The <a href="/pkg/database/sql#IsolationLevel"><code>IsolationLevel</code></a>
can now be set when starting a transaction by setting the isolation level
- on the <code>Context</code> then passing that <code>Context</code> to
- <a href="/pkg/database/sql#DB.BeginContext"><code>DB.BeginContext</code></a>.
+ on <a href="/pkg/database/sql#TxOptions.Isolation"><code>TxOptions.Isolation</code></a> and passing
+ it to <a href="/pkg/database/sql#DB.BeginTx"><code>DB.BeginTx</code></a>.
An error will be returned if an isolation level is selected that the driver
does not support. A read-only attribute may also be set on the transaction
- with <a href="/pkg/database/sql/#ReadOnlyContext"><code>ReadOnlyContext</code></a>.
+ by setting <a href="/pkg/database/sql/#TxOptions.ReadOnly"><code>TxOptions.ReadOnly</code></a>
+ to true.
</p>
<p>
Queries now expose the SQL column type information for drivers that support it.
June 31 and July 32.
</p>
- <p> <!-- CL 33029 -->
+ <p> <!-- CL 33029 --> <!-- CL 34816 -->
The <code>tzdata</code> database has been updated to version
- 2016i for systems that don't already have a local time zone
+ 2016j for systems that don't already have a local time zone
database.
</p>
and only the overall execution of the test binary would fail.
</p>
+ <p><!-- CL 32455 -->
+ The signature of the
+ <a href="/pkg/testing/#MainStart"><code>MainStart</code></a>
+ function has changed, as allowed by the documentation. It is an
+ internal detail and not part of the Go 1 compatibility promise.
+ If you're not calling <code>MainStart</code> directly but see
+ errors, that likely means you set the
+ normally-empty <code>GOROOT</code> environment variable and it
+ doesn't match the version of your <code>go</code> command's binary.
+ </p>
+
</dd>
</dl>
which contains the Go 1.4 source code plus accumulated fixes
to keep the tools running on newer operating systems.
(Go 1.4 was the last distribution in which the tool chain was written in C.)
+After unpacking the Go 1.4 source, <code>cd</code> to
+the <code>src</code> subdirectory and run <code>make.bash</code> (or,
+on Windows, <code>make.bat</code>).
</p>
<p>
$ <b>go build</b>
</pre>
-<pre class="testWindows" style="display: none">
+<pre class="testWindows">
C:\> <b>cd %USERPROFILE%\go\src\hello</b>
C:\Users\Gopher\go\src\hello> <b>go build</b>
</pre>
hello, world
</pre>
-<pre class="testWindows" style="display: none">
+<pre class="testWindows">
C:\Users\Gopher\go\src\hello> <b>hello</b>
hello, world
</pre>
}
runtime.GOMAXPROCS(threads)
argv := append(os.Args, "-test.run=NoSuchTestExists")
- if err := syscall.Exec(os.Args[0], argv, nil); err != nil {
+ if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil {
t.Fatal(err)
}
}
static stack_t oss;
static char signalStack[SIGSTKSZ];
-static void changeSignalStack() {
+static void changeSignalStack(void) {
stack_t ss;
memset(&ss, 0, sizeof ss);
ss.ss_sp = signalStack;
}
}
-static void restoreSignalStack() {
+static void restoreSignalStack(void) {
#if (defined(__x86_64__) || defined(__i386__)) && defined(__APPLE__)
// The Darwin C library enforces a minimum that the kernel does not.
// This is OK since we allocated this much space in mpreinit,
}
}
-static int zero() {
+static int zero(void) {
return 0;
}
*/
--- /dev/null
+// Copyright 2017 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 (
+ "iface_i"
+ "log"
+ "plugin"
+)
+
+func main() {
+ a, err := plugin.Open("iface_a.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("iface_a.so"): %v`, err)
+ }
+ b, err := plugin.Open("iface_b.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("iface_b.so"): %v`, err)
+ }
+
+ af, err := a.Lookup("F")
+ if err != nil {
+ log.Fatalf(`a.Lookup("F") failed: %v`, err)
+ }
+ bf, err := b.Lookup("F")
+ if err != nil {
+ log.Fatalf(`b.Lookup("F") failed: %v`, err)
+ }
+ if af.(func() interface{})() != bf.(func() interface{})() {
+ panic("empty interfaces not equal")
+ }
+
+ ag, err := a.Lookup("G")
+ if err != nil {
+ log.Fatalf(`a.Lookup("G") failed: %v`, err)
+ }
+ bg, err := b.Lookup("G")
+ if err != nil {
+ log.Fatalf(`b.Lookup("G") failed: %v`, err)
+ }
+ if ag.(func() iface_i.I)() != bg.(func() iface_i.I)() {
+ panic("nonempty interfaces not equal")
+ }
+}
--- /dev/null
+// Copyright 2017 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 "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
--- /dev/null
+// Copyright 2017 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 "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
--- /dev/null
+// Copyright 2017 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 iface_i
+
+type I interface {
+ M()
+}
+
+type T struct {
+}
+
+func (t *T) M() {
+}
+
+// *T implements I
--- /dev/null
+// Copyright 2017 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 dynamodbstreamsevt
+
+import "encoding/json"
+
+var foo json.RawMessage
+
+type Event struct{}
+
+func (e *Event) Dummy() {}
--- /dev/null
+// Copyright 2017 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.
+
+// The bug happened like this:
+// 1) The main binary adds an itab for *json.UnsupportedValueError / error
+// (concrete type / interface type). This itab goes in hash bucket 0x111.
+// 2) The plugin adds that same itab again. That makes a cycle in the itab
+// chain rooted at hash bucket 0x111.
+// 3) The main binary then asks for the itab for *dynamodbstreamsevt.Event /
+// json.Unmarshaler. This itab happens to also live in bucket 0x111.
+// The lookup code goes into an infinite loop searching for this itab.
+// The code is carefully crafted so that the two itabs are both from the
+// same bucket, and so that the second itab doesn't exist in
+// the itab hashmap yet (so the entire linked list must be searched).
+package main
+
+import (
+ "encoding/json"
+ "issue18676/dynamodbstreamsevt"
+ "plugin"
+)
+
+func main() {
+ plugin.Open("plugin.so")
+
+ var x interface{} = (*dynamodbstreamsevt.Event)(nil)
+ if _, ok := x.(json.Unmarshaler); !ok {
+ println("something")
+ }
+}
--- /dev/null
+// Copyright 2017 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 "C"
+
+import "issue18676/dynamodbstreamsevt"
+
+func F(evt *dynamodbstreamsevt.Event) {}
goarch=$(go env GOARCH)
function cleanup() {
- rm -f plugin*.so unnamed*.so
- rm -rf host pkg sub
+ rm -f plugin*.so unnamed*.so iface*.so
+ rm -rf host pkg sub iface issue18676
}
trap cleanup EXIT
GOPATH=$(pwd) go build host
LD_LIBRARY_PATH=$(pwd) ./host
+
+# Test that types and itabs get properly uniqified.
+GOPATH=$(pwd) go build -buildmode=plugin iface_a
+GOPATH=$(pwd) go build -buildmode=plugin iface_b
+GOPATH=$(pwd) go build iface
+LD_LIBRARY_PATH=$(pwd) ./iface
+
+# Test for issue 18676 - make sure we don't add the same itab twice.
+# The buggy code hangs forever, so use a timeout to check for that.
+GOPATH=$(pwd) go build -buildmode=plugin -o plugin.so src/issue18676/plugin.go
+GOPATH=$(pwd) go build -o issue18676 src/issue18676/main.go
+timeout 10s ./issue18676
--- /dev/null
+// Copyright 2017 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.
+
+// This program segfaulted during libpreinit when built with -msan:
+// http://golang.org/issue/18707
+
+package main
+
+import "C"
+
+func main() {}
status=0
+testmsanshared() {
+ goos=$(go env GOOS)
+ suffix="-installsuffix testsanitizers"
+ libext="so"
+ if [ "$goos" == "darwin" ]; then
+ libext="dylib"
+ fi
+ go build -msan -buildmode=c-shared $suffix -o ${TMPDIR}/libmsanshared.$libext msan_shared.go
+
+ echo 'int main() { return 0; }' > ${TMPDIR}/testmsanshared.c
+ $CC $(go env GOGCCFLAGS) -fsanitize=memory -o ${TMPDIR}/testmsanshared ${TMPDIR}/testmsanshared.c ${TMPDIR}/libmsanshared.$libext
+
+ if ! LD_LIBRARY_PATH=. ${TMPDIR}/testmsanshared; then
+ echo "FAIL: msan_shared"
+ status=1
+ fi
+ rm -f ${TMPDIR}/{testmsanshared,testmsanshared.c,libmsanshared.$libext}
+}
+
if test "$msan" = "yes"; then
if ! go build -msan std; then
echo "FAIL: build -msan std"
echo "FAIL: msan_fail"
status=1
fi
+
+ testmsanshared
fi
if test "$tsan" = "yes"; then
goCmd(t, "install", "-linkshared", "implicitcmd")
run(t, "running executable linked against library that contains same package as it", "./bin/implicitcmd")
}
+
+// Tests to make sure that the type fields of empty interfaces and itab
+// fields of nonempty interfaces are unique even across modules,
+// so that interface equality works correctly.
+func TestInterface(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_a")
+ // Note: iface_i gets installed implicitly as a dependency of iface_a.
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_b")
+ goCmd(t, "install", "-linkshared", "iface")
+ run(t, "running type/itab uniqueness tester", "./bin/iface")
+}
"reflect"
)
+var SlicePtr interface{} = &[]int{}
+
var V int = 1
var HasMask []string = []string{"hi"}
return nil
}
+var slicePtr interface{} = &[]int{}
+
func main() {
defer depBase.ImplementedInAsm()
// This code below causes various go.itab.* symbols to be generated in
if reflect.TypeOf(F).Out(0) != reflect.TypeOf(c) {
panic("bad reflection results, see golang.org/issue/18252")
}
+
+ sp := reflect.New(reflect.TypeOf(slicePtr).Elem())
+ s := sp.Interface()
+
+ if reflect.TypeOf(s) != reflect.TypeOf(slicePtr) {
+ panic("bad reflection results, see golang.org/issue/18729")
+ }
}
--- /dev/null
+// Copyright 2017 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 "iface_a"
+import "iface_b"
+
+func main() {
+ if iface_a.F() != iface_b.F() {
+ panic("empty interfaces not equal")
+ }
+ if iface_a.G() != iface_b.G() {
+ panic("non-empty interfaces not equal")
+ }
+}
--- /dev/null
+// Copyright 2017 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 iface_a
+
+import "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
--- /dev/null
+// Copyright 2017 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 iface_b
+
+import "iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
--- /dev/null
+// Copyright 2017 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 iface_i
+
+type I interface {
+ M()
+}
+
+type T struct {
+}
+
+func (t *T) M() {
+}
+
+// *T implements I
// Approximately 1 in a 100 binaries fail to start. If it happens,
// try again. These failures happen for several reasons beyond
// our control, but all of them are safe to retry as they happen
- // before lldb encounters the initial getwd breakpoint. As we
+ // before lldb encounters the initial SIGUSR2 stop. As we
// know the tests haven't started, we are not hiding flaky tests
// with this retry.
for i := 0; i < 5; i++ {
var opts options
opts, args = parseArgs(args)
+ // Pass the suffix for the current working directory as the
+ // first argument to the test. For iOS, cmd/go generates
+ // special handling of this argument.
+ args = append([]string{"cwdSuffix=" + pkgpath}, args...)
+
// ios-deploy invokes lldb to give us a shell session with the app.
s, err := newSession(appdir, args, opts)
if err != nil {
s.do(`process handle SIGHUP --stop false --pass true --notify false`)
s.do(`process handle SIGPIPE --stop false --pass true --notify false`)
s.do(`process handle SIGUSR1 --stop false --pass true --notify false`)
+ s.do(`process handle SIGUSR2 --stop true --pass false --notify true`) // sent by test harness
s.do(`process handle SIGCONT --stop false --pass true --notify false`)
s.do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work
s.do(`process handle SIGBUS --stop false --pass true --notify false`) // does not work
return nil
}
- s.do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go
-
started = true
- s.doCmd("run", "stop reason = breakpoint", 20*time.Second)
-
- // Move the current working directory into the faux gopath.
- if pkgpath != "src" {
- s.do(`breakpoint delete 1`)
- s.do(`expr char* $mem = (char*)malloc(512)`)
- s.do(`expr $mem = (char*)getwd($mem, 512)`)
- s.do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`)
- s.do(`call (void)chdir($mem)`)
- }
+ s.doCmd("run", "stop reason = signal SIGUSR2", 20*time.Second)
startTestsLen := s.out.Len()
fmt.Fprintln(s.in, `process continue`)
// Copy timezone file.
//
- // Typical apps have the zoneinfo.zip in the root of their app bundle,
+ // Apps have the zoneinfo.zip in the root of their app bundle,
// read by the time package as the working directory at initialization.
- // As we move the working directory to the GOROOT pkg directory, we
- // install the zoneinfo.zip file in the pkgpath.
if underGoRoot {
err := cp(
- filepath.Join(dstbase, pkgpath),
+ dstbase,
filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
)
if err != nil {
typecheckslice(fn.Nbody.Slice(), Etop)
Curfn = nil
popdcl()
- testdclstack()
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
typecheckslice(fn.Nbody.Slice(), Etop)
Curfn = nil
popdcl()
- testdclstack()
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
typecheckok = tcok
resumecheckwidth()
- testdclstack() // debugging only
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
}
func formatErrorf(format string, args ...interface{}) {
{"panicwrap", funcTag, 7},
{"gopanic", funcTag, 9},
{"gorecover", funcTag, 12},
+ {"goschedguarded", funcTag, 5},
{"printbool", funcTag, 14},
{"printfloat", funcTag, 16},
{"printint", funcTag, 18},
func gopanic(interface{})
func gorecover(*int32) interface{}
+func goschedguarded()
func printbool(bool)
func printfloat(float64)
)
var (
- Debug_append int
- Debug_closure int
- Debug_panic int
- Debug_slice int
- Debug_wb int
+ Debug_append int
+ Debug_closure int
+ debug_dclstack int
+ Debug_panic int
+ Debug_slice int
+ Debug_wb int
)
// Debug arguments.
{"append", &Debug_append}, // print information about append compilation
{"closure", &Debug_closure}, // print information about closure compilation
{"disablenil", &disable_checknil}, // disable nil checks
+ {"dclstack", &debug_dclstack}, // run internal dclstack checks
{"gcprog", &Debug_gcprog}, // print dump of GC programs
{"nil", &Debug_checknil}, // print information about nil checks
{"panic", &Debug_panic}, // do not hide any compiler panic
timings.Stop()
timings.AddEvent(int64(lexlineno-lexlineno0), "lines")
- testdclstack()
mkpackage(localpkg.Name) // final import not used checks
finishUniverse()
}
if nsyntaxerrors == 0 {
+ // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
testdclstack()
}
}
lookup(f[1]).Linkname = f[2]
case strings.HasPrefix(text, "go:cgo_"):
+ lineno = p.baseline + int32(line) - 1 // pragcgo may call yyerror
pragcgobuf += pragcgo(text)
fallthrough // because of //go:cgo_unsafe_args
default:
if i := strings.Index(text, " "); i >= 0 {
verb = verb[:i]
}
+ lineno = p.baseline + int32(line) - 1 // pragmaValue may call yyerror
return syntax.Pragma(pragmaValue(verb))
}
s.config = initssa()
s.f = s.config.NewFunc()
s.f.Name = name
+ if fn.Func.Pragma&Nosplit != 0 {
+ s.f.NoSplit = true
+ }
s.exitCode = fn.Func.Exit
s.panics = map[funcLine]*ssa.Block{}
s.config.DebugTest = s.config.DebugHashMatch("GOSSAHASH", name)
funcbody(fn)
Curfn = fn
popdcl()
- testdclstack()
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
// wrappers where T is anonymous (struct or interface) can be duplicated.
if rcvr.IsStruct() || rcvr.IsInterface() || rcvr.IsPtr() && rcvr.Elem().IsStruct() {
Fatalf("%v", err)
}
atExit(func() {
- runtime.GC() // profile all outstanding allocations
- if err := pprof.WriteHeapProfile(f); err != nil {
+ // Profile all outstanding allocations.
+ runtime.GC()
+ // compilebench parses the memory profile to extract memstats,
+ // which are only written in the legacy pprof format.
+ // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
+ const writeLegacyFormat = 1
+ if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
Fatalf("%v", err)
}
})
cmpr = cmpr.Left
}
- if !islvalue(cmpl) || !islvalue(cmpr) {
- Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
- }
-
// Chose not to inline. Call equality function directly.
if !inline {
+ if !islvalue(cmpl) || !islvalue(cmpr) {
+ Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
+ }
+
// eq algs take pointers
pl := temp(ptrto(t))
al := nod(OAS, pl, nod(OADDR, cmpl, nil))
package ssa
import (
+ "cmd/internal/obj"
"fmt"
"log"
"os"
{name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops
{name: "fuse", fn: fuse},
{name: "dse", fn: dse},
+ {name: "insert resched checks", fn: insertLoopReschedChecks,
+ disabled: obj.Preemptibleloops_enabled == 0}, // insert resched checks in loops.
{name: "tighten", fn: tighten}, // move values closer to their uses
{name: "lower", fn: lower, required: true},
{name: "lowered cse", fn: cse},
}
var passOrder = [...]constraint{
- // prove reliese on common-subexpression elimination for maximum benefits.
+ // "insert resched checks" uses mem, better to clean out stores first.
+ {"dse", "insert resched checks"},
+ // insert resched checks adds new blocks containing generic instructions
+ {"insert resched checks", "lower"},
+ {"insert resched checks", "tighten"},
+
+ // prove relies on common-subexpression elimination for maximum benefits.
{"generic cse", "prove"},
// deadcode after prove to eliminate all new dead blocks.
{"prove", "generic deadcode"},
vid idAlloc // value ID allocator
scheduled bool // Values in Blocks are in final order
+ NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
// when register allocation is done, maps value ids to locations
RegAlloc []Location
(Geq32F x y) -> (FGreaterEqual (FCMPU x y))
(Geq64F x y) -> (FGreaterEqual (FCMPU x y))
-(Geq8U x y) -> (GreaterEqual (CMPU (ZeroExt8to32 x) (ZeroExt8to32 y)))
-(Geq16U x y) -> (GreaterEqual (CMPU (ZeroExt16to32 x) (ZeroExt16to32 y)))
-(Geq32U x y) -> (GreaterEqual (CMPU x y))
+(Geq8U x y) -> (GreaterEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Geq16U x y) -> (GreaterEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Geq32U x y) -> (GreaterEqual (CMPWU x y))
(Geq64U x y) -> (GreaterEqual (CMPU x y))
// Absorb pseudo-ops into blocks.
(MOVDEQ y _ (FlagLT)) -> y
(MOVDEQ y _ (FlagGT)) -> y
-(MOVDNE _ y (FlagEQ)) -> y
-(MOVDNE x _ (FlagLT)) -> x
-(MOVDNE x _ (FlagGT)) -> x
+(MOVDNE y _ (FlagEQ)) -> y
+(MOVDNE _ x (FlagLT)) -> x
+(MOVDNE _ x (FlagGT)) -> x
(MOVDLT y _ (FlagEQ)) -> y
(MOVDLT _ x (FlagLT)) -> x
--- /dev/null
+// Copyright 2016 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 ssa
+
+import "fmt"
+
+// an edgeMemCtr records a backedge, together with the memory and
+// counter phi functions at the target of the backedge that must
+// be updated when a rescheduling check replaces the backedge.
+type edgeMemCtr struct {
+ e Edge
+ m *Value // phi for memory at dest of e
+ c *Value // phi for counter at dest of e
+}
+
+// a rewriteTarget is a a value-argindex pair indicating
+// where a rewrite is applied. Note that this is for values,
+// not for block controls, because block controls are not targets
+// for the rewrites performed in inserting rescheduling checks.
+type rewriteTarget struct {
+ v *Value
+ i int
+}
+
+type rewrite struct {
+ before, after *Value // before is the expected value before rewrite, after is the new value installed.
+ rewrites []rewriteTarget // all the targets for this rewrite.
+}
+
+func (r *rewrite) String() string {
+ s := "\n\tbefore=" + r.before.String() + ", after=" + r.after.String()
+ for _, rw := range r.rewrites {
+ s += ", (i=" + fmt.Sprint(rw.i) + ", v=" + rw.v.LongString() + ")"
+ }
+ s += "\n"
+ return s
+}
+
+const initialRescheduleCounterValue = 1021 // Largest 10-bit prime. 97 nSec loop bodies will check every 100 uSec.
+
+// insertLoopReschedChecks inserts rescheduling checks on loop backedges.
+func insertLoopReschedChecks(f *Func) {
+ // TODO: when split information is recorded in export data, insert checks only on backedges that can be reached on a split-call-free path.
+
+ // Loop reschedule checks decrement a per-function counter
+ // shared by all loops, and when the counter becomes non-positive
+ // a call is made to a rescheduling check in the runtime.
+ //
+ // Steps:
+ // 1. locate backedges.
+ // 2. Record memory definitions at block end so that
+ // the SSA graph for mem can be prperly modified.
+ // 3. Define a counter and record its future uses (at backedges)
+ // (Same process as 2, applied to a single definition of the counter.
+ // difference for mem is that there are zero-to-many existing mem
+ // definitions, versus exactly one for the new counter.)
+ // 4. Ensure that phi functions that will-be-needed for mem and counter
+ // are present in the graph, initially with trivial inputs.
+ // 5. Record all to-be-modified uses of mem and counter;
+ // apply modifications (split into two steps to simplify and
+ // avoided nagging order-dependences).
+ // 6. Rewrite backedges to include counter check, reschedule check,
+ // and modify destination phi function appropriately with new
+ // definitions for mem and counter.
+
+ if f.NoSplit { // nosplit functions don't reschedule.
+ return
+ }
+
+ backedges := backedges(f)
+ if len(backedges) == 0 { // no backedges means no rescheduling checks.
+ return
+ }
+
+ lastMems := findLastMems(f)
+
+ idom := f.Idom()
+ sdom := f.sdom()
+
+ if f.pass.debug > 2 {
+ fmt.Printf("before %s = %s\n", f.Name, sdom.treestructure(f.Entry))
+ }
+
+ tofixBackedges := []edgeMemCtr{}
+
+ for _, e := range backedges { // TODO: could filter here by calls in loops, if declared and inferred nosplit are recorded in export data.
+ tofixBackedges = append(tofixBackedges, edgeMemCtr{e, nil, nil})
+ }
+
+ // It's possible that there is no memory state (no global/pointer loads/stores or calls)
+ if lastMems[f.Entry.ID] == nil {
+ lastMems[f.Entry.ID] = f.Entry.NewValue0(f.Entry.Line, OpInitMem, TypeMem)
+ }
+
+ memDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, the mem def seen at its bottom. Could be from earlier block.
+
+ // Propagate last mem definitions forward through successor blocks.
+ po := f.postorder()
+ for i := len(po) - 1; i >= 0; i-- {
+ b := po[i]
+ mem := lastMems[b.ID]
+ for j := 0; mem == nil; j++ { // if there's no def, then there's no phi, so the visible mem is identical in all predecessors.
+ // loop because there might be backedges that haven't been visited yet.
+ mem = memDefsAtBlockEnds[b.Preds[j].b.ID]
+ }
+ memDefsAtBlockEnds[b.ID] = mem
+ }
+
+ // Set up counter. There are no phis etc pre-existing for it.
+ counter0 := f.Entry.NewValue0I(f.Entry.Line, OpConst32, f.Config.fe.TypeInt32(), initialRescheduleCounterValue)
+ ctrDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, def visible at its end, if that def will be used.
+
+ // There's a minor difference between memDefsAtBlockEnds and ctrDefsAtBlockEnds;
+ // because the counter only matter for loops and code that reaches them, it is nil for blocks where the ctr is no
+ // longer live. This will avoid creation of dead phi functions. This optimization is ignored for the mem variable
+ // because it is harder and also less likely to be helpful, though dead code elimination ought to clean this out anyhow.
+
+ for _, emc := range tofixBackedges {
+ e := emc.e
+ // set initial uses of counter zero (note available-at-bottom and use are the same thing initially.)
+ // each back-edge will be rewritten to include a reschedule check, and that will use the counter.
+ src := e.b.Preds[e.i].b
+ ctrDefsAtBlockEnds[src.ID] = counter0
+ }
+
+ // Push uses towards root
+ for _, b := range f.postorder() {
+ bd := ctrDefsAtBlockEnds[b.ID]
+ if bd == nil {
+ continue
+ }
+ for _, e := range b.Preds {
+ p := e.b
+ if ctrDefsAtBlockEnds[p.ID] == nil {
+ ctrDefsAtBlockEnds[p.ID] = bd
+ }
+ }
+ }
+
+ // Maps from block to newly-inserted phi function in block.
+ newmemphis := make(map[*Block]rewrite)
+ newctrphis := make(map[*Block]rewrite)
+
+ // Insert phi functions as necessary for future changes to flow graph.
+ for i, emc := range tofixBackedges {
+ e := emc.e
+ h := e.b
+
+ // find the phi function for the memory input at "h", if there is one.
+ var headerMemPhi *Value // look for header mem phi
+
+ for _, v := range h.Values {
+ if v.Op == OpPhi && v.Type.IsMemory() {
+ headerMemPhi = v
+ }
+ }
+
+ if headerMemPhi == nil {
+ // if the header is nil, make a trivial phi from the dominator
+ mem0 := memDefsAtBlockEnds[idom[h.ID].ID]
+ headerMemPhi = newPhiFor(h, mem0)
+ newmemphis[h] = rewrite{before: mem0, after: headerMemPhi}
+ addDFphis(mem0, h, h, f, memDefsAtBlockEnds, newmemphis)
+
+ }
+ tofixBackedges[i].m = headerMemPhi
+
+ var headerCtrPhi *Value
+ rw, ok := newctrphis[h]
+ if !ok {
+ headerCtrPhi = newPhiFor(h, counter0)
+ newctrphis[h] = rewrite{before: counter0, after: headerCtrPhi}
+ addDFphis(counter0, h, h, f, ctrDefsAtBlockEnds, newctrphis)
+ } else {
+ headerCtrPhi = rw.after
+ }
+ tofixBackedges[i].c = headerCtrPhi
+ }
+
+ rewriteNewPhis(f.Entry, f.Entry, f, memDefsAtBlockEnds, newmemphis)
+ rewriteNewPhis(f.Entry, f.Entry, f, ctrDefsAtBlockEnds, newctrphis)
+
+ if f.pass.debug > 0 {
+ for b, r := range newmemphis {
+ fmt.Printf("b=%s, rewrite=%s\n", b, r.String())
+ }
+
+ for b, r := range newctrphis {
+ fmt.Printf("b=%s, rewrite=%s\n", b, r.String())
+ }
+ }
+
+ // Apply collected rewrites.
+ for _, r := range newmemphis {
+ for _, rw := range r.rewrites {
+ rw.v.SetArg(rw.i, r.after)
+ }
+ }
+
+ for _, r := range newctrphis {
+ for _, rw := range r.rewrites {
+ rw.v.SetArg(rw.i, r.after)
+ }
+ }
+
+ zero := f.Entry.NewValue0I(f.Entry.Line, OpConst32, f.Config.fe.TypeInt32(), 0)
+ one := f.Entry.NewValue0I(f.Entry.Line, OpConst32, f.Config.fe.TypeInt32(), 1)
+
+ // Rewrite backedges to include reschedule checks.
+ for _, emc := range tofixBackedges {
+ e := emc.e
+ headerMemPhi := emc.m
+ headerCtrPhi := emc.c
+ h := e.b
+ i := e.i
+ p := h.Preds[i]
+ bb := p.b
+ mem0 := headerMemPhi.Args[i]
+ ctr0 := headerCtrPhi.Args[i]
+ // bb e->p h,
+ // Because we're going to insert a rare-call, make sure the
+ // looping edge still looks likely.
+ likely := BranchLikely
+ if p.i != 0 {
+ likely = BranchUnlikely
+ }
+ bb.Likely = likely
+
+ // rewrite edge to include reschedule check
+ // existing edges:
+ //
+ // bb.Succs[p.i] == Edge{h, i}
+ // h.Preds[i] == p == Edge{bb,p.i}
+ //
+ // new block(s):
+ // test:
+ // ctr1 := ctr0 - 1
+ // if ctr1 <= 0 { goto sched }
+ // goto join
+ // sched:
+ // mem1 := call resched (mem0)
+ // goto join
+ // join:
+ // ctr2 := phi(ctr1, counter0) // counter0 is the constant
+ // mem2 := phi(mem0, mem1)
+ // goto h
+ //
+ // and correct arg i of headerMemPhi and headerCtrPhi
+ //
+ // EXCEPT: block containing only phi functions is bad
+ // for the register allocator. Therefore, there is no
+ // join, and instead branches targeting join instead target
+ // the header, and the other phi functions within header are
+ // adjusted for the additional input.
+
+ test := f.NewBlock(BlockIf)
+ sched := f.NewBlock(BlockPlain)
+
+ test.Line = bb.Line
+ sched.Line = bb.Line
+
+ // ctr1 := ctr0 - 1
+ // if ctr1 <= 0 { goto sched }
+ // goto header
+ ctr1 := test.NewValue2(bb.Line, OpSub32, f.Config.fe.TypeInt32(), ctr0, one)
+ cmp := test.NewValue2(bb.Line, OpLeq32, f.Config.fe.TypeBool(), ctr1, zero)
+ test.SetControl(cmp)
+ test.AddEdgeTo(sched) // if true
+ // if false -- rewrite edge to header.
+ // do NOT remove+add, because that will perturb all the other phi functions
+ // as well as messing up other edges to the header.
+ test.Succs = append(test.Succs, Edge{h, i})
+ h.Preds[i] = Edge{test, 1}
+ headerMemPhi.SetArg(i, mem0)
+ headerCtrPhi.SetArg(i, ctr1)
+
+ test.Likely = BranchUnlikely
+
+ // sched:
+ // mem1 := call resched (mem0)
+ // goto header
+ resched := f.Config.fe.Syslook("goschedguarded")
+ mem1 := sched.NewValue1A(bb.Line, OpStaticCall, TypeMem, resched, mem0)
+ sched.AddEdgeTo(h)
+ headerMemPhi.AddArg(mem1)
+ headerCtrPhi.AddArg(counter0)
+
+ bb.Succs[p.i] = Edge{test, 0}
+ test.Preds = append(test.Preds, Edge{bb, p.i})
+
+ // Must correct all the other phi functions in the header for new incoming edge.
+ // Except for mem and counter phis, it will be the same value seen on the original
+ // backedge at index i.
+ for _, v := range h.Values {
+ if v.Op == OpPhi && v != headerMemPhi && v != headerCtrPhi {
+ v.AddArg(v.Args[i])
+ }
+ }
+ }
+
+ f.invalidateCFG()
+
+ if f.pass.debug > 2 {
+ sdom = newSparseTree(f, f.Idom())
+ fmt.Printf("after %s = %s\n", f.Name, sdom.treestructure(f.Entry))
+ }
+
+ return
+}
+
+// newPhiFor inserts a new Phi function into b,
+// with all inputs set to v.
+func newPhiFor(b *Block, v *Value) *Value {
+ phiV := b.NewValue0(b.Line, OpPhi, v.Type)
+
+ for range b.Preds {
+ phiV.AddArg(v)
+ }
+ return phiV
+}
+
+// rewriteNewPhis updates newphis[h] to record all places where the new phi function inserted
+// in block h will replace a previous definition. Block b is the block currently being processed;
+// if b has its own phi definition then it takes the place of h.
+// defsForUses provides information about other definitions of the variable that are present
+// (and if nil, indicates that the variable is no longer live)
+func rewriteNewPhis(h, b *Block, f *Func, defsForUses []*Value, newphis map[*Block]rewrite) {
+ // If b is a block with a new phi, then a new rewrite applies below it in the dominator tree.
+ if _, ok := newphis[b]; ok {
+ h = b
+ }
+ change := newphis[h]
+ x := change.before
+ y := change.after
+
+ // Apply rewrites to this block
+ if x != nil { // don't waste time on the common case of no definition.
+ p := &change.rewrites
+ for _, v := range b.Values {
+ if v == y { // don't rewrite self -- phi inputs are handled below.
+ continue
+ }
+ for i, w := range v.Args {
+ if w != x {
+ continue
+ }
+ *p = append(*p, rewriteTarget{v, i})
+ }
+ }
+
+ // Rewrite appropriate inputs of phis reached in successors
+ // in dominance frontier, self, and dominated.
+ // If the variable def reaching uses in b is itself defined in b, then the new phi function
+ // does not reach the successors of b. (This assumes a bit about the structure of the
+ // phi use-def graph, but it's true for memory and the inserted counter.)
+ if dfu := defsForUses[b.ID]; dfu != nil && dfu.Block != b {
+ for _, e := range b.Succs {
+ s := e.b
+ if sphi, ok := newphis[s]; ok { // saves time to find the phi this way.
+ *p = append(*p, rewriteTarget{sphi.after, e.i})
+ continue
+ }
+ for _, v := range s.Values {
+ if v.Op == OpPhi && v.Args[e.i] == x {
+ *p = append(*p, rewriteTarget{v, e.i})
+ break
+ }
+ }
+ }
+ }
+ newphis[h] = change
+ }
+
+ sdom := f.sdom()
+
+ for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling {
+ rewriteNewPhis(h, c, f, defsForUses, newphis) // TODO: convert to explicit stack from recursion.
+ }
+}
+
+// addDFphis creates new trivial phis that are necessary to correctly reflect (within SSA)
+// a new definition for variable "x" inserted at h (usually but not necessarily a phi).
+// These new phis can only occur at the dominance frontier of h; block s is in the dominance
+// frontier of h if h does not strictly dominate s and if s is a successor of a block b where
+// either b = h or h strictly dominates b.
+// These newly created phis are themselves new definitions that may require addition of their
+// own trivial phi functions in their own dominance frontier, and this is handled recursively.
+func addDFphis(x *Value, h, b *Block, f *Func, defForUses []*Value, newphis map[*Block]rewrite) {
+ oldv := defForUses[b.ID]
+ if oldv != x { // either a new definition replacing x, or nil if it is proven that there are no uses reachable from b
+ return
+ }
+ sdom := f.sdom()
+ idom := f.Idom()
+outer:
+ for _, e := range b.Succs {
+ s := e.b
+ // check phi functions in the dominance frontier
+ if sdom.isAncestor(h, s) {
+ continue // h dominates s, successor of b, therefore s is not in the frontier.
+ }
+ if _, ok := newphis[s]; ok {
+ continue // successor s of b already has a new phi function, so there is no need to add another.
+ }
+ if x != nil {
+ for _, v := range s.Values {
+ if v.Op == OpPhi && v.Args[e.i] == x {
+ continue outer // successor s of b has an old phi function, so there is no need to add another.
+ }
+ }
+ }
+
+ old := defForUses[idom[s.ID].ID] // new phi function is correct-but-redundant, combining value "old" on all inputs.
+ headerPhi := newPhiFor(s, old)
+ // the new phi will replace "old" in block s and all blocks dominated by s.
+ newphis[s] = rewrite{before: old, after: headerPhi} // record new phi, to have inputs labeled "old" rewritten to "headerPhi"
+ addDFphis(old, s, s, f, defForUses, newphis) // the new definition may also create new phi functions.
+ }
+ for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling {
+ addDFphis(x, h, c, f, defForUses, newphis) // TODO: convert to explicit stack from recursion.
+ }
+}
+
+// findLastMems maps block ids to last memory-output op in a block, if any
+func findLastMems(f *Func) []*Value {
+
+ var stores []*Value
+ lastMems := make([]*Value, f.NumBlocks())
+ storeUse := f.newSparseSet(f.NumValues())
+ defer f.retSparseSet(storeUse)
+ for _, b := range f.Blocks {
+ // Find all the stores in this block. Categorize their uses:
+ // storeUse contains stores which are used by a subsequent store.
+ storeUse.clear()
+ stores = stores[:0]
+ var memPhi *Value
+ for _, v := range b.Values {
+ if v.Op == OpPhi {
+ if v.Type.IsMemory() {
+ memPhi = v
+ }
+ continue
+ }
+ if v.Type.IsMemory() {
+ stores = append(stores, v)
+ if v.Op == OpSelect1 {
+ // Use the arg of the tuple-generating op.
+ v = v.Args[0]
+ }
+ for _, a := range v.Args {
+ if a.Block == b && a.Type.IsMemory() {
+ storeUse.add(a.ID)
+ }
+ }
+ }
+ }
+ if len(stores) == 0 {
+ lastMems[b.ID] = memPhi
+ continue
+ }
+
+ // find last store in the block
+ var last *Value
+ for _, v := range stores {
+ if storeUse.contains(v.ID) {
+ continue
+ }
+ if last != nil {
+ b.Fatalf("two final stores - simultaneous live stores %s %s", last, v)
+ }
+ last = v
+ }
+ if last == nil {
+ b.Fatalf("no last store found - cycle?")
+ }
+ lastMems[b.ID] = last
+ }
+ return lastMems
+}
+
+type backedgesState struct {
+ b *Block
+ i int
+}
+
+// backedges returns a slice of successor edges that are back
+// edges. For reducible loops, edge.b is the header.
+func backedges(f *Func) []Edge {
+ edges := []Edge{}
+ mark := make([]markKind, f.NumBlocks())
+ stack := []backedgesState{}
+
+ mark[f.Entry.ID] = notExplored
+ stack = append(stack, backedgesState{f.Entry, 0})
+
+ for len(stack) > 0 {
+ l := len(stack)
+ x := stack[l-1]
+ if x.i < len(x.b.Succs) {
+ e := x.b.Succs[x.i]
+ stack[l-1].i++
+ s := e.b
+ if mark[s.ID] == notFound {
+ mark[s.ID] = notExplored
+ stack = append(stack, backedgesState{s, 0})
+ } else if mark[s.ID] == notExplored {
+ edges = append(edges, e)
+ }
+ } else {
+ mark[x.b.ID] = done
+ stack = stack[0 : l-1]
+ }
+ }
+ return edges
+}
}
}
- // Next, process values in the block.
+ // Next, eliminate any redundant nil checks in this block.
i := 0
for _, v := range b.Values {
b.Values[i] = v
f.Config.Warnl(v.Line, "removed nil check")
}
v.reset(OpUnknown)
+ // TODO: f.freeValue(v)
i--
continue
}
- // Record the fact that we know ptr is non nil, and remember to
- // undo that information when this dominator subtree is done.
- nonNilValues[ptr.ID] = true
- work = append(work, bp{op: ClearPtr, ptr: ptr})
}
}
for j := i; j < len(b.Values); j++ {
}
b.Values = b.Values[:i]
+ // Finally, find redundant nil checks for subsequent blocks.
+ // Note that we can't add these until the loop above is done, as the
+ // values in the block are not ordered in any way when this pass runs.
+ // This was the cause of issue #18725.
+ for _, v := range b.Values {
+ if v.Op != OpNilCheck {
+ continue
+ }
+ ptr := v.Args[0]
+ // Record the fact that we know ptr is non nil, and remember to
+ // undo that information when this dominator subtree is done.
+ nonNilValues[ptr.ID] = true
+ work = append(work, bp{op: ClearPtr, ptr: ptr})
+ }
+
// Add all dominated blocks to the work list.
for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
work = append(work, bp{op: Work, block: w})
_ = b
// match: (Geq16U x y)
// cond:
- // result: (GreaterEqual (CMPU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+ // result: (GreaterEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
for {
x := v.Args[0]
y := v.Args[1]
v.reset(OpPPC64GreaterEqual)
- v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+ v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
v1.AddArg(x)
v0.AddArg(v1)
_ = b
// match: (Geq32U x y)
// cond:
- // result: (GreaterEqual (CMPU x y))
+ // result: (GreaterEqual (CMPWU x y))
for {
x := v.Args[0]
y := v.Args[1]
v.reset(OpPPC64GreaterEqual)
- v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+ v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
_ = b
// match: (Geq8U x y)
// cond:
- // result: (GreaterEqual (CMPU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+ // result: (GreaterEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
for {
x := v.Args[0]
y := v.Args[1]
v.reset(OpPPC64GreaterEqual)
- v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+ v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
v1.AddArg(x)
v0.AddArg(v1)
v.AddArg(cmp)
return true
}
- // match: (MOVDNE _ y (FlagEQ))
+ // match: (MOVDNE y _ (FlagEQ))
// cond:
// result: y
for {
- y := v.Args[1]
+ y := v.Args[0]
v_2 := v.Args[2]
if v_2.Op != OpS390XFlagEQ {
break
v.AddArg(y)
return true
}
- // match: (MOVDNE x _ (FlagLT))
+ // match: (MOVDNE _ x (FlagLT))
// cond:
// result: x
for {
- x := v.Args[0]
+ x := v.Args[1]
v_2 := v.Args[2]
if v_2.Op != OpS390XFlagLT {
break
v.AddArg(x)
return true
}
- // match: (MOVDNE x _ (FlagGT))
+ // match: (MOVDNE _ x (FlagGT))
// cond:
// result: x
for {
- x := v.Args[0]
+ x := v.Args[1]
v_2 := v.Args[2]
if v_2.Op != OpS390XFlagGT {
break
package ssa
-import "fmt"
+import (
+ "fmt"
+ "strings"
+)
type SparseTreeNode struct {
child *Block
return t
}
+// treestructure provides a string description of the dominator
+// tree and flow structure of block b and all blocks that it
+// dominates.
+func (t SparseTree) treestructure(b *Block) string {
+ return t.treestructure1(b, 0)
+}
+func (t SparseTree) treestructure1(b *Block, i int) string {
+ s := "\n" + strings.Repeat("\t", i) + b.String() + "->["
+ for i, e := range b.Succs {
+ if i > 0 {
+ s = s + ","
+ }
+ s = s + e.b.String()
+ }
+ s += "]"
+ if c0 := t[b.ID].child; c0 != nil {
+ s += "("
+ for c := c0; c != nil; c = t[c.ID].sibling {
+ if c != c0 {
+ s += " "
+ }
+ s += t.treestructure1(c, i+1)
+ }
+ s += ")"
+ }
+ return s
+}
+
// numberBlock assigns entry and exit numbers for b and b's
// children in an in-order walk from a gappy sequence, where n
// is the first number not yet assigned or reserved. N should
valueLoop:
for i, v := range b.Values {
switch v.Op {
- case OpStoreWB, OpMoveWB, OpMoveWBVolatile:
+ case OpStoreWB, OpMoveWB, OpMoveWBVolatile, OpZeroWB:
if IsStackAddr(v.Args[0]) {
switch v.Op {
case OpStoreWB:
"os/exec"
"path/filepath"
"regexp"
- "runtime"
"strconv"
"strings"
"sync"
// This test needs its stdout/stderr to be terminals, so we don't run it from cmd/go's tests.
// See issue 18153.
- if runtime.GOOS == "linux" {
+ if t.goos == "linux" {
t.tests = append(t.tests, distTest{
name: "cmd_go_test_terminal",
heading: "cmd/go terminal test",
if t.gohostos == "linux" && t.goarch == "amd64" {
t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
}
- if t.gohostos == "linux" && t.goarch == "amd64" {
+ if t.goos == "linux" && t.goarch == "amd64" {
t.registerTest("testsanitizers", "../misc/cgo/testsanitizers", "./test.bash")
}
if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" {
// clean remove object files
// doc show documentation for package or symbol
// env print Go environment information
-// bug print information for bug reports
+// bug start a bug report
// fix run go tool fix on packages
// fmt run gofmt on package sources
// generate generate Go files by processing source
// each named variable on its own line.
//
//
-// Print information for bug reports
+// Start a bug report
//
// Usage:
//
// go bug
//
-// Bug prints information that helps file effective bug reports.
-//
-// Bugs may be reported at https://golang.org/issue/new.
+// Bug opens the default browser and starts a new bug report.
+// The report includes useful system information.
//
//
// Run go tool fix on packages
var cmdBug = &Command{
Run: runBug,
UsageLine: "bug",
- Short: "print information for bug reports",
+ Short: "start a bug report",
Long: `
-Bug prints information that helps file effective bug reports.
-
-Bugs may be reported at https://golang.org/issue/new.
+Bug opens the default browser and starts a new bug report.
+The report includes useful system information.
`,
}
tg.grepBoth(okPattern, "go test did not say ok")
}
+// Issue 18845
+func TestBenchTimeout(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go")
+}
+
func TestLinkXImportPathEscape(t *testing.T) {
// golang.org/issue/16710
tg := testgo(t)
tg.setenv("GOPATH", tg.path("go"))
tg.run("build", "p")
}
+
+// Issue 18778.
+func TestDotDotDotOutsideGOPATH(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+
+ tg.tempFile("pkgs/a.go", `package x`)
+ tg.tempFile("pkgs/a_test.go", `package x_test
+import "testing"
+func TestX(t *testing.T) {}`)
+
+ tg.tempFile("pkgs/a/a.go", `package a`)
+ tg.tempFile("pkgs/a/a_test.go", `package a_test
+import "testing"
+func TestA(t *testing.T) {}`)
+
+ tg.cd(tg.path("pkgs"))
+ tg.run("build", "./...")
+ tg.run("test", "./...")
+ tg.run("list", "./...")
+ tg.grepStdout("pkgs$", "expected package not listed")
+ tg.grepStdout("pkgs/a", "expected package not listed")
+}
func cleanImport(path string) string {
orig := path
path = pathpkg.Clean(path)
- if strings.HasPrefix(orig, "./") && path != ".." && path != "." && !strings.HasPrefix(path, "../") {
+ if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") {
path = "./" + path
}
return path
if buildContext.GOOS == "darwin" {
if buildContext.GOARCH == "arm" || buildContext.GOARCH == "arm64" {
- t.NeedCgo = true
+ t.IsIOS = true
+ t.NeedOS = true
}
}
+ if t.TestMain == nil {
+ t.NeedOS = true
+ }
for _, cp := range pmain.imports {
if len(cp.coverVars) > 0 {
NeedTest bool
ImportXtest bool
NeedXtest bool
- NeedCgo bool
+ NeedOS bool
+ IsIOS bool
Cover []coverInfo
}
package main
import (
-{{if not .TestMain}}
+{{if .NeedOS}}
"os"
{{end}}
"testing"
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
{{end}}
-{{if .NeedCgo}}
+{{if .IsIOS}}
+ "os/signal"
_ "runtime/cgo"
+ "syscall"
{{end}}
)
{{end}}
func main() {
+{{if .IsIOS}}
+ // Send a SIGUSR2, which will be intercepted by LLDB to
+ // tell the test harness that installation was successful.
+ // See misc/ios/go_darwin_arm_exec.go.
+ signal.Notify(make(chan os.Signal), syscall.SIGUSR2)
+ syscall.Kill(0, syscall.SIGUSR2)
+ signal.Reset(syscall.SIGUSR2)
+
+ // The first argument supplied to an iOS test is an offset
+ // suffix for the current working directory.
+ // Process it here, and remove it from os.Args.
+ const hdr = "cwdSuffix="
+ if len(os.Args) < 2 || len(os.Args[1]) <= len(hdr) || os.Args[1][:len(hdr)] != hdr {
+ panic("iOS test not passed a working directory suffix")
+ }
+ suffix := os.Args[1][len(hdr):]
+ dir, err := os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+ if err := os.Chdir(dir + "/" + suffix); err != nil {
+ panic(err)
+ }
+ os.Args = append([]string{os.Args[0]}, os.Args[2:]...)
+{{end}}
+
{{if .CoverEnabled}}
testing.RegisterCover(testing.Cover{
Mode: {{printf "%q" .CoverMode}},
--- /dev/null
+package timeoutbench_test
+
+import (
+ "testing"
+ "time"
+)
+
+func BenchmarkSleep1s(b *testing.B) {
+ time.Sleep(1 * time.Second)
+}
// go-specific code shared across loaders (5l, 6l, 8l).
var (
- framepointer_enabled int
- Fieldtrack_enabled int
+ framepointer_enabled int
+ Fieldtrack_enabled int
+ Preemptibleloops_enabled int
)
// Toolchain experiments.
}{
{"fieldtrack", &Fieldtrack_enabled},
{"framepointer", &framepointer_enabled},
+ {"preemptibleloops", &Preemptibleloops_enabled},
}
func addexp(s string) {
epcs = s
dsym := ctxt.Syms.Lookup(dwarf.InfoPrefix+s.Name, int(s.Version))
- dsym.Attr |= AttrHidden
+ dsym.Attr |= AttrHidden | AttrReachable
dsym.Type = obj.SDWARFINFO
for _, r := range dsym.R {
if r.Type == obj.R_DWARFREF && r.Sym.Size == 0 {
if s == nil {
return 0
}
- if Buildmode == BuildmodePlugin && onlycsymbol(s) {
+ if Buildmode == BuildmodePlugin && Headtype == obj.Hdarwin && onlycsymbol(s) {
return 1
}
// We want to generate func table entries only for the "lowest level" symbols,
}
}
-var (
- digits = mustLoadFile("testdata/e.txt.bz2")
- twain = mustLoadFile("testdata/Mark.Twain-Tom.Sawyer.txt.bz2")
- random = mustLoadFile("testdata/random.data.bz2")
-)
-
func benchmarkDecode(b *testing.B, compressed []byte) {
// Determine the uncompressed size of testfile.
uncompressedSize, err := io.Copy(ioutil.Discard, NewReader(bytes.NewReader(compressed)))
}
}
-func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) }
-func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) }
-func BenchmarkDecodeRand(b *testing.B) { benchmarkDecode(b, random) }
+func BenchmarkDecodeDigits(b *testing.B) {
+ digits := mustLoadFile("testdata/e.txt.bz2")
+ b.ResetTimer()
+ benchmarkDecode(b, digits)
+}
+func BenchmarkDecodeTwain(b *testing.B) {
+ twain := mustLoadFile("testdata/Mark.Twain-Tom.Sawyer.txt.bz2")
+ b.ResetTimer()
+ benchmarkDecode(b, twain)
+}
+func BenchmarkDecodeRand(b *testing.B) {
+ random := mustLoadFile("testdata/random.data.bz2")
+ b.ResetTimer()
+ benchmarkDecode(b, random)
+}
delta := d.hashOffset - 1
d.hashOffset -= delta
d.chainHead -= delta
- for i, v := range d.hashPrev {
+
+ // Iterate over slices instead of arrays to avoid copying
+ // the entire table onto the stack (Issue #18625).
+ for i, v := range d.hashPrev[:] {
if int(v) > delta {
d.hashPrev[i] = uint32(int(v) - delta)
} else {
d.hashPrev[i] = 0
}
}
- for i, v := range d.hashHead {
+ for i, v := range d.hashHead[:] {
if int(v) > delta {
d.hashHead[i] = uint32(int(v) - delta)
} else {
"io"
"io/ioutil"
"reflect"
+ "runtime/debug"
"sync"
"testing"
)
}
}
}
+
+func TestMaxStackSize(t *testing.T) {
+ // This test must not run in parallel with other tests as debug.SetMaxStack
+ // affects all goroutines.
+ n := debug.SetMaxStack(1 << 16)
+ defer debug.SetMaxStack(n)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ b := make([]byte, 1<<20)
+ for level := HuffmanOnly; level <= BestCompression; level++ {
+ // Run in separate goroutine to increase probability of stack regrowth.
+ wg.Add(1)
+ go func(level int) {
+ defer wg.Done()
+ zw, err := NewWriter(ioutil.Discard, level)
+ if err != nil {
+ t.Errorf("level %d, NewWriter() = %v, want nil", level, err)
+ }
+ if n, err := zw.Write(b); n != len(b) || err != nil {
+ t.Errorf("level %d, Write() = (%d, %v), want (%d, nil)", level, n, err, len(b))
+ }
+ if err := zw.Close(); err != nil {
+ t.Errorf("level %d, Close() = %v, want nil", level, err)
+ }
+ zw.Reset(ioutil.Discard)
+ }(level)
+ }
+}
func (e *deflateFast) encode(dst []token, src []byte) []token {
// Ensure that e.cur doesn't wrap.
if e.cur > 1<<30 {
- *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+ e.resetAll()
}
// This check isn't in the Snappy implementation, but there, the caller
// Protect against e.cur wraparound.
if e.cur > 1<<30 {
- *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+ e.resetAll()
+ }
+}
+
+// resetAll resets the deflateFast struct and is only called in rare
+// situations to prevent integer overflow. It manually resets each field
+// to avoid causing large stack growth.
+//
+// See https://golang.org/issue/18636.
+func (e *deflateFast) resetAll() {
+ // This is equivalent to:
+ // *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+ e.cur = maxStoreBlockSize
+ e.prev = e.prev[:0]
+ for i := range e.table {
+ e.table[i] = tableEntry{}
}
}
"testing"
)
-// Per golang.org/issue/14937, check that every .gz file
-// in the tree has a zero mtime.
+// TestGZIPFilesHaveZeroMTimes checks that every .gz file in the tree
+// has a zero MTIME. This is a requirement for the Debian maintainers
+// to be able to have deterministic packages.
+//
+// See https://golang.org/issue/14937.
func TestGZIPFilesHaveZeroMTimes(t *testing.T) {
- if testing.Short() && testenv.Builder() == "" {
- t.Skip("skipping in short mode")
+ // To avoid spurious false positives due to untracked GZIP files that
+ // may be in the user's GOROOT (Issue 18604), we only run this test on
+ // the builders, which should have a clean checkout of the tree.
+ if testenv.Builder() == "" {
+ t.Skip("skipping test on non-builder")
}
goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
if err != nil {
func TestSigningWithDegenerateKeys(t *testing.T) {
// Signing with degenerate private keys should not cause an infinite
// loop.
- badKeys := []struct{
+ badKeys := []struct {
p, q, g, y, x string
}{
{"00", "01", "00", "00", "00"},
for i, test := range badKeys {
priv := PrivateKey{
PublicKey: PublicKey{
- Parameters: Parameters {
+ Parameters: Parameters{
P: fromHex(test.p),
Q: fromHex(test.q),
G: fromHex(test.g),
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
- {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
- {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
- {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
package tls
// BUG(agl): The crypto/tls package only implements some countermeasures
-// against Lucky13 attacks on CBC-mode encryption. See
-// http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
+// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
+// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
import (
package x509
-import "encoding/pem"
+import (
+ "encoding/pem"
+ "errors"
+ "runtime"
+)
// CertPool is a set of certificates.
type CertPool struct {
// Any mutations to the returned pool are not written to disk and do
// not affect any other pool.
func SystemCertPool() (*CertPool, error) {
+ if runtime.GOOS == "windows" {
+ // Issue 16736, 18609:
+ return nil, errors.New("crypto/x509: system root pool is not available on Windows")
+ }
+
return loadSystemRoots()
}
}
func loadSystemRoots() (*CertPool, error) {
+ // TODO: restore this functionality on Windows. We tried to do
+ // it in Go 1.8 but had to revert it. See Issue 18609.
+ // Returning (nil, nil) was the old behavior, prior to CL 30578.
+ return nil, nil
+
const CRYPT_E_NOT_FOUND = 0x80092004
store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
"net"
"os/exec"
"reflect"
+ "runtime"
"strings"
"testing"
"time"
}
func TestSystemCertPool(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not implemented on Windows; Issue 16736, 18609")
+ }
_, err := SystemCertPool()
if err != nil {
t.Fatal(err)
cancel: cancel,
ctx: ctx,
}
- go func(tx *Tx) {
- select {
- case <-tx.ctx.Done():
- if !tx.isDone() {
- // Discard and close the connection used to ensure the transaction
- // is closed and the resources are released.
- tx.rollback(true)
- }
- }
- }(tx)
+ go tx.awaitDone()
return tx, nil
}
type Tx struct {
db *DB
+ // closemu prevents the transaction from closing while there
+ // is an active query. It is held for read during queries
+ // and exclusively during close.
+ closemu sync.RWMutex
+
// dc is owned exclusively until Commit or Rollback, at which point
// it's returned with putConn.
dc *driverConn
ctx context.Context
}
+// awaitDone blocks until the context in Tx is canceled and rolls back
+// the transaction if it's not already done.
+func (tx *Tx) awaitDone() {
+ // Wait for either the transaction to be committed or rolled
+ // back, or for the associated context to be closed.
+ <-tx.ctx.Done()
+
+ // Discard and close the connection used to ensure the
+ // transaction is closed and the resources are released. This
+ // rollback does nothing if the transaction has already been
+ // committed or rolled back.
+ tx.rollback(true)
+}
+
func (tx *Tx) isDone() bool {
return atomic.LoadInt32(&tx.done) != 0
}
// close returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit.
func (tx *Tx) close(err error) {
+ tx.closemu.Lock()
+ defer tx.closemu.Unlock()
+
tx.db.putConn(tx.dc, err)
tx.cancel()
tx.dc = nil
tx.txi = nil
}
+// hookTxGrabConn specifies an optional hook to be called on
+// a successful call to (*Tx).grabConn. For tests.
+var hookTxGrabConn func()
+
func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
+ select {
+ default:
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
if tx.isDone() {
return nil, ErrTxDone
}
+ if hookTxGrabConn != nil { // test hook
+ hookTxGrabConn()
+ }
return tx.dc, nil
}
// for the execution of the returned statement. The returned statement
// will run in the transaction context.
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
// TODO(bradfitz): We could be more efficient here and either
// provide a method to take an existing Stmt (created on
// perhaps a different Conn), and re-create it on this Conn if
// The returned statement operates within the transaction and will be closed
// when the transaction has been committed or rolled back.
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
// TODO(bradfitz): optimize this. Currently this re-prepares
// each time. This is fine for now to illustrate the API but
// we should really cache already-prepared statements
// ExecContext executes a query that doesn't return rows.
// For example: an INSERT and UPDATE.
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
dc, err := tx.grabConn(ctx)
if err != nil {
return nil, err
// QueryContext executes a query that returns rows, typically a SELECT.
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
+ tx.closemu.RLock()
+ defer tx.closemu.RUnlock()
+
dc, err := tx.grabConn(ctx)
if err != nil {
return nil, err
// closed value is 1 when the Rows is closed.
// Use atomic operations on value when checking value.
closed int32
- ctxClose chan struct{} // closed when Rows is closed, may be null.
+ cancel func() // called when Rows is closed, may be nil.
lastcols []driver.Value
lasterr error // non-nil only if closed is true
closeStmt *driverStmt // if non-nil, statement to Close on close
}
func (rs *Rows) initContextClose(ctx context.Context) {
- if ctx.Done() == context.Background().Done() {
- return
- }
+ ctx, rs.cancel = context.WithCancel(ctx)
+ go rs.awaitDone(ctx)
+}
- rs.ctxClose = make(chan struct{})
- go func() {
- select {
- case <-ctx.Done():
- rs.Close()
- case <-rs.ctxClose:
- }
- }()
+// awaitDone blocks until the rows are closed or the context canceled.
+func (rs *Rows) awaitDone(ctx context.Context) {
+ <-ctx.Done()
+ rs.Close()
}
// Next prepares the next result row for reading with the Scan method. It
return nil
}
-var rowsCloseHook func(*Rows, *error)
+// rowsCloseHook returns a function so tests may install the
+// hook throug a test only mutex.
+var rowsCloseHook = func() func(*Rows, *error) { return nil }
func (rs *Rows) isClosed() bool {
return atomic.LoadInt32(&rs.closed) != 0
if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
return nil
}
- if rs.ctxClose != nil {
- close(rs.ctxClose)
- }
+
err := rs.rowsi.Close()
- if fn := rowsCloseHook; fn != nil {
+ if fn := rowsCloseHook(); fn != nil {
fn(rs, &err)
}
+ if rs.cancel != nil {
+ rs.cancel()
+ }
+
if rs.closeStmt != nil {
rs.closeStmt.Close()
}
"runtime"
"strings"
"sync"
+ "sync/atomic"
"testing"
"time"
)
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- if n := db.numFreeConns(); n != 1 {
- t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
- }
+ waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
return false
}
+// waitForFree checks db.numFreeConns until either it equals want or
+// the maxWait time elapses.
+func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
+ var numFree int
+ if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+ numFree = db.numFreeConns()
+ return numFree == want
+ }) {
+ t.Fatalf("free conns after hitting EOF = %d; want %d", numFree, want)
+ }
+}
+
func TestQueryContextWait(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
}
// Verify closed rows connection after error condition.
- if n := db.numFreeConns(); n != 1 {
- t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
- }
+ waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
}
- var numFree int
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
- numFree = db.numFreeConns()
- return numFree == 0
- }) {
- t.Fatalf("free conns after hitting EOF = %d; want 0", numFree)
- }
+ waitForFree(t, db, 5*time.Second, 0)
// Ensure the dropped connection allows more connections to be made.
// Checked on DB Close.
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- if n := db.numFreeConns(); n != 1 {
- t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
- }
+ waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
}
}
+var atomicRowsCloseHook atomic.Value // of func(*Rows, *error)
+
+func init() {
+ rowsCloseHook = func() func(*Rows, *error) {
+ fn, _ := atomicRowsCloseHook.Load().(func(*Rows, *error))
+ return fn
+ }
+}
+
+func setRowsCloseHook(fn func(*Rows, *error)) {
+ if fn == nil {
+ // Can't change an atomic.Value back to nil, so set it to this
+ // no-op func instead.
+ fn = func(*Rows, *error) {}
+ }
+ atomicRowsCloseHook.Store(fn)
+}
+
// Test issue 6651
func TestIssue6651(t *testing.T) {
db := newTestDB(t, "people")
return fmt.Errorf(want)
}
defer func() { rowsCursorNextHook = nil }()
+
err := db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want)
rowsCursorNextHook = nil
want = "error in rows.Close"
- rowsCloseHook = func(rows *Rows, err *error) {
+ setRowsCloseHook(func(rows *Rows, err *error) {
*err = fmt.Errorf(want)
- }
- defer func() { rowsCloseHook = nil }()
+ })
+ defer setRowsCloseHook(nil)
err = db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want)
db.dumpDeps(t)
}
- if len(stmt.css) > nquery {
+ if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ return len(stmt.css) <= nquery
+ }) {
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
}
if err != nil {
t.Fatal(err)
}
- rowsCloseHook = func(rows *Rows, err *error) {
+ setRowsCloseHook(func(rows *Rows, err *error) {
*err = driver.ErrBadConn
- }
- defer func() { rowsCloseHook = nil }()
+ })
+ defer setRowsCloseHook(nil)
for i := 0; i < 10; i++ {
rows, err := stmt.Query()
if err != nil {
if err != nil {
return
}
- rows, err := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
+ // This is expected to give a cancel error many, but not all the time.
+ // Test failure will happen with a panic or other race condition being
+ // reported.
+ rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
if rows != nil {
rows.Close()
}
time.Sleep(milliWait * 3 * time.Millisecond)
}
+// TestIssue18719 closes the context right before use. The sql.driverConn
+// will nil out the ci on close in a lock, but if another process uses it right after
+// it will panic with on the nil ref.
+//
+// See https://golang.org/cl/35550 .
+func TestIssue18719(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ tx, err := db.BeginTx(ctx, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ hookTxGrabConn = func() {
+ cancel()
+
+ // Wait for the context to cancel and tx to rollback.
+ for tx.isDone() == false {
+ time.Sleep(time.Millisecond * 3)
+ }
+ }
+ defer func() { hookTxGrabConn = nil }()
+
+ // This call will grab the connection and cancel the context
+ // after it has done so. Code after must deal with the canceled state.
+ rows, err := tx.QueryContext(ctx, "SELECT|people|name|")
+ if err != nil {
+ rows.Close()
+ t.Fatalf("expected error %v but got %v", nil, err)
+ }
+
+ // Rows may be ignored because it will be closed when the context is canceled.
+
+ // Do not explicitly rollback. The rollback will happen from the
+ // canceled context.
+
+ // Wait for connections to return to pool.
+ var numOpen int
+ if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ numOpen = db.numOpenConns()
+ return numOpen == 0
+ }) {
+ t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
+ }
+}
+
func TestConcurrency(t *testing.T) {
doConcurrentTest(t, new(concurrentDBQueryTest))
doConcurrentTest(t, new(concurrentDBExecTest))
// The Data fields contains object-specific data:
//
// Kind Data type Data value
-// Pkg *types.Package package scope
+// Pkg *Scope package scope
// Con int iota for the respective declaration
-// Con != nil constant value
-// Typ *Scope (used as method scope during type checking - transient)
//
type Object struct {
Kind ObjKind
const dataDir = "testdata"
-var templateTxt = readTemplate("template.txt")
+var templateTxt *template.Template
func readTemplate(filename string) *template.Template {
t := template.New(filename)
if err != nil {
t.Fatal(err)
}
+ if templateTxt == nil {
+ templateTxt = readTemplate("template.txt")
+ }
// test packages
for _, pkg := range pkgs {
"testing"
)
-var src = readFile("parser.go")
-
-func readFile(filename string) []byte {
- data, err := ioutil.ReadFile(filename)
+func BenchmarkParse(b *testing.B) {
+ src, err := ioutil.ReadFile("parser.go")
if err != nil {
- panic(err)
+ b.Fatal(err)
}
- return data
-}
-
-func BenchmarkParse(b *testing.B) {
+ b.ResetTimer()
b.SetBytes(int64(len(src)))
for i := 0; i < b.N; i++ {
if _, err := ParseFile(token.NewFileSet(), "", src, ParseComments); err != nil {
case *ast.FuncLit:
p.expr(x.Type)
- p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
+ p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
if x.Type != nil {
p.expr1(x.Type, token.HighestPrec, depth)
}
+ p.level++
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
// do not insert extra line break following a /*-style comment
mode |= noExtraBlank
}
p.print(mode, x.Rbrace, token.RBRACE, mode)
+ p.level--
case *ast.Ellipsis:
p.print(token.ELLIPSIS)
return bodySize
}
-// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
-// a header (e.g., a for-loop control clause or function signature) of given headerSize.
+// funcBody prints a function body following a function header of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}".
//
-func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
+func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil {
return
}
+ // save/restore composite literal nesting level
+ defer func(level int) {
+ p.level = level
+ }(p.level)
+ p.level = 0
+
const maxSize = 100
if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE)
}
p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results)
- p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
+ p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
}
func (p *printer) decl(decl ast.Decl) {
// Current state
output []byte // raw printer result
indent int // current indentation
+ level int // level == 0: outside composite literal; level > 0: inside composite literal
mode pmode // current printer mode
impliedSemi bool // if set, a linebreak implies a semicolon
lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace)
// follows on the same line but is not a comma, and not a "closing"
// token immediately following its corresponding "opening" token,
// add an extra separator unless explicitly disabled. Use a blank
- // as separator unless we have pending linebreaks and they are not
- // disabled, in which case we want a linebreak (issue 15137).
+ // as separator unless we have pending linebreaks, they are not
+ // disabled, and we are outside a composite literal, in which case
+ // we want a linebreak (issue 15137).
+ // TODO(gri) This has become overly complicated. We should be able
+ // to track whether we're inside an expression or statement and
+ // use that information to decide more directly.
needsLinebreak := false
if p.mode&noExtraBlank == 0 &&
last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
tok != token.COMMA &&
(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
(tok != token.RBRACK || p.prevOpen == token.LBRACK) {
- if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 {
+ if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {
needsLinebreak = true
} else {
p.writeByte(' ', 1)
mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations
}
+
+// Test cases for issue 18782
+var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+}
+
+var _ = T{ /* a */ 0}
+
+var _ = T{ /* a */ /* b */ 0}
+
+var _ = T{ /* a */ /* b */
+ /* c */ 0,
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ 0,
+}
+
+var _ = T{
+ /* a */
+ /* b */ 0,
+}
+
+var _ = T{ /* a */ {}}
+
+var _ = T{ /* a */ /* b */ {}}
+
+var _ = T{ /* a */ /* b */
+ /* c */ {},
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ {},
+}
+
+var _ = T{
+ /* a */
+ /* b */ {},
+}
+
+var _ = []T{
+ func() {
+ var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+ }
+ },
+}
mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations
}
+
+// Test cases for issue 18782
+var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+}
+
+var _ = T{ /* a */ 0,
+}
+
+var _ = T{ /* a */ /* b */ 0,
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */ 0,
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ 0,
+}
+
+var _ = T{
+ /* a */
+ /* b */ 0,
+}
+
+var _ = T{ /* a */ {},
+}
+
+var _ = T{ /* a */ /* b */ {},
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */ {},
+}
+
+var _ = T{ /* a */ /* b */
+ /* c */
+ /* d */ {},
+}
+
+var _ = T{
+ /* a */
+ /* b */ {},
+}
+
+var _ = []T{
+ func() {
+ var _ = [][]int{
+ /* a, b, c, d, e */
+ /* a */ {0, 0, 0, 0, 0},
+ /* b */ {0, 5, 4, 4, 4},
+ /* c */ {0, 4, 5, 4, 4},
+ /* d */ {0, 4, 4, 5, 4},
+ /* e */ {0, 4, 4, 4, 5},
+ }
+ },
+}
"issue11362.go", // canonical import path check
"issue15002.go", // uses Mmap; testTestDir should consult build tags
"issue16369.go", // go/types handles this correctly - not an issue
+ "issue18459.go", // go/types doesn't check validity of //go:xxx directives
)
}
// redirectBehavior describes what should happen when the
// client encounters a 3xx status code from the server
-func redirectBehavior(reqMethod string, resp *Response, via []*Request) (redirectMethod string, shouldRedirect bool) {
+func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode {
case 301, 302, 303:
- redirectMethod = "GET"
+ redirectMethod = reqMethod
shouldRedirect = true
+ includeBody = false
+
+ // RFC 2616 allowed automatic redirection only with GET and
+ // HEAD requests. RFC 7231 lifts this restriction, but we still
+ // restrict other methods to GET to maintain compatibility.
+ // See Issue 18570.
+ if reqMethod != "GET" && reqMethod != "HEAD" {
+ redirectMethod = "GET"
+ }
case 307, 308:
redirectMethod = reqMethod
shouldRedirect = true
+ includeBody = true
// Treat 307 and 308 specially, since they're new in
// Go 1.8, and they also require re-sending the request body.
- loc := resp.Header.Get("Location")
- if loc == "" {
+ if resp.Header.Get("Location") == "" {
// 308s have been observed in the wild being served
// without Location headers. Since Go 1.7 and earlier
// didn't follow these codes, just stop here instead
shouldRedirect = false
break
}
- ireq := via[0]
if ireq.GetBody == nil && ireq.outgoingLength() != 0 {
// We had a request body, and 307/308 require
// re-sending it, but GetBody is not defined. So just
shouldRedirect = false
}
}
-
- return redirectMethod, shouldRedirect
+ return redirectMethod, shouldRedirect, includeBody
}
// Do sends an HTTP request and returns an HTTP response, following
// If the server replies with a redirect, the Client first uses the
// CheckRedirect function to determine whether the redirect should be
// followed. If permitted, a 301, 302, or 303 redirect causes
-// subsequent requests to use HTTP method "GET", with no body.
+// subsequent requests to use HTTP method GET
+// (or HEAD if the original request was HEAD), with no body.
// A 307 or 308 redirect preserves the original HTTP method and body,
// provided that the Request.GetBody function is defined.
// The NewRequest function automatically sets GetBody for common
}
var (
- deadline = c.deadline()
- reqs []*Request
- resp *Response
- copyHeaders = c.makeHeadersCopier(req)
+ deadline = c.deadline()
+ reqs []*Request
+ resp *Response
+ copyHeaders = c.makeHeadersCopier(req)
+
+ // Redirect behavior:
redirectMethod string
+ includeBody bool
)
uerr := func(err error) error {
req.closeBody()
Cancel: ireq.Cancel,
ctx: ireq.ctx,
}
- if ireq.GetBody != nil {
+ if includeBody && ireq.GetBody != nil {
req.Body, err = ireq.GetBody()
if err != nil {
return nil, uerr(err)
}
var shouldRedirect bool
- redirectMethod, shouldRedirect = redirectBehavior(req.Method, resp, reqs)
+ redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])
if !shouldRedirect {
return resp, nil
}
wantSegments := []string{
`POST / "first"`,
`POST /?code=301&next=302 "c301"`,
- `GET /?code=302 "c301"`,
- `GET / "c301"`,
+ `GET /?code=302 ""`,
+ `GET / ""`,
`POST /?code=302&next=302 "c302"`,
- `GET /?code=302 "c302"`,
- `GET / "c302"`,
+ `GET /?code=302 ""`,
+ `GET / ""`,
`POST /?code=303&next=301 "c303wc301"`,
- `GET /?code=301 "c303wc301"`,
- `GET / "c303wc301"`,
+ `GET /?code=301 ""`,
+ `GET / ""`,
`POST /?code=304 "c304"`,
`POST /?code=305 "c305"`,
`POST /?code=307&next=303,308,302 "c307"`,
`POST /?code=303&next=308,302 "c307"`,
- `GET /?code=308&next=302 "c307"`,
+ `GET /?code=308&next=302 ""`,
`GET /?code=302 "c307"`,
- `GET / "c307"`,
+ `GET / ""`,
`POST /?code=308&next=302,301 "c308"`,
`POST /?code=302&next=301 "c308"`,
- `GET /?code=301 "c308"`,
- `GET / "c308"`,
+ `GET /?code=301 ""`,
+ `GET / ""`,
`POST /?code=404 "c404"`,
}
want := strings.Join(wantSegments, "\n")
wantSegments := []string{
`DELETE / "first"`,
`DELETE /?code=301&next=302,308 "c301"`,
- `GET /?code=302&next=308 "c301"`,
- `GET /?code=308 "c301"`,
+ `GET /?code=302&next=308 ""`,
+ `GET /?code=308 ""`,
`GET / "c301"`,
`DELETE /?code=302&next=302 "c302"`,
- `GET /?code=302 "c302"`,
- `GET / "c302"`,
+ `GET /?code=302 ""`,
+ `GET / ""`,
`DELETE /?code=303 "c303"`,
- `GET / "c303"`,
+ `GET / ""`,
`DELETE /?code=307&next=301,308,303,302,304 "c307"`,
`DELETE /?code=301&next=308,303,302,304 "c307"`,
- `GET /?code=308&next=303,302,304 "c307"`,
+ `GET /?code=308&next=303,302,304 ""`,
`GET /?code=303&next=302,304 "c307"`,
- `GET /?code=302&next=304 "c307"`,
- `GET /?code=304 "c307"`,
+ `GET /?code=302&next=304 ""`,
+ `GET /?code=304 ""`,
`DELETE /?code=308&next=307 "c308"`,
`DELETE /?code=307 "c308"`,
`DELETE / "c308"`,
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
log.Lock()
slurp, _ := ioutil.ReadAll(r.Body)
- fmt.Fprintf(&log.Buffer, "%s %s %q\n", r.Method, r.RequestURI, slurp)
+ fmt.Fprintf(&log.Buffer, "%s %s %q", r.Method, r.RequestURI, slurp)
+ if cl := r.Header.Get("Content-Length"); r.Method == "GET" && len(slurp) == 0 && (r.ContentLength != 0 || cl != "") {
+ fmt.Fprintf(&log.Buffer, " (but with body=%T, content-length = %v, %q)", r.Body, r.ContentLength, cl)
+ }
+ log.WriteByte('\n')
log.Unlock()
urlQuery := r.URL.Query()
if v := urlQuery.Get("code"); v != "" {
want = strings.TrimSpace(want)
if got != want {
- t.Errorf("Log differs.\n Got:\n%s\nWant:\n%s\n", got, want)
+ got, want, lines := removeCommonLines(got, want)
+ t.Errorf("Log differs after %d common lines.\n\nGot:\n%s\n\nWant:\n%s\n", lines, got, want)
+ }
+}
+
+func removeCommonLines(a, b string) (asuffix, bsuffix string, commonLines int) {
+ for {
+ nl := strings.IndexByte(a, '\n')
+ if nl < 0 {
+ return a, b, commonLines
+ }
+ line := a[:nl+1]
+ if !strings.HasPrefix(b, line) {
+ return a, b, commonLines
+ }
+ commonLines++
+ a = a[len(line):]
+ b = b[len(line):]
}
}
3: {method: "POST", serverStatus: 307, wantMethod: "POST"},
4: {method: "POST", serverStatus: 308, wantMethod: "POST"},
- 5: {method: "HEAD", serverStatus: 301, wantMethod: "GET"},
- 6: {method: "HEAD", serverStatus: 302, wantMethod: "GET"},
- 7: {method: "HEAD", serverStatus: 303, wantMethod: "GET"},
+ 5: {method: "HEAD", serverStatus: 301, wantMethod: "HEAD"},
+ 6: {method: "HEAD", serverStatus: 302, wantMethod: "HEAD"},
+ 7: {method: "HEAD", serverStatus: 303, wantMethod: "HEAD"},
8: {method: "HEAD", serverStatus: 307, wantMethod: "HEAD"},
9: {method: "HEAD", serverStatus: 308, wantMethod: "HEAD"},
}
wg.Wait()
}
+
+// Test that the bufio.Reader returned by Hijack includes any buffered
+// byte (from the Server's backgroundRead) in its buffer. We want the
+// Handler code to be able to tell that a byte is available via
+// bufio.Reader.Buffered(), without resorting to Reading it
+// (potentially blocking) to get at it.
+func TestServerHijackGetsBackgroundByte(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test; see https://golang.org/issue/18657")
+ }
+ setParallel(t)
+ defer afterTest(t)
+ done := make(chan struct{})
+ inHandler := make(chan bool, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ defer close(done)
+
+ // Tell the client to send more data after the GET request.
+ inHandler <- true
+
+ // Wait until the HTTP server sees the extra data
+ // after the GET request. The HTTP server fires the
+ // close notifier here, assuming it's a pipelined
+ // request, as documented.
+ select {
+ case <-w.(CloseNotifier).CloseNotify():
+ case <-time.After(5 * time.Second):
+ t.Error("timeout")
+ return
+ }
+
+ conn, buf, err := w.(Hijacker).Hijack()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer conn.Close()
+ n := buf.Reader.Buffered()
+ if n != 1 {
+ t.Errorf("buffered data = %d; want 1", n)
+ }
+ peek, err := buf.Reader.Peek(3)
+ if string(peek) != "foo" || err != nil {
+ t.Errorf("Peek = %q, %v; want foo, nil", peek, err)
+ }
+ }))
+ defer ts.Close()
+
+ cn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cn.Close()
+ if _, err := cn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil {
+ t.Fatal(err)
+ }
+ <-inHandler
+ if _, err := cn.Write([]byte("foo")); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := cn.(*net.TCPConn).CloseWrite(); err != nil {
+ t.Fatal(err)
+ }
+ select {
+ case <-done:
+ case <-time.After(2 * time.Second):
+ t.Error("timeout")
+ }
+}
+
+// Like TestServerHijackGetsBackgroundByte above but sending a
+// immediate 1MB of data to the server to fill up the server's 4KB
+// buffer.
+func TestServerHijackGetsBackgroundByte_big(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test; see https://golang.org/issue/18657")
+ }
+ setParallel(t)
+ defer afterTest(t)
+ done := make(chan struct{})
+ const size = 8 << 10
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ defer close(done)
+
+ // Wait until the HTTP server sees the extra data
+ // after the GET request. The HTTP server fires the
+ // close notifier here, assuming it's a pipelined
+ // request, as documented.
+ select {
+ case <-w.(CloseNotifier).CloseNotify():
+ case <-time.After(5 * time.Second):
+ t.Error("timeout")
+ return
+ }
+
+ conn, buf, err := w.(Hijacker).Hijack()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer conn.Close()
+ slurp, err := ioutil.ReadAll(buf.Reader)
+ if err != nil {
+ t.Errorf("Copy: %v", err)
+ }
+ allX := true
+ for _, v := range slurp {
+ if v != 'x' {
+ allX = false
+ }
+ }
+ if len(slurp) != size {
+ t.Errorf("read %d; want %d", len(slurp), size)
+ } else if !allX {
+ t.Errorf("read %q; want %d 'x'", slurp, size)
+ }
+ }))
+ defer ts.Close()
+
+ cn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cn.Close()
+ if _, err := fmt.Fprintf(cn, "GET / HTTP/1.1\r\nHost: e.com\r\n\r\n%s",
+ strings.Repeat("x", size)); err != nil {
+ t.Fatal(err)
+ }
+ if err := cn.(*net.TCPConn).CloseWrite(); err != nil {
+ t.Fatal(err)
+ }
+
+ select {
+ case <-done:
+ case <-time.After(2 * time.Second):
+ t.Error("timeout")
+ }
+}
// should always test for this ability at runtime.
type Hijacker interface {
// Hijack lets the caller take over the connection.
- // After a call to Hijack(), the HTTP server library
+ // After a call to Hijack the HTTP server library
// will not do anything else with the connection.
//
// It becomes the caller's responsibility to manage
// already set, depending on the configuration of the
// Server. It is the caller's responsibility to set
// or clear those deadlines as needed.
+ //
+ // The returned bufio.Reader may contain unprocessed buffered
+ // data from the client.
Hijack() (net.Conn, *bufio.ReadWriter, error)
}
rwc.SetDeadline(time.Time{})
buf = bufio.NewReadWriter(c.bufr, bufio.NewWriter(rwc))
+ if c.r.hasByte {
+ if _, err := c.bufr.Peek(c.bufr.Buffered() + 1); err != nil {
+ return nil, nil, fmt.Errorf("unexpected Peek failure reading buffered byte: %v", err)
+ }
+ }
c.setState(rwc, StateHijacked)
return
}
"strconv"
"strings"
"sync"
+ "sync/atomic"
"testing"
"time"
)
func (f closerFunc) Close() error { return f() }
+type writerFuncConn struct {
+ net.Conn
+ write func(p []byte) (n int, err error)
+}
+
+func (c writerFuncConn) Write(p []byte) (n int, err error) { return c.write(p) }
+
// Issue 4677. If we try to reuse a connection that the server is in the
// process of closing, we may end up successfully writing out our request (or a
// portion of our request) only to find a connection error when we try to read
func TestRetryIdempotentRequestsOnError(t *testing.T) {
defer afterTest(t)
+ var (
+ mu sync.Mutex
+ logbuf bytes.Buffer
+ )
+ logf := func(format string, args ...interface{}) {
+ mu.Lock()
+ defer mu.Unlock()
+ fmt.Fprintf(&logbuf, format, args...)
+ logbuf.WriteByte('\n')
+ }
+
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ logf("Handler")
+ w.Header().Set("X-Status", "ok")
}))
defer ts.Close()
- tr := &Transport{}
+ var writeNumAtomic int32
+ tr := &Transport{
+ Dial: func(network, addr string) (net.Conn, error) {
+ logf("Dial")
+ c, err := net.Dial(network, ts.Listener.Addr().String())
+ if err != nil {
+ logf("Dial error: %v", err)
+ return nil, err
+ }
+ return &writerFuncConn{
+ Conn: c,
+ write: func(p []byte) (n int, err error) {
+ if atomic.AddInt32(&writeNumAtomic, 1) == 2 {
+ logf("intentional write failure")
+ return 0, errors.New("second write fails")
+ }
+ logf("Write(%q)", p)
+ return c.Write(p)
+ },
+ }, nil
+ },
+ }
+ defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
- const N = 2
- retryc := make(chan struct{}, N)
SetRoundTripRetried(func() {
- retryc <- struct{}{}
+ logf("Retried.")
})
defer SetRoundTripRetried(nil)
- for n := 0; n < 100; n++ {
- // open 2 conns
- errc := make(chan error, N)
- for i := 0; i < N; i++ {
- // start goroutines, send on errc
- go func() {
- res, err := c.Get(ts.URL)
- if err == nil {
- res.Body.Close()
- }
- errc <- err
- }()
- }
- for i := 0; i < N; i++ {
- if err := <-errc; err != nil {
- t.Fatal(err)
- }
- }
-
- ts.CloseClientConnections()
- for i := 0; i < N; i++ {
- go func() {
- res, err := c.Get(ts.URL)
- if err == nil {
- res.Body.Close()
- }
- errc <- err
- }()
+ for i := 0; i < 3; i++ {
+ res, err := c.Get("http://fake.golang/")
+ if err != nil {
+ t.Fatalf("i=%d: Get = %v", i, err)
}
+ res.Body.Close()
+ }
- for i := 0; i < N; i++ {
- if err := <-errc; err != nil {
- t.Fatal(err)
- }
- }
- for i := 0; i < N; i++ {
- select {
- case <-retryc:
- // we triggered a retry, test was successful
- t.Logf("finished after %d runs\n", n)
- return
- default:
- }
- }
+ mu.Lock()
+ got := logbuf.String()
+ mu.Unlock()
+ const want = `Dial
+Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n")
+Handler
+intentional write failure
+Retried.
+Dial
+Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n")
+Handler
+Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n")
+Handler
+`
+ if got != want {
+ t.Errorf("Log of events differs. Got:\n%s\nWant:\n%s", got, want)
}
- t.Fatal("did not trigger any retries")
}
// Issue 6981
case "darwin":
switch runtime.GOARCH {
case "arm", "arm64":
+ /// At this point the test harness has not had a chance
+ // to move us into the ./src/os directory, so the
+ // current working directory is the root of the app.
wd, err := syscall.Getwd()
if err != nil {
wd = err.Error()
}
return &sysDir{
- filepath.Join(wd, "..", ".."),
+ wd,
[]string{
"ResourceRules.plist",
"Info.plist",
"unsafe"
)
+var sink interface{}
+
func TestBool(t *testing.T) {
v := ValueOf(true)
if v.Bool() != true {
f2("four", "five5", "six666", "seven77", "eight888")
}
+// Issue 18635 (function version).
+func TestKeepFuncLive(t *testing.T) {
+ // Test that we keep makeFuncImpl live as long as it is
+ // referenced on the stack.
+ typ := TypeOf(func(i int) {})
+ var f, g func(in []Value) []Value
+ f = func(in []Value) []Value {
+ clobber()
+ i := int(in[0].Int())
+ if i > 0 {
+ // We can't use Value.Call here because
+ // runtime.call* will keep the makeFuncImpl
+ // alive. However, by converting it to an
+ // interface value and calling that,
+ // reflect.callReflect is the only thing that
+ // can keep the makeFuncImpl live.
+ //
+ // Alternate between f and g so that if we do
+ // reuse the memory prematurely it's more
+ // likely to get obviously corrupted.
+ MakeFunc(typ, g).Interface().(func(i int))(i - 1)
+ }
+ return nil
+ }
+ g = func(in []Value) []Value {
+ clobber()
+ i := int(in[0].Int())
+ MakeFunc(typ, f).Interface().(func(i int))(i)
+ return nil
+ }
+ MakeFunc(typ, f).Call([]Value{ValueOf(10)})
+}
+
+// Issue 18635 (method version).
+type KeepMethodLive struct{}
+
+func (k KeepMethodLive) Method1(i int) {
+ clobber()
+ if i > 0 {
+ ValueOf(k).MethodByName("Method2").Interface().(func(i int))(i - 1)
+ }
+}
+
+func (k KeepMethodLive) Method2(i int) {
+ clobber()
+ ValueOf(k).MethodByName("Method1").Interface().(func(i int))(i)
+}
+
+func TestKeepMethodLive(t *testing.T) {
+ // Test that we keep methodValue live as long as it is
+ // referenced on the stack.
+ KeepMethodLive{}.Method1(10)
+}
+
+// clobber tries to clobber unreachable memory.
+func clobber() {
+ runtime.GC()
+ for i := 1; i < 32; i++ {
+ for j := 0; j < 10; j++ {
+ obj := make([]*byte, i)
+ sink = obj
+ }
+ }
+ runtime.GC()
+}
+
type funcLayoutTest struct {
rcvr, t Type
size, argsize, retOffset uintptr
off += typ.size
}
}
+
+ // runtime.getArgInfo expects to be able to find ctxt on the
+ // stack when it finds our caller, makeFuncStub. Make sure it
+ // doesn't get garbage collected.
+ runtime.KeepAlive(ctxt)
}
// methodReceiver returns information about the receiver
// though it's a heap object.
memclrNoHeapPointers(args, frametype.size)
framePool.Put(args)
+
+ // See the comment in callReflect.
+ runtime.KeepAlive(ctxt)
}
// funcName returns the name of f, for use in error messages.
move_0:
RET
move_3or4:
+ CMPQ BX, $4
+ JB move_3
+ MOVL (SI), AX
+ MOVL AX, (DI)
+ RET
+move_3:
MOVW (SI), AX
- MOVW -2(SI)(BX*1), CX
+ MOVB 2(SI), CX
MOVW AX, (DI)
- MOVW CX, -2(DI)(BX*1)
+ MOVB CX, 2(DI)
RET
move_5through7:
MOVL (SI), AX
import (
"crypto/rand"
+ "encoding/binary"
"fmt"
"internal/race"
. "runtime"
_ = y
}
}
+
+func BenchmarkIssue18740(b *testing.B) {
+ // This tests that memmove uses one 4-byte load/store to move 4 bytes.
+ // It used to do 2 2-byte load/stores, which leads to a pipeline stall
+ // when we try to read the result with one 4-byte load.
+ var buf [4]byte
+ for j := 0; j < b.N; j++ {
+ s := uint32(0)
+ for i := 0; i < 4096; i += 4 {
+ copy(buf[:], g[i:])
+ s += binary.LittleEndian.Uint32(buf[:])
+ }
+ sink = uint64(s)
+ }
+}
+
+// TODO: 2 byte and 8 byte benchmarks also.
+
+var g [4096]byte
// sitting in the per-P work caches.
// Flush and disable work caches.
- gcMarkRootCheck()
-
// Disallow caching workbufs and indicate that we're in mark 2.
gcBlackenPromptly = true
})
})
+ // Check that roots are marked. We should be able to
+ // do this before the forEachP, but based on issue
+ // #16083 there may be a (harmless) race where we can
+ // enter mark 2 while some workers are still scanning
+ // stacks. The forEachP ensures these scans are done.
+ //
+ // TODO(austin): Figure out the race and fix this
+ // properly.
+ gcMarkRootCheck()
+
// Now we can start up mark 2 workers.
atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 0xffffffff)
atomic.Xaddint64(&gcController.fractionalMarkWorkersNeeded, 0xffffffff)
// Nobody cares about the credit. Avoid the atomic.
return
}
- if int64(atomic.Xadd64(&mheap_.spanBytesAlloc, -int64(unusableBytes))) < 0 {
+ nval := atomic.Xadd64(&mheap_.spanBytesAlloc, -int64(unusableBytes))
+ if int64(nval) < 0 {
+ // Debugging for #18043.
+ print("runtime: bad spanBytesAlloc=", nval, " (was ", nval+uint64(unusableBytes), ") unusableBytes=", unusableBytes, " sweepPagesPerByte=", mheap_.sweepPagesPerByte, "\n")
throw("spanBytesAlloc underflow")
}
}
// the runtime, but operations like a slice copy can call msanread
// anyhow for values on the stack. Just ignore msanread when running
// on the system stack. The other msan functions are fine.
+//
+//go:nosplit
func msanread(addr unsafe.Pointer, sz uintptr) {
g := getg()
- if g == g.m.g0 || g == g.m.gsignal {
+ if g == nil || g.m == nil || g == g.m.g0 || g == g.m.gsignal {
return
}
domsanread(addr, sz)
lock(&ifaceLock)
for _, i := range md.itablinks {
- additab(i, true, false)
+ if i.inhash == 0 {
+ additab(i, true, false)
+ }
}
unlock(&ifaceLock)
mcall(gosched_m)
}
+var alwaysFalse bool
+
+// goschedguarded does nothing, but is written in a way that guarantees a preemption check in its prologue.
+// Calls to this function are inserted by the compiler in otherwise uninterruptible loops (see insertLoopReschedChecks).
+func goschedguarded() {
+ if alwaysFalse {
+ goschedguarded()
+ }
+}
+
// Puts the current goroutine into a waiting state and calls unlockf.
// If unlockf returns false, the goroutine is resumed.
// unlockf must not access this G's stack, as it may be moved between
import (
"bytes"
"fmt"
+ "go/build"
"internal/testenv"
"io/ioutil"
"os"
}
const helloSource = `
-package main
import "fmt"
var gslice []string
func main() {
`
func TestGdbPython(t *testing.T) {
+ testGdbPython(t, false)
+}
+
+func TestGdbPythonCgo(t *testing.T) {
+ testGdbPython(t, true)
+}
+
+func testGdbPython(t *testing.T, cgo bool) {
if runtime.GOARCH == "mips64" {
testenv.SkipFlaky(t, 18173)
}
+ if cgo && !build.Default.CgoEnabled {
+ t.Skip("skipping because cgo is not enabled")
+ }
t.Parallel()
checkGdbEnvironment(t)
}
defer os.RemoveAll(dir)
+ var buf bytes.Buffer
+ buf.WriteString("package main\n")
+ if cgo {
+ buf.WriteString(`import "C"` + "\n")
+ }
+ buf.WriteString(helloSource)
+
src := filepath.Join(dir, "main.go")
- err = ioutil.WriteFile(src, []byte(helloSource), 0644)
+ err = ioutil.WriteFile(src, buf.Bytes(), 0644)
if err != nil {
t.Fatalf("failed to create file: %v", err)
}
md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss)
}
}
+
+ // Modules appear in the moduledata linked list in the order they are
+ // loaded by the dynamic loader, with one exception: the
+ // firstmoduledata itself the module that contains the runtime. This
+ // is not always the first module (when using -buildmode=shared, it
+ // is typically libstd.so, the second module). The order matters for
+ // typelinksinit, so we swap the first module with whatever module
+ // contains the main function.
+ //
+ // See Issue #18729.
+ mainText := funcPC(main_main)
+ for i, md := range *modules {
+ if md.text <= mainText && mainText <= md.etext {
+ (*modules)[0] = md
+ (*modules)[i] = &firstmoduledata
+ break
+ }
+ }
+
atomicstorep(unsafe.Pointer(&modulesSlice), unsafe.Pointer(modules))
}
// Lock sigprofCallersUse.
MOVL $0, AX
MOVL $1, CX
- MOVQ $runtime·sigprofCallersUse(SB), BX
+ MOVQ $runtime·sigprofCallersUse(SB), R11
LOCK
- CMPXCHGL CX, 0(BX)
+ CMPXCHGL CX, 0(R11)
JNZ sigtramp // Skip stack trace if already locked.
// Jump to the traceback function in runtime/cgo.
return 0;
}
-void runCPUHogThread() {
+void runCPUHogThread(void) {
pthread_t tid;
pthread_create(&tid, 0, cpuHogDriver, 0);
}
char *p;
-static int f3() {
+static int f3(void) {
*p = 0;
return 0;
}
-static int f2() {
+static int f2(void) {
return f3();
}
-static int f1() {
+static int f1(void) {
return f2();
}
"log"
"os"
"regexp"
+ "strings"
)
func main() {
re = regexp.MustCompile("Pad_cgo[A-Za-z0-9_]*")
s = re.ReplaceAllString(s, "_")
+ // We want to keep X__val in Fsid. Hide it and restore it later.
+ s = strings.Replace(s, "X__val", "MKPOSTFSIDVAL", 1)
+
// Replace other unwanted fields with blank identifiers.
re = regexp.MustCompile("X_[A-Za-z0-9_]*")
s = re.ReplaceAllString(s, "_")
+ // Restore X__val in Fsid.
+ s = strings.Replace(s, "MKPOSTFSIDVAL", "X__val", 1)
+
// Force the type of RawSockaddr.Data to [14]int8 to match
// the existing gccgo API.
re = regexp.MustCompile("(Data\\s+\\[14\\])uint8")
}
type Fsid struct {
- _ [2]int32
+ X__val [2]int32
}
type Flock_t struct {
}
// Only print the output if we know we are not going to proceed.
// Otherwise it is printed in processBench.
- if b.hasSub || b.finished {
+ if atomic.LoadInt32(&b.hasSub) != 0 || b.finished {
tag := "BENCH"
if b.skipped {
tag = "SKIP"
//
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
// least once will not be measured itself and will be called once with N=1.
+//
+// Run may be called simultaneously from multiple goroutines, but all such
+// calls must happen before the outer benchmark function for b returns.
func (b *B) Run(name string, f func(b *B)) bool {
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
// Release the lock and acquire it on exit to ensure locks stay paired.
- b.hasSub = true
+ atomic.StoreInt32(&b.hasSub, 1)
benchmarkLock.Unlock()
defer benchmarkLock.Lock()
import (
"bytes"
+ "fmt"
"regexp"
"strings"
"sync/atomic"
Benchmark(func(b *B) { b.Error("do not print this output") })
Benchmark(func(b *B) {})
}
+
+func TestParallelSub(t *T) {
+ c := make(chan int)
+ block := make(chan int)
+ for i := 0; i < 10; i++ {
+ go func(i int) {
+ <-block
+ t.Run(fmt.Sprint(i), func(t *T) {})
+ c <- 1
+ }(i)
+ }
+ close(block)
+ for i := 0; i < 10; i++ {
+ <-c
+ }
+}
"strconv"
"strings"
"sync"
+ "sync/atomic"
"time"
)
skipped bool // Test of benchmark has been skipped.
finished bool // Test function has completed.
done bool // Test is finished and all subtests have completed.
- hasSub bool
- raceErrors int // number of races detected during test
+ hasSub int32 // written atomically
+ raceErrors int // number of races detected during test
parent *common
level int // Nesting depth of test or benchmark.
// Do not lock t.done to allow race detector to detect race in case
// the user does not appropriately synchronizes a goroutine.
t.done = true
- if t.parent != nil && !t.hasSub {
+ if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
t.setRan()
}
t.signal <- true
// Run runs f as a subtest of t called name. It reports whether f succeeded.
// Run will block until all its parallel subtests have completed.
+//
+// Run may be called simultaneously from multiple goroutines, but all such
+// calls must happen before the outer test function for t returns.
func (t *T) Run(name string, f func(t *T)) bool {
- t.hasSub = true
+ atomic.StoreInt32(&t.hasSub, 1)
testName, ok := t.context.match.fullName(&t.common, name)
if !ok {
return true
haveExamples = len(m.examples) > 0
testRan, testOk := runTests(m.deps.MatchString, m.tests)
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
+ stopAlarm()
if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
ADCQ t3, h1; \
ADCQ $0, h2
-DATA poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
-DATA poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
-GLOBL poly1305Mask<>(SB), RODATA, $16
+DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
+DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
+GLOBL ·poly1305Mask<>(SB), RODATA, $16
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
TEXT ·poly1305(SB), $0-32
MOVQ 0(AX), R11
MOVQ 8(AX), R12
- ANDQ poly1305Mask<>(SB), R11 // r0
- ANDQ poly1305Mask<>+8(SB), R12 // r1
+ ANDQ ·poly1305Mask<>(SB), R11 // r0
+ ANDQ ·poly1305Mask<>+8(SB), R12 // r1
XORQ R8, R8 // h0
XORQ R9, R9 // h1
XORQ R10, R10 // h2
// This code was translated into a form compatible with 5a from the public
// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
-DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
-DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
-DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
-DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
-DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
-GLOBL poly1305_init_constants_armv6<>(SB), 8, $20
+DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
+DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
+DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
+DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
+DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
+GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20
// Warning: the linker may use R11 to synthesize certain instructions. Please
// take care and verify that no synthetic instructions use it.
ADD $4, R13, R8
MOVM.IB [R4-R7], (R8)
MOVM.IA.W (R1), [R2-R5]
- MOVW $poly1305_init_constants_armv6<>(SB), R7
+ MOVW $·poly1305_init_constants_armv6<>(SB), R7
MOVW R2, R8
MOVW R2>>26, R9
MOVW R3>>20, g
--- /dev/null
+// +build !nacl,disabled
+// buildrun -t 10 -gcflags=-d=ssa/insert_resched_checks/on,ssa/check/on
+
+// Copyright 2016 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.
+
+// Test is disabled because it flakes when run in all.bash
+// on some platforms, but is useful standalone to verify
+// that rescheduling checks are working (and we may wish
+// to investigate the flake, since it suggests that the
+// loop rescheduling check may not work right on those
+// platforms).
+
+// This checks to see that call-free infinite loops do not
+// block garbage collection. IF YOU RUN IT STANDALONE without
+// -gcflags=-d=ssa/insert_resched_checks/on in a not-experimental
+// build, it should hang.
+
+package main
+
+import (
+ "runtime"
+)
+
+var someglobal1 int
+var someglobal2 int
+var someglobal3 int
+
+//go:noinline
+func f() {}
+
+func standinacorner1() {
+ for someglobal1&1 == 0 {
+ someglobal1++
+ someglobal1++
+ }
+}
+
+func standinacorner2(i int) {
+ // contains an irreducible loop containing changes to memory
+ if i != 0 {
+ goto midloop
+ }
+
+loop:
+ if someglobal2&1 != 0 {
+ goto done
+ }
+ someglobal2++
+midloop:
+ someglobal2++
+ goto loop
+
+done:
+ return
+}
+
+func standinacorner3() {
+ for someglobal3&1 == 0 {
+ if someglobal3&2 != 0 {
+ for someglobal3&3 == 2 {
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ }
+ }
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ someglobal3++
+ }
+}
+
+func main() {
+ go standinacorner1()
+ go standinacorner2(0)
+ go standinacorner3()
+ // println("About to stand in a corner1")
+ for someglobal1 == 0 {
+ runtime.Gosched()
+ }
+ // println("About to stand in a corner2")
+ for someglobal2 == 0 {
+ runtime.Gosched()
+ }
+ // println("About to stand in a corner3")
+ for someglobal3 == 0 {
+ runtime.Gosched()
+ }
+ // println("About to GC")
+ runtime.GC()
+ // println("Success")
+}
--- /dev/null
+// errorcheck
+
+// Copyright 2016 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.
+
+// Verify that we have a line number for this error.
+
+package main
+
+//go:nowritebarrier // ERROR "go:nowritebarrier only allowed in runtime"
+func main() {
+}
--- /dev/null
+// compile
+
+// Copyright 2017 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 p
+
+var (
+ e interface{}
+ s = struct{ a *int }{}
+ b = e == s
+)
+
+func test(obj interface{}) {
+ if obj != struct{ a *string }{} {
+ }
+}
--- /dev/null
+// run
+
+// Copyright 2017 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 "os"
+
+func panicWhenNot(cond bool) {
+ if cond {
+ os.Exit(0)
+ } else {
+ panic("nilcheck elim failed")
+ }
+}
+
+func main() {
+ e := (*string)(nil)
+ panicWhenNot(e == e)
+ // Should never reach this line.
+ panicWhenNot(*e == *e)
+}
--- /dev/null
+// run
+
+// Copyright 2017 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
+
+const lim = 0x80000000
+
+//go:noinline
+func eq(x uint32) {
+ if x == lim {
+ return
+ }
+ panic("x == lim returned false")
+}
+
+//go:noinline
+func neq(x uint32) {
+ if x != lim {
+ panic("x != lim returned true")
+ }
+}
+
+//go:noinline
+func gt(x uint32) {
+ if x > lim {
+ return
+ }
+ panic("x > lim returned false")
+}
+
+//go:noinline
+func gte(x uint32) {
+ if x >= lim {
+ return
+ }
+ panic("x >= lim returned false")
+}
+
+//go:noinline
+func lt(x uint32) {
+ if x < lim {
+ panic("x < lim returned true")
+ }
+}
+
+//go:noinline
+func lte(x uint32) {
+ if x <= lim {
+ panic("x <= lim returned true")
+ }
+}
+
+func main() {
+ eq(lim)
+ neq(lim)
+ gt(lim+1)
+ gte(lim+1)
+ lt(lim+1)
+ lte(lim+1)
+}
-// errorcheckwithauto -0 -l -live -wb=0
+// errorcheckwithauto -0 -l -live -wb=0 -d=ssa/insert_resched_checks/off
// +build !ppc64,!ppc64le
// ppc64 needs a better tighten pass to make f18 pass
+// rescheduling checks need to be turned off because there are some live variables across the inserted check call
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
)
func f1() {
- _ = *intp // ERROR "generated nil check"
+ _ = *intp // ERROR "removed nil check"
// This one should be removed but the block copy needs
// to be turned into its own pseudo-op in order to see
// the indirect.
- _ = *arrayp // ERROR "generated nil check"
+ _ = *arrayp // ERROR "removed nil check"
// 0-byte indirect doesn't suffice.
// we don't registerize globals, so there are no removed.* nil checks.
- _ = *array0p // ERROR "generated nil check"
_ = *array0p // ERROR "removed nil check"
+ _ = *array0p // ERROR "generated nil check"
- _ = *intp // ERROR "removed nil check"
+ _ = *intp // ERROR "generated nil check"
_ = *arrayp // ERROR "removed nil check"
_ = *structp // ERROR "generated nil check"
_ = *emptyp // ERROR "generated nil check"
- _ = *arrayp // ERROR "removed nil check"
+ _ = *arrayp // ERROR "generated nil check"
}
func f2() {
empty1p *Empty1
)
- _ = *intp // ERROR "generated nil check"
- _ = *arrayp // ERROR "generated nil check"
- _ = *array0p // ERROR "generated nil check"
- _ = *array0p // ERROR "removed.* nil check"
_ = *intp // ERROR "removed.* nil check"
_ = *arrayp // ERROR "removed.* nil check"
+ _ = *array0p // ERROR "removed.* nil check"
+ _ = *array0p // ERROR "generated nil check"
+ _ = *intp // ERROR "generated nil check"
+ _ = *arrayp // ERROR "removed.* nil check"
_ = *structp // ERROR "generated nil check"
_ = *emptyp // ERROR "generated nil check"
- _ = *arrayp // ERROR "removed.* nil check"
+ _ = *arrayp // ERROR "generated nil check"
_ = *bigarrayp // ERROR "generated nil check" ARM removed nil check before indirect!!
_ = *bigstructp // ERROR "generated nil check"
_ = *empty1p // ERROR "generated nil check"
// x wasn't going to change across the function call.
// But it's a little complex to do and in practice doesn't
// matter enough.
- _ = x[9999] // ERROR "removed nil check"
+ _ = x[9999] // ERROR "generated nil check" // TODO: fix
}
func f3a() {
x := fx10k()
y := fx10k()
z := fx10k()
- _ = &x[9] // ERROR "generated nil check"
- y = z
_ = &x[9] // ERROR "removed.* nil check"
+ y = z
+ _ = &x[9] // ERROR "generated nil check"
x = y
_ = &x[9] // ERROR "generated nil check"
}
func f3b() {
x := fx10k()
y := fx10k()
- _ = &x[9] // ERROR "generated nil check"
+ _ = &x[9] // ERROR "removed.* nil check"
y = x
_ = &x[9] // ERROR "removed.* nil check"
x = y
- _ = &x[9] // ERROR "removed.* nil check"
+ _ = &x[9] // ERROR "generated nil check"
}
func fx10() *[10]int
_ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
fx10()
- _ = x[9] // ERROR "removed nil check"
+ _ = x[9] // ERROR "generated nil check" // TODO: fix
x = fx10()
y := fx10()
- _ = &x[9] // ERROR "generated nil check"
+ _ = &x[9] // ERROR "removed[a-z ]* nil check"
y = x
_ = &x[9] // ERROR "removed[a-z ]* nil check"
x = y
- _ = &x[9] // ERROR "removed[a-z ]* nil check"
+ _ = &x[9] // ERROR "generated nil check"
}
func f5(p *float32, q *float64, r *float32, s *float64) float64 {
// +build amd64
-// errorcheck -0 -d=ssa/likelyadjust/debug=1
+// errorcheck -0 -d=ssa/likelyadjust/debug=1,ssa/insert_resched_checks/off
+// rescheduling check insertion is turend off because the inserted conditional branches perturb the errorcheck
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
}
var args, flags []string
+ var tim int
wantError := false
wantAuto := false
singlefilepkgs := false
action = "rundir"
case "cmpout":
action = "run" // the run case already looks for <dir>/<test>.out files
- case "compile", "compiledir", "build", "run", "runoutput", "rundir":
+ case "compile", "compiledir", "build", "run", "buildrun", "runoutput", "rundir":
// nothing to do
case "errorcheckandrundir":
wantError = false // should be no error if also will run
wantError = false
case "-s":
singlefilepkgs = true
+ case "-t": // timeout in seconds
+ args = args[1:]
+ var err error
+ tim, err = strconv.Atoi(args[0])
+ if err != nil {
+ t.err = fmt.Errorf("need number of seconds for -t timeout, got %s instead", args[0])
+ }
+
default:
flags = append(flags, args[0])
}
} else {
cmd.Env = os.Environ()
}
- err := cmd.Run()
+
+ var err error
+
+ if tim != 0 {
+ err = cmd.Start()
+ // This command-timeout code adapted from cmd/go/test.go
+ if err == nil {
+ tick := time.NewTimer(time.Duration(tim) * time.Second)
+ done := make(chan error)
+ go func() {
+ done <- cmd.Wait()
+ }()
+ select {
+ case err = <-done:
+ // ok
+ case <-tick.C:
+ cmd.Process.Kill()
+ err = <-done
+ // err = errors.New("Test timeout")
+ }
+ tick.Stop()
+ }
+ } else {
+ err = cmd.Run()
+ }
if err != nil {
err = fmt.Errorf("%s\n%s", err, buf.Bytes())
}
t.err = err
}
+ case "buildrun": // build binary, then run binary, instead of go run. Useful for timeout tests where failure mode is infinite loop.
+ // TODO: not supported on NaCl
+ useTmp = true
+ cmd := []string{"go", "build", "-o", "a.exe"}
+ if *linkshared {
+ cmd = append(cmd, "-linkshared")
+ }
+ longdirgofile := filepath.Join(filepath.Join(cwd, t.dir), t.gofile)
+ cmd = append(cmd, flags...)
+ cmd = append(cmd, longdirgofile)
+ out, err := runcmd(cmd...)
+ if err != nil {
+ t.err = err
+ return
+ }
+ cmd = []string{"./a.exe"}
+ out, err = runcmd(append(cmd, args...)...)
+ if err != nil {
+ t.err = err
+ return
+ }
+
+ if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
+ t.err = fmt.Errorf("incorrect output\n%s", out)
+ }
+
case "run":
useTmp = false
cmd := []string{"go", "run"}