From 955cc07dde70415489fb2096eb575654181e21fe Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 29 Jun 2018 14:56:48 -0400 Subject: runtime: remap stack spans with MAP_STACK on OpenBSD OpenBSD 6.4 is going to start requiring that the SP points to memory that was mapped with MAP_STACK on system call entry, traps, and when switching to the alternate signal stack [1]. Currently, Go doesn't map any memory MAP_STACK, so the kernel quickly kills Go processes. Fix this by remapping the memory that backs stack spans with MAP_STACK, and re-remapping it without MAP_STACK when it's returned to the heap. [1] http://openbsd-archive.7691.n7.nabble.com/stack-register-checking-td338238.html Fixes #26142. Change-Id: I656eb84385a22833445d49328bb304f8cdd0e225 Reviewed-on: https://go-review.googlesource.com/121657 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/runtime/defs_openbsd.go | 1 + src/runtime/defs_openbsd_386.go | 1 + src/runtime/defs_openbsd_amd64.go | 1 + src/runtime/defs_openbsd_arm.go | 1 + src/runtime/os_nonopenbsd.go | 17 ++++++++++++++ src/runtime/os_openbsd.go | 47 +++++++++++++++++++++++++++++++++++++++ src/runtime/stack.go | 6 +++++ 7 files changed, 74 insertions(+) create mode 100644 src/runtime/os_nonopenbsd.go (limited to 'src/runtime') diff --git a/src/runtime/defs_openbsd.go b/src/runtime/defs_openbsd.go index 9ff13dfcbf..a328d25db3 100644 --- a/src/runtime/defs_openbsd.go +++ b/src/runtime/defs_openbsd.go @@ -37,6 +37,7 @@ const ( MAP_ANON = C.MAP_ANON MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED + MAP_STACK = C.MAP_STACK MADV_FREE = C.MADV_FREE diff --git a/src/runtime/defs_openbsd_386.go b/src/runtime/defs_openbsd_386.go index 1185530964..7b956c44f0 100644 --- a/src/runtime/defs_openbsd_386.go +++ b/src/runtime/defs_openbsd_386.go @@ -17,6 +17,7 @@ const ( _MAP_ANON = 0x1000 _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 + _MAP_STACK = 0x4000 _MADV_FREE = 0x6 diff --git a/src/runtime/defs_openbsd_amd64.go b/src/runtime/defs_openbsd_amd64.go index 4bb8eac08f..0a93905717 100644 --- a/src/runtime/defs_openbsd_amd64.go +++ b/src/runtime/defs_openbsd_amd64.go @@ -17,6 +17,7 @@ const ( _MAP_ANON = 0x1000 _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 + _MAP_STACK = 0x4000 _MADV_FREE = 0x6 diff --git a/src/runtime/defs_openbsd_arm.go b/src/runtime/defs_openbsd_arm.go index 38b77c92d0..1eea9ad45a 100644 --- a/src/runtime/defs_openbsd_arm.go +++ b/src/runtime/defs_openbsd_arm.go @@ -17,6 +17,7 @@ const ( _MAP_ANON = 0x1000 _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 + _MAP_STACK = 0x4000 _MADV_FREE = 0x6 diff --git a/src/runtime/os_nonopenbsd.go b/src/runtime/os_nonopenbsd.go new file mode 100644 index 0000000000..e65697bdb3 --- /dev/null +++ b/src/runtime/os_nonopenbsd.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !openbsd + +package runtime + +// osStackAlloc performs OS-specific initialization before s is used +// as stack memory. +func osStackAlloc(s *mspan) { +} + +// osStackFree undoes the effect of osStackAlloc before s is returned +// to the heap. +func osStackFree(s *mspan) { +} diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index c359ceb280..73b01daec4 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -80,6 +80,9 @@ var sigset_all = ^sigset(0) // From OpenBSD's const ( + _CTL_KERN = 1 + _KERN_OSREV = 3 + _CTL_HW = 6 _HW_NCPU = 3 _HW_PAGESIZE = 7 @@ -109,6 +112,17 @@ func getPageSize() uintptr { return 0 } +func getOSRev() int32 { + mib := [2]uint32{_CTL_KERN, _KERN_OSREV} + out := uint32(0) + nout := unsafe.Sizeof(out) + ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret >= 0 { + return int32(out) + } + return 0 +} + //go:nosplit func semacreate(mp *m) { } @@ -194,6 +208,7 @@ func newosproc(mp *m) { func osinit() { ncpu = getncpu() physPageSize = getPageSize() + haveMapStack = getOSRev() >= 201805 // OpenBSD 6.3 } var urandom_dev = []byte("/dev/urandom\x00") @@ -286,3 +301,35 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } + +var haveMapStack = false + +func osStackAlloc(s *mspan) { + // OpenBSD 6.4+ requires that stacks be mapped with MAP_STACK. + // It will check this on entry to system calls, traps, and + // when switching to the alternate system stack. + // + // This function is called before s is used for any data, so + // it's safe to simply re-map it. + osStackRemap(s, _MAP_STACK) +} + +func osStackFree(s *mspan) { + // Undo MAP_STACK. + osStackRemap(s, 0) +} + +func osStackRemap(s *mspan, flags int32) { + if !haveMapStack { + // OpenBSD prior to 6.3 did not have MAP_STACK and so + // the following mmap will fail. But it also didn't + // require MAP_STACK (obviously), so there's no need + // to do the mmap. + return + } + a, err := mmap(unsafe.Pointer(s.base()), s.npages*pageSize, _PROT_READ|_PROT_WRITE, _MAP_PRIVATE|_MAP_ANON|_MAP_FIXED|flags, -1, 0) + if err != 0 || uintptr(a) != s.base() { + print("runtime: remapping stack memory ", hex(s.base()), " ", s.npages*pageSize, " a=", a, " err=", err, "\n") + throw("remapping stack memory failed") + } +} diff --git a/src/runtime/stack.go b/src/runtime/stack.go index 648603db35..c7bfc0434b 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -186,6 +186,7 @@ func stackpoolalloc(order uint8) gclinkptr { if s.manualFreeList.ptr() != nil { throw("bad manualFreeList") } + osStackAlloc(s) s.elemsize = _FixedStack << order for i := uintptr(0); i < _StackCacheSize; i += s.elemsize { x := gclinkptr(s.base() + i) @@ -238,6 +239,7 @@ func stackpoolfree(x gclinkptr, order uint8) { // By not freeing, we prevent step #4 until GC is done. stackpool[order].remove(s) s.manualFreeList = 0 + osStackFree(s) mheap_.freeManual(s, &memstats.stacks_inuse) } } @@ -385,6 +387,7 @@ func stackalloc(n uint32) stack { if s == nil { throw("out of memory") } + osStackAlloc(s) s.elemsize = uintptr(n) } v = unsafe.Pointer(s.base()) @@ -463,6 +466,7 @@ func stackfree(stk stack) { if gcphase == _GCoff { // Free the stack immediately if we're // sweeping. + osStackFree(s) mheap_.freeManual(s, &memstats.stacks_inuse) } else { // If the GC is running, we can't return a @@ -1112,6 +1116,7 @@ func freeStackSpans() { if s.allocCount == 0 { list.remove(s) s.manualFreeList = 0 + osStackFree(s) mheap_.freeManual(s, &memstats.stacks_inuse) } s = next @@ -1126,6 +1131,7 @@ func freeStackSpans() { for s := stackLarge.free[i].first; s != nil; { next := s.next stackLarge.free[i].remove(s) + osStackFree(s) mheap_.freeManual(s, &memstats.stacks_inuse) s = next } -- cgit v1.3-5-g9baa From f43aa1df701d7190eeaf301d3a41e1714c516c45 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 28 Jun 2018 16:45:28 -0700 Subject: runtime: throw if the runtime panics with out of bounds index If the runtime code panics due to a bad index or slice expression, then throw instead of panicing. This will skip calls to recover and dump the entire runtime stack trace. The runtime should never panic due to an out of bounds index, and this will help with debugging if it does. For #24991 Updates #25201 Change-Id: I85a9feded8f0de914ee1558425931853223c0514 Reviewed-on: https://go-review.googlesource.com/121515 Reviewed-by: Austin Clements --- src/runtime/crash_test.go | 30 ++++++++++++++++++++++++++++++ src/runtime/export_test.go | 10 ++++++++++ src/runtime/panic.go | 19 +++++++++++++++++++ 3 files changed, 59 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 843b415006..9f11aea4e9 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -654,3 +654,33 @@ func TestAbort(t *testing.T) { } } } + +// For TestRuntimePanic: test a panic in the runtime package without +// involving the testing harness. +func init() { + if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" { + defer func() { + if r := recover(); r != nil { + // We expect to crash, so exit 0 + // to indicate failure. + os.Exit(0) + } + }() + runtime.PanicForTesting(nil, 1) + // We expect to crash, so exit 0 to indicate failure. + os.Exit(0) + } +} + +func TestRuntimePanic(t *testing.T) { + testenv.MustHaveExec(t) + cmd := exec.Command(os.Args[0], "-test.run=TestRuntimePanic") + cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err == nil { + t.Error("child process did not fail") + } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) { + t.Errorf("output did not contain expected string %q", want) + } +} diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index b21179cc8c..7ebdfc1520 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -451,3 +451,13 @@ type G = g func Getg() *G { return getg() } + +//go:noinline +func PanicForTesting(b []byte, i int) byte { + return unexportedPanicForTesting(b, i) +} + +//go:noinline +func unexportedPanicForTesting(b []byte, i int) byte { + return b[i] +} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index ce367cfa70..7bb7f9b90c 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -23,7 +23,23 @@ func panicCheckMalloc(err error) { var indexError = error(errorString("index out of range")) +// The panicindex, panicslice, and panicdivide functions are called by +// code generated by the compiler for out of bounds index expressions, +// out of bounds slice expressions, and division by zero. The +// panicdivide (again), panicoverflow, panicfloat, and panicmem +// functions are called by the signal handler when a signal occurs +// indicating the respective problem. +// +// Since panicindex and panicslice are never called directly, and +// since the runtime package should never have an out of bounds slice +// or array reference, if we see those functions called from the +// runtime package we turn the panic into a throw. That will dump the +// entire runtime stack for easier debugging. + func panicindex() { + if hasprefix(funcname(findfunc(getcallerpc())), "runtime.") { + throw(string(indexError.(errorString))) + } panicCheckMalloc(indexError) panic(indexError) } @@ -31,6 +47,9 @@ func panicindex() { var sliceError = error(errorString("slice bounds out of range")) func panicslice() { + if hasprefix(funcname(findfunc(getcallerpc())), "runtime.") { + throw(string(sliceError.(errorString))) + } panicCheckMalloc(sliceError) panic(sliceError) } -- cgit v1.3-5-g9baa From 5110d19fc2986b702ba0cfcbeacfe692154c77b1 Mon Sep 17 00:00:00 2001 From: cch123 Date: Mon, 2 Jul 2018 04:32:48 +0000 Subject: runtime: fix typo in mapextra comment Change-Id: Idbd8a1b5bfeb1c23c86cef0697cf0380900e95f3 GitHub-Last-Rev: a8c2b27046582c4eef932a8502826a3b23b8dab3 GitHub-Pull-Request: golang/go#26175 Reviewed-on: https://go-review.googlesource.com/121821 Reviewed-by: Keith Randall --- src/runtime/map.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/map.go b/src/runtime/map.go index 0e00f12974..208c92cb0d 100644 --- a/src/runtime/map.go +++ b/src/runtime/map.go @@ -126,7 +126,7 @@ type mapextra struct { // If both key and value do not contain pointers and are inline, then we mark bucket // type as containing no pointers. This avoids scanning such maps. // However, bmap.overflow is a pointer. In order to keep overflow buckets - // alive, we store pointers to all overflow buckets in hmap.overflow and h.map.oldoverflow. + // alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow. // overflow and oldoverflow are only used if key and value do not contain pointers. // overflow contains overflow buckets for hmap.buckets. // oldoverflow contains overflow buckets for hmap.oldbuckets. -- cgit v1.3-5-g9baa From 9daa35edf08bb76948368fecf388572a4a77c14f Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 29 Jun 2018 16:09:01 -0400 Subject: runtime: tidy OpenBSD sysctl code The OpenBSD sysctl code has been copy-pasted three times now. Abstract it. Change-Id: Ia5558927f0bc2b218b5af425dab368b5485d266c Reviewed-on: https://go-review.googlesource.com/121775 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/runtime/os_openbsd.go | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index 73b01daec4..1660511616 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -88,37 +88,34 @@ const ( _HW_PAGESIZE = 7 ) -func getncpu() int32 { - mib := [2]uint32{_CTL_HW, _HW_NCPU} - out := uint32(0) +func sysctlInt(mib []uint32) (int32, bool) { + var out int32 nout := unsafe.Sizeof(out) + ret := sysctl(&mib[0], uint32(len(mib)), (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret < 0 { + return 0, false + } + return out, true +} +func getncpu() int32 { // Fetch hw.ncpu via sysctl. - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return int32(out) + if ncpu, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPU}); ok { + return int32(ncpu) } return 1 } func getPageSize() uintptr { - mib := [2]uint32{_CTL_HW, _HW_PAGESIZE} - out := uint32(0) - nout := unsafe.Sizeof(out) - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return uintptr(out) + if ps, ok := sysctlInt([]uint32{_CTL_HW, _HW_PAGESIZE}); ok { + return uintptr(ps) } return 0 } -func getOSRev() int32 { - mib := [2]uint32{_CTL_KERN, _KERN_OSREV} - out := uint32(0) - nout := unsafe.Sizeof(out) - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return int32(out) +func getOSRev() int { + if osrev, ok := sysctlInt([]uint32{_CTL_KERN, _KERN_OSREV}); ok { + return int(osrev) } return 0 } -- cgit v1.3-5-g9baa From 52e782a2d6e83065e394d127ea5df20e4aaaa8af Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 20 Jun 2018 16:25:41 -0400 Subject: runtime: initialize g0 stack bounds on Windows to full stack Currently, we allocate 1MB or 2MB thread stacks on Windows, but in non-cgo binaries still set the g0 stack bounds assuming only 64k is available. While this is fine in pure Go binaries, a non-cgo Go binary on Windows can use the syscall package to call arbitrary DLLs, which may call back into Go. If a DLL function uses more than 64k of stack and then calls back into Go, the Go runtime will believe that it's out of stack space and crash. Fix this by plumbing the correct stack size into the g0 stacks of non-cgo binaries. Cgo binaries already use the correct size because their g0 stack sizes are set by a different code path. Fixes #20975. Change-Id: Id6fb559cfe1e1ea0dfac56d4654865c20dccf68d Reviewed-on: https://go-review.googlesource.com/120195 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman Reviewed-by: Ian Lance Taylor --- src/cmd/link/internal/ld/pe.go | 4 ++-- src/runtime/cgo/gcc_windows_386.c | 2 +- src/runtime/cgo/gcc_windows_amd64.c | 2 +- src/runtime/os_windows.go | 19 ++++++++++++++++--- src/runtime/sys_windows_386.s | 3 ++- src/runtime/sys_windows_amd64.s | 3 ++- 6 files changed, 24 insertions(+), 9 deletions(-) (limited to 'src/runtime') diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index 85acb7a11b..3b7df9aef8 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -852,8 +852,8 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) { // // For other threads we specify stack size in runtime explicitly. // For these, the reserve must match STACKSIZE in - // runtime/cgo/gcc_windows_{386,amd64}.c and the correspondent - // CreateThread parameter in runtime.newosproc. + // runtime/cgo/gcc_windows_{386,amd64}.c and osStackSize in + // runtime/os_windows.go. oh64.SizeOfStackReserve = 0x00200000 if !iscgo { oh64.SizeOfStackCommit = 0x00001000 diff --git a/src/runtime/cgo/gcc_windows_386.c b/src/runtime/cgo/gcc_windows_386.c index fa0c69bc13..e80a564943 100644 --- a/src/runtime/cgo/gcc_windows_386.c +++ b/src/runtime/cgo/gcc_windows_386.c @@ -13,7 +13,7 @@ static void threadentry(void*); /* 1MB is default stack size for 32-bit Windows. Allocation granularity on Windows is typically 64 KB. - The constant is also hardcoded in cmd/ld/pe.c (keep synchronized). */ + This constant must match SizeOfStackReserve in ../cmd/link/internal/ld/pe.go. */ #define STACKSIZE (1*1024*1024) void diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c index a3c3896edf..75a7dc8ec2 100644 --- a/src/runtime/cgo/gcc_windows_amd64.c +++ b/src/runtime/cgo/gcc_windows_amd64.c @@ -13,7 +13,7 @@ static void threadentry(void*); /* 2MB is default stack size for 64-bit Windows. Allocation granularity on Windows is typically 64 KB. - The constant is also hardcoded in cmd/ld/pe.c (keep synchronized). */ + This constant must match SizeOfStackReserve in ../cmd/link/internal/ld/pe.go. */ #define STACKSIZE (2*1024*1024) void diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 68e404e675..1f3ebf6072 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -291,6 +291,9 @@ func osRelax(relax bool) uint32 { } } +// osStackSize must match SizeOfStackReserve in ../cmd/link/internal/ld/pe.go. +var osStackSize uintptr = 0x00200000*_64bit + 0x00100000*(1-_64bit) + func osinit() { asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall)) usleep2Addr = unsafe.Pointer(funcPC(usleep2)) @@ -319,6 +322,18 @@ func osinit() { // equivalent threads that all do a mix of GUI, IO, computations, etc. // In such context dynamic priority boosting does nothing but harm, so we turn it off. stdcall2(_SetProcessPriorityBoost, currentProcess, 1) + + // Fix the entry thread's stack bounds, since runtime entry + // assumed we were on a tiny stack. If this is a cgo binary, + // x_cgo_init already fixed these. + if !iscgo { + // Leave 8K of slop for calling C functions that don't + // have stack checks. We shouldn't be anywhere near + // this bound anyway. + g0.stack.lo = g0.stack.hi - osStackSize + 8*1024 + g0.stackguard0 = g0.stack.lo + _StackGuard + g0.stackguard1 = g0.stackguard0 + } } func nanotime() int64 @@ -620,9 +635,7 @@ func semacreate(mp *m) { //go:nosplit func newosproc(mp *m) { const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000 - // stackSize must match SizeOfStackReserve in cmd/link/internal/ld/pe.go. - const stackSize = 0x00200000*_64bit + 0x00100000*(1-_64bit) - thandle := stdcall6(_CreateThread, 0, stackSize, + thandle := stdcall6(_CreateThread, 0, osStackSize, funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)), _STACK_SIZE_PARAM_IS_A_RESERVATION, 0) diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s index 5f0d8b7c2a..56d5cfaa82 100644 --- a/src/runtime/sys_windows_386.s +++ b/src/runtime/sys_windows_386.s @@ -315,7 +315,8 @@ TEXT runtime·tstart(SB),NOSPLIT,$0 // Layout new m scheduler stack on os stack. MOVL SP, AX MOVL AX, (g_stack+stack_hi)(DX) - SUBL $(64*1024), AX // stack size + SUBL runtime·osStackSize(SB), AX // stack size + ADDL $(8*1024), AX // slop for calling C MOVL AX, (g_stack+stack_lo)(DX) ADDL $const__StackGuard, AX MOVL AX, g_stackguard0(DX) diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s index 2e5b29ba55..119e04c704 100644 --- a/src/runtime/sys_windows_amd64.s +++ b/src/runtime/sys_windows_amd64.s @@ -363,7 +363,8 @@ TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 // Layout new m scheduler stack on os stack. MOVQ SP, AX MOVQ AX, (g_stack+stack_hi)(DX) - SUBQ $(64*1024), AX // stack size + SUBQ runtime·osStackSize(SB), AX // stack size + ADDQ $(8*1024), AX // slop for calling C MOVQ AX, (g_stack+stack_lo)(DX) ADDQ $const__StackGuard, AX MOVQ AX, g_stackguard0(DX) -- cgit v1.3-5-g9baa From 99e9be804379d0607de4a322353b317aa087073d Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 21 Jun 2018 12:08:36 -0400 Subject: runtime: query thread stack size from OS on Windows Currently, on Windows, the thread stack size is set or assumed in many different places. In non-cgo binaries, both the Go linker and the runtime have a copy of the stack size, the Go linker sets the size of the main thread stack, and the runtime sets the size of other thread stacks. In cgo binaries, the external linker sets the main thread stack size, the runtime assumes the size of the main thread stack will be the same as used by the Go linker, and the cgo entry code assumes the same. Furthermore, users can change the main thread stack size using editbin, so the runtime doesn't even really know what size it is, and user C code can create threads with unknown thread stack sizes, which we also assume have the same default stack size. This is all a mess. Fix the corner cases of this and the duplication of knowledge between the linker and the runtime by querying the OS for the stack bounds during thread setup. Furthermore, we unify all of this into just runtime.minit for both cgo and non-cgo binaries and for the main thread, other runtime-created threads, and C-created threads. Updates #20975. Change-Id: I45dbee2b5ea2ae721a85a27680737ff046f9d464 Reviewed-on: https://go-review.googlesource.com/120336 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman Reviewed-by: Ian Lance Taylor --- src/cmd/link/internal/ld/pe.go | 13 +++--- src/cmd/vet/all/whitelist/386.txt | 2 - src/cmd/vet/all/whitelist/amd64.txt | 1 - src/cmd/vet/all/whitelist/nacl_amd64p32.txt | 2 - src/runtime/cgo/gcc_windows_386.c | 10 +---- src/runtime/cgo/gcc_windows_amd64.c | 10 +---- src/runtime/crash_cgo_test.go | 16 ++++++++ src/runtime/defs_windows.go | 1 + src/runtime/defs_windows_386.go | 10 +++++ src/runtime/defs_windows_amd64.go | 10 +++++ src/runtime/os_windows.go | 47 +++++++++++++--------- src/runtime/proc.go | 1 + src/runtime/stubs_x86.go | 10 +++++ src/runtime/sys_windows_386.s | 3 +- src/runtime/sys_windows_amd64.s | 3 +- src/runtime/syscall_windows_test.go | 46 +++++++++++++++++++++ .../testdata/testprogcgo/bigstack_windows.c | 46 +++++++++++++++++++++ .../testdata/testprogcgo/bigstack_windows.go | 27 +++++++++++++ 18 files changed, 208 insertions(+), 50 deletions(-) create mode 100644 src/runtime/stubs_x86.go create mode 100644 src/runtime/testdata/testprogcgo/bigstack_windows.c create mode 100644 src/runtime/testdata/testprogcgo/bigstack_windows.go (limited to 'src/runtime') diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index 3b7df9aef8..efd971c1cf 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -845,15 +845,18 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) { // and system calls even in "pure" Go code are actually C // calls that may need more stack than we think. // - // The default stack reserve size affects only the main + // The default stack reserve size directly affects only the main // thread, ctrlhandler thread, and profileloop thread. For // these, it must be greater than the stack size assumed by // externalthreadhandler. // - // For other threads we specify stack size in runtime explicitly. - // For these, the reserve must match STACKSIZE in - // runtime/cgo/gcc_windows_{386,amd64}.c and osStackSize in - // runtime/os_windows.go. + // For other threads, the runtime explicitly asks the kernel + // to use the default stack size so that all stacks are + // consistent. + // + // At thread start, in minit, the runtime queries the OS for + // the actual stack bounds so that the stack size doesn't need + // to be hard-coded into the runtime. oh64.SizeOfStackReserve = 0x00200000 if !iscgo { oh64.SizeOfStackCommit = 0x00001000 diff --git a/src/cmd/vet/all/whitelist/386.txt b/src/cmd/vet/all/whitelist/386.txt index 76e82317ed..f59094eb14 100644 --- a/src/cmd/vet/all/whitelist/386.txt +++ b/src/cmd/vet/all/whitelist/386.txt @@ -22,5 +22,3 @@ runtime/duff_386.s: [386] duffcopy: function duffcopy missing Go declaration runtime/asm_386.s: [386] uint32tofloat64: function uint32tofloat64 missing Go declaration runtime/asm_386.s: [386] float64touint32: function float64touint32 missing Go declaration - -runtime/asm_386.s: [386] stackcheck: function stackcheck missing Go declaration diff --git a/src/cmd/vet/all/whitelist/amd64.txt b/src/cmd/vet/all/whitelist/amd64.txt index 2268b39353..20e0d48d53 100644 --- a/src/cmd/vet/all/whitelist/amd64.txt +++ b/src/cmd/vet/all/whitelist/amd64.txt @@ -20,4 +20,3 @@ runtime/asm_amd64.s: [amd64] aeshashbody: function aeshashbody missing Go declar runtime/asm_amd64.s: [amd64] addmoduledata: function addmoduledata missing Go declaration runtime/duff_amd64.s: [amd64] duffzero: function duffzero missing Go declaration runtime/duff_amd64.s: [amd64] duffcopy: function duffcopy missing Go declaration -runtime/asm_amd64.s: [amd64] stackcheck: function stackcheck missing Go declaration diff --git a/src/cmd/vet/all/whitelist/nacl_amd64p32.txt b/src/cmd/vet/all/whitelist/nacl_amd64p32.txt index 9280c68d2c..1ec11f7ca8 100644 --- a/src/cmd/vet/all/whitelist/nacl_amd64p32.txt +++ b/src/cmd/vet/all/whitelist/nacl_amd64p32.txt @@ -24,5 +24,3 @@ runtime/asm_amd64p32.s: [amd64p32] rt0_go: unknown variable argc runtime/asm_amd64p32.s: [amd64p32] rt0_go: unknown variable argv runtime/asm_amd64p32.s: [amd64p32] asmcgocall: RET without writing to 4-byte ret+8(FP) - -runtime/asm_amd64p32.s: [amd64p32] stackcheck: function stackcheck missing Go declaration diff --git a/src/runtime/cgo/gcc_windows_386.c b/src/runtime/cgo/gcc_windows_386.c index e80a564943..f2ff710f60 100644 --- a/src/runtime/cgo/gcc_windows_386.c +++ b/src/runtime/cgo/gcc_windows_386.c @@ -11,16 +11,9 @@ static void threadentry(void*); -/* 1MB is default stack size for 32-bit Windows. - Allocation granularity on Windows is typically 64 KB. - This constant must match SizeOfStackReserve in ../cmd/link/internal/ld/pe.go. */ -#define STACKSIZE (1*1024*1024) - void x_cgo_init(G *g) { - int tmp; - g->stacklo = (uintptr)&tmp - STACKSIZE + 8*1024; } @@ -44,8 +37,7 @@ threadentry(void *v) ts = *(ThreadStart*)v; free(v); - ts.g->stackhi = (uintptr)&ts; - ts.g->stacklo = (uintptr)&ts - STACKSIZE + 8*1024; + // minit queries stack bounds from the OS. /* * Set specific keys in thread local storage. diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c index 75a7dc8ec2..511ab44fa9 100644 --- a/src/runtime/cgo/gcc_windows_amd64.c +++ b/src/runtime/cgo/gcc_windows_amd64.c @@ -11,16 +11,9 @@ static void threadentry(void*); -/* 2MB is default stack size for 64-bit Windows. - Allocation granularity on Windows is typically 64 KB. - This constant must match SizeOfStackReserve in ../cmd/link/internal/ld/pe.go. */ -#define STACKSIZE (2*1024*1024) - void x_cgo_init(G *g) { - int tmp; - g->stacklo = (uintptr)&tmp - STACKSIZE + 8*1024; } @@ -44,8 +37,7 @@ threadentry(void *v) ts = *(ThreadStart*)v; free(v); - ts.g->stackhi = (uintptr)&ts; - ts.g->stacklo = (uintptr)&ts - STACKSIZE + 8*1024; + // minit queries stack bounds from the OS. /* * Set specific keys in thread local storage. diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index d8f75a468b..b2ee8df1f0 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -489,3 +489,19 @@ func TestCgoTracebackSigpanic(t *testing.T) { t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got) } } + +// Test that C code called via cgo can use large Windows thread stacks +// and call back in to Go without crashing. See issue #20975. +// +// See also TestBigStackCallbackSyscall. +func TestBigStackCallbackCgo(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("skipping windows specific test") + } + t.Parallel() + got := runTestProg(t, "testprogcgo", "BigStack") + want := "OK\n" + if got != want { + t.Errorf("expected %q got %v", want, got) + } +} diff --git a/src/runtime/defs_windows.go b/src/runtime/defs_windows.go index 7ce6797414..9bd9107476 100644 --- a/src/runtime/defs_windows.go +++ b/src/runtime/defs_windows.go @@ -71,3 +71,4 @@ type FloatingSaveArea C.FLOATING_SAVE_AREA type M128a C.M128A type Context C.CONTEXT type Overlapped C.OVERLAPPED +type MemoryBasicInformation C.MEMORY_BASIC_INFORMATION diff --git a/src/runtime/defs_windows_386.go b/src/runtime/defs_windows_386.go index bac6ce78ce..589a7884cd 100644 --- a/src/runtime/defs_windows_386.go +++ b/src/runtime/defs_windows_386.go @@ -129,3 +129,13 @@ type overlapped struct { anon0 [8]byte hevent *byte } + +type memoryBasicInformation struct { + baseAddress uintptr + allocationBase uintptr + allocationProtect uint32 + regionSize uintptr + state uint32 + protect uint32 + type_ uint32 +} diff --git a/src/runtime/defs_windows_amd64.go b/src/runtime/defs_windows_amd64.go index 6e04568114..1e173e934d 100644 --- a/src/runtime/defs_windows_amd64.go +++ b/src/runtime/defs_windows_amd64.go @@ -151,3 +151,13 @@ type overlapped struct { anon0 [8]byte hevent *byte } + +type memoryBasicInformation struct { + baseAddress uintptr + allocationBase uintptr + allocationProtect uint32 + regionSize uintptr + state uint32 + protect uint32 + type_ uint32 +} diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 1f3ebf6072..bf5baea13e 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -45,6 +45,7 @@ const ( //go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll" //go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll" //go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll" +//go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll" //go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll" //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" @@ -92,6 +93,7 @@ var ( _SwitchToThread, _VirtualAlloc, _VirtualFree, + _VirtualQuery, _WSAGetOverlappedResult, _WaitForSingleObject, _WriteConsoleW, @@ -291,9 +293,6 @@ func osRelax(relax bool) uint32 { } } -// osStackSize must match SizeOfStackReserve in ../cmd/link/internal/ld/pe.go. -var osStackSize uintptr = 0x00200000*_64bit + 0x00100000*(1-_64bit) - func osinit() { asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall)) usleep2Addr = unsafe.Pointer(funcPC(usleep2)) @@ -322,18 +321,6 @@ func osinit() { // equivalent threads that all do a mix of GUI, IO, computations, etc. // In such context dynamic priority boosting does nothing but harm, so we turn it off. stdcall2(_SetProcessPriorityBoost, currentProcess, 1) - - // Fix the entry thread's stack bounds, since runtime entry - // assumed we were on a tiny stack. If this is a cgo binary, - // x_cgo_init already fixed these. - if !iscgo { - // Leave 8K of slop for calling C functions that don't - // have stack checks. We shouldn't be anywhere near - // this bound anyway. - g0.stack.lo = g0.stack.hi - osStackSize + 8*1024 - g0.stackguard0 = g0.stack.lo + _StackGuard - g0.stackguard1 = g0.stackguard0 - } } func nanotime() int64 @@ -634,10 +621,10 @@ func semacreate(mp *m) { //go:nowritebarrierrec //go:nosplit func newosproc(mp *m) { - const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000 - thandle := stdcall6(_CreateThread, 0, osStackSize, + // We pass 0 for the stack size to use the default for this binary. + thandle := stdcall6(_CreateThread, 0, 0, funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)), - _STACK_SIZE_PARAM_IS_A_RESERVATION, 0) + 0, 0) if thandle == 0 { if atomic.Load(&exiting) != 0 { @@ -702,6 +689,30 @@ func minit() { var thandle uintptr stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS) atomic.Storeuintptr(&getg().m.thread, thandle) + + // Query the true stack base from the OS. Currently we're + // running on a small assumed stack. + var mbi memoryBasicInformation + res := stdcall3(_VirtualQuery, uintptr(unsafe.Pointer(&mbi)), uintptr(unsafe.Pointer(&mbi)), unsafe.Sizeof(mbi)) + if res == 0 { + print("runtime: VirtualQuery failed; errno=", getlasterror(), "\n") + throw("VirtualQuery for stack base failed") + } + // Add 8K of slop for calling C functions that don't have + // stack checks. We shouldn't be anywhere near this bound + // anyway. + base := mbi.allocationBase + 8*1024 + // Sanity check the stack bounds. + g0 := getg() + if base > g0.stack.hi || g0.stack.hi-base > 64<<20 { + print("runtime: g0 stack [", hex(base), ",", hex(g0.stack.hi), ")\n") + throw("bad g0 stack") + } + g0.stack.lo = base + g0.stackguard0 = g0.stack.lo + _StackGuard + g0.stackguard1 = g0.stackguard0 + // Sanity check the SP. + stackcheck() } // Called from dropm to undo the effect of an minit. diff --git a/src/runtime/proc.go b/src/runtime/proc.go index b5486321ed..f82014eb92 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1233,6 +1233,7 @@ func mstart() { if osStack { // Initialize stack bounds from system stack. // Cgo may have left stack size in stack.hi. + // minit may update the stack bounds. size := _g_.stack.hi if size == 0 { size = 8192 * sys.StackGuardMultiplier diff --git a/src/runtime/stubs_x86.go b/src/runtime/stubs_x86.go new file mode 100644 index 0000000000..830c48bd01 --- /dev/null +++ b/src/runtime/stubs_x86.go @@ -0,0 +1,10 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 amd64p32 386 + +package runtime + +// stackcheck checks that SP is in range [g->stack.lo, g->stack.hi). +func stackcheck() diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s index 56d5cfaa82..3c091adcb1 100644 --- a/src/runtime/sys_windows_386.s +++ b/src/runtime/sys_windows_386.s @@ -315,8 +315,7 @@ TEXT runtime·tstart(SB),NOSPLIT,$0 // Layout new m scheduler stack on os stack. MOVL SP, AX MOVL AX, (g_stack+stack_hi)(DX) - SUBL runtime·osStackSize(SB), AX // stack size - ADDL $(8*1024), AX // slop for calling C + SUBL $(64*1024), AX // initial stack size (adjusted later) MOVL AX, (g_stack+stack_lo)(DX) ADDL $const__StackGuard, AX MOVL AX, g_stackguard0(DX) diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s index 119e04c704..c1449dba60 100644 --- a/src/runtime/sys_windows_amd64.s +++ b/src/runtime/sys_windows_amd64.s @@ -363,8 +363,7 @@ TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 // Layout new m scheduler stack on os stack. MOVQ SP, AX MOVQ AX, (g_stack+stack_hi)(DX) - SUBQ runtime·osStackSize(SB), AX // stack size - ADDQ $(8*1024), AX // slop for calling C + SUBQ $(64*1024), AX // inital stack size (adjusted later) MOVQ AX, (g_stack+stack_lo)(DX) ADDQ $const__StackGuard, AX MOVQ AX, g_stackguard0(DX) diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index 0f5e13f97e..0882e9cb73 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -957,6 +957,52 @@ uintptr_t cfunc() { } } +// Test that C code called via a DLL can use large Windows thread +// stacks and call back in to Go without crashing. See issue #20975. +// +// See also TestBigStackCallbackCgo. +func TestBigStackCallbackSyscall(t *testing.T) { + if _, err := exec.LookPath("gcc"); err != nil { + t.Skip("skipping test: gcc is missing") + } + + srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c") + if err != nil { + t.Fatal("Abs failed: ", err) + } + + tmpdir, err := ioutil.TempDir("", "TestBigStackCallback") + if err != nil { + t.Fatal("TempDir failed: ", err) + } + defer os.RemoveAll(tmpdir) + + outname := "mydll.dll" + cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) + cmd.Dir = tmpdir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to build dll: %v - %v", err, string(out)) + } + dllpath := filepath.Join(tmpdir, outname) + + dll := syscall.MustLoadDLL(dllpath) + defer dll.Release() + + var ok bool + proc := dll.MustFindProc("bigStack") + cb := syscall.NewCallback(func() uintptr { + // Do something interesting to force stack checks. + forceStackCopy() + ok = true + return 0 + }) + proc.Call(cb) + if !ok { + t.Fatalf("callback not called") + } +} + // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. func wantLoadLibraryEx() bool { return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" diff --git a/src/runtime/testdata/testprogcgo/bigstack_windows.c b/src/runtime/testdata/testprogcgo/bigstack_windows.c new file mode 100644 index 0000000000..cd85ac88d0 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/bigstack_windows.c @@ -0,0 +1,46 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This test source is used by both TestBigStackCallbackCgo (linked +// directly into the Go binary) and TestBigStackCallbackSyscall +// (compiled into a DLL). + +#include +#include + +#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION +#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 +#endif + +typedef void callback(char*); + +// Allocate a stack that's much larger than the default. +static const int STACK_SIZE = 16<<20; + +static callback *bigStackCallback; + +static void useStack(int bytes) { + // Windows doesn't like huge frames, so we grow the stack 64k at a time. + char x[64<<10]; + if (bytes < sizeof x) { + bigStackCallback(x); + } else { + useStack(bytes - sizeof x); + } +} + +static DWORD WINAPI threadEntry(LPVOID lpParam) { + useStack(STACK_SIZE - (128<<10)); + return 0; +} + +void bigStack(callback *cb) { + bigStackCallback = cb; + HANDLE hThread = CreateThread(NULL, STACK_SIZE, threadEntry, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + if (hThread == NULL) { + fprintf(stderr, "CreateThread failed\n"); + exit(1); + } + WaitForSingleObject(hThread, INFINITE); +} diff --git a/src/runtime/testdata/testprogcgo/bigstack_windows.go b/src/runtime/testdata/testprogcgo/bigstack_windows.go new file mode 100644 index 0000000000..f58fcf993f --- /dev/null +++ b/src/runtime/testdata/testprogcgo/bigstack_windows.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +typedef void callback(char*); +extern void goBigStack1(char*); +extern void bigStack(callback*); +*/ +import "C" + +func init() { + register("BigStack", BigStack) +} + +func BigStack() { + // Create a large thread stack and call back into Go to test + // if Go correctly determines the stack bounds. + C.bigStack((*C.callback)(C.goBigStack1)) +} + +//export goBigStack1 +func goBigStack1(x *C.char) { + println("OK") +} -- cgit v1.3-5-g9baa From 0e0cd70ecf6b5f0d9c8271f68b8fcc9f85cd6598 Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Mon, 2 Jul 2018 11:29:11 -0400 Subject: runtime: document when cgo traceback function is called Fixes #24518. Change-Id: I99c79c5a2ab9dbe7f0d257c263da9d2b5d1d55c4 Reviewed-on: https://go-review.googlesource.com/121917 Reviewed-by: Ian Lance Taylor --- src/runtime/traceback.go | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index cc5e01eb8b..4953653900 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -1115,6 +1115,13 @@ func isSystemGoroutine(gp *g) bool { // to the symbolizer function, return the file/line of the call // instruction. No additional subtraction is required or appropriate. // +// On all platforms, the traceback function is invoked when a call from +// Go to C to Go requests a stack trace. On linux/amd64, linux/ppc64le, +// and freebsd/amd64, the traceback function is also invoked when a +// signal is received by a thread that is executing a cgo call. The +// traceback function should not make assumptions about when it is +// called, as future versions of Go may make additional calls. +// // The symbolizer function will be called with a single argument, a // pointer to a struct: // -- cgit v1.3-5-g9baa From 5929ead6fbdec684c38157d45715c46107fa6ada Mon Sep 17 00:00:00 2001 From: Nikhil Benesch Date: Sat, 24 Mar 2018 18:51:01 -0400 Subject: runtime: support capturing C backtrace from signal handler on darwin/amd64 The implementation is mostly copied from the commit that added linux/amd64 support for this feature (https://golang.org/cl/17761). Change-Id: I3f482167620a7a3daf50a48087f8849a30d713bd Reviewed-on: https://go-review.googlesource.com/102438 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- src/runtime/cgo/callbacks_traceback.go | 2 +- src/runtime/cgo/gcc_traceback.c | 11 +---- src/runtime/cgo/libcgo.h | 10 +++++ src/runtime/crash_cgo_test.go | 8 +++- src/runtime/os_darwin.go | 7 +++- src/runtime/sys_darwin_386.s | 3 ++ src/runtime/sys_darwin_amd64.s | 76 ++++++++++++++++++++++++++++++++++ src/runtime/sys_darwin_arm.s | 3 ++ src/runtime/sys_darwin_arm64.s | 3 ++ 9 files changed, 110 insertions(+), 13 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/cgo/callbacks_traceback.go b/src/runtime/cgo/callbacks_traceback.go index f754846722..cdadf9e66f 100644 --- a/src/runtime/cgo/callbacks_traceback.go +++ b/src/runtime/cgo/callbacks_traceback.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux +// +build darwin linux package cgo diff --git a/src/runtime/cgo/gcc_traceback.c b/src/runtime/cgo/gcc_traceback.c index 667ea4c0cf..d86331c583 100644 --- a/src/runtime/cgo/gcc_traceback.c +++ b/src/runtime/cgo/gcc_traceback.c @@ -2,17 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build cgo -// +build linux +// +build cgo,darwin cgo,linux #include - -struct cgoTracebackArg { - uintptr_t Context; - uintptr_t SigContext; - uintptr_t* Buf; - uintptr_t Max; -}; +#include "libcgo.h" // Call the user's traceback function and then call sigtramp. // The runtime signal handler will jump to this code. diff --git a/src/runtime/cgo/libcgo.h b/src/runtime/cgo/libcgo.h index c38fb643ff..60326720a7 100644 --- a/src/runtime/cgo/libcgo.h +++ b/src/runtime/cgo/libcgo.h @@ -96,6 +96,16 @@ struct context_arg { }; extern void (*(_cgo_get_context_function(void)))(struct context_arg*); +/* + * The argument for the cgo traceback callback. See runtime.SetCgoTraceback. + */ +struct cgoTracebackArg { + uintptr_t Context; + uintptr_t SigContext; + uintptr_t* Buf; + uintptr_t Max; +}; + /* * TSAN support. This is only useful when building with * CGO_CFLAGS="-fsanitize=thread" CGO_LDFLAGS="-fsanitize=thread" go install diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index b2ee8df1f0..6da8341e84 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -239,8 +239,12 @@ func TestCgoCCodeSIGPROF(t *testing.T) { func TestCgoCrashTraceback(t *testing.T) { t.Parallel() - if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") { - t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) + switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { + case "darwin/amd64": + case "linux/amd64": + case "linux/ppc64le": + default: + t.Skipf("not yet supported on %s", platform) } got := runTestProg(t, "testprogcgo", "CrashTraceback") for i := 1; i <= 3; i++ { diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index ff375004a3..d2144edf2e 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -274,7 +274,11 @@ func setsig(i uint32, fn uintptr) { sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = ^uint32(0) if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) + if iscgo { + fn = funcPC(cgoSigtramp) + } else { + fn = funcPC(sigtramp) + } } *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn sigaction(i, &sa, nil) @@ -283,6 +287,7 @@ func setsig(i uint32, fn uintptr) { // sigtramp is the callback from libc when a signal is received. // It is called with the C calling convention. func sigtramp() +func cgoSigtramp() //go:nosplit //go:nowritebarrierrec diff --git a/src/runtime/sys_darwin_386.s b/src/runtime/sys_darwin_386.s index 09f12283a1..4bfb9b8362 100644 --- a/src/runtime/sys_darwin_386.s +++ b/src/runtime/sys_darwin_386.s @@ -326,6 +326,9 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0 ADDL $28, SP RET +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + JMP runtime·sigtramp(SB) + TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0 PUSHL BP MOVL SP, BP diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s index 142933585d..2a2e7379ca 100644 --- a/src/runtime/sys_darwin_amd64.s +++ b/src/runtime/sys_darwin_amd64.s @@ -215,6 +215,82 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0 POPQ BP RET +// Used instead of sigtramp in programs that use cgo. +// Arguments from kernel are in DI, SI, DX. +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + // If no traceback function, do usual sigtramp. + MOVQ runtime·cgoTraceback(SB), AX + TESTQ AX, AX + JZ sigtramp + + // If no traceback support function, which means that + // runtime/cgo was not linked in, do usual sigtramp. + MOVQ _cgo_callers(SB), AX + TESTQ AX, AX + JZ sigtramp + + // Figure out if we are currently in a cgo call. + // If not, just do usual sigtramp. + get_tls(CX) + MOVQ g(CX),AX + TESTQ AX, AX + JZ sigtrampnog // g == nil + MOVQ g_m(AX), AX + TESTQ AX, AX + JZ sigtramp // g.m == nil + MOVL m_ncgo(AX), CX + TESTL CX, CX + JZ sigtramp // g.m.ncgo == 0 + MOVQ m_curg(AX), CX + TESTQ CX, CX + JZ sigtramp // g.m.curg == nil + MOVQ g_syscallsp(CX), CX + TESTQ CX, CX + JZ sigtramp // g.m.curg.syscallsp == 0 + MOVQ m_cgoCallers(AX), R8 + TESTQ R8, R8 + JZ sigtramp // g.m.cgoCallers == nil + MOVL m_cgoCallersUse(AX), CX + TESTL CX, CX + JNZ sigtramp // g.m.cgoCallersUse != 0 + + // Jump to a function in runtime/cgo. + // That function, written in C, will call the user's traceback + // function with proper unwind info, and will then call back here. + // The first three arguments, and the fifth, are already in registers. + // Set the two remaining arguments now. + MOVQ runtime·cgoTraceback(SB), CX + MOVQ $runtime·sigtramp(SB), R9 + MOVQ _cgo_callers(SB), AX + JMP AX + +sigtramp: + JMP runtime·sigtramp(SB) + +sigtrampnog: + // Signal arrived on a non-Go thread. If this is SIGPROF, get a + // stack trace. + CMPL DI, $27 // 27 == SIGPROF + JNZ sigtramp + + // Lock sigprofCallersUse. + MOVL $0, AX + MOVL $1, CX + MOVQ $runtime·sigprofCallersUse(SB), R11 + LOCK + CMPXCHGL CX, 0(R11) + JNZ sigtramp // Skip stack trace if already locked. + + // Jump to the traceback function in runtime/cgo. + // It will call back to sigprofNonGo, which will ignore the + // arguments passed in registers. + // First three arguments to traceback function are in registers already. + MOVQ runtime·cgoTraceback(SB), CX + MOVQ $runtime·sigprofCallers(SB), R8 + MOVQ $runtime·sigprofNonGo(SB), R9 + MOVQ _cgo_callers(SB), AX + JMP AX + TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0 PUSHQ BP // make a frame; keep stack aligned MOVQ SP, BP diff --git a/src/runtime/sys_darwin_arm.s b/src/runtime/sys_darwin_arm.s index 9b5c667f45..7a269cf576 100644 --- a/src/runtime/sys_darwin_arm.s +++ b/src/runtime/sys_darwin_arm.s @@ -227,6 +227,9 @@ nog: RET +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + JMP runtime·sigtramp(SB) + TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0 MOVW 4(R0), R1 // arg 2 new MOVW 8(R0), R2 // arg 3 old diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s index c324994d26..4f9d0b8d58 100644 --- a/src/runtime/sys_darwin_arm64.s +++ b/src/runtime/sys_darwin_arm64.s @@ -223,6 +223,9 @@ nog: RET +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + JMP runtime·sigtramp(SB) + TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0 MOVD 8(R0), R1 // arg 2 new MOVD 16(R0), R2 // arg 3 old -- cgit v1.3-5-g9baa From 7001ac53946bb05c88a0e7d7c3c64dfb1cca1fca Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 6 Jul 2018 09:50:05 -0400 Subject: runtime: fix abort handling on Windows On Windows, the IP recorded in the breakpoint exception caused by runtime.abort is actually one byte after the INT3, unlike on UNIX OSes. Account for this in isgoexception. It turns out TestAbort was "passing" on Windows anyway because abort still caused a fatal panic, just not the one we were expecting. This CL tightens this test to check that the runtime specifically reports a breakpoint exception. Fixing this is related to #21382, since we use runtime.abort in reporting g0 stack overflows, and it's important that we detect this and not try to handle it. Change-Id: I66120944d138eb80f839346b157a3759c1019e34 Reviewed-on: https://go-review.googlesource.com/122515 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Alex Brainman --- src/runtime/crash_test.go | 15 +++++++++------ src/runtime/signal_windows.go | 4 +++- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 9f11aea4e9..b266d7b77e 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -640,18 +640,21 @@ func TestTimePprof(t *testing.T) { // Test that runtime.abort does so. func TestAbort(t *testing.T) { - output := runTestProg(t, "testprog", "Abort") + // Pass GOTRACEBACK to ensure we get runtime frames. + output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system") if want := "runtime.abort"; !strings.Contains(output, want) { t.Errorf("output does not contain %q:\n%s", want, output) } if strings.Contains(output, "BAD") { t.Errorf("output contains BAD:\n%s", output) } - // Check that it's a signal-style traceback. - if runtime.GOOS != "windows" { - if want := "PC="; !strings.Contains(output, want) { - t.Errorf("output does not contain %q:\n%s", want, output) - } + // Check that it's a breakpoint traceback. + want := "SIGTRAP" + if runtime.GOOS == "windows" { + want = "Exception 0x80000003" + } + if !strings.Contains(output, want) { + t.Errorf("output does not contain %q:\n%s", want, output) } } diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index 500b02880d..fe5ff87cd6 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -46,7 +46,9 @@ func isgoexception(info *exceptionrecord, r *context) bool { return false } - if isAbortPC(r.ip()) { + // In the case of an abort, the exception IP is one byte after + // the INT3 (this differs from UNIX OSes). + if isAbortPC(r.ip() - 1) { // Never turn abort into a panic. return false } -- cgit v1.3-5-g9baa From d6b56bb301470c62634d1747cc155489c4e0f18a Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 6 Jul 2018 09:55:33 -0400 Subject: runtime: account for guard zone in Windows stack size Windows includes an 8K guard in system-allocated thread stacks, which we currently don't account for when setting the g0 stack bounds. As a result, if we do overflow the g0 stack bounds, we'll get a STATUS_GUARD_PAGE_VIOLATION exception, which we're not expecting. Fix the g0 stack bounds to include a total of 16K of slop to account for this 8K guard. Updates #21382. Change-Id: Ia89b741b1413328e4681a237f5a7ee645531fe16 Reviewed-on: https://go-review.googlesource.com/122516 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Alex Brainman --- src/runtime/os_windows.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index bf5baea13e..6f73a5ba24 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -698,10 +698,12 @@ func minit() { print("runtime: VirtualQuery failed; errno=", getlasterror(), "\n") throw("VirtualQuery for stack base failed") } - // Add 8K of slop for calling C functions that don't have - // stack checks. We shouldn't be anywhere near this bound - // anyway. - base := mbi.allocationBase + 8*1024 + // The system leaves an 8K PAGE_GUARD region at the bottom of + // the stack (in theory VirtualQuery isn't supposed to include + // that, but it does). Add an additional 8K of slop for + // calling C functions that don't have stack checks. We + // shouldn't be anywhere near this bound anyway. + base := mbi.allocationBase + 16<<10 // Sanity check the stack bounds. g0 := getg() if base > g0.stack.hi || g0.stack.hi-base > 64<<20 { -- cgit v1.3-5-g9baa From 78561c4ae9b18e111ef0e25478f24e5e21dcff69 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 25 Jun 2018 18:00:43 -0400 Subject: runtime: handle g0 stack overflows gracefully Currently, if the runtime overflows the g0 stack on Windows, it leads to an infinite recursion: 1. Something overflows the g0 stack bounds and calls morestack. 2. morestack determines it's on the g0 stack and hence cannot grow the stack, so it calls badmorestackg0 (which prints "fatal: morestack on g0") followed by abort. 3. abort performs an INT $3, which turns into a Windows _EXCEPTION_BREAKPOINT exception. 4. This enters the Windows sigtramp, which ensures we're on the g0 stack and calls exceptionhandler. 5. exceptionhandler has a stack check prologue, so it determines that it's out of stack and calls morestack. 6. goto 2 Fix this by making the exception handler avoid stack checks until it has ruled out an abort and by blowing away the stack bounds in lastcontinuehandler before we print the final fatal traceback (which itself involves a lot of stack bounds checks). Fixes #21382. Change-Id: Ie66e91f708e18d131d97f22b43f9ac26f3aece5a Reviewed-on: https://go-review.googlesource.com/120857 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Alex Brainman --- src/runtime/crash_test.go | 29 +++++++++++++++++++++++++++++ src/runtime/export_test.go | 11 +++++++++++ src/runtime/os_windows.go | 5 +++-- src/runtime/panic.go | 5 +++++ src/runtime/signal_windows.go | 30 ++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index b266d7b77e..7eb20f24ea 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -687,3 +687,32 @@ func TestRuntimePanic(t *testing.T) { t.Errorf("output did not contain expected string %q", want) } } + +// Test that g0 stack overflows are handled gracefully. +func TestG0StackOverflow(t *testing.T) { + testenv.MustHaveExec(t) + + switch runtime.GOOS { + case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd": + t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)") + } + + if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" { + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestG0StackOverflow", "-test.v")) + cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1") + out, err := cmd.CombinedOutput() + // Don't check err since it's expected to crash. + if n := strings.Count(string(out), "morestack on g0\n"); n != 1 { + t.Fatalf("%s\n(exit status %v)", out, err) + } + // Check that it's a signal-style traceback. + if runtime.GOOS != "windows" { + if want := "PC="; !strings.Contains(string(out), want) { + t.Errorf("output does not contain %q:\n%s", want, out) + } + } + return + } + + runtime.G0StackOverflow() +} diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 7ebdfc1520..89f887b765 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -461,3 +461,14 @@ func PanicForTesting(b []byte, i int) byte { func unexportedPanicForTesting(b []byte, i int) byte { return b[i] } + +func G0StackOverflow() { + systemstack(func() { + stackOverflow(nil) + }) +} + +func stackOverflow(x *byte) { + var buf [256]byte + stackOverflow(&buf[0]) +} diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 6f73a5ba24..6180dd3a60 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -701,8 +701,9 @@ func minit() { // The system leaves an 8K PAGE_GUARD region at the bottom of // the stack (in theory VirtualQuery isn't supposed to include // that, but it does). Add an additional 8K of slop for - // calling C functions that don't have stack checks. We - // shouldn't be anywhere near this bound anyway. + // calling C functions that don't have stack checks and for + // lastcontinuehandler. We shouldn't be anywhere near this + // bound anyway. base := mbi.allocationBase + 16<<10 // Sanity check the stack bounds. g0 := getg() diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 7bb7f9b90c..a5287a0b86 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -889,6 +889,11 @@ func shouldPushSigpanic(gp *g, pc, lr uintptr) bool { // isAbortPC returns true if pc is the program counter at which // runtime.abort raises a signal. +// +// It is nosplit because it's part of the isgoexception +// implementation. +// +//go:nosplit func isAbortPC(pc uintptr) bool { return pc == funcPC(abort) || ((GOARCH == "arm" || GOARCH == "arm64") && pc == funcPC(abort)+sys.PCQuantum) } diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index fe5ff87cd6..a63450038d 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -38,6 +38,13 @@ func initExceptionHandler() { } } +// isgoexception returns true if this exception should be translated +// into a Go panic. +// +// It is nosplit to avoid growing the stack in case we're aborting +// because of a stack overflow. +// +//go:nosplit func isgoexception(info *exceptionrecord, r *context) bool { // Only handle exception if executing instructions in Go binary // (not Windows library code). @@ -73,11 +80,19 @@ func isgoexception(info *exceptionrecord, r *context) bool { // Called by sigtramp from Windows VEH handler. // Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION) // or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH). +// +// This is the first entry into Go code for exception handling. This +// is nosplit to avoid growing the stack until we've checked for +// _EXCEPTION_BREAKPOINT, which is raised if we overflow the g0 stack, +// +//go:nosplit func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 { if !isgoexception(info, r) { return _EXCEPTION_CONTINUE_SEARCH } + // After this point, it is safe to grow the stack. + if gp.throwsplit { // We can't safely sigpanic because it may grow the // stack. Let it fall through. @@ -113,6 +128,10 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 { // if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION. // firstcontinuehandler will stop that search, // if exceptionhandler did the same earlier. +// +// It is nosplit for the same reason as exceptionhandler. +// +//go:nosplit func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { if !isgoexception(info, r) { return _EXCEPTION_CONTINUE_SEARCH @@ -124,6 +143,10 @@ var testingWER bool // lastcontinuehandler is reached, because runtime cannot handle // current exception. lastcontinuehandler will print crash info and exit. +// +// It is nosplit for the same reason as exceptionhandler. +// +//go:nosplit func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { if testingWER { return _EXCEPTION_CONTINUE_SEARCH @@ -136,6 +159,13 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { } panicking = 1 + // In case we're handling a g0 stack overflow, blow away the + // g0 stack bounds so we have room to print the traceback. If + // this somehow overflows the stack, the OS will trap it. + _g_.stack.lo = 0 + _g_.stackguard0 = _g_.stack.lo + _StackGuard + _g_.stackguard1 = _g_.stackguard0 + print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n") print("PC=", hex(r.ip()), "\n") -- cgit v1.3-5-g9baa From b001ffb864ce5486c6edbe98202d3e0687313ce2 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Sun, 8 Jul 2018 01:05:18 +0200 Subject: runtime: fix TestAbort on Plan 9 Since CL 122515, TestAbort is failing on Plan 9 because there is no SIGTRAP signal on Plan 9, but a note containing the "sys: breakpoint" string. This change fixes the TestAbort test by handling the Plan 9 case. Fixes #26265. Change-Id: I2fae00130bcee1cf946d8cc9d147a77f951be390 Reviewed-on: https://go-review.googlesource.com/122464 Run-TryBot: David du Colombier <0intro@gmail.com> TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- src/runtime/crash_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 7eb20f24ea..f1229f154b 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -650,7 +650,10 @@ func TestAbort(t *testing.T) { } // Check that it's a breakpoint traceback. want := "SIGTRAP" - if runtime.GOOS == "windows" { + switch runtime.GOOS { + case "plan9": + want = "sys: breakpoint" + case "windows": want = "Exception 0x80000003" } if !strings.Contains(output, want) { -- cgit v1.3-5-g9baa From 5cb1b1773fba1e1503493eea5c4ffa49fc5b5f08 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 8 Jul 2018 00:21:27 -0400 Subject: runtime: fix TestAbort on non-x86 arches CL 122515 tightened TestAbort to look for breakpoint exceptions and not just general signal crashes, but this only applies on x86 arches. On non-x86 arches we use a nil pointer dereference to abort, so the test is now failing. This CL re-loosens TestAbort on non-x86 arches to only expect a signal traceback. Should fix the build on linux/arm, linux/arm64, linux/ppc64, and linux/s390x. Change-Id: I1065341180ab5ab4da63b406c641dcde93c9490b Reviewed-on: https://go-review.googlesource.com/122580 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/crash_test.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index f1229f154b..5c255efd26 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -648,13 +648,19 @@ func TestAbort(t *testing.T) { if strings.Contains(output, "BAD") { t.Errorf("output contains BAD:\n%s", output) } - // Check that it's a breakpoint traceback. - want := "SIGTRAP" - switch runtime.GOOS { - case "plan9": - want = "sys: breakpoint" - case "windows": - want = "Exception 0x80000003" + // Check that it's a signal traceback. + want := "PC=" + // For systems that use a breakpoint, check specifically for that. + switch runtime.GOARCH { + case "386", "amd64": + switch runtime.GOOS { + case "plan9": + want = "sys: breakpoint" + case "windows": + want = "Exception 0x80000003" + default: + want = "SIGTRAP" + } } if !strings.Contains(output, want) { t.Errorf("output does not contain %q:\n%s", want, output) -- cgit v1.3-5-g9baa From d82256ac11762f9e5069d84065de89919c337d58 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 8 Jul 2018 15:28:21 -0400 Subject: runtime: skip TestG0StackOverflow on Android This test is skipped on Linux and should be skipped on Android for the same reason. Change-Id: I753c4788d935bd58874554b455c0d5be2315b794 Reviewed-on: https://go-review.googlesource.com/122585 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/runtime/crash_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 5c255efd26..2766b8850a 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -702,7 +702,7 @@ func TestG0StackOverflow(t *testing.T) { testenv.MustHaveExec(t) switch runtime.GOOS { - case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd": + case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "android": t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)") } -- cgit v1.3-5-g9baa From b56e24782f81002fabe06bdf65a735ac7e2b6e1f Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 8 Jul 2018 17:02:48 -0700 Subject: runtime: scale timeout in TestStackGrowth Updates #19381 Change-Id: I62b8b0cd7170941af77281eb3aada3802623ec27 Reviewed-on: https://go-review.googlesource.com/122587 Reviewed-by: Brad Fitzpatrick --- src/runtime/stack_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go index 5d674470c1..dc65395141 100644 --- a/src/runtime/stack_test.go +++ b/src/runtime/stack_test.go @@ -7,9 +7,11 @@ package runtime_test import ( "bytes" "fmt" + "os" "reflect" "regexp" . "runtime" + "strconv" "strings" "sync" "sync/atomic" @@ -126,9 +128,18 @@ func TestStackGrowth(t *testing.T) { }() <-done GC() + + timeout := 20 * time.Second + if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { + scale, err := strconv.Atoi(s) + if err == nil { + timeout *= time.Duration(scale) + } + } + select { case <-done: - case <-time.After(20 * time.Second): + case <-time.After(timeout): if atomic.LoadUint32(&started) == 0 { t.Log("finalizer did not start") } else { -- cgit v1.3-5-g9baa From 9b7a8aaaf3adbc330ef724fb581b3bfa72ab2a49 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 8 Jul 2018 16:42:33 -0700 Subject: runtime: only run TestMemStats sanity tests once Fixes #22696 Change-Id: Ibe4628f71d64a2b36b655ea69710a925924b12a3 Reviewed-on: https://go-review.googlesource.com/122586 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick Reviewed-by: Austin Clements --- src/runtime/malloc_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/malloc_test.go b/src/runtime/malloc_test.go index 0bce059f7f..e6afc25ea9 100644 --- a/src/runtime/malloc_test.go +++ b/src/runtime/malloc_test.go @@ -19,7 +19,11 @@ import ( "unsafe" ) +var testMemStatsCount int + func TestMemStats(t *testing.T) { + testMemStatsCount++ + // Make sure there's at least one forced GC. GC() @@ -35,6 +39,13 @@ func TestMemStats(t *testing.T) { } le := func(thresh float64) func(interface{}) error { return func(x interface{}) error { + // These sanity tests aren't necessarily valid + // with high -test.count values, so only run + // them once. + if testMemStatsCount > 1 { + return nil + } + if reflect.ValueOf(x).Convert(reflect.TypeOf(thresh)).Float() < thresh { return nil } -- cgit v1.3-5-g9baa From fb59bccef32780d9f94e5bd22344d514087efd07 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 6 Jul 2018 17:06:55 -0700 Subject: runtime: clarify SetFinalizer docs Fixes #24480 Change-Id: I7db721fb71a17f07472ec7f216478e7887435639 Reviewed-on: https://go-review.googlesource.com/122557 Reviewed-by: Brad Fitzpatrick --- src/runtime/mfinal.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 6ce0312712..a8c51e3e02 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -267,8 +267,8 @@ func runfinq() { // is not guaranteed to run, because there is no ordering that // respects the dependencies. // -// The finalizer for obj is scheduled to run at some arbitrary time after -// obj becomes unreachable. +// The finalizer is scheduled to run at some arbitrary time after the +// program can no longer reach the object to which obj points. // There is no guarantee that finalizers will run before a program exits, // so typically they are useful only for releasing non-memory resources // associated with an object during a long-running program. -- cgit v1.3-5-g9baa From 34619d5d13ac79c605e0941f62730f5c8dfea4c1 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Tue, 10 Jul 2018 10:02:02 +0200 Subject: runtime/trace: comment newlines to restore correct doc summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #26309 Change-Id: I0e0b61b885817e514aa46e299b00833f16e98b2a Reviewed-on: https://go-review.googlesource.com/122898 Reviewed-by: Айнар Гарипов Reviewed-by: Ian Lance Taylor --- src/runtime/trace/trace.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/trace/trace.go b/src/runtime/trace/trace.go index a40f87e53c..7f9d72a846 100644 --- a/src/runtime/trace/trace.go +++ b/src/runtime/trace/trace.go @@ -82,10 +82,10 @@ // // ctx, task := trace.NewTask(ctx, "makeCappuccino") // trace.Log(ctx, "orderID", orderID) - +// // milk := make(chan bool) // espresso := make(chan bool) - +// // go func() { // trace.WithRegion(ctx, "steamMilk", steamMilk) // milk <- true -- cgit v1.3-5-g9baa From f5921d48f1f98a4803b1b9c112cab501bfb1713b Mon Sep 17 00:00:00 2001 From: Kamil Rytarowski Date: Sat, 30 Jun 2018 23:29:41 +0000 Subject: runtime/cgo: Add initial NetBSD Thread Sanitizer support Recognize NetBSD in: - go/internal/work/init.go - race.bash - runtime/race/race.go Add __ps_strings symbol in runtime/cgo/netbsd.go as this is used internally in the TSan library for NetBSD and used for ReExec(). Tested on NetBSD/amd64 v. 8.99.12. Around 98% tests are passing for the ./race.bash target. Updates #19273 Change-Id: Ic0e48d2fb159a7868aab5e17156eeaca1225e513 GitHub-Last-Rev: d6e082707b9b18df1fe63f723666f4d2eb5e6cfe GitHub-Pull-Request: golang/go#24322 Reviewed-on: https://go-review.googlesource.com/99835 Reviewed-by: Brad Fitzpatrick --- src/cmd/go/internal/work/init.go | 4 ++-- src/race.bash | 7 ++++++- src/runtime/cgo/netbsd.go | 2 ++ src/runtime/race/race.go | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 1081e5147e..608f5648a4 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -47,9 +47,9 @@ func instrumentInit() { platform := cfg.Goos + "/" + cfg.Goarch switch platform { default: - fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, freebsd/amd64, netbsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) os.Exit(2) - case "linux/amd64", "linux/ppc64le", "freebsd/amd64", "darwin/amd64", "windows/amd64": + case "linux/amd64", "linux/ppc64le", "freebsd/amd64", "netbsd/amd64", "darwin/amd64", "windows/amd64": // race supported on these platforms } } diff --git a/src/race.bash b/src/race.bash index 73cb1e583b..d673f503a9 100755 --- a/src/race.bash +++ b/src/race.bash @@ -9,7 +9,7 @@ set -e function usage { - echo 'race detector is only supported on linux/amd64, linux/ppc64le, freebsd/amd64 and darwin/amd64' 1>&2 + echo 'race detector is only supported on linux/amd64, linux/ppc64le, freebsd/amd64, netbsd/amd64 and darwin/amd64' 1>&2 exit 1 } @@ -30,6 +30,11 @@ case $(uname) in usage fi ;; +"NetBSD") + if [ $(uname -m) != "amd64" ]; then + usage + fi + ;; *) usage ;; diff --git a/src/runtime/cgo/netbsd.go b/src/runtime/cgo/netbsd.go index 2cecd0c57a..74d0aed014 100644 --- a/src/runtime/cgo/netbsd.go +++ b/src/runtime/cgo/netbsd.go @@ -14,6 +14,8 @@ import _ "unsafe" // for go:linkname //go:linkname _environ environ //go:linkname _progname __progname +//go:linkname ___ps_strings __ps_strings var _environ uintptr var _progname uintptr +var ___ps_strings uintptr diff --git a/src/runtime/race/race.go b/src/runtime/race/race.go index f702c7a5d4..95e965411b 100644 --- a/src/runtime/race/race.go +++ b/src/runtime/race/race.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build race,linux,amd64 race,freebsd,amd64 race,darwin,amd64 race,windows,amd64 race,linux,ppc64le +// +build race,linux,amd64 race,freebsd,amd64 race,netbsd,amd64 race,darwin,amd64 race,windows,amd64 race,linux,ppc64le package race -- cgit v1.3-5-g9baa From 8ec35ab66ae4abdb98b3b0d7c3ca4522601c7168 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 10 Jul 2018 04:54:37 +1000 Subject: runtime: correct new thread stack for openbsd MAP_STACK OpenBSD 6.4 will require the stack pointer to be pointing at an area that is marked as MAP_STACK when entering and exiting syscalls. Adjust the stack pointer used for a new thread such that it points within the stack, not at the top of it (i.e. outside). Fixes #26142 Change-Id: I905bd8e5be3dfc325392e7ac490fb56a7c71b3aa Reviewed-on: https://go-review.googlesource.com/122735 Reviewed-by: Austin Clements Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot --- src/runtime/os_openbsd.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index 1660511616..96112cb25b 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -6,6 +6,7 @@ package runtime import ( "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) @@ -182,10 +183,12 @@ func newosproc(mp *m) { print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n") } + // Stack pointer must point inside stack area (as marked with MAP_STACK), + // rather than at the top of it. param := tforkt{ tf_tcb: unsafe.Pointer(&mp.tls[0]), tf_tid: (*int32)(unsafe.Pointer(&mp.procid)), - tf_stack: uintptr(stk), + tf_stack: uintptr(stk) - sys.PtrSize, } var oset sigset -- cgit v1.3-5-g9baa From 9300d223a3986b4419372bcd58c6cce7c845e8ae Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 11 Jul 2018 09:02:46 -0400 Subject: runtime: document philosophy behind runtime atomic usage Based on Dmitry Vyukov's comments in CL 65210. Change-Id: I5dce7286b0d180cd43cad3aaf70f537fafcda588 Reviewed-on: https://go-review.googlesource.com/123275 Reviewed-by: Ian Lance Taylor Reviewed-by: Rick Hudson Reviewed-by: Dmitry Vyukov --- src/runtime/HACKING.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/HACKING.md b/src/runtime/HACKING.md index 0b390c34d9..72ba61970b 100644 --- a/src/runtime/HACKING.md +++ b/src/runtime/HACKING.md @@ -135,6 +135,47 @@ In summary, parkYNN +Atomics +======= + +The runtime uses its own atomics package at `runtime/internal/atomic`. +This corresponds to `sync/atomic`, but functions have different names +for historical reasons and there are a few additional functions needed +by the runtime. + +In general, we think hard about the uses of atomics in the runtime and +try to avoid unnecessary atomic operations. If access to a variable is +sometimes protected by another synchronization mechanism, the +already-protected accesses generally don't need to be atomic. There +are several reasons for this: + +1. Using non-atomic or atomic access where appropriate makes the code + more self-documenting. Atomic access to a variable implies there's + somewhere else that may concurrently access the variable. + +2. Non-atomic access allows for automatic race detection. The runtime + doesn't currently have a race detector, but it may in the future. + Atomic access defeats the race detector, while non-atomic access + allows the race detector to check your assumptions. + +3. Non-atomic access may improve performance. + +Of course, any non-atomic access to a shared variable should be +documented to explain how that access is protected. + +Some common patterns that mix atomic and non-atomic access are: + +* Read-mostly variables where updates are protected by a lock. Within + the locked region, reads do not need to be atomic, but the write + does. Outside the locked region, reads need to be atomic. + +* Reads that only happen during STW, where no writes can happen during + STW, do not need to be atomic. + +That said, the advice from the Go memory model stands: "Don't be +[too] clever." The performance of the runtime matters, but its +robustness matters more. + Unmanaged memory ================ -- cgit v1.3-5-g9baa From cda1947fd12ad31060b30a0a601130bfaa26d234 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 11 Jul 2018 13:51:39 -0700 Subject: runtime: don't say "different packages" if they may not be different Fix the panic message produced for an interface conversion error to only say "types from different packages" if they are definitely from different packges. If they may be from the same package, say "types from different scopes." Updates #18911 Fixes #26094 Change-Id: I0cea50ba31007d88e70c067b4680009ede69bab9 Reviewed-on: https://go-review.googlesource.com/123395 Reviewed-by: Austin Clements --- src/runtime/error.go | 33 ++++++++++++++++------------- src/runtime/iface.go | 16 ++++++--------- src/runtime/type.go | 19 +++++++++++++++++ test/fixedbugs/issue26094.go | 49 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 test/fixedbugs/issue26094.go (limited to 'src/runtime') diff --git a/src/runtime/error.go b/src/runtime/error.go index 1f77c0a0b5..9a2beaeb95 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -19,32 +19,37 @@ type Error interface { // A TypeAssertionError explains a failed type assertion. type TypeAssertionError struct { - interfaceString string - concreteString string - assertedString string - missingMethod string // one method needed by Interface, missing from Concrete + _interface *_type + concrete *_type + asserted *_type + missingMethod string // one method needed by Interface, missing from Concrete } func (*TypeAssertionError) RuntimeError() {} func (e *TypeAssertionError) Error() string { - inter := e.interfaceString - if inter == "" { - inter = "interface" + inter := "interface" + if e._interface != nil { + inter = e._interface.string() } - if e.concreteString == "" { - return "interface conversion: " + inter + " is nil, not " + e.assertedString + as := e.asserted.string() + if e.concrete == nil { + return "interface conversion: " + inter + " is nil, not " + as } + cs := e.concrete.string() if e.missingMethod == "" { - msg := "interface conversion: " + inter + " is " + e.concreteString + - ", not " + e.assertedString - if e.concreteString == e.assertedString { + msg := "interface conversion: " + inter + " is " + cs + ", not " + as + if cs == as { // provide slightly clearer error message - msg += " (types from different packages)" + if e.concrete.pkgpath() != e.asserted.pkgpath() { + msg += " (types from different packages)" + } else { + msg += " (types from different scopes)" + } } return msg } - return "interface conversion: " + e.concreteString + " is not " + e.assertedString + + return "interface conversion: " + cs + " is not " + as + ": missing method " + e.missingMethod } diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 15c412c4e6..7ab731151e 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -41,7 +41,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { return nil } name := inter.typ.nameOff(inter.mhdr[0].name) - panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), name.name()}) + panic(&TypeAssertionError{nil, typ, &inter.typ, name.name()}) } var m *itab @@ -82,7 +82,7 @@ finish: // The cached result doesn't record which // interface function was missing, so initialize // the itab again to get the missing function name. - panic(&TypeAssertionError{concreteString: typ.string(), assertedString: inter.typ.string(), missingMethod: m.init()}) + panic(&TypeAssertionError{concrete: typ, asserted: &inter.typ, missingMethod: m.init()}) } // find finds the given interface/type pair in t. @@ -245,11 +245,7 @@ func itabsinit() { // want = the static type we're trying to convert to. // iface = the static type we're converting from. func panicdottypeE(have, want, iface *_type) { - haveString := "" - if have != nil { - haveString = have.string() - } - panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""}) + panic(&TypeAssertionError{iface, have, want, ""}) } // panicdottypeI is called when doing an i.(T) conversion and the conversion fails. @@ -265,7 +261,7 @@ func panicdottypeI(have *itab, want, iface *_type) { // panicnildottype is called when doing a i.(T) conversion and the interface i is nil. // want = the static type we're trying to convert to. func panicnildottype(want *_type) { - panic(&TypeAssertionError{"", "", want.string(), ""}) + panic(&TypeAssertionError{nil, nil, want, ""}) // TODO: Add the static type we're converting from as well. // It might generate a better error message. // Just to match other nil conversion errors, we don't for now. @@ -516,7 +512,7 @@ func assertI2I(inter *interfacetype, i iface) (r iface) { tab := i.tab if tab == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) + panic(&TypeAssertionError{nil, nil, &inter.typ, ""}) } if tab.inter == inter { r.tab = tab @@ -549,7 +545,7 @@ func assertE2I(inter *interfacetype, e eface) (r iface) { t := e._type if t == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) + panic(&TypeAssertionError{nil, nil, &inter.typ, ""}) } r.tab = getitab(inter, t, false) r.data = e.data diff --git a/src/runtime/type.go b/src/runtime/type.go index d87d6e1507..4b38c351c7 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -131,6 +131,25 @@ func (t *_type) name() string { return s[i+1:] } +// pkgpath returns the path of the package where t was defined, if +// available. This is not the same as the reflect package's PkgPath +// method, in that it returns the package path for struct and interface +// types, not just named types. +func (t *_type) pkgpath() string { + if u := t.uncommon(); u != nil { + return t.nameOff(u.pkgpath).name() + } + switch t.kind & kindMask { + case kindStruct: + st := (*structtype)(unsafe.Pointer(t)) + return st.pkgPath.name() + case kindInterface: + it := (*interfacetype)(unsafe.Pointer(t)) + return it.pkgpath.name() + } + return "" +} + // reflectOffs holds type offsets defined at run time by the reflect package. // // When a type is defined at run time, its *rtype data lives on the heap. diff --git a/test/fixedbugs/issue26094.go b/test/fixedbugs/issue26094.go new file mode 100644 index 0000000000..7af8fac6b3 --- /dev/null +++ b/test/fixedbugs/issue26094.go @@ -0,0 +1,49 @@ +// run + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "strings" + +var X interface{} + +type T struct{} + +func scopes() { + p, ok := recover().(error) + if ok && strings.Contains(p.Error(), "different scopes") { + return + } + panic(p) +} + +func F1() { + type T struct{} + X = T{} +} + +func F2() { + type T struct{} + defer scopes() + _ = X.(T) +} + +func F3() { + defer scopes() + _ = X.(T) +} + +func F4() { + X = T{} +} + +func main() { + F1() // set X to F1's T + F2() // check that X is not F2's T + F3() // check that X is not package T + F4() // set X to package T + F2() // check that X is not F2's T +} -- cgit v1.3-5-g9baa From 4b74506da1ccf8c2f57f11991b432a6d5ac86e4f Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 11 Jul 2018 21:14:42 +0000 Subject: doc: update the minimum support Windows version Also, remove some test code that was trying to work on XP and fix up some comments referencing XP. Fixes #26191 Updates #23380 Change-Id: I0b7319fe1954afddb22d396e5ec91d8c960268d8 Reviewed-on: https://go-review.googlesource.com/123415 Run-TryBot: Brad Fitzpatrick Reviewed-by: Ian Lance Taylor Reviewed-by: Alex Brainman TryBot-Result: Gobot Gobot --- doc/install.html | 2 +- src/crypto/x509/sha2_windows_test.go | 19 ------------------- src/crypto/x509/verify_test.go | 5 ----- src/internal/poll/fd_windows.go | 11 +++++------ src/net/interface_windows.go | 5 ++--- src/runtime/syscall_windows.go | 11 ++++++----- 6 files changed, 14 insertions(+), 39 deletions(-) delete mode 100644 src/crypto/x509/sha2_windows_test.go (limited to 'src/runtime') diff --git a/doc/install.html b/doc/install.html index 3bb4a15b25..2e0c7f859d 100644 --- a/doc/install.html +++ b/doc/install.html @@ -50,7 +50,7 @@ If your OS or architecture is not on the list, you may be able to FreeBSD 10.3 or later amd64, 386 Debian GNU/kFreeBSD not supported Linux 2.6.23 or later with glibc amd64, 386, arm, arm64,
s390x, ppc64le CentOS/RHEL 5.x not supported.
Install from source for other libc. macOS 10.10 or later amd64 use the clang or gcc that comes with Xcode for cgo support -Windows XP SP2 or later amd64, 386 use MinGW gcc. No need for cygwin or msys. +Windows 7, Server 2008R2 or later amd64, 386 use MinGW gcc. No need for cygwin or msys.

diff --git a/src/crypto/x509/sha2_windows_test.go b/src/crypto/x509/sha2_windows_test.go deleted file mode 100644 index 620b7b9e77..0000000000 --- a/src/crypto/x509/sha2_windows_test.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 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 x509 - -import "syscall" - -func init() { - v, err := syscall.GetVersion() - if err != nil { - return - } - if major := byte(v); major < 6 { - // Windows XP SP2 and Windows 2003 do not support SHA2. - // https://blogs.technet.com/b/pki/archive/2010/09/30/sha2-and-windows.aspx - supportSHA2 = false - } -} diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index bd3df47907..95034dbba3 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -15,8 +15,6 @@ import ( "time" ) -var supportSHA2 = true - type verifyTest struct { leaf string intermediates []string @@ -427,9 +425,6 @@ func testVerify(t *testing.T, useSystemRoots bool) { if runtime.GOOS == "windows" && test.testSystemRootsError { continue } - if useSystemRoots && !supportSHA2 && test.sha2 { - continue - } opts := VerifyOptions{ Intermediates: NewCertPool(), diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index 74135ce716..d04d332696 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -34,12 +34,11 @@ var ( var canCancelIO bool // determines if CancelIoEx API is present -// This package uses SetFileCompletionNotificationModes Windows API -// to skip calling GetQueuedCompletionStatus if an IO operation completes -// synchronously. Unfortuently SetFileCompletionNotificationModes is not -// available on Windows XP. Also there is a known bug where -// SetFileCompletionNotificationModes crashes on some systems -// (see https://support.microsoft.com/kb/2568167 for details). +// This package uses the SetFileCompletionNotificationModes Windows +// API to skip calling GetQueuedCompletionStatus if an IO operation +// completes synchronously. There is a known bug where +// SetFileCompletionNotificationModes crashes on some systems (see +// https://support.microsoft.com/kb/2568167 for details). var useSetFileCompletionNotificationModes bool // determines is SetFileCompletionNotificationModes is present and safe to use diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go index be96c586c7..28b0a65f66 100644 --- a/src/net/interface_windows.go +++ b/src/net/interface_windows.go @@ -65,9 +65,8 @@ func interfaceTable(ifindex int) ([]Interface, error) { } // For now we need to infer link-layer service // capabilities from media types. - // We will be able to use - // MIB_IF_ROW2.AccessType once we drop support - // for Windows XP. + // TODO: use MIB_IF_ROW2.AccessType now that we no longer support + // Windows XP. switch aa.IfType { case windows.IF_TYPE_ETHERNET_CSMACD, windows.IF_TYPE_ISO88025_TOKENRING, windows.IF_TYPE_IEEE80211, windows.IF_TYPE_IEEE1394: ifi.Flags |= FlagBroadcast | FlagMulticast diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go index 134d4dbd99..5ab78fdbf2 100644 --- a/src/runtime/syscall_windows.go +++ b/src/runtime/syscall_windows.go @@ -107,11 +107,12 @@ func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32} c.args = uintptr(noescape(unsafe.Pointer(&args))) } else { - // User is on Windows XP or something ancient. - // The caller wanted to only load the filename DLL - // from the System32 directory but that facility - // doesn't exist, so just load it the normal way. This - // is a potential security risk, but so is Windows XP. + // User doesn't have KB2533623 installed. The caller + // wanted to only load the filename DLL from the + // System32 directory but that facility doesn't exist, + // so just load it the normal way. This is a potential + // security risk, but so is not installing security + // updates. c.fn = getLoadLibrary() c.n = 1 c.args = uintptr(noescape(unsafe.Pointer(&filename))) -- cgit v1.3-5-g9baa From c19f86fbfba484695b7785bd7bdcc1a43adbf505 Mon Sep 17 00:00:00 2001 From: Xia Bin Date: Thu, 12 Jul 2018 18:34:34 +0800 Subject: runtime: fix reference to funcdata.go in comment Change-Id: I6c8699cd71b41cf8d178a0af3a745a19dcf60905 Reviewed-on: https://go-review.googlesource.com/123536 Reviewed-by: Brad Fitzpatrick --- src/runtime/funcdata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/funcdata.h b/src/runtime/funcdata.h index 4c290b9b9a..e6e0306e65 100644 --- a/src/runtime/funcdata.h +++ b/src/runtime/funcdata.h @@ -6,7 +6,7 @@ // in Go binaries. It is included by assembly sources, so it must // be written using #defines. // -// These must agree with symtab.go and ../cmd/internal/obj/funcdata.go. +// These must agree with symtab.go and ../cmd/internal/objabi/funcdata.go. #define PCDATA_StackMapIndex 0 #define PCDATA_InlTreeIndex 1 -- cgit v1.3-5-g9baa From ebdba42d9e1de46ebf611baec98d53f01c534cac Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 10 Jul 2018 21:07:55 -0700 Subject: runtime: check tgkill error in Debug tests Updates #25519 Change-Id: Ibcdf948fd38d8d02d467b62213566ec0d7ce0d6a Reviewed-on: https://go-review.googlesource.com/123180 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- src/runtime/debug_test.go | 4 ++-- src/runtime/export_debug_test.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/debug_test.go b/src/runtime/debug_test.go index 4181d59c1f..a34f4c77f7 100644 --- a/src/runtime/debug_test.go +++ b/src/runtime/debug_test.go @@ -69,8 +69,8 @@ func debugCallWorker2(stop *uint32, x *int) { *x = 1 } -func debugCallTKill(tid int) { - syscall.Tgkill(syscall.Getpid(), tid, syscall.SIGTRAP) +func debugCallTKill(tid int) error { + return syscall.Tgkill(syscall.Getpid(), tid, syscall.SIGTRAP) } func TestDebugCall(t *testing.T) { diff --git a/src/runtime/export_debug_test.go b/src/runtime/export_debug_test.go index 78436f36cf..d34c1fd7dc 100644 --- a/src/runtime/export_debug_test.go +++ b/src/runtime/export_debug_test.go @@ -20,7 +20,7 @@ import ( // // On success, InjectDebugCall returns the panic value of fn or nil. // If fn did not panic, its results will be available in args. -func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int)) (interface{}, error) { +func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int) error) (interface{}, error) { if gp.lockedm == 0 { return nil, plainError("goroutine not locked to thread") } @@ -54,7 +54,9 @@ func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int)) (interfac defer func() { testSigtrap = nil }() testSigtrap = h.inject - tkill(tid) + if err := tkill(tid); err != nil { + return nil, err + } // Wait for completion. notetsleepg(&h.done, -1) if len(h.err) != 0 { -- cgit v1.3-5-g9baa From ba02264446fa1863c0a4c8339bbcf2bec57b620a Mon Sep 17 00:00:00 2001 From: "Hana (Hyang-Ah) Kim" Date: Fri, 13 Jul 2018 16:11:24 -0400 Subject: runtime/pprof: add a fake mapping when /proc/self/maps is unavailable Profile's Mapping field is currently populated by reading /proc/self/maps. On systems where /proc/self/maps is not available, the profile generated by Go's runtime will not have any Mapping entry. Pprof command then adds a fake entry and links all Location entries in the profile with the fake entry to be used during symbolization. https://github.com/google/pprof/blob/a8644067d5a3c9a6386e7c88fa4a3d9d37877ca3/internal/driver/fetch.go#L437 The fake entry is not enough to suppress the error or warning messages pprof command produces. We need to tell pprof that Location entries are symbolized already by Go runtime and pprof does not have to attempt to perform further symbolization. In #25743, we made Go runtime mark Mapping entries with HasFunctions=true when all Location entries from the Mapping entries are successfully symbolized. This change makes the Go runtime add a fake mapping entry, otherwise the pprof command tool would add, and set the HasFunctions=true following the same logic taken when the real mapping information is available. Updates #19790. Fixes #26255. Tested pprof doesn't report the error message any more for pure Go program. Change-Id: Ib12b62e15073f5d6c80967e44b3e8709277c11bd Reviewed-on: https://go-review.googlesource.com/123779 Run-TryBot: Hyang-Ah Hana Kim TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/pprof/proto.go | 14 ++++++++++++- src/runtime/pprof/proto_test.go | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/pprof/proto.go b/src/runtime/pprof/proto.go index 1cf3a5154f..cbd0b83376 100644 --- a/src/runtime/pprof/proto.go +++ b/src/runtime/pprof/proto.go @@ -54,6 +54,7 @@ type memMap struct { file, buildID string funcs symbolizeFlag + fake bool // map entry was faked; /proc/self/maps wasn't available } // symbolizeFlag keeps track of symbolization result. @@ -267,7 +268,7 @@ func (b *profileBuilder) locForPC(addr uintptr) uint64 { frame, more = frames.Next() } for i := range b.mem { - if b.mem[i].start <= addr && addr < b.mem[i].end { + if b.mem[i].start <= addr && addr < b.mem[i].end || b.mem[i].fake { b.pb.uint64Opt(tagLocation_MappingID, uint64(i+1)) m := b.mem[i] @@ -440,6 +441,12 @@ func (b *profileBuilder) build() { func (b *profileBuilder) readMapping() { data, _ := ioutil.ReadFile("/proc/self/maps") parseProcSelfMaps(data, b.addMapping) + if len(b.mem) == 0 { // pprof expects a map entry, so fake one. + b.addMappingEntry(0, 0, 0, "", "", true) + // TODO(hyangah): make addMapping return *memMap or + // take a memMap struct, and get rid of addMappingEntry + // that takes a bunch of positional arguments. + } } func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, buildID string)) { @@ -540,11 +547,16 @@ func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, } func (b *profileBuilder) addMapping(lo, hi, offset uint64, file, buildID string) { + b.addMappingEntry(lo, hi, offset, file, buildID, false) +} + +func (b *profileBuilder) addMappingEntry(lo, hi, offset uint64, file, buildID string, fake bool) { b.mem = append(b.mem, memMap{ start: uintptr(lo), end: uintptr(hi), offset: offset, file: file, buildID: buildID, + fake: fake, }) } diff --git a/src/runtime/pprof/proto_test.go b/src/runtime/pprof/proto_test.go index 36c345b6d9..76bd46da02 100644 --- a/src/runtime/pprof/proto_test.go +++ b/src/runtime/pprof/proto_test.go @@ -97,9 +97,16 @@ func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) { addr2 = mprof.Mapping[1].Start map2 = mprof.Mapping[1] map2.BuildID, _ = elfBuildID(map2.File) + case "js": + addr1 = uint64(funcPC(f1)) + addr2 = uint64(funcPC(f2)) default: addr1 = uint64(funcPC(f1)) addr2 = uint64(funcPC(f2)) + // Fake mapping - HasFunctions will be true because two PCs from Go + // will be fully symbolized. + fake := &profile.Mapping{ID: 1, HasFunctions: true} + map1, map2 = fake, fake } return } @@ -301,3 +308,41 @@ func symbolized(loc *profile.Location) bool { } return true } + +// TestFakeMapping tests if at least one mapping exists +// (including a fake mapping), and their HasFunctions bits +// are set correctly. +func TestFakeMapping(t *testing.T) { + var buf bytes.Buffer + if err := Lookup("heap").WriteTo(&buf, 0); err != nil { + t.Fatalf("failed to write heap profile: %v", err) + } + prof, err := profile.Parse(&buf) + if err != nil { + t.Fatalf("failed to parse the generated profile data: %v", err) + } + t.Logf("Profile: %s", prof) + if len(prof.Mapping) == 0 { + t.Fatal("want profile with at least one mapping entry, got 0 mapping") + } + + hit := make(map[*profile.Mapping]bool) + miss := make(map[*profile.Mapping]bool) + for _, loc := range prof.Location { + if symbolized(loc) { + hit[loc.Mapping] = true + } else { + miss[loc.Mapping] = true + } + } + for _, m := range prof.Mapping { + if miss[m] && m.HasFunctions { + t.Errorf("mapping %+v has HasFunctions=true, but contains locations with failed symbolization", m) + continue + } + if !miss[m] && hit[m] && !m.HasFunctions { + t.Errorf("mapping %+v has HasFunctions=false, but all referenced locations from this lapping were symbolized successfully", m) + continue + } + } +} -- cgit v1.3-5-g9baa From ff5b54d24a7fda589d3fc4516aa153a3f2e1a224 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 17 Jul 2018 11:36:29 -0400 Subject: runtime: disable compressed DWARF for lldb test lldb doesn't support compressed DWARF, so right now we're just always skipping the lldb test. This CL makes the test run again by disabling compressed DWARF just for this test. Updates #25925. Change-Id: Ib9ddc442305fe6d37060d48f36bc4458b6fd8c86 Reviewed-on: https://go-review.googlesource.com/124385 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/runtime-lldb_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/runtime-lldb_test.go b/src/runtime/runtime-lldb_test.go index a036fd8480..3cc154667a 100644 --- a/src/runtime/runtime-lldb_test.go +++ b/src/runtime/runtime-lldb_test.go @@ -159,7 +159,9 @@ func TestLldbPython(t *testing.T) { t.Fatalf("failed to create file: %v", err) } - cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe") + // As of 2018-07-17, lldb doesn't support compressed DWARF, so + // disable it for this test. + cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-ldflags=-compressdwarf=false", "-o", "a.exe") cmd.Dir = dir out, err := cmd.CombinedOutput() if err != nil { -- cgit v1.3-5-g9baa From 57b5a71d7c897a2f3a21ba8b875bb9264faed326 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 17 Jul 2018 11:38:35 -0400 Subject: Revert "runtime: fix lldb test after DWARF compression" This reverts commit c99300229de4e69220790c71da14785dc52c3d68. The original CL skipped the lldb test if it couldn't read compressed DWARF, but lldb can never read compressed DWARF, so this effectively disabled this test unconditionally. The previous commit disabled DWARF compression for this test, so the test now works on its own merits again. This CL reverts the change to skip the test so we don't simply mask lldb failures. Updates #25925. Change-Id: I3e1c787b658257b542c3c70807065dde9cfe05ee Reviewed-on: https://go-review.googlesource.com/124386 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/runtime-lldb_test.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/runtime-lldb_test.go b/src/runtime/runtime-lldb_test.go index 3cc154667a..fe3a0eb90d 100644 --- a/src/runtime/runtime-lldb_test.go +++ b/src/runtime/runtime-lldb_test.go @@ -10,7 +10,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "runtime" "strings" "testing" @@ -83,12 +82,8 @@ target = debugger.CreateTargetWithFileAndArch("a.exe", None) if target: print "Created target" main_bp = target.BreakpointCreateByLocation("main.go", 10) - if main_bp.GetNumLocations() != 0: + if main_bp: print "Created breakpoint" - else: - # This happens if lldb can't read the program's DWARF. See https://golang.org/issue/25925. - print "SKIP: no matching locations for breakpoint" - exit(1) process = target.LaunchSimple(None, None, os.getcwd()) if process: print "Process launched" @@ -103,7 +98,7 @@ if target: if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]: continue else: - print "SKIP: Timeout launching" + print "Timeout launching" break if state == lldb.eStateStopped: for t in process.threads: @@ -179,9 +174,8 @@ func TestLldbPython(t *testing.T) { got, _ := cmd.CombinedOutput() if string(got) != expectedLldbOutput { - skipReason := regexp.MustCompile("SKIP: .*\n").Find(got) - if skipReason != nil { - t.Skip(string(skipReason)) + if strings.Contains(string(got), "Timeout launching") { + t.Skip("Timeout launching") } t.Fatalf("Unexpected lldb output:\n%s", got) } -- cgit v1.3-5-g9baa From 2d4c1ff73992fa817213c7866be8030d9c78b5ba Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 13 Jul 2018 17:29:39 -0400 Subject: runtime: don't create heap hints outside TSAN's supported heap TSAN for Go only supports heap address in the range [0x00c000000000, 0x00e000000000). However, we currently create heap hints of the form 0xXXc000000000 for XX between 0x00 and 0x7f. Even for XX=0x01, this hint is outside TSAN's supported heap address range. Fix this by creating a slightly different set of hints in race mode, all of which fall inside TSAN's heap address range. This should fix TestArenaCollision flakes. That test forces the runtime to use later heap hints. Currently, this always results in TSAN "failed to allocate" failures on Windows (which happens to have a slightly more constrained TSAN layout than non-Windows). Most of the time we don't notice these failures, but sometimes it crashes TSAN, leading to a test failure. Fixes #25698. Change-Id: I8926cd61f0ee5ee00efa77b283f7b809c555be46 Reviewed-on: https://go-review.googlesource.com/123780 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/malloc.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index e75edf05fd..07e0a67240 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -427,6 +427,14 @@ func mallocinit() { p = uintptr(i)<<40 | uintptrMask&(0x0013<<28) case GOARCH == "arm64": p = uintptr(i)<<40 | uintptrMask&(0x0040<<32) + case raceenabled: + // The TSAN runtime requires the heap + // to be in the range [0x00c000000000, + // 0x00e000000000). + p = uintptr(i)<<32 | uintptrMask&(0x00c0<<32) + if p >= uintptrMask&0x00e000000000 { + continue + } default: p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32) } -- cgit v1.3-5-g9baa From fe68ab3bcde97e3a325e4aa3c70f5f9172540453 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 16 Jul 2018 14:39:40 -0700 Subject: runtime: traceback from outermost libc call If we're in a libc call and get a trap, don't try to traceback the libc call. Start from the state we had at entry to libc. If there are multiple libc calls outstanding, remember the outermost one. Fixes #26393 Change-Id: Icfe8794b95bf3bfd1a0679b456dcde2481dcabf3 Reviewed-on: https://go-review.googlesource.com/124195 Reviewed-by: Austin Clements Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot --- src/runtime/os_solaris.go | 28 +++++++++++++++++++++------- src/runtime/os_windows.go | 9 ++++++--- src/runtime/sys_darwin.go | 20 +++++++++++++++++++- src/runtime/traceback.go | 7 +++++++ 4 files changed, 53 insertions(+), 11 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/os_solaris.go b/src/runtime/os_solaris.go index 703a2e5430..4575b5e641 100644 --- a/src/runtime/os_solaris.go +++ b/src/runtime/os_solaris.go @@ -37,12 +37,14 @@ func sysvicall0(fn *libcFunc) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil // See comment in sys_darwin.go:libcCall } var libcall libcall @@ -64,12 +66,14 @@ func sysvicall1(fn *libcFunc, a1 uintptr) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil } var libcall libcall @@ -92,12 +96,14 @@ func sysvicall2(fn *libcFunc, a1, a2 uintptr) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil } var libcall libcall @@ -119,12 +125,14 @@ func sysvicall3(fn *libcFunc, a1, a2, a3 uintptr) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil } var libcall libcall @@ -146,12 +154,14 @@ func sysvicall4(fn *libcFunc, a1, a2, a3, a4 uintptr) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil } var libcall libcall @@ -173,12 +183,14 @@ func sysvicall5(fn *libcFunc, a1, a2, a3, a4, a5 uintptr) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil } var libcall libcall @@ -200,12 +212,14 @@ func sysvicall6(fn *libcFunc, a1, a2, a3, a4, a5, a6 uintptr) uintptr { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + mp = nil } var libcall libcall diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 6180dd3a60..5607bf95c1 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -734,17 +734,20 @@ func stdcall(fn stdFunction) uintptr { gp := getg() mp := gp.m mp.libcall.fn = uintptr(unsafe.Pointer(fn)) - - if mp.profilehz != 0 { + resetLibcall := false + if mp.profilehz != 0 && mp.libcallsp == 0 { // leave pc/sp for cpu profiler mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + resetLibcall = true // See comment in sys_darwin.go:libcCall } asmcgocall(asmstdcallAddr, unsafe.Pointer(&mp.libcall)) - mp.libcallsp = 0 + if resetLibcall { + mp.libcallsp = 0 + } return mp.libcall.r1 } diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go index f0d0815903..7efbef746c 100644 --- a/src/runtime/sys_darwin.go +++ b/src/runtime/sys_darwin.go @@ -18,12 +18,30 @@ func libcCall(fn, arg unsafe.Pointer) int32 { if gp != nil { mp = gp.m } - if mp != nil { + if mp != nil && mp.libcallsp == 0 { mp.libcallg.set(gp) mp.libcallpc = getcallerpc() // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them mp.libcallsp = getcallersp() + } else { + // Make sure we don't reset libcallsp. This makes + // libcCall reentrant; We remember the g/pc/sp for the + // first call on an M, until that libcCall instance + // returns. Reentrance only matters for signals, as + // libc never calls back into Go. The tricky case is + // where we call libcX from an M and record g/pc/sp. + // Before that call returns, a signal arrives on the + // same M and the signal handling code calls another + // libc function. We don't want that second libcCall + // from within the handler to be recorded, and we + // don't want that call's completion to zero + // libcallsp. + // We don't need to set libcall* while we're in a sighandler + // (even if we're not currently in libc) because we block all + // signals while we're handling a signal. That includes the + // profile signal, which is the one that uses the libcall* info. + mp = nil } res := asmcgocall(fn, arg) if mp != nil { diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 4953653900..d8c225d975 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -679,7 +679,14 @@ func traceback(pc, sp, lr uintptr, gp *g) { // the initial PC must not be rewound to the previous instruction. // (All the saved pairs record a PC that is a return address, so we // rewind it into the CALL instruction.) +// If gp.m.libcall{g,pc,sp} information is available, it uses that information in preference to +// the pc/sp/lr passed in. func tracebacktrap(pc, sp, lr uintptr, gp *g) { + if gp.m.libcallsp != 0 { + // We're in C code somewhere, traceback from the saved position. + traceback1(gp.m.libcallpc, gp.m.libcallsp, 0, gp.m.libcallg.ptr(), 0) + return + } traceback1(pc, sp, lr, gp, _TraceTrap) } -- cgit v1.3-5-g9baa From c0e5485bd59827d6def05020b201133982a52790 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 25 Jul 2018 14:09:02 -0700 Subject: runtime: ignore GNU/Linux sigaction errors for signals 32 and 33 This avoids problems when running under QEMU. It seems that at least some QEMU versions turn the sigaction implementation into a call to the C library sigaction function. The C library function will reject attempts to set the signal handler for signals 32 and 33. Ignore errors in that case. Change-Id: Id443a9a32f6fb0ceef5c59a398e7ede30bf71646 Reviewed-on: https://go-review.googlesource.com/125955 Run-TryBot: Ian Lance Taylor Reviewed-by: Heschi Kreinick TryBot-Result: Gobot Gobot --- src/runtime/os_linux.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 68f99de115..a04c995c00 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -415,9 +415,18 @@ func (c *sigctxt) fixsigcode(sig uint32) { //go:nosplit func sysSigaction(sig uint32, new, old *sigactiont) { if rt_sigaction(uintptr(sig), new, old, unsafe.Sizeof(sigactiont{}.sa_mask)) != 0 { - // Workaround for bug in Qemu user mode emulation. (qemu - // rejects rt_sigaction of signal 64, SIGRTMAX). - if sig != 64 { + // Workaround for bugs in QEMU user mode emulation. + // + // QEMU turns calls to the sigaction system call into + // calls to the C library sigaction call; the C + // library call rejects attempts to call sigaction for + // SIGCANCEL (32) or SIGSETXID (33). + // + // QEMU rejects calling sigaction on SIGRTMAX (64). + // + // Just ignore the error in these case. There isn't + // anything we can do about it anyhow. + if sig != 32 && sig != 33 && sig != 64 { // Use system stack to avoid split stack overflow on ppc64/ppc64le. systemstack(func() { throw("sigaction failed") -- cgit v1.3-5-g9baa From 05f9b369525f682020df8a55c3fc83b520575912 Mon Sep 17 00:00:00 2001 From: Jeet Parekh Date: Fri, 27 Jul 2018 15:52:36 +0000 Subject: syscall: improve NewCallback documentation and panic message Fixes #26138 Change-Id: If77b2839bccc600223735df42438a19131cd051c GitHub-Last-Rev: 64ceaea9f1cb7afb3d552dce0223b818bac1faf9 GitHub-Pull-Request: golang/go#26617 Reviewed-on: https://go-review.googlesource.com/126035 Reviewed-by: Austin Clements Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot --- src/runtime/syscall_windows.go | 8 ++++---- src/syscall/syscall_windows.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go index 5ab78fdbf2..8264070569 100644 --- a/src/runtime/syscall_windows.go +++ b/src/runtime/syscall_windows.go @@ -42,20 +42,20 @@ func callbackasmAddr(i int) uintptr { //go:linkname compileCallback syscall.compileCallback func compileCallback(fn eface, cleanstack bool) (code uintptr) { if fn._type == nil || (fn._type.kind&kindMask) != kindFunc { - panic("compileCallback: not a function") + panic("compileCallback: expected function with one uintptr-sized result") } ft := (*functype)(unsafe.Pointer(fn._type)) if len(ft.out()) != 1 { - panic("compileCallback: function must have one output parameter") + panic("compileCallback: expected function with one uintptr-sized result") } uintptrSize := unsafe.Sizeof(uintptr(0)) if ft.out()[0].size != uintptrSize { - panic("compileCallback: output parameter size is wrong") + panic("compileCallback: expected function with one uintptr-sized result") } argsize := uintptr(0) for _, t := range ft.in() { if t.size > uintptrSize { - panic("compileCallback: input parameter size is wrong") + panic("compileCallback: argument size is larger than uintptr") } argsize += uintptrSize } diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 5cfdb76e2b..b234f3d67d 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -120,16 +120,16 @@ func (e Errno) Timeout() bool { // Implemented in runtime/syscall_windows.go. func compileCallback(fn interface{}, cleanstack bool) uintptr -// Converts a Go function to a function pointer conforming -// to the stdcall calling convention. This is useful when -// interoperating with Windows code requiring callbacks. +// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. +// This is useful when interoperating with Windows code requiring callbacks. +// The argument is expected to be a function with with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr. func NewCallback(fn interface{}) uintptr { return compileCallback(fn, true) } -// Converts a Go function to a function pointer conforming -// to the cdecl calling convention. This is useful when -// interoperating with Windows code requiring callbacks. +// NewCallbackCDecl converts a Go function to a function pointer conforming to the cdecl calling convention. +// This is useful when interoperating with Windows code requiring callbacks. +// The argument is expected to be a function with with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr. func NewCallbackCDecl(fn interface{}) uintptr { return compileCallback(fn, false) } -- cgit v1.3-5-g9baa From acd30e9a827a1df4d99592cdd16e26a4395ebcfe Mon Sep 17 00:00:00 2001 From: David Wimmer Date: Wed, 25 Jul 2018 18:44:07 +0000 Subject: runtime: fix syscall error returns on mips/mips64/ppc64 The linux syscall functions used in runtime are designed around the calling convention of returning errors as negative numbers. On some other systems (like mips and ppc) the actual syscalls signal errors in other ways. This means that the assembly implementations of the syscall functions on these platforms need to transform the return values in the error cases to match the expected negative errno values. This was addressed for certain syscalls in https://golang.org/cl/19455 and https://golang.org/cl/89235. This patch handles the rest of the syscall functions in sys_linux_*.s that return any value for mips/mips64/ppc64. Fixes #23446 Change-Id: I302100261231f76d5850ab2c2ea080170d7dba72 GitHub-Last-Rev: e358e2b08c76897b13f917cfa12b5085e20337f9 GitHub-Pull-Request: golang/go#26606 Reviewed-on: https://go-review.googlesource.com/125895 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/runtime/sys_linux_mips64x.s | 14 ++++++++++++++ src/runtime/sys_linux_mipsx.s | 18 +++++++++++++++--- src/runtime/sys_linux_ppc64x.s | 14 ++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s index 9ce810a6b6..8e64f1c562 100644 --- a/src/runtime/sys_linux_mips64x.s +++ b/src/runtime/sys_linux_mips64x.s @@ -218,6 +218,8 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36 MOVV size+24(FP), R7 MOVV $SYS_rt_sigaction, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+32(FP) RET @@ -299,6 +301,8 @@ TEXT runtime·futex(SB),NOSPLIT|NOFRAME,$0 MOVW val3+32(FP), R9 MOVV $SYS_futex, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+40(FP) RET @@ -321,6 +325,8 @@ TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0 MOVV $SYS_clone, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno // In parent, return. BEQ R2, 3(PC) @@ -383,6 +389,8 @@ TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0 MOVV buf+16(FP), R6 MOVV $SYS_sched_getaffinity, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+24(FP) RET @@ -391,6 +399,8 @@ TEXT runtime·epollcreate(SB),NOSPLIT|NOFRAME,$0 MOVW size+0(FP), R4 MOVV $SYS_epoll_create, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+8(FP) RET @@ -399,6 +409,8 @@ TEXT runtime·epollcreate1(SB),NOSPLIT|NOFRAME,$0 MOVW flags+0(FP), R4 MOVV $SYS_epoll_create1, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+8(FP) RET @@ -424,6 +436,8 @@ TEXT runtime·epollwait(SB),NOSPLIT|NOFRAME,$0 MOVV $0, R8 MOVV $SYS_epoll_pwait, R2 SYSCALL + BEQ R7, 2(PC) + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+24(FP) RET diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s index 95f6367893..a6bca3bebd 100644 --- a/src/runtime/sys_linux_mipsx.s +++ b/src/runtime/sys_linux_mipsx.s @@ -234,6 +234,8 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-20 MOVW size+12(FP), R7 MOVW $SYS_rt_sigaction, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno MOVW R2, ret+16(FP) RET @@ -320,6 +322,8 @@ TEXT runtime·futex(SB),NOSPLIT,$20-28 MOVW $SYS_futex, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno MOVW R2, ret+24(FP) RET @@ -351,11 +355,11 @@ TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0-24 MOVW $SYS_clone, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno // In parent, return. - BEQ R2, 5(PC) - SUBU R2, R0, R3 - CMOVN R7, R3, R2 + BEQ R2, 3(PC) MOVW R2, ret+20(FP) RET @@ -417,6 +421,8 @@ TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0-16 MOVW buf+8(FP), R6 MOVW $SYS_sched_getaffinity, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno MOVW R2, ret+12(FP) RET @@ -425,6 +431,8 @@ TEXT runtime·epollcreate(SB),NOSPLIT,$0-8 MOVW size+0(FP), R4 MOVW $SYS_epoll_create, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno MOVW R2, ret+4(FP) RET @@ -433,6 +441,8 @@ TEXT runtime·epollcreate1(SB),NOSPLIT,$0-8 MOVW flags+0(FP), R4 MOVW $SYS_epoll_create1, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno MOVW R2, ret+4(FP) RET @@ -456,6 +466,8 @@ TEXT runtime·epollwait(SB),NOSPLIT,$0-20 MOVW timeout+12(FP), R7 MOVW $SYS_epoll_wait, R2 SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno MOVW R2, ret+16(FP) RET diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index b7d34b00da..483cb8ef9a 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -193,6 +193,8 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36 MOVD old+16(FP), R5 MOVD size+24(FP), R6 SYSCALL $SYS_rt_sigaction + BVC 2(PC) + NEG R3 // caller expects negative errno MOVW R3, ret+32(FP) RET @@ -388,6 +390,8 @@ TEXT runtime·futex(SB),NOSPLIT|NOFRAME,$0 MOVD addr2+24(FP), R7 MOVW val3+32(FP), R8 SYSCALL $SYS_futex + BVC 2(PC) + NEG R3 // caller expects negative errno MOVW R3, ret+40(FP) RET @@ -409,6 +413,8 @@ TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0 MOVD R7, -32(R4) SYSCALL $SYS_clone + BVC 2(PC) + NEG R3 // caller expects negative errno // In parent, return. CMP R3, $0 @@ -472,6 +478,8 @@ TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0 MOVD len+8(FP), R4 MOVD buf+16(FP), R5 SYSCALL $SYS_sched_getaffinity + BVC 2(PC) + NEG R3 // caller expects negative errno MOVW R3, ret+24(FP) RET @@ -479,6 +487,8 @@ TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0 TEXT runtime·epollcreate(SB),NOSPLIT|NOFRAME,$0 MOVW size+0(FP), R3 SYSCALL $SYS_epoll_create + BVC 2(PC) + NEG R3 // caller expects negative errno MOVW R3, ret+8(FP) RET @@ -486,6 +496,8 @@ TEXT runtime·epollcreate(SB),NOSPLIT|NOFRAME,$0 TEXT runtime·epollcreate1(SB),NOSPLIT|NOFRAME,$0 MOVW flags+0(FP), R3 SYSCALL $SYS_epoll_create1 + BVC 2(PC) + NEG R3 // caller expects negative errno MOVW R3, ret+8(FP) RET @@ -507,6 +519,8 @@ TEXT runtime·epollwait(SB),NOSPLIT|NOFRAME,$0 MOVW nev+16(FP), R5 MOVW timeout+20(FP), R6 SYSCALL $SYS_epoll_wait + BVC 2(PC) + NEG R3 // caller expects negative errno MOVW R3, ret+24(FP) RET -- cgit v1.3-5-g9baa From b800f202dc4d4281edd56ec40ea58f09dac8b730 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 3 Aug 2018 11:34:11 -0400 Subject: runtime: document assumption about wbBufFlush argument slots gcWriteBarrier and wbBufFlush assume that not writing to an argument variable is sufficient to not clobber the corresponding argument slot. This assumption lets us simplify the write barrier assembly code, speed up the flush path, and reduce the stack usage of the write barrier. But it is an assumption, so this CL documents it to make this clear. Alternatively, we could separate the register spill slots from the argument slots in the write barrier, but that loses the advantages above. On the other hand, it's extremely unlikely that we'll change the behavior of the compiler to start clobbering argument slots (if anything, we'd probably change it to *not* clobber argument slots even if you wrote to the arguments). Fixes #25512. Change-Id: Ib2cf29c0d90956ca02b997ef6e7fa56fc8044efe Reviewed-on: https://go-review.googlesource.com/127815 Reviewed-by: Cherry Zhang --- src/runtime/mwbbuf.go | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/mwbbuf.go b/src/runtime/mwbbuf.go index c02ccd8ab7..4df16d55b8 100644 --- a/src/runtime/mwbbuf.go +++ b/src/runtime/mwbbuf.go @@ -163,6 +163,13 @@ func wbBufFlush(dst *uintptr, src uintptr) { // Note: Every possible return from this function must reset // the buffer's next pointer to prevent buffer overflow. + // This *must not* modify its arguments because this + // function's argument slots do double duty in gcWriteBarrier + // as register spill slots. Currently, not modifying the + // arguments is sufficient to keep the spill slots unmodified + // (which seems unlikely to change since it costs little and + // helps with debugging). + if getg().m.dying > 0 { // We're going down. Not much point in write barriers // and this way we can allow write barriers in the -- cgit v1.3-5-g9baa From a0127c1921fdba2b4384d599407a3eedfe547803 Mon Sep 17 00:00:00 2001 From: Daniel Martí Date: Thu, 9 Aug 2018 17:25:13 +0100 Subject: runtime: fix TestGdbPythonCgo failure with ld.gold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See the added comment for the reasoning behind the workaround. Fixes #26868. Change-Id: Idede020ec88a49595dc233d9a1346b12691186f4 Reviewed-on: https://go-review.googlesource.com/128815 Run-TryBot: Daniel Martí TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/runtime/runtime-gdb_test.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 4733efba6d..d9c6f6d22a 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -162,7 +162,22 @@ func testGdbPython(t *testing.T, cgo bool) { args := []string{"-nx", "-q", "--batch", "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-ex", "set startup-with-shell off", - "-ex", "info auto-load python-scripts", + } + if cgo { + // When we build the cgo version of the program, the system's + // linker is used. Some external linkers, like GNU gold, + // compress the .debug_gdb_scripts into .zdebug_gdb_scripts. + // Until gold and gdb can work together, temporarily load the + // python script directly. + args = append(args, + "-ex", "source "+filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime-gdb.py"), + ) + } else { + args = append(args, + "-ex", "info auto-load python-scripts", + ) + } + args = append(args, "-ex", "set python print-stack full", "-ex", "br fmt.Println", "-ex", "run", @@ -193,7 +208,7 @@ func testGdbPython(t *testing.T, cgo bool) { "-ex", "goroutine 1 bt", "-ex", "echo END\n", filepath.Join(dir, "a.exe"), - } + ) got, _ := exec.Command("gdb", args...).CombinedOutput() t.Logf("gdb output: %s\n", got) -- cgit v1.3-5-g9baa From 469ada6ed9cbf921a268f867d3fde0f311532ff5 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sat, 28 Jul 2018 16:46:22 +0200 Subject: runtime: go fmt runtime2.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I29a6125c9ef285fc365c4e11ab158b79224ae333 Reviewed-on: https://go-review.googlesource.com/126602 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/runtime2.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index a3193b63c5..ad47d1275e 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -842,11 +842,11 @@ var ( lfenceBeforeRdtsc bool // Set in runtime.cpuinit. - support_erms bool - support_popcnt bool - support_sse2 bool - support_sse41 bool - arm64_support_atomics bool + support_erms bool + support_popcnt bool + support_sse2 bool + support_sse41 bool + arm64_support_atomics bool goarm uint8 // set by cmd/link on arm systems framepointer_enabled bool // set by cmd/link -- cgit v1.3-5-g9baa From a68b713ef600a14e15b59f5bac424be73e1acaf5 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 15 Aug 2018 16:21:00 -0700 Subject: runtime: load errno as signed 32-bit the function libc_errno returns a pointer to a signed-32 bit quantity, not a 64-bit quantity. Fixes #27004 Change-Id: I0623835ee34fd9655532251f096022a5accb58cd Reviewed-on: https://go-review.googlesource.com/129475 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/sys_darwin_amd64.s | 4 ++-- src/runtime/sys_darwin_arm64.s | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s index 2a2e7379ca..db74352613 100644 --- a/src/runtime/sys_darwin_amd64.s +++ b/src/runtime/sys_darwin_amd64.s @@ -306,7 +306,7 @@ TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0 CMPQ AX, $-1 JNE ok CALL libc_error(SB) - MOVQ (AX), DX // errno + MOVLQSX (AX), DX // errno XORL AX, AX ok: MOVQ AX, 32(BX) @@ -371,7 +371,7 @@ TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0 CMPQ AX, $-1 JNE ok CALL libc_error(SB) - MOVQ (AX), AX // errno + MOVLQSX (AX), AX // errno NEGQ AX // caller wants it as a negative error code ok: POPQ BP diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s index 4f9d0b8d58..d7ba116b84 100644 --- a/src/runtime/sys_darwin_arm64.s +++ b/src/runtime/sys_darwin_arm64.s @@ -70,7 +70,7 @@ TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0 CMP R0, R2 BNE ok BL libc_error(SB) - MOVD (R0), R1 + MOVW (R0), R1 MOVD $0, R0 ok: MOVD R0, 32(R19) // ret 1 p @@ -277,7 +277,7 @@ TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0 CMP R0, R2 BNE ok BL libc_error(SB) - MOVD (R0), R0 // errno + MOVW (R0), R0 // errno NEG R0, R0 // caller wants it as a negative error code ok: RET -- cgit v1.3-5-g9baa From 3578918b6614effaeaa581687d810b74e342e0f8 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 9 Aug 2018 23:47:37 -0400 Subject: runtime: replace manually managed G dequeues with a type There are two manually managed G dequeues. Abstract these both into a shared gQueue type. This also introduces a gList type, which we'll use to replace several manually-managed G lists in follow-up CLs. This makes the code more readable and maintainable. gcFlushBgCredit in particular becomes much easier to follow. It also makes it easier to introduce more G queues in the future. Finally, the gList type clearly distinguishes between lists of Gs and individual Gs; currently both are represented by a *g, which can easily lead to confusion and bugs. Change-Id: Ic7798841b405d311fc8b6aa5a958ffa4c7993c6c Reviewed-on: https://go-review.googlesource.com/129396 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/mgc.go | 4 +- src/runtime/mgcmark.go | 48 +++++------------ src/runtime/proc.go | 141 ++++++++++++++++++++++++++++++++++++++---------- src/runtime/runtime2.go | 3 +- 4 files changed, 128 insertions(+), 68 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 6a3219de73..7d4ba9f9cd 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1023,8 +1023,8 @@ var work struct { // there was neither enough credit to steal or enough work to // do. assistQueue struct { - lock mutex - head, tail guintptr + lock mutex + q gQueue } // sweepWaiters is a list of blocked goroutines to wake when diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index e8cfdce4fc..7850f86bb2 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -602,9 +602,7 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // new assists from going to sleep after this point. func gcWakeAllAssists() { lock(&work.assistQueue.lock) - injectglist(work.assistQueue.head.ptr()) - work.assistQueue.head.set(nil) - work.assistQueue.tail.set(nil) + injectglist(work.assistQueue.q.popList().head.ptr()) unlock(&work.assistQueue.lock) } @@ -625,24 +623,17 @@ func gcParkAssist() bool { } gp := getg() - oldHead, oldTail := work.assistQueue.head, work.assistQueue.tail - if oldHead == 0 { - work.assistQueue.head.set(gp) - } else { - oldTail.ptr().schedlink.set(gp) - } - work.assistQueue.tail.set(gp) - gp.schedlink.set(nil) + oldList := work.assistQueue.q + work.assistQueue.q.pushBack(gp) // Recheck for background credit now that this G is in // the queue, but can still back out. This avoids a // race in case background marking has flushed more // credit since we checked above. if atomic.Loadint64(&gcController.bgScanCredit) > 0 { - work.assistQueue.head = oldHead - work.assistQueue.tail = oldTail - if oldTail != 0 { - oldTail.ptr().schedlink.set(nil) + work.assistQueue.q = oldList + if oldList.tail != 0 { + oldList.tail.ptr().schedlink.set(nil) } unlock(&work.assistQueue.lock) return false @@ -663,7 +654,7 @@ func gcParkAssist() bool { // //go:nowritebarrierrec func gcFlushBgCredit(scanWork int64) { - if work.assistQueue.head == 0 { + if work.assistQueue.q.empty() { // Fast path; there are no blocked assists. There's a // small window here where an assist may add itself to // the blocked queue and park. If that happens, we'll @@ -675,23 +666,21 @@ func gcFlushBgCredit(scanWork int64) { scanBytes := int64(float64(scanWork) * gcController.assistBytesPerWork) lock(&work.assistQueue.lock) - gp := work.assistQueue.head.ptr() - for gp != nil && scanBytes > 0 { + for !work.assistQueue.q.empty() && scanBytes > 0 { + gp := work.assistQueue.q.pop() // Note that gp.gcAssistBytes is negative because gp // is in debt. Think carefully about the signs below. if scanBytes+gp.gcAssistBytes >= 0 { // Satisfy this entire assist debt. scanBytes += gp.gcAssistBytes gp.gcAssistBytes = 0 - xgp := gp - gp = gp.schedlink.ptr() - // It's important that we *not* put xgp in + // It's important that we *not* put gp in // runnext. Otherwise, it's possible for user // code to exploit the GC worker's high // scheduler priority to get itself always run // before other goroutines and always in the // fresh quantum started by GC. - ready(xgp, 0, false) + ready(gp, 0, false) } else { // Partially satisfy this assist. gp.gcAssistBytes += scanBytes @@ -700,23 +689,10 @@ func gcFlushBgCredit(scanWork int64) { // back of the queue so that large assists // can't clog up the assist queue and // substantially delay small assists. - xgp := gp - gp = gp.schedlink.ptr() - if gp == nil { - // gp is the only assist in the queue. - gp = xgp - } else { - xgp.schedlink = 0 - work.assistQueue.tail.ptr().schedlink.set(xgp) - work.assistQueue.tail.set(xgp) - } + work.assistQueue.q.pushBack(gp) break } } - work.assistQueue.head.set(gp) - if gp == nil { - work.assistQueue.tail.set(nil) - } if scanBytes > 0 { // Convert from scan bytes back to work. diff --git a/src/runtime/proc.go b/src/runtime/proc.go index f82014eb92..fbb1ce1750 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -4667,13 +4667,7 @@ func mget() *m { // May run during STW, so write barriers are not allowed. //go:nowritebarrierrec func globrunqput(gp *g) { - gp.schedlink = 0 - if sched.runqtail != 0 { - sched.runqtail.ptr().schedlink.set(gp) - } else { - sched.runqhead.set(gp) - } - sched.runqtail.set(gp) + sched.runq.pushBack(gp) sched.runqsize++ } @@ -4682,25 +4676,17 @@ func globrunqput(gp *g) { // May run during STW, so write barriers are not allowed. //go:nowritebarrierrec func globrunqputhead(gp *g) { - gp.schedlink = sched.runqhead - sched.runqhead.set(gp) - if sched.runqtail == 0 { - sched.runqtail.set(gp) - } + sched.runq.push(gp) sched.runqsize++ } // Put a batch of runnable goroutines on the global runnable queue. +// This clears *batch. // Sched must be locked. -func globrunqputbatch(ghead *g, gtail *g, n int32) { - gtail.schedlink = 0 - if sched.runqtail != 0 { - sched.runqtail.ptr().schedlink.set(ghead) - } else { - sched.runqhead.set(ghead) - } - sched.runqtail.set(gtail) +func globrunqputbatch(batch *gQueue, n int32) { + sched.runq.pushBackAll(*batch) sched.runqsize += n + *batch = gQueue{} } // Try get a batch of G's from the global runnable queue. @@ -4722,16 +4708,11 @@ func globrunqget(_p_ *p, max int32) *g { } sched.runqsize -= n - if sched.runqsize == 0 { - sched.runqtail = 0 - } - gp := sched.runqhead.ptr() - sched.runqhead = gp.schedlink + gp := sched.runq.pop() n-- for ; n > 0; n-- { - gp1 := sched.runqhead.ptr() - sched.runqhead = gp1.schedlink + gp1 := sched.runq.pop() runqput(_p_, gp1, false) } return gp @@ -4859,10 +4840,13 @@ func runqputslow(_p_ *p, gp *g, h, t uint32) bool { for i := uint32(0); i < n; i++ { batch[i].schedlink.set(batch[i+1]) } + var q gQueue + q.head.set(batch[0]) + q.tail.set(batch[n]) // Now put the batch on global queue. lock(&sched.lock) - globrunqputbatch(batch[0], batch[n], int32(n+1)) + globrunqputbatch(&q, int32(n+1)) unlock(&sched.lock) return true } @@ -4974,6 +4958,107 @@ func runqsteal(_p_, p2 *p, stealRunNextG bool) *g { return gp } +// A gQueue is a dequeue of Gs linked through g.schedlink. A G can only +// be on one gQueue or gList at a time. +type gQueue struct { + head guintptr + tail guintptr +} + +// empty returns true if q is empty. +func (q *gQueue) empty() bool { + return q.head == 0 +} + +// push adds gp to the head of q. +func (q *gQueue) push(gp *g) { + gp.schedlink = q.head + q.head.set(gp) + if q.tail == 0 { + q.tail.set(gp) + } +} + +// pushBack adds gp to the tail of q. +func (q *gQueue) pushBack(gp *g) { + gp.schedlink = 0 + if q.tail != 0 { + q.tail.ptr().schedlink.set(gp) + } else { + q.head.set(gp) + } + q.tail.set(gp) +} + +// pushBackAll adds all Gs in l2 to the tail of q. After this q2 must +// not be used. +func (q *gQueue) pushBackAll(q2 gQueue) { + if q2.tail == 0 { + return + } + q2.tail.ptr().schedlink = 0 + if q.tail != 0 { + q.tail.ptr().schedlink = q2.head + } else { + q.head = q2.head + } + q.tail = q2.tail +} + +// pop removes and returns the head of queue q. It returns nil if +// q is empty. +func (q *gQueue) pop() *g { + gp := q.head.ptr() + if gp != nil { + q.head = gp.schedlink + if q.head == 0 { + q.tail = 0 + } + } + return gp +} + +// popList takes all Gs in q and returns them as a gList. +func (q *gQueue) popList() gList { + stack := gList{q.head} + *q = gQueue{} + return stack +} + +// A gList is a list of Gs linked through g.schedlink. A G can only be +// on one gQueue or gList at a time. +type gList struct { + head guintptr +} + +// empty returns true if l is empty. +func (l *gList) empty() bool { + return l.head == 0 +} + +// push adds gp to the head of l. +func (l *gList) push(gp *g) { + gp.schedlink = l.head + l.head.set(gp) +} + +// pushAll prepends all Gs in q to l. +func (l *gList) pushAll(q gQueue) { + if !q.empty() { + q.tail.ptr().schedlink = l.head + l.head = q.head + } +} + +// pop removes and returns the head of l. If l is empty, it returns nil. +func (l *gList) pop() *g { + gp := l.head.ptr() + if gp != nil { + l.head = gp.schedlink + } + return gp +} + //go:linkname setMaxThreads runtime/debug.setMaxThreads func setMaxThreads(in int) (out int) { lock(&sched.lock) diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index ad47d1275e..5bd37e49be 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -574,8 +574,7 @@ type schedt struct { nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go. // Global runnable queue. - runqhead guintptr - runqtail guintptr + runq gQueue runqsize int32 // Global cache of dead G's. -- cgit v1.3-5-g9baa From de990545c3ce65926491c123bb2536168cd21cf3 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 10 Aug 2018 00:09:00 -0400 Subject: runtime: use gList in netpoll netpoll is perhaps one of the most confusing uses of G lists currently since it passes around many lists as bare *g values right now. Switching to gList makes it much clearer what's an individual g and what's a list. Change-Id: I8d8993c4967c5bae049c7a094aad3a657928ba6c Reviewed-on: https://go-review.googlesource.com/129397 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/netpoll.go | 20 +++++++++----------- src/runtime/netpoll_epoll.go | 12 ++++++------ src/runtime/netpoll_fake.go | 4 ++-- src/runtime/netpoll_kqueue.go | 12 ++++++------ src/runtime/netpoll_solaris.go | 12 ++++++------ src/runtime/netpoll_stub.go | 4 ++-- src/runtime/netpoll_windows.go | 22 +++++++++++----------- src/runtime/proc.go | 29 +++++++++++++++-------------- 8 files changed, 57 insertions(+), 58 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index c8fb95d3aa..f9c422650a 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -289,24 +289,22 @@ func poll_runtime_pollUnblock(pd *pollDesc) { } } -// make pd ready, newly runnable goroutines (if any) are returned in rg/wg +// make pd ready, newly runnable goroutines (if any) are added to toRun. // May run during STW, so write barriers are not allowed. //go:nowritebarrier -func netpollready(gpp *guintptr, pd *pollDesc, mode int32) { - var rg, wg guintptr +func netpollready(toRun *gList, pd *pollDesc, mode int32) { + var rg, wg *g if mode == 'r' || mode == 'r'+'w' { - rg.set(netpollunblock(pd, 'r', true)) + rg = netpollunblock(pd, 'r', true) } if mode == 'w' || mode == 'r'+'w' { - wg.set(netpollunblock(pd, 'w', true)) + wg = netpollunblock(pd, 'w', true) } - if rg != 0 { - rg.ptr().schedlink = *gpp - *gpp = rg + if rg != nil { + toRun.push(rg) } - if wg != 0 { - wg.ptr().schedlink = *gpp - *gpp = wg + if wg != nil { + toRun.push(wg) } } diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go index 1908220ebb..f764d6ff7c 100644 --- a/src/runtime/netpoll_epoll.go +++ b/src/runtime/netpoll_epoll.go @@ -58,9 +58,9 @@ func netpollarm(pd *pollDesc, mode int) { // polls for ready network connections // returns list of goroutines that become runnable -func netpoll(block bool) *g { +func netpoll(block bool) gList { if epfd == -1 { - return nil + return gList{} } waitms := int32(-1) if !block { @@ -76,7 +76,7 @@ retry: } goto retry } - var gp guintptr + var toRun gList for i := int32(0); i < n; i++ { ev := &events[i] if ev.events == 0 { @@ -92,11 +92,11 @@ retry: if mode != 0 { pd := *(**pollDesc)(unsafe.Pointer(&ev.data)) - netpollready(&gp, pd, mode) + netpollready(&toRun, pd, mode) } } - if block && gp == 0 { + if block && toRun.empty() { goto retry } - return gp.ptr() + return toRun } diff --git a/src/runtime/netpoll_fake.go b/src/runtime/netpoll_fake.go index aab18dc846..5b1a63a878 100644 --- a/src/runtime/netpoll_fake.go +++ b/src/runtime/netpoll_fake.go @@ -27,6 +27,6 @@ func netpollclose(fd uintptr) int32 { func netpollarm(pd *pollDesc, mode int) { } -func netpoll(block bool) *g { - return nil +func netpoll(block bool) gList { + return gList{} } diff --git a/src/runtime/netpoll_kqueue.go b/src/runtime/netpoll_kqueue.go index 0f73bf385e..fdaa1cd80d 100644 --- a/src/runtime/netpoll_kqueue.go +++ b/src/runtime/netpoll_kqueue.go @@ -59,9 +59,9 @@ func netpollarm(pd *pollDesc, mode int) { // Polls for ready network connections. // Returns list of goroutines that become runnable. -func netpoll(block bool) *g { +func netpoll(block bool) gList { if kq == -1 { - return nil + return gList{} } var tp *timespec var ts timespec @@ -78,7 +78,7 @@ retry: } goto retry } - var gp guintptr + var toRun gList for i := 0; i < int(n); i++ { ev := &events[i] var mode int32 @@ -102,11 +102,11 @@ retry: mode += 'w' } if mode != 0 { - netpollready(&gp, (*pollDesc)(unsafe.Pointer(ev.udata)), mode) + netpollready(&toRun, (*pollDesc)(unsafe.Pointer(ev.udata)), mode) } } - if block && gp == 0 { + if block && toRun.empty() { goto retry } - return gp.ptr() + return toRun } diff --git a/src/runtime/netpoll_solaris.go b/src/runtime/netpoll_solaris.go index 853e5f63e3..6bd484afaa 100644 --- a/src/runtime/netpoll_solaris.go +++ b/src/runtime/netpoll_solaris.go @@ -180,9 +180,9 @@ func netpollarm(pd *pollDesc, mode int) { // polls for ready network connections // returns list of goroutines that become runnable -func netpoll(block bool) *g { +func netpoll(block bool) gList { if portfd == -1 { - return nil + return gList{} } var wait *timespec @@ -202,7 +202,7 @@ retry: goto retry } - var gp guintptr + var toRun gList for i := 0; i < int(n); i++ { ev := &events[i] @@ -233,12 +233,12 @@ retry: } if mode != 0 { - netpollready(&gp, pd, mode) + netpollready(&toRun, pd, mode) } } - if block && gp == 0 { + if block && toRun.empty() { goto retry } - return gp.ptr() + return toRun } diff --git a/src/runtime/netpoll_stub.go b/src/runtime/netpoll_stub.go index a4d6b4608a..f585333579 100644 --- a/src/runtime/netpoll_stub.go +++ b/src/runtime/netpoll_stub.go @@ -10,10 +10,10 @@ var netpollWaiters uint32 // Polls for ready network connections. // Returns list of goroutines that become runnable. -func netpoll(block bool) (gp *g) { +func netpoll(block bool) gList { // Implementation for platforms that do not support // integrated network poller. - return + return gList{} } func netpollinited() bool { diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go index 134071f5e3..07ef15ce2f 100644 --- a/src/runtime/netpoll_windows.go +++ b/src/runtime/netpoll_windows.go @@ -63,17 +63,17 @@ func netpollarm(pd *pollDesc, mode int) { // Polls for completed network IO. // Returns list of goroutines that become runnable. -func netpoll(block bool) *g { +func netpoll(block bool) gList { var entries [64]overlappedEntry var wait, qty, key, flags, n, i uint32 var errno int32 var op *net_op - var gp guintptr + var toRun gList mp := getg().m if iocphandle == _INVALID_HANDLE_VALUE { - return nil + return gList{} } wait = 0 if block { @@ -92,7 +92,7 @@ retry: mp.blocked = false errno = int32(getlasterror()) if !block && errno == _WAIT_TIMEOUT { - return nil + return gList{} } println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")") throw("runtime: netpoll failed") @@ -105,7 +105,7 @@ retry: if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 { errno = int32(getlasterror()) } - handlecompletion(&gp, op, errno, qty) + handlecompletion(&toRun, op, errno, qty) } } else { op = nil @@ -118,7 +118,7 @@ retry: mp.blocked = false errno = int32(getlasterror()) if !block && errno == _WAIT_TIMEOUT { - return nil + return gList{} } if op == nil { println("runtime: GetQueuedCompletionStatus failed (errno=", errno, ")") @@ -127,15 +127,15 @@ retry: // dequeued failed IO packet, so report that } mp.blocked = false - handlecompletion(&gp, op, errno, qty) + handlecompletion(&toRun, op, errno, qty) } - if block && gp == 0 { + if block && toRun.empty() { goto retry } - return gp.ptr() + return toRun } -func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) { +func handlecompletion(toRun *gList, op *net_op, errno int32, qty uint32) { if op == nil { println("runtime: GetQueuedCompletionStatus returned op == nil") throw("runtime: netpoll failed") @@ -147,5 +147,5 @@ func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) { } op.errno = errno op.qty = qty - netpollready(gpp, op.pd, mode) + netpollready(toRun, op.pd, mode) } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index fbb1ce1750..2a780e49ee 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1148,8 +1148,8 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 { _g_.m.locks++ // disable preemption because it can be holding p in a local var if netpollinited() { - gp := netpoll(false) // non-blocking - injectglist(gp) + list := netpoll(false) // non-blocking + injectglist(list.head.ptr()) } add := needaddgcproc() lock(&sched.lock) @@ -2312,9 +2312,9 @@ top: // not set lastpoll yet), this thread will do blocking netpoll below // anyway. if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 { - if gp := netpoll(false); gp != nil { // non-blocking - // netpoll returns list of goroutines linked by schedlink. - injectglist(gp.schedlink.ptr()) + if list := netpoll(false); !list.empty() { // non-blocking + gp := list.pop() + injectglist(list.head.ptr()) casgstatus(gp, _Gwaiting, _Grunnable) if trace.enabled { traceGoUnpark(gp, 0) @@ -2466,22 +2466,23 @@ stop: if _g_.m.spinning { throw("findrunnable: netpoll with spinning") } - gp := netpoll(true) // block until new work is available + list := netpoll(true) // block until new work is available atomic.Store64(&sched.lastpoll, uint64(nanotime())) - if gp != nil { + if !list.empty() { lock(&sched.lock) _p_ = pidleget() unlock(&sched.lock) if _p_ != nil { acquirep(_p_) - injectglist(gp.schedlink.ptr()) + gp := list.pop() + injectglist(list.head.ptr()) casgstatus(gp, _Gwaiting, _Grunnable) if trace.enabled { traceGoUnpark(gp, 0) } return gp, false } - injectglist(gp) + injectglist(list.head.ptr()) } } stopm() @@ -2501,8 +2502,8 @@ func pollWork() bool { return true } if netpollinited() && atomic.Load(&netpollWaiters) > 0 && sched.lastpoll != 0 { - if gp := netpoll(false); gp != nil { - injectglist(gp) + if list := netpoll(false); !list.empty() { + injectglist(list.head.ptr()) return true } } @@ -4387,8 +4388,8 @@ func sysmon() { now := nanotime() if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now { atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now)) - gp := netpoll(false) // non-blocking - returns list of goroutines - if gp != nil { + list := netpoll(false) // non-blocking - returns list of goroutines + if !list.empty() { // Need to decrement number of idle locked M's // (pretending that one more is running) before injectglist. // Otherwise it can lead to the following situation: @@ -4397,7 +4398,7 @@ func sysmon() { // observes that there is no work to do and no other running M's // and reports deadlock. incidlelocked(-1) - injectglist(gp) + injectglist(list.head.ptr()) incidlelocked(1) } } -- cgit v1.3-5-g9baa From 8e8cc9db0fe3c30852d4fc9ad82c9922bff7d26f Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 10 Aug 2018 10:19:03 -0400 Subject: runtime: use gList for gfree lists Change-Id: I3d21587e02264fe5da1cc38d98779facfa09b927 Reviewed-on: https://go-review.googlesource.com/129398 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/mgcmark.go | 25 ++++++----- src/runtime/proc.go | 117 ++++++++++++++++++++++-------------------------- src/runtime/runtime2.go | 16 ++++--- 3 files changed, 77 insertions(+), 81 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 7850f86bb2..d6ee7ff6fa 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -302,26 +302,27 @@ func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) { //TODO go:nowritebarrier func markrootFreeGStacks() { // Take list of dead Gs with stacks. - lock(&sched.gflock) - list := sched.gfreeStack - sched.gfreeStack = nil - unlock(&sched.gflock) - if list == nil { + lock(&sched.gFree.lock) + list := sched.gFree.stack + sched.gFree.stack = gList{} + unlock(&sched.gFree.lock) + if list.empty() { return } // Free stacks. - tail := list - for gp := list; gp != nil; gp = gp.schedlink.ptr() { + q := gQueue{list.head, list.head} + for gp := list.head.ptr(); gp != nil; gp = gp.schedlink.ptr() { shrinkstack(gp) - tail = gp + // Manipulate the queue directly since the Gs are + // already all linked the right way. + q.tail.set(gp) } // Put Gs back on the free list. - lock(&sched.gflock) - tail.schedlink.set(sched.gfreeNoStack) - sched.gfreeNoStack = list - unlock(&sched.gflock) + lock(&sched.gFree.lock) + sched.gFree.noStack.pushAll(q) + unlock(&sched.gFree.lock) } // markrootSpans marks roots for one shard of work.spans. diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 2a780e49ee..5cb7f13016 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -3471,25 +3471,21 @@ func gfput(_p_ *p, gp *g) { gp.stackguard0 = 0 } - gp.schedlink.set(_p_.gfree) - _p_.gfree = gp - _p_.gfreecnt++ - if _p_.gfreecnt >= 64 { - lock(&sched.gflock) - for _p_.gfreecnt >= 32 { - _p_.gfreecnt-- - gp = _p_.gfree - _p_.gfree = gp.schedlink.ptr() + _p_.gFree.push(gp) + _p_.gFree.n++ + if _p_.gFree.n >= 64 { + lock(&sched.gFree.lock) + for _p_.gFree.n >= 32 { + _p_.gFree.n-- + gp = _p_.gFree.pop() if gp.stack.lo == 0 { - gp.schedlink.set(sched.gfreeNoStack) - sched.gfreeNoStack = gp + sched.gFree.noStack.push(gp) } else { - gp.schedlink.set(sched.gfreeStack) - sched.gfreeStack = gp + sched.gFree.stack.push(gp) } - sched.ngfree++ + sched.gFree.n++ } - unlock(&sched.gflock) + unlock(&sched.gFree.lock) } } @@ -3497,44 +3493,42 @@ func gfput(_p_ *p, gp *g) { // If local list is empty, grab a batch from global list. func gfget(_p_ *p) *g { retry: - gp := _p_.gfree - if gp == nil && (sched.gfreeStack != nil || sched.gfreeNoStack != nil) { - lock(&sched.gflock) - for _p_.gfreecnt < 32 { - if sched.gfreeStack != nil { - // Prefer Gs with stacks. - gp = sched.gfreeStack - sched.gfreeStack = gp.schedlink.ptr() - } else if sched.gfreeNoStack != nil { - gp = sched.gfreeNoStack - sched.gfreeNoStack = gp.schedlink.ptr() - } else { - break + if _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) { + lock(&sched.gFree.lock) + // Move a batch of free Gs to the P. + for _p_.gFree.n < 32 { + // Prefer Gs with stacks. + gp := sched.gFree.stack.pop() + if gp == nil { + gp = sched.gFree.noStack.pop() + if gp == nil { + break + } } - _p_.gfreecnt++ - sched.ngfree-- - gp.schedlink.set(_p_.gfree) - _p_.gfree = gp + sched.gFree.n-- + _p_.gFree.push(gp) + _p_.gFree.n++ } - unlock(&sched.gflock) + unlock(&sched.gFree.lock) goto retry } - if gp != nil { - _p_.gfree = gp.schedlink.ptr() - _p_.gfreecnt-- - if gp.stack.lo == 0 { - // Stack was deallocated in gfput. Allocate a new one. - systemstack(func() { - gp.stack = stackalloc(_FixedStack) - }) - gp.stackguard0 = gp.stack.lo + _StackGuard - } else { - if raceenabled { - racemalloc(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo) - } - if msanenabled { - msanmalloc(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo) - } + gp := _p_.gFree.pop() + if gp == nil { + return nil + } + _p_.gFree.n-- + if gp.stack.lo == 0 { + // Stack was deallocated in gfput. Allocate a new one. + systemstack(func() { + gp.stack = stackalloc(_FixedStack) + }) + gp.stackguard0 = gp.stack.lo + _StackGuard + } else { + if raceenabled { + racemalloc(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo) + } + if msanenabled { + msanmalloc(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo) } } return gp @@ -3542,21 +3536,18 @@ retry: // Purge all cached G's from gfree list to the global list. func gfpurge(_p_ *p) { - lock(&sched.gflock) - for _p_.gfreecnt != 0 { - _p_.gfreecnt-- - gp := _p_.gfree - _p_.gfree = gp.schedlink.ptr() + lock(&sched.gFree.lock) + for !_p_.gFree.empty() { + gp := _p_.gFree.pop() + _p_.gFree.n-- if gp.stack.lo == 0 { - gp.schedlink.set(sched.gfreeNoStack) - sched.gfreeNoStack = gp + sched.gFree.noStack.push(gp) } else { - gp.schedlink.set(sched.gfreeStack) - sched.gfreeStack = gp + sched.gFree.stack.push(gp) } - sched.ngfree++ + sched.gFree.n++ } - unlock(&sched.gflock) + unlock(&sched.gFree.lock) } // Breakpoint executes a breakpoint trap. @@ -3669,9 +3660,9 @@ func badunlockosthread() { } func gcount() int32 { - n := int32(allglen) - sched.ngfree - int32(atomic.Load(&sched.ngsys)) + n := int32(allglen) - sched.gFree.n - int32(atomic.Load(&sched.ngsys)) for _, _p_ := range allp { - n -= _p_.gfreecnt + n -= _p_.gFree.n } // All these variables can be changed concurrently, so the result can be inconsistent. @@ -4581,7 +4572,7 @@ func schedtrace(detailed bool) { if mp != nil { id = mp.id } - print(" P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gfreecnt, "\n") + print(" P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gFree.n, "\n") } else { // In non-detailed mode format lengths of per-P run queues as: // [len1 len2 len3 len4] diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 5bd37e49be..bbbe1ee852 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -506,8 +506,10 @@ type p struct { runnext guintptr // Available G's (status == Gdead) - gfree *g - gfreecnt int32 + gFree struct { + gList + n int32 + } sudogcache []*sudog sudogbuf [128]*sudog @@ -578,10 +580,12 @@ type schedt struct { runqsize int32 // Global cache of dead G's. - gflock mutex - gfreeStack *g - gfreeNoStack *g - ngfree int32 + gFree struct { + lock mutex + stack gList // Gs with stacks + noStack gList // Gs without stacks + n int32 + } // Central cache of sudog structs. sudoglock mutex -- cgit v1.3-5-g9baa From d02169d380a762863147d57b7665cb31de0a4ee7 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 10 Aug 2018 10:28:44 -0400 Subject: runtime: use gList for work.sweepWaiters Change-Id: Ibae474a5c9a3528a042ddf19ddb4a88913a87606 Reviewed-on: https://go-review.googlesource.com/129399 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/mgc.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 7d4ba9f9cd..2d67c4d8c4 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1031,7 +1031,7 @@ var work struct { // we transition from mark termination to sweep. sweepWaiters struct { lock mutex - head guintptr + list gList } // cycles is the number of completed GC cycles, where a GC @@ -1146,9 +1146,7 @@ func gcWaitOnMark(n uint32) { // Wait until sweep termination, mark, and mark // termination of cycle N complete. - gp := getg() - gp.schedlink = work.sweepWaiters.head - work.sweepWaiters.head.set(gp) + work.sweepWaiters.list.push(getg()) goparkunlock(&work.sweepWaiters.lock, waitReasonWaitForGCCycle, traceEvGoBlock, 1) } } @@ -1632,8 +1630,8 @@ func gcMarkTermination(nextTriggerRatio float64) { // Bump GC cycle count and wake goroutines waiting on sweep. lock(&work.sweepWaiters.lock) memstats.numgc++ - injectglist(work.sweepWaiters.head.ptr()) - work.sweepWaiters.head = 0 + injectglist(work.sweepWaiters.list.head.ptr()) + work.sweepWaiters.list = gList{} unlock(&work.sweepWaiters.lock) // Finish the current heap profiling cycle and start a new -- cgit v1.3-5-g9baa From 083938df14c0602a44d055bd3f4427980cf51c27 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 10 Aug 2018 10:33:05 -0400 Subject: runtime: use gList for injectglist Change-Id: Id5af75eaaf41f43bc6baa6d3fe2b852a2f93bb6f Reviewed-on: https://go-review.googlesource.com/129400 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/mgc.go | 3 +-- src/runtime/mgcmark.go | 3 ++- src/runtime/proc.go | 31 ++++++++++++++++--------------- 3 files changed, 19 insertions(+), 18 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 2d67c4d8c4..e4c0f5a587 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1630,8 +1630,7 @@ func gcMarkTermination(nextTriggerRatio float64) { // Bump GC cycle count and wake goroutines waiting on sweep. lock(&work.sweepWaiters.lock) memstats.numgc++ - injectglist(work.sweepWaiters.list.head.ptr()) - work.sweepWaiters.list = gList{} + injectglist(&work.sweepWaiters.list) unlock(&work.sweepWaiters.lock) // Finish the current heap profiling cycle and start a new diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index d6ee7ff6fa..69ff895512 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -603,7 +603,8 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // new assists from going to sleep after this point. func gcWakeAllAssists() { lock(&work.assistQueue.lock) - injectglist(work.assistQueue.q.popList().head.ptr()) + list := work.assistQueue.q.popList() + injectglist(&list) unlock(&work.assistQueue.lock) } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 5cb7f13016..7875b38e2e 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1149,7 +1149,7 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 { _g_.m.locks++ // disable preemption because it can be holding p in a local var if netpollinited() { list := netpoll(false) // non-blocking - injectglist(list.head.ptr()) + injectglist(&list) } add := needaddgcproc() lock(&sched.lock) @@ -2314,7 +2314,7 @@ top: if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 { if list := netpoll(false); !list.empty() { // non-blocking gp := list.pop() - injectglist(list.head.ptr()) + injectglist(&list) casgstatus(gp, _Gwaiting, _Grunnable) if trace.enabled { traceGoUnpark(gp, 0) @@ -2475,14 +2475,14 @@ stop: if _p_ != nil { acquirep(_p_) gp := list.pop() - injectglist(list.head.ptr()) + injectglist(&list) casgstatus(gp, _Gwaiting, _Grunnable) if trace.enabled { traceGoUnpark(gp, 0) } return gp, false } - injectglist(list.head.ptr()) + injectglist(&list) } } stopm() @@ -2503,7 +2503,7 @@ func pollWork() bool { } if netpollinited() && atomic.Load(&netpollWaiters) > 0 && sched.lastpoll != 0 { if list := netpoll(false); !list.empty() { - injectglist(list.head.ptr()) + injectglist(&list) return true } } @@ -2528,22 +2528,21 @@ func resetspinning() { } } -// Injects the list of runnable G's into the scheduler. +// Injects the list of runnable G's into the scheduler and clears glist. // Can run concurrently with GC. -func injectglist(glist *g) { - if glist == nil { +func injectglist(glist *gList) { + if glist.empty() { return } if trace.enabled { - for gp := glist; gp != nil; gp = gp.schedlink.ptr() { + for gp := glist.head.ptr(); gp != nil; gp = gp.schedlink.ptr() { traceGoUnpark(gp, 0) } } lock(&sched.lock) var n int - for n = 0; glist != nil; n++ { - gp := glist - glist = gp.schedlink.ptr() + for n = 0; !glist.empty(); n++ { + gp := glist.pop() casgstatus(gp, _Gwaiting, _Grunnable) globrunqput(gp) } @@ -2551,6 +2550,7 @@ func injectglist(glist *g) { for ; n != 0 && sched.npidle != 0; n-- { startm(nil, false) } + *glist = gList{} } // One round of scheduler: find a runnable goroutine and execute it. @@ -4389,7 +4389,7 @@ func sysmon() { // observes that there is no work to do and no other running M's // and reports deadlock. incidlelocked(-1) - injectglist(list.head.ptr()) + injectglist(&list) incidlelocked(1) } } @@ -4404,8 +4404,9 @@ func sysmon() { if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 { lock(&forcegc.lock) forcegc.idle = 0 - forcegc.g.schedlink = 0 - injectglist(forcegc.g) + var list gList + list.push(forcegc.g) + injectglist(&list) unlock(&forcegc.lock) } // scavenge heap once in a while -- cgit v1.3-5-g9baa From a034f310b040a4252843683f59555ded07016eae Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 10 Aug 2018 10:34:41 -0400 Subject: runtime: use gList in closechan Change-Id: I8148eb17fe9f2cbb659c35d84cdd212b46dc23bf Reviewed-on: https://go-review.googlesource.com/129401 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- src/runtime/chan.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/chan.go b/src/runtime/chan.go index ce71cee4c5..615643e6a6 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -343,7 +343,7 @@ func closechan(c *hchan) { c.closed = 1 - var glist *g + var glist gList // release all readers for { @@ -363,8 +363,7 @@ func closechan(c *hchan) { if raceenabled { raceacquireg(gp, unsafe.Pointer(c)) } - gp.schedlink.set(glist) - glist = gp + glist.push(gp) } // release all writers (they will panic) @@ -382,15 +381,13 @@ func closechan(c *hchan) { if raceenabled { raceacquireg(gp, unsafe.Pointer(c)) } - gp.schedlink.set(glist) - glist = gp + glist.push(gp) } unlock(&c.lock) // Ready all Gs now that we've dropped the channel lock. - for glist != nil { - gp := glist - glist = glist.schedlink.ptr() + for !glist.empty() { + gp := glist.pop() gp.schedlink = 0 goready(gp, 3) } -- cgit v1.3-5-g9baa From 1ba74448f25fa9e69c4f9ed155dd43eb496cfa70 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Mon, 20 Aug 2018 23:45:34 +0700 Subject: runtime: document all possible values for GOOS and GOARCH The updated list is taken from "src/go/build/syslist.go". Reason: one should not do web search to know the possible values of GOOS and GOARCH. The search result point to stackoverflow page which reference the above source and documentation on installation page [1]. It should available offline (as in local godoc), as part of package documentation. [1] https://golang.org/doc/install/source#environment Change-Id: I736804b8ef4dc11e0260fa862999212ab3f7b3fd Reviewed-on: https://go-review.googlesource.com/129935 Reviewed-by: Brad Fitzpatrick --- src/runtime/extern.go | 1 + 1 file changed, 1 insertion(+) (limited to 'src/runtime') diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 7171b139c3..2788bd354b 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -238,6 +238,7 @@ func Version() string { // GOOS is the running program's operating system target: // one of darwin, freebsd, linux, and so on. +// To view possible combinations of GOOS and GOARCH, run "go tool dist list". const GOOS string = sys.GOOS // GOARCH is the running program's architecture target: -- cgit v1.3-5-g9baa From cd7cb86d8e56d28bfe9bd522baa44c95127028d7 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 8 Aug 2018 17:26:23 -0700 Subject: runtime: don't use linkname to refer to internal/cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runtime package already imports the internal/cpu package, so there is no reason for it to use go:linkname comments to refer to internal/cpu functions and variables. Since internal/cpu is internal, we can just export those names. Removing the obscurity of go:linkname outweighs the minor additional complexity added to the internal/cpu API. Change-Id: Id89951b7f3fc67cd9bce67ac6d01d44a647a10ad Reviewed-on: https://go-review.googlesource.com/128755 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick Reviewed-by: Martin Möhrmann --- src/internal/cpu/cpu.go | 11 ++++---- src/internal/cpu/cpu_arm64.go | 56 ++++++++++++++++++++--------------------- src/internal/cpu/cpu_ppc64x.go | 30 +++++++++++----------- src/internal/cpu/export_test.go | 3 +-- src/runtime/os_linux_arm64.go | 19 +++++--------- src/runtime/os_linux_ppc64x.go | 19 +++++--------- src/runtime/proc.go | 12 +++------ 7 files changed, 65 insertions(+), 85 deletions(-) (limited to 'src/runtime') diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go index 701584dd3d..f2dfadbff8 100644 --- a/src/internal/cpu/cpu.go +++ b/src/internal/cpu/cpu.go @@ -6,9 +6,10 @@ // used by the Go standard library. package cpu -// debugOptions is set to true by the runtime if go was compiled with GOEXPERIMENT=debugcpu -// and GOOS is Linux or Darwin. This variable is linknamed in runtime/proc.go. -var debugOptions bool +// DebugOptions is set to true by the runtime if go was compiled with GOEXPERIMENT=debugcpu +// and GOOS is Linux or Darwin. +// This should not be changed after it is initialized. +var DebugOptions bool // CacheLinePad is used to pad structs to avoid false sharing. type CacheLinePad struct{ _ [CacheLineSize]byte } @@ -121,11 +122,11 @@ type s390x struct { _ CacheLinePad } -// initialize examines the processor and sets the relevant variables above. +// Initialize examines the processor and sets the relevant variables above. // This is called by the runtime package early in program initialization, // before normal init functions are run. env is set by runtime on Linux and Darwin // if go was compiled with GOEXPERIMENT=debugcpu. -func initialize(env string) { +func Initialize(env string) { doinit() processOptions(env) } diff --git a/src/internal/cpu/cpu_arm64.go b/src/internal/cpu/cpu_arm64.go index 48607575ba..77b617e49f 100644 --- a/src/internal/cpu/cpu_arm64.go +++ b/src/internal/cpu/cpu_arm64.go @@ -7,10 +7,10 @@ package cpu const CacheLineSize = 64 // arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. -// These are linknamed in runtime/os_linux_arm64.go and are initialized by -// archauxv(). -var hwcap uint -var hwcap2 uint +// These are initialized by archauxv in runtime/os_linux_arm64.go. +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint // HWCAP/HWCAP2 bits. These are exposed by Linux. const ( @@ -71,30 +71,30 @@ func doinit() { } // HWCAP feature bits - ARM64.HasFP = isSet(hwcap, hwcap_FP) - ARM64.HasASIMD = isSet(hwcap, hwcap_ASIMD) - ARM64.HasEVTSTRM = isSet(hwcap, hwcap_EVTSTRM) - ARM64.HasAES = isSet(hwcap, hwcap_AES) - ARM64.HasPMULL = isSet(hwcap, hwcap_PMULL) - ARM64.HasSHA1 = isSet(hwcap, hwcap_SHA1) - ARM64.HasSHA2 = isSet(hwcap, hwcap_SHA2) - ARM64.HasCRC32 = isSet(hwcap, hwcap_CRC32) - ARM64.HasATOMICS = isSet(hwcap, hwcap_ATOMICS) - ARM64.HasFPHP = isSet(hwcap, hwcap_FPHP) - ARM64.HasASIMDHP = isSet(hwcap, hwcap_ASIMDHP) - ARM64.HasCPUID = isSet(hwcap, hwcap_CPUID) - ARM64.HasASIMDRDM = isSet(hwcap, hwcap_ASIMDRDM) - ARM64.HasJSCVT = isSet(hwcap, hwcap_JSCVT) - ARM64.HasFCMA = isSet(hwcap, hwcap_FCMA) - ARM64.HasLRCPC = isSet(hwcap, hwcap_LRCPC) - ARM64.HasDCPOP = isSet(hwcap, hwcap_DCPOP) - ARM64.HasSHA3 = isSet(hwcap, hwcap_SHA3) - ARM64.HasSM3 = isSet(hwcap, hwcap_SM3) - ARM64.HasSM4 = isSet(hwcap, hwcap_SM4) - ARM64.HasASIMDDP = isSet(hwcap, hwcap_ASIMDDP) - ARM64.HasSHA512 = isSet(hwcap, hwcap_SHA512) - ARM64.HasSVE = isSet(hwcap, hwcap_SVE) - ARM64.HasASIMDFHM = isSet(hwcap, hwcap_ASIMDFHM) + ARM64.HasFP = isSet(HWCap, hwcap_FP) + ARM64.HasASIMD = isSet(HWCap, hwcap_ASIMD) + ARM64.HasEVTSTRM = isSet(HWCap, hwcap_EVTSTRM) + ARM64.HasAES = isSet(HWCap, hwcap_AES) + ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL) + ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1) + ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2) + ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32) + ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) + ARM64.HasFPHP = isSet(HWCap, hwcap_FPHP) + ARM64.HasASIMDHP = isSet(HWCap, hwcap_ASIMDHP) + ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID) + ARM64.HasASIMDRDM = isSet(HWCap, hwcap_ASIMDRDM) + ARM64.HasJSCVT = isSet(HWCap, hwcap_JSCVT) + ARM64.HasFCMA = isSet(HWCap, hwcap_FCMA) + ARM64.HasLRCPC = isSet(HWCap, hwcap_LRCPC) + ARM64.HasDCPOP = isSet(HWCap, hwcap_DCPOP) + ARM64.HasSHA3 = isSet(HWCap, hwcap_SHA3) + ARM64.HasSM3 = isSet(HWCap, hwcap_SM3) + ARM64.HasSM4 = isSet(HWCap, hwcap_SM4) + ARM64.HasASIMDDP = isSet(HWCap, hwcap_ASIMDDP) + ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512) + ARM64.HasSVE = isSet(HWCap, hwcap_SVE) + ARM64.HasASIMDFHM = isSet(HWCap, hwcap_ASIMDFHM) } func isSet(hwc uint, value uint) bool { diff --git a/src/internal/cpu/cpu_ppc64x.go b/src/internal/cpu/cpu_ppc64x.go index 995cf02081..d3f02efa7f 100644 --- a/src/internal/cpu/cpu_ppc64x.go +++ b/src/internal/cpu/cpu_ppc64x.go @@ -9,10 +9,10 @@ package cpu const CacheLineSize = 128 // ppc64x doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. -// These are linknamed in runtime/os_linux_ppc64x.go and are initialized by -// archauxv(). -var hwcap uint -var hwcap2 uint +// These are initialized by archauxv in runtime/os_linux_ppc64x.go. +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint // HWCAP/HWCAP2 bits. These are exposed by the kernel. const ( @@ -48,19 +48,19 @@ func doinit() { } // HWCAP feature bits - PPC64.HasVMX = isSet(hwcap, _PPC_FEATURE_HAS_ALTIVEC) - PPC64.HasDFP = isSet(hwcap, _PPC_FEATURE_HAS_DFP) - PPC64.HasVSX = isSet(hwcap, _PPC_FEATURE_HAS_VSX) + PPC64.HasVMX = isSet(HWCap, _PPC_FEATURE_HAS_ALTIVEC) + PPC64.HasDFP = isSet(HWCap, _PPC_FEATURE_HAS_DFP) + PPC64.HasVSX = isSet(HWCap, _PPC_FEATURE_HAS_VSX) // HWCAP2 feature bits - PPC64.IsPOWER8 = isSet(hwcap2, _PPC_FEATURE2_ARCH_2_07) - PPC64.HasHTM = isSet(hwcap2, _PPC_FEATURE2_HAS_HTM) - PPC64.HasISEL = isSet(hwcap2, _PPC_FEATURE2_HAS_ISEL) - PPC64.HasVCRYPTO = isSet(hwcap2, _PPC_FEATURE2_HAS_VEC_CRYPTO) - PPC64.HasHTMNOSC = isSet(hwcap2, _PPC_FEATURE2_HTM_NOSC) - PPC64.IsPOWER9 = isSet(hwcap2, _PPC_FEATURE2_ARCH_3_00) - PPC64.HasDARN = isSet(hwcap2, _PPC_FEATURE2_DARN) - PPC64.HasSCV = isSet(hwcap2, _PPC_FEATURE2_SCV) + PPC64.IsPOWER8 = isSet(HWCap2, _PPC_FEATURE2_ARCH_2_07) + PPC64.HasHTM = isSet(HWCap2, _PPC_FEATURE2_HAS_HTM) + PPC64.HasISEL = isSet(HWCap2, _PPC_FEATURE2_HAS_ISEL) + PPC64.HasVCRYPTO = isSet(HWCap2, _PPC_FEATURE2_HAS_VEC_CRYPTO) + PPC64.HasHTMNOSC = isSet(HWCap2, _PPC_FEATURE2_HTM_NOSC) + PPC64.IsPOWER9 = isSet(HWCap2, _PPC_FEATURE2_ARCH_3_00) + PPC64.HasDARN = isSet(HWCap2, _PPC_FEATURE2_DARN) + PPC64.HasSCV = isSet(HWCap2, _PPC_FEATURE2_SCV) } func isSet(hwc uint, value uint) bool { diff --git a/src/internal/cpu/export_test.go b/src/internal/cpu/export_test.go index 4e53c5a084..91bfc1bbc3 100644 --- a/src/internal/cpu/export_test.go +++ b/src/internal/cpu/export_test.go @@ -5,6 +5,5 @@ package cpu var ( - Options = options - DebugOptions = debugOptions + Options = options ) diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index 28a0319f10..cbe528b4af 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -6,20 +6,10 @@ package runtime -// For go:linkname -import _ "unsafe" +import "internal/cpu" var randomNumber uint32 -// arm64 doesn't have a 'cpuid' instruction equivalent and relies on -// HWCAP/HWCAP2 bits for hardware capabilities. - -//go:linkname cpu_hwcap internal/cpu.hwcap -var cpu_hwcap uint - -//go:linkname cpu_hwcap2 internal/cpu.hwcap2 -var cpu_hwcap2 uint - func archauxv(tag, val uintptr) { switch tag { case _AT_RANDOM: @@ -28,10 +18,13 @@ func archauxv(tag, val uintptr) { // it as a byte array. randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + case _AT_HWCAP: - cpu_hwcap = uint(val) + // arm64 doesn't have a 'cpuid' instruction equivalent and relies on + // HWCAP/HWCAP2 bits for hardware capabilities. + cpu.HWCap = uint(val) case _AT_HWCAP2: - cpu_hwcap2 = uint(val) + cpu.HWCap2 = uint(val) } } diff --git a/src/runtime/os_linux_ppc64x.go b/src/runtime/os_linux_ppc64x.go index 2c67864a96..cc79cc4a66 100644 --- a/src/runtime/os_linux_ppc64x.go +++ b/src/runtime/os_linux_ppc64x.go @@ -7,23 +7,16 @@ package runtime -// For go:linkname -import _ "unsafe" - -// ppc64x doesn't have a 'cpuid' instruction equivalent and relies on -// HWCAP/HWCAP2 bits for hardware capabilities. - -//go:linkname cpu_hwcap internal/cpu.hwcap -var cpu_hwcap uint - -//go:linkname cpu_hwcap2 internal/cpu.hwcap2 -var cpu_hwcap2 uint +import "internal/cpu" func archauxv(tag, val uintptr) { switch tag { case _AT_HWCAP: - cpu_hwcap = uint(val) + // ppc64x doesn't have a 'cpuid' instruction + // equivalent and relies on HWCAP/HWCAP2 bits for + // hardware capabilities. + cpu.HWCap = uint(val) case _AT_HWCAP2: - cpu_hwcap2 = uint(val) + cpu.HWCap2 = uint(val) } } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 7875b38e2e..31b188efd9 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -477,20 +477,14 @@ const ( _GoidCacheBatch = 16 ) -//go:linkname internal_cpu_initialize internal/cpu.initialize -func internal_cpu_initialize(env string) - -//go:linkname internal_cpu_debugOptions internal/cpu.debugOptions -var internal_cpu_debugOptions bool - // cpuinit extracts the environment variable GODEBUGCPU from the environment on -// Linux and Darwin if the GOEXPERIMENT debugcpu was set and calls internal/cpu.initialize. +// Linux and Darwin if the GOEXPERIMENT debugcpu was set and calls internal/cpu.Initialize. func cpuinit() { const prefix = "GODEBUGCPU=" var env string if haveexperiment("debugcpu") && (GOOS == "linux" || GOOS == "darwin") { - internal_cpu_debugOptions = true + cpu.DebugOptions = true // Similar to goenv_unix but extracts the environment value for // GODEBUGCPU directly. @@ -511,7 +505,7 @@ func cpuinit() { } } - internal_cpu_initialize(env) + cpu.Initialize(env) support_erms = cpu.X86.HasERMS support_popcnt = cpu.X86.HasPOPCNT -- cgit v1.3-5-g9baa From c92354f46e468f89fcf1497e7c9e2f3c66025dfa Mon Sep 17 00:00:00 2001 From: Shivansh Rai Date: Sun, 20 May 2018 22:35:02 +0530 Subject: all: use consistent shebang line across all shell scripts Change-Id: I4aac882b1b618a388d0748a427dc998203d3a1b2 Reviewed-on: https://go-review.googlesource.com/113856 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/go/mkalldocs.sh | 2 +- src/naclmake.bash | 2 +- src/nacltest.bash | 2 +- src/runtime/mknacl.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/cmd/go/mkalldocs.sh b/src/cmd/go/mkalldocs.sh index 4e7a509805..f37d59d2d7 100755 --- a/src/cmd/go/mkalldocs.sh +++ b/src/cmd/go/mkalldocs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2012 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. diff --git a/src/naclmake.bash b/src/naclmake.bash index 74fd802f41..5e6c3ce05e 100755 --- a/src/naclmake.bash +++ b/src/naclmake.bash @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. diff --git a/src/nacltest.bash b/src/nacltest.bash index 3e929a14a4..dc245b484c 100755 --- a/src/nacltest.bash +++ b/src/nacltest.bash @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2014 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. diff --git a/src/runtime/mknacl.sh b/src/runtime/mknacl.sh index 3454b624d6..306ae3d9c1 100644 --- a/src/runtime/mknacl.sh +++ b/src/runtime/mknacl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2013 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. -- cgit v1.3-5-g9baa From 2fad8b219ff9f13f10396c97c0a3bca5c6153d78 Mon Sep 17 00:00:00 2001 From: Zhou Peng Date: Mon, 20 Aug 2018 01:13:33 +0000 Subject: runtime: fix typo: there -> the Change-Id: I2ecbd68b1b30ab64e64ae120101761400c22457b Reviewed-on: https://go-review.googlesource.com/129757 Reviewed-by: Brad Fitzpatrick --- src/runtime/mgc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index e4c0f5a587..f54c8eb14f 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -407,7 +407,7 @@ type gcControllerState struct { // each P that isn't running a dedicated worker. // // For example, if the utilization goal is 25% and there are - // no dedicated workers, this will be 0.25. If there goal is + // no dedicated workers, this will be 0.25. If the goal is // 25%, there is one dedicated worker, and GOMAXPROCS is 5, // this will be 0.05 to make up the missing 5%. // -- cgit v1.3-5-g9baa From b0dc54697ba34494a4d77e8d3e446070fc7b223b Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Fri, 1 Jun 2018 19:25:57 +0200 Subject: runtime: replace calls to hasprefix with hasPrefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hasprefix function is redundant and can be removed since it has the same implementation as hasPrefix modulo variable names. Fixes #25688 Change-Id: I499cc24a2b5c38d1301718a4e66f555fd138386f Reviewed-on: https://go-review.googlesource.com/115835 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Ilya Tocar --- src/runtime/export_debug_test.go | 2 +- src/runtime/os3_plan9.go | 2 +- src/runtime/os_plan9.go | 10 +++++----- src/runtime/proc.go | 4 ++-- src/runtime/string.go | 6 +++--- src/runtime/traceback.go | 4 ++-- src/runtime/type.go | 4 ---- 7 files changed, 14 insertions(+), 18 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/export_debug_test.go b/src/runtime/export_debug_test.go index d34c1fd7dc..74f8855de6 100644 --- a/src/runtime/export_debug_test.go +++ b/src/runtime/export_debug_test.go @@ -115,7 +115,7 @@ func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool { return false } f := findfunc(uintptr(ctxt.rip())) - if !(hasprefix(funcname(f), "runtime.debugCall") || hasprefix(funcname(f), "debugCall")) { + if !(hasPrefix(funcname(f), "runtime.debugCall") || hasPrefix(funcname(f), "debugCall")) { println("trap in unknown function", funcname(f)) return false } diff --git a/src/runtime/os3_plan9.go b/src/runtime/os3_plan9.go index 0e3a4c8024..15ca3359d2 100644 --- a/src/runtime/os3_plan9.go +++ b/src/runtime/os3_plan9.go @@ -44,7 +44,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int { // level by the program but will otherwise be ignored. flags = _SigNotify for sig, t = range sigtable { - if hasprefix(notestr, t.name) { + if hasPrefix(notestr, t.name) { flags = t.flags break } diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go index 9f41c5ac83..5469114a2b 100644 --- a/src/runtime/os_plan9.go +++ b/src/runtime/os_plan9.go @@ -112,20 +112,20 @@ func sigpanic() { } func atolwhex(p string) int64 { - for hasprefix(p, " ") || hasprefix(p, "\t") { + for hasPrefix(p, " ") || hasPrefix(p, "\t") { p = p[1:] } neg := false - if hasprefix(p, "-") || hasprefix(p, "+") { + if hasPrefix(p, "-") || hasPrefix(p, "+") { neg = p[0] == '-' p = p[1:] - for hasprefix(p, " ") || hasprefix(p, "\t") { + for hasPrefix(p, " ") || hasPrefix(p, "\t") { p = p[1:] } } var n int64 switch { - case hasprefix(p, "0x"), hasprefix(p, "0X"): + case hasPrefix(p, "0x"), hasPrefix(p, "0X"): p = p[2:] for ; len(p) > 0; p = p[1:] { if '0' <= p[0] && p[0] <= '9' { @@ -138,7 +138,7 @@ func atolwhex(p string) int64 { break } } - case hasprefix(p, "0"): + case hasPrefix(p, "0"): for ; len(p) > 0 && '0' <= p[0] && p[0] <= '7'; p = p[1:] { n = n*8 + int64(p[0]-'0') } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 31b188efd9..32467715c4 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -498,7 +498,7 @@ func cpuinit() { p := argv_index(argv, argc+1+i) s := *(*string)(unsafe.Pointer(&stringStruct{unsafe.Pointer(p), findnull(p)})) - if hasprefix(s, prefix) { + if hasPrefix(s, prefix) { env = gostring(p)[len(prefix):] break } @@ -3702,7 +3702,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { // received from somewhere else (with _LostSIGPROFDuringAtomic64 as pc). if GOARCH == "mips" || GOARCH == "mipsle" || GOARCH == "arm" { if f := findfunc(pc); f.valid() { - if hasprefix(funcname(f), "runtime/internal/atomic") { + if hasPrefix(funcname(f), "runtime/internal/atomic") { lostAtomic64Count++ return } diff --git a/src/runtime/string.go b/src/runtime/string.go index 6e42483b13..d10bd96f43 100644 --- a/src/runtime/string.go +++ b/src/runtime/string.go @@ -333,7 +333,7 @@ func index(s, t string) int { return 0 } for i := 0; i < len(s); i++ { - if s[i] == t[0] && hasprefix(s[i:], t) { + if s[i] == t[0] && hasPrefix(s[i:], t) { return i } } @@ -344,8 +344,8 @@ func contains(s, t string) bool { return index(s, t) >= 0 } -func hasprefix(s, t string) bool { - return len(s) >= len(t) && s[:len(t)] == t +func hasPrefix(s, prefix string) bool { + return len(s) >= len(prefix) && s[:len(prefix)] == prefix } const ( diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index d8c225d975..a1f32016b9 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -843,7 +843,7 @@ func showfuncinfo(f funcInfo, firstFrame, elideWrapper bool) bool { return true } - return contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) + return contains(name, ".") && (!hasPrefix(name, "runtime.") || isExportedRuntime(name)) } // isExportedRuntime reports whether name is an exported runtime function. @@ -1022,7 +1022,7 @@ func isSystemGoroutine(gp *g) bool { // back into user code. return !fingRunning } - return hasprefix(funcname(f), "runtime.") + return hasPrefix(funcname(f), "runtime.") } // SetCgoTraceback records three C functions to use to gather diff --git a/src/runtime/type.go b/src/runtime/type.go index 4b38c351c7..88a44a37ed 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -112,10 +112,6 @@ func (t *_type) uncommon() *uncommontype { } } -func hasPrefix(s, prefix string) bool { - return len(s) >= len(prefix) && s[:len(prefix)] == prefix -} - func (t *_type) name() string { if t.tflag&tflagNamed == 0 { return "" -- cgit v1.3-5-g9baa From 3879ea54ed1f5c266657ffe593e2a8e1b63401ec Mon Sep 17 00:00:00 2001 From: Iskander Sharipov Date: Mon, 6 Aug 2018 14:16:43 +0300 Subject: runtime: fix Go prototypes in amd64 asm code Also adds some missing asmdecl comments for funcs with Go proto. Change-Id: Iabc68e8c0ad936e06ed719e0f030bfc5f6f6e168 Reviewed-on: https://go-review.googlesource.com/127760 Run-TryBot: Iskander Sharipov TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/asm_amd64.s | 22 +++++++++++++++------- src/runtime/stubs.go | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 6902ce2c22..6c65674b3b 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -228,7 +228,7 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0 * go-routine */ -// void gosave(Gobuf*) +// func gosave(buf *gobuf) // save state in Gobuf; setjmp TEXT runtime·gosave(SB), NOSPLIT, $0-8 MOVQ buf+0(FP), AX // gobuf @@ -248,7 +248,7 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-8 MOVQ BX, gobuf_g(AX) RET -// void gogo(Gobuf*) +// func gogo(buf *gobuf) // restore state from Gobuf; longjmp TEXT runtime·gogo(SB), NOSPLIT, $16-8 MOVQ buf+0(FP), BX // gobuf @@ -560,7 +560,8 @@ TEXT ·publicationBarrier(SB),NOSPLIT,$0-0 // compile barrier. RET -// void jmpdefer(fn, sp); +// func jmpdefer(fv *funcval, argp uintptr) +// argp is a caller SP. // called from deferreturn. // 1. pop the caller // 2. sub 5 bytes from the callers return @@ -670,7 +671,7 @@ nosave: MOVL AX, ret+16(FP) RET -// cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt) +// func cgocallback(fn, frame unsafe.Pointer, framesize, ctxt uintptr) // Turn the fn into a Go func (by taking its address) and call // cgocallback_gofunc. TEXT runtime·cgocallback(SB),NOSPLIT,$32-32 @@ -686,7 +687,7 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$32-32 CALL AX RET -// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt) +// func cgocallback_gofunc(fn, frame, framesize, ctxt uintptr) // See cgocall.go for more details. TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-32 NO_LOCAL_POINTERS @@ -811,7 +812,8 @@ havem: // Done! RET -// void setg(G*); set g. for use by needm. +// func setg(gg *g) +// set g. for use by needm. TEXT runtime·setg(SB), NOSPLIT, $0-8 MOVQ gg+0(FP), BX #ifdef GOOS_windows @@ -866,6 +868,7 @@ done: MOVQ AX, ret+0(FP) RET +// func aeshash(p unsafe.Pointer, h, s uintptr) uintptr // hash function using AES hardware instructions TEXT runtime·aeshash(SB),NOSPLIT,$0-32 MOVQ p+0(FP), AX // ptr to data @@ -873,6 +876,7 @@ TEXT runtime·aeshash(SB),NOSPLIT,$0-32 LEAQ ret+24(FP), DX JMP runtime·aeshashbody(SB) +// func aeshashstr(p unsafe.Pointer, h uintptr) uintptr TEXT runtime·aeshashstr(SB),NOSPLIT,$0-24 MOVQ p+0(FP), AX // ptr to string struct MOVQ 8(AX), CX // length of string @@ -1210,7 +1214,8 @@ aesloop: PXOR X9, X8 MOVQ X8, (DX) RET - + +// func aeshash32(p unsafe.Pointer, h uintptr) uintptr TEXT runtime·aeshash32(SB),NOSPLIT,$0-24 MOVQ p+0(FP), AX // ptr to data MOVQ h+8(FP), X0 // seed @@ -1221,6 +1226,7 @@ TEXT runtime·aeshash32(SB),NOSPLIT,$0-24 MOVQ X0, ret+16(FP) RET +// func aeshash64(p unsafe.Pointer, h uintptr) uintptr TEXT runtime·aeshash64(SB),NOSPLIT,$0-24 MOVQ p+0(FP), AX // ptr to data MOVQ h+8(FP), X0 // seed @@ -1266,6 +1272,7 @@ DATA masks<>+0xf0(SB)/8, $0xffffffffffffffff DATA masks<>+0xf8(SB)/8, $0x00ffffffffffffff GLOBL masks<>(SB),RODATA,$256 +// func checkASM() bool TEXT ·checkASM(SB),NOSPLIT,$0-1 // check that masks<>(SB) and shifts<>(SB) are aligned to 16-byte MOVQ $masks<>(SB), AX @@ -1616,6 +1623,7 @@ DEBUG_CALL_FN(debugCall16384<>, 16384) DEBUG_CALL_FN(debugCall32768<>, 32768) DEBUG_CALL_FN(debugCall65536<>, 65536) +// func debugCallPanicked(val interface{}) TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16 // Copy the panic value to the top of stack. MOVQ val_type+0(FP), AX diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 74b385d596..632b1e2293 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -178,7 +178,7 @@ func goexit(neverCallThisFunction) // cgocallback_gofunc is not called from go, only from cgocallback, // so the arguments will be found via cgocallback's pointer-declared arguments. // See the assembly implementations for more details. -func cgocallback_gofunc(fv uintptr, frame uintptr, framesize, ctxt uintptr) +func cgocallback_gofunc(fv, frame, framesize, ctxt uintptr) // publicationBarrier performs a store/store barrier (a "publication" // or "export" barrier). Some form of synchronization is required -- cgit v1.3-5-g9baa From f2d7e66e98d64941313147c0dfe2f31645830716 Mon Sep 17 00:00:00 2001 From: Roland Illig Date: Mon, 13 Aug 2018 21:28:28 +0200 Subject: runtime/pprof: fix resource leak in documentation Fixes #26970 Change-Id: I0f2695434a53550cf84f702e9d8d02a37448d396 Reviewed-on: https://go-review.googlesource.com/129195 Reviewed-by: Brad Fitzpatrick --- src/runtime/pprof/pprof.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index c1024c99ed..74cdd15cfb 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -28,6 +28,7 @@ // if err != nil { // log.Fatal("could not create CPU profile: ", err) // } +// defer f.Close() // if err := pprof.StartCPUProfile(f); err != nil { // log.Fatal("could not start CPU profile: ", err) // } @@ -41,11 +42,11 @@ // if err != nil { // log.Fatal("could not create memory profile: ", err) // } +// defer f.Close() // runtime.GC() // get up-to-date statistics // if err := pprof.WriteHeapProfile(f); err != nil { // log.Fatal("could not write memory profile: ", err) // } -// f.Close() // } // } // -- cgit v1.3-5-g9baa From cfbe3cfbeb6b2de561fc709b8644d4d7a4e182bb Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Thu, 16 Aug 2018 10:49:35 -0400 Subject: runtime: fix implementation of cputicks for ppc64x The implementation of cputicks has been wrong for ppc64x. The previous code sequence is for 32 bit, not 64 bit. Change-Id: I308ae6cf9131f53a0100cd3f8ae4e16601f2d553 Reviewed-on: https://go-review.googlesource.com/129595 Run-TryBot: Lynn Boger TryBot-Result: Gobot Gobot Reviewed-by: Carlos Eduardo Seo Reviewed-by: Brad Fitzpatrick --- src/runtime/asm_ppc64x.s | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index 0886de9f2b..57877c0194 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -723,18 +723,11 @@ TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0 MOVW (R0), R0 UNDEF -#define TBRL 268 -#define TBRU 269 /* Time base Upper/Lower */ +#define TBR 268 // int64 runtime·cputicks(void) TEXT runtime·cputicks(SB),NOSPLIT,$0-8 - MOVW SPR(TBRU), R4 - MOVW SPR(TBRL), R3 - MOVW SPR(TBRU), R5 - CMPW R4, R5 - BNE -4(PC) - SLD $32, R5 - OR R5, R3 + MOVD SPR(TBR), R3 MOVD R3, ret+0(FP) RET -- cgit v1.3-5-g9baa From 68527ff4fb32c98b1f15367c65c526c2b2b3a57a Mon Sep 17 00:00:00 2001 From: Thanabodee Charoenpiriyakij Date: Thu, 19 Jul 2018 08:16:44 +0700 Subject: runtime: remove +1-1 when asking PC values Fixes #26437 Change-Id: Id47b3bcc23ea7b7b17b55dd96b5830c48fd8d53d Reviewed-on: https://go-review.googlesource.com/124895 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/runtime/extern.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 2788bd354b..1773c8fe7e 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -176,7 +176,7 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) { // what it called, so that CallersFrames can see if it "called" // sigpanic, and possibly a PC for skipPleaseUseCallersFrames. var rpc [3]uintptr - if callers(1+skip-1, rpc[:]) < 2 { + if callers(skip, rpc[:]) < 2 { return } var stackExpander stackExpander -- cgit v1.3-5-g9baa From 0a519401651f46a098c5c295943cc1c48e53a48c Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 6 Jul 2018 22:57:35 -0700 Subject: runtime: make TestGcSys actually test something The workthegc function was being inlined, and the slice did not escape, so there was no memory allocation. Use a sink variable to force memory allocation, at least for now. Fixes #23343 Change-Id: I02f4618e343c8b6cb552cb4e9f272e112785f7cf Reviewed-on: https://go-review.googlesource.com/122576 Run-TryBot: Ian Lance Taylor Reviewed-by: Brad Fitzpatrick --- src/runtime/testdata/testprog/gc.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/testdata/testprog/gc.go b/src/runtime/testdata/testprog/gc.go index 744b6108e2..3ca74ba5fe 100644 --- a/src/runtime/testdata/testprog/gc.go +++ b/src/runtime/testdata/testprog/gc.go @@ -48,8 +48,11 @@ func GCSys() { fmt.Printf("OK\n") } +var sink []byte + func workthegc() []byte { - return make([]byte, 1029) + sink = make([]byte, 1029) + return sink } func GCFairness() { -- cgit v1.3-5-g9baa From fa6639d62664ecd710b112eb1c1e759104c9193c Mon Sep 17 00:00:00 2001 From: Iskander Sharipov Date: Wed, 11 Jul 2018 23:34:18 +0300 Subject: runtime: simplify slice expression to sliced value itself Replace `x[:]` where x is a slice with just `x`. Found using https://go-critic.github.io/overview.html#unslice-ref Change-Id: Ib0ee16e1d49b2a875b6b92a770049acc33208362 Reviewed-on: https://go-review.googlesource.com/123375 Run-TryBot: Iskander Sharipov TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/trace.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/trace.go b/src/runtime/trace.go index 61f7513ee0..22d8d026dc 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -584,10 +584,10 @@ func traceStackID(mp *m, buf []uintptr, skip int) uint64 { gp := mp.curg var nstk int if gp == _g_ { - nstk = callers(skip+1, buf[:]) + nstk = callers(skip+1, buf) } else if gp != nil { gp = mp.curg - nstk = gcallers(gp, skip, buf[:]) + nstk = gcallers(gp, skip, buf) } if nstk > 0 { nstk-- // skip runtime.goexit -- cgit v1.3-5-g9baa From fd7d3259c93e8901f5645fd5de620cd75053c7ca Mon Sep 17 00:00:00 2001 From: Iskander Sharipov Date: Tue, 10 Jul 2018 08:32:56 +0300 Subject: runtime: remove redundant explicit deref in trace.go Replaces legacy Go syntax for pointer struct member access with more modern auto-deref alternative. Found using https://go-critic.github.io/overview#underef-ref Change-Id: I71a3c424126c4ff5d89f9e4bacb6cc01c6fa2ddf Reviewed-on: https://go-review.googlesource.com/122895 Run-TryBot: Iskander Sharipov TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/trace.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/trace.go b/src/runtime/trace.go index 22d8d026dc..08e92d2efe 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -532,12 +532,12 @@ func traceEvent(ev byte, skip int, args ...uint64) { } func traceEventLocked(extraBytes int, mp *m, pid int32, bufp *traceBufPtr, ev byte, skip int, args ...uint64) { - buf := (*bufp).ptr() + buf := bufp.ptr() // TODO: test on non-zero extraBytes param. maxSize := 2 + 5*traceBytesPerNumber + extraBytes // event type, length, sequence, timestamp, stack id and two add params if buf == nil || len(buf.arr)-buf.pos < maxSize { buf = traceFlush(traceBufPtrOf(buf), pid).ptr() - (*bufp).set(buf) + bufp.set(buf) } ticks := uint64(cputicks()) / traceTickDiv @@ -689,11 +689,11 @@ func traceString(bufp *traceBufPtr, pid int32, s string) (uint64, *traceBufPtr) // so there must be no memory allocation or any activities // that causes tracing after this point. - buf := (*bufp).ptr() + buf := bufp.ptr() size := 1 + 2*traceBytesPerNumber + len(s) if buf == nil || len(buf.arr)-buf.pos < size { buf = traceFlush(traceBufPtrOf(buf), pid).ptr() - (*bufp).set(buf) + bufp.set(buf) } buf.byte(traceEvString) buf.varint(id) @@ -708,7 +708,7 @@ func traceString(bufp *traceBufPtr, pid int32, s string) (uint64, *traceBufPtr) buf.varint(uint64(slen)) buf.pos += copy(buf.arr[buf.pos:], s[:slen]) - (*bufp).set(buf) + bufp.set(buf) return id, bufp } @@ -1206,7 +1206,7 @@ func trace_userLog(id uint64, category, message string) { traceEventLocked(extraSpace, mp, pid, bufp, traceEvUserLog, 3, id, categoryID) // traceEventLocked reserved extra space for val and len(val) // in buf, so buf now has room for the following. - buf := (*bufp).ptr() + buf := bufp.ptr() // double-check the message and its length can fit. // Otherwise, truncate the message. -- cgit v1.3-5-g9baa From 7b8930ed4587a7f423380220be170daedc620c49 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 22 Aug 2018 19:53:45 +0000 Subject: runtime: fix build, rename a since-renamed hasprefix to hasPrefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I merged CL 115835 without testing it after a rebase. My bad. Change-Id: I0acc6ed78ea7d718ac2df11d509cfcf4364dfaee Reviewed-on: https://go-review.googlesource.com/130815 Run-TryBot: Brad Fitzpatrick Reviewed-by: Martin Möhrmann --- src/runtime/panic.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/panic.go b/src/runtime/panic.go index a5287a0b86..45be886196 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -37,7 +37,7 @@ var indexError = error(errorString("index out of range")) // entire runtime stack for easier debugging. func panicindex() { - if hasprefix(funcname(findfunc(getcallerpc())), "runtime.") { + if hasPrefix(funcname(findfunc(getcallerpc())), "runtime.") { throw(string(indexError.(errorString))) } panicCheckMalloc(indexError) @@ -47,7 +47,7 @@ func panicindex() { var sliceError = error(errorString("slice bounds out of range")) func panicslice() { - if hasprefix(funcname(findfunc(getcallerpc())), "runtime.") { + if hasPrefix(funcname(findfunc(getcallerpc())), "runtime.") { throw(string(sliceError.(errorString))) } panicCheckMalloc(sliceError) -- cgit v1.3-5-g9baa From ede59583858bd64a09479f624e989e7c35df0c52 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 23 Nov 2016 15:34:08 -0500 Subject: reflect: add Value.MapRange method and MapIter type Example of use: iter := reflect.ValueOf(m).MapRange() for iter.Next() { k := iter.Key() v := iter.Value() ... } See issue golang/go#11104 Q. Are there any benchmarks that would exercise the new calls to copyval in existing code? Change-Id: Ic469fcab5f1d9d853e76225f89bde01ee1d36e7a Reviewed-on: https://go-review.googlesource.com/33572 Reviewed-by: Keith Randall --- src/reflect/all_test.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ src/reflect/value.go | 106 +++++++++++++++++++++++++++++++++++------- src/runtime/map.go | 5 ++ 3 files changed, 215 insertions(+), 17 deletions(-) (limited to 'src/runtime') diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index cf7fe3cf7a..33bd75fda5 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -6576,3 +6576,124 @@ func TestIssue22073(t *testing.T) { // Shouldn't panic. m.Call(nil) } + +func TestMapIterNonEmptyMap(t *testing.T) { + m := map[string]int{"one": 1, "two": 2, "three": 3} + iter := ValueOf(m).MapRange() + if got, want := iterateToString(iter), `[one: 1, three: 3, two: 2]`; got != want { + t.Errorf("iterator returned %s (after sorting), want %s", got, want) + } +} + +func TestMapIterNilMap(t *testing.T) { + var m map[string]int + iter := ValueOf(m).MapRange() + if got, want := iterateToString(iter), `[]`; got != want { + t.Errorf("non-empty result iteratoring nil map: %s", got) + } +} + +func TestMapIterSafety(t *testing.T) { + // Using a zero MapIter causes a panic, but not a crash. + func() { + defer func() { recover() }() + new(MapIter).Key() + t.Fatal("Key did not panic") + }() + func() { + defer func() { recover() }() + new(MapIter).Value() + t.Fatal("Value did not panic") + }() + func() { + defer func() { recover() }() + new(MapIter).Next() + t.Fatal("Next did not panic") + }() + + // Calling Key/Value on a MapIter before Next + // causes a panic, but not a crash. + var m map[string]int + iter := ValueOf(m).MapRange() + + func() { + defer func() { recover() }() + iter.Key() + t.Fatal("Key did not panic") + }() + func() { + defer func() { recover() }() + iter.Value() + t.Fatal("Value did not panic") + }() + + // Calling Next, Key, or Value on an exhausted iterator + // causes a panic, but not a crash. + iter.Next() // -> false + func() { + defer func() { recover() }() + iter.Key() + t.Fatal("Key did not panic") + }() + func() { + defer func() { recover() }() + iter.Value() + t.Fatal("Value did not panic") + }() + func() { + defer func() { recover() }() + iter.Next() + t.Fatal("Next did not panic") + }() +} + +func TestMapIterNext(t *testing.T) { + // The first call to Next should reflect any + // insertions to the map since the iterator was created. + m := map[string]int{} + iter := ValueOf(m).MapRange() + m["one"] = 1 + if got, want := iterateToString(iter), `[one: 1]`; got != want { + t.Errorf("iterator returned deleted elements: got %s, want %s", got, want) + } +} + +func TestMapIterDelete0(t *testing.T) { + // Delete all elements before first iteration. + m := map[string]int{"one": 1, "two": 2, "three": 3} + iter := ValueOf(m).MapRange() + delete(m, "one") + delete(m, "two") + delete(m, "three") + if got, want := iterateToString(iter), `[]`; got != want { + t.Errorf("iterator returned deleted elements: got %s, want %s", got, want) + } +} + +func TestMapIterDelete1(t *testing.T) { + // Delete all elements after first iteration. + m := map[string]int{"one": 1, "two": 2, "three": 3} + iter := ValueOf(m).MapRange() + var got []string + for iter.Next() { + got = append(got, fmt.Sprint(iter.Key(), iter.Value())) + delete(m, "one") + delete(m, "two") + delete(m, "three") + } + if len(got) != 1 { + t.Errorf("iterator returned wrong number of elements: got %d, want 1", len(got)) + } +} + +// iterateToString returns the set of elements +// returned by an iterator in readable form. +func iterateToString(it *MapIter) string { + var got []string + for it.Next() { + line := fmt.Sprintf("%v: %v", it.Key(), it.Value()) + got = append(got, line) + } + sort.Strings(got) + return "[" + strings.Join(got, ", ") + "]" +} diff --git a/src/reflect/value.go b/src/reflect/value.go index 4e7b1d74db..1c3e590377 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1085,14 +1085,7 @@ func (v Value) MapIndex(key Value) Value { typ := tt.elem fl := (v.flag | key.flag).ro() fl |= flag(typ.Kind()) - if !ifaceIndir(typ) { - return Value{typ, *(*unsafe.Pointer)(e), fl} - } - // Copy result so future changes to the map - // won't change the underlying value. - c := unsafe_New(typ) - typedmemmove(typ, c, e) - return Value{typ, c, fl | flagIndir} + return copyVal(typ, fl, e) } // MapKeys returns a slice containing all the keys present in the map, @@ -1122,20 +1115,96 @@ func (v Value) MapKeys() []Value { // we can do about it. break } - if ifaceIndir(keyType) { - // Copy result so future changes to the map - // won't change the underlying value. - c := unsafe_New(keyType) - typedmemmove(keyType, c, key) - a[i] = Value{keyType, c, fl | flagIndir} - } else { - a[i] = Value{keyType, *(*unsafe.Pointer)(key), fl} - } + a[i] = copyVal(keyType, fl, key) mapiternext(it) } return a[:i] } +// A MapIter is an iterator for ranging over a map. +// See Value.MapRange. +type MapIter struct { + m Value + it unsafe.Pointer +} + +// Key returns the key of the iterator's current map entry. +func (it *MapIter) Key() Value { + if it.it == nil { + panic("MapIter.Key called before Next") + } + if mapiterkey(it.it) == nil { + panic("MapIter.Key called on exhausted iterator") + } + + t := (*mapType)(unsafe.Pointer(it.m.typ)) + ktype := t.key + return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), mapiterkey(it.it)) +} + +// Value returns the value of the iterator's current map entry. +func (it *MapIter) Value() Value { + if it.it == nil { + panic("MapIter.Value called before Next") + } + if mapiterkey(it.it) == nil { + panic("MapIter.Value called on exhausted iterator") + } + + t := (*mapType)(unsafe.Pointer(it.m.typ)) + vtype := t.elem + return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), mapitervalue(it.it)) +} + +// Next advances the map iterator and reports whether there is another +// entry. It returns false when the iterator is exhausted; subsequent +// calls to Key, Value, or Next will panic. +func (it *MapIter) Next() bool { + if it.it == nil { + it.it = mapiterinit(it.m.typ, it.m.pointer()) + } else { + if mapiterkey(it.it) == nil { + panic("MapIter.Next called on exhausted iterator") + } + mapiternext(it.it) + } + return mapiterkey(it.it) != nil +} + +// MapRange returns a range iterator for a map. +// It panics if v's Kind is not Map. +// +// Call Next to advance the iterator, and Key/Value to access each entry. +// Next returns false when the iterator is exhausted. +// MapRange follows the same iteration semantics as a range statement. +// +// Example: +// +// iter := reflect.ValueOf(m).MapRange() +// for iter.Next() { +// k := iter.Key() +// v := iter.Value() +// ... +// } +// +func (v Value) MapRange() *MapIter { + v.mustBe(Map) + return &MapIter{m: v} +} + +// copyVal returns a Value containing the map key or value at ptr, +// allocating a new variable as needed. +func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value { + if ifaceIndir(typ) { + // Copy result so future changes to the map + // won't change the underlying value. + c := unsafe_New(typ) + typedmemmove(typ, c, ptr) + return Value{typ, c, fl | flagIndir} + } + return Value{typ, *(*unsafe.Pointer)(ptr), fl} +} + // Method returns a function value corresponding to v's i'th method. // The arguments to a Call on the returned function should not include // a receiver; the returned function will always use v as the receiver. @@ -2554,6 +2623,9 @@ func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer //go:noescape func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer) +//go:noescape +func mapitervalue(it unsafe.Pointer) (value unsafe.Pointer) + //go:noescape func mapiternext(it unsafe.Pointer) diff --git a/src/runtime/map.go b/src/runtime/map.go index 208c92cb0d..c03e745dc5 100644 --- a/src/runtime/map.go +++ b/src/runtime/map.go @@ -1282,6 +1282,11 @@ func reflect_mapiterkey(it *hiter) unsafe.Pointer { return it.key } +//go:linkname reflect_mapitervalue reflect.mapitervalue +func reflect_mapitervalue(it *hiter) unsafe.Pointer { + return it.value +} + //go:linkname reflect_maplen reflect.maplen func reflect_maplen(h *hmap) int { if h == nil { -- cgit v1.3-5-g9baa From 28fbf5b831e3c577c2e220daa82a85065047e356 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Wed, 22 Aug 2018 22:34:39 +0200 Subject: runtime: skip TestGcSys on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is causing failures on TryBots and BuildBots: --- FAIL: TestGcSys (0.06s) gc_test.go:27: expected "OK\n", but got "using too much memory: 39882752 bytes\n" FAIL Updates #27156 Change-Id: I418bbec89002574cd583c97422e433f042c07492 Reviewed-on: https://go-review.googlesource.com/130875 Run-TryBot: Martin Möhrmann Reviewed-by: Brad Fitzpatrick --- src/runtime/gc_test.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index 4895a0e2ac..0da19cdf34 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -21,6 +21,9 @@ func TestGcSys(t *testing.T) { if os.Getenv("GOGC") == "off" { t.Skip("skipping test; GOGC=off in environment") } + if runtime.GOOS == "windows" { + t.Skip("skipping test; GOOS=windows http://golang.org/issue/27156") + } got := runTestProg(t, "testprog", "GCSys") want := "OK\n" if got != want { -- cgit v1.3-5-g9baa From 37ea182660e31f4e21b2bc34d2438455269e5f78 Mon Sep 17 00:00:00 2001 From: Zachary Amsden Date: Tue, 31 Jul 2018 11:24:37 -0700 Subject: runtime: catch concurrent stacks more often If two goroutines are racing on a map, one of them will exit cleanly, clearing the hashWriting bit, and the other will likely notice and panic. If we use XOR instead of OR to set the bit in the first place, even numbers of racers will hopefully all see the bit cleared and panic simultaneously, giving the full set of available stacks. If a third racer sneaks in, we are no worse than the current code, and the generated code should be no more expensive. In practice, this catches most racing goroutines even in very tight races. See the demonstration program posted on https://github.com/golang/go/issues/26703 for an example. Fixes #26703 Change-Id: Idad17841a3127c24bd0a659b754734f70e307434 Reviewed-on: https://go-review.googlesource.com/126936 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick Reviewed-by: Keith Randall --- src/runtime/map.go | 6 +++--- src/runtime/map_fast32.go | 6 +++--- src/runtime/map_fast64.go | 6 +++--- src/runtime/map_faststr.go | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/map.go b/src/runtime/map.go index c03e745dc5..c3fcfbfdbe 100644 --- a/src/runtime/map.go +++ b/src/runtime/map.go @@ -567,7 +567,7 @@ func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { // Set hashWriting after calling alg.hash, since alg.hash may panic, // in which case we have not actually done a write. - h.flags |= hashWriting + h.flags ^= hashWriting if h.buckets == nil { h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) @@ -679,7 +679,7 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) { // Set hashWriting after calling alg.hash, since alg.hash may panic, // in which case we have not actually done a write (delete). - h.flags |= hashWriting + h.flags ^= hashWriting bucket := hash & bucketMask(h.B) if h.growing() { @@ -921,7 +921,7 @@ func mapclear(t *maptype, h *hmap) { throw("concurrent map writes") } - h.flags |= hashWriting + h.flags ^= hashWriting h.flags &^= sameSizeGrow h.oldbuckets = nil diff --git a/src/runtime/map_fast32.go b/src/runtime/map_fast32.go index bf0b23604b..671558545a 100644 --- a/src/runtime/map_fast32.go +++ b/src/runtime/map_fast32.go @@ -103,7 +103,7 @@ func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting + h.flags ^= hashWriting if h.buckets == nil { h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) @@ -189,7 +189,7 @@ func mapassign_fast32ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting + h.flags ^= hashWriting if h.buckets == nil { h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) @@ -276,7 +276,7 @@ func mapdelete_fast32(t *maptype, h *hmap, key uint32) { hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapdelete - h.flags |= hashWriting + h.flags ^= hashWriting bucket := hash & bucketMask(h.B) if h.growing() { diff --git a/src/runtime/map_fast64.go b/src/runtime/map_fast64.go index 4bde9e2be0..164a4dd1ce 100644 --- a/src/runtime/map_fast64.go +++ b/src/runtime/map_fast64.go @@ -103,7 +103,7 @@ func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting + h.flags ^= hashWriting if h.buckets == nil { h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) @@ -189,7 +189,7 @@ func mapassign_fast64ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting + h.flags ^= hashWriting if h.buckets == nil { h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) @@ -276,7 +276,7 @@ func mapdelete_fast64(t *maptype, h *hmap, key uint64) { hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapdelete - h.flags |= hashWriting + h.flags ^= hashWriting bucket := hash & bucketMask(h.B) if h.growing() { diff --git a/src/runtime/map_faststr.go b/src/runtime/map_faststr.go index 415bbff143..bee62dfb03 100644 --- a/src/runtime/map_faststr.go +++ b/src/runtime/map_faststr.go @@ -202,7 +202,7 @@ func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer { hash := t.key.alg.hash(noescape(unsafe.Pointer(&s)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapassign. - h.flags |= hashWriting + h.flags ^= hashWriting if h.buckets == nil { h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) @@ -294,7 +294,7 @@ func mapdelete_faststr(t *maptype, h *hmap, ky string) { hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0)) // Set hashWriting after calling alg.hash for consistency with mapdelete - h.flags |= hashWriting + h.flags ^= hashWriting bucket := hash & bucketMask(h.B) if h.growing() { -- cgit v1.3-5-g9baa From 6ebc31f9fb0ade22c605c146f651f18f9fc0b61d Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 12 Feb 2018 11:22:00 -0800 Subject: runtime: remove unused function casp Change-Id: I7c9c83ba236e1050e04377a7591fef7174df698b Reviewed-on: https://go-review.googlesource.com/130415 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/runtime/atomic_pointer.go | 13 ------------- src/runtime/proc.go | 2 +- src/runtime/runtime1.go | 17 +---------------- 3 files changed, 2 insertions(+), 30 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go index 09cfbda9b1..b8f0c22c63 100644 --- a/src/runtime/atomic_pointer.go +++ b/src/runtime/atomic_pointer.go @@ -13,8 +13,6 @@ import ( // because while ptr does not escape, new does. // If new is marked as not escaping, the compiler will make incorrect // escape analysis decisions about the pointer value being stored. -// Instead, these are wrappers around the actual atomics (casp1 and so on) -// that use noescape to convey which arguments do not escape. // atomicwb performs a write barrier before an atomic pointer write. // The caller should guard the call with "if writeBarrier.enabled". @@ -37,17 +35,6 @@ func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) { atomic.StorepNoWB(noescape(ptr), new) } -//go:nosplit -func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool { - // The write barrier is only necessary if the CAS succeeds, - // but since it needs to happen before the write becomes - // public, we have to do it conservatively all the time. - if writeBarrier.enabled { - atomicwb(ptr, new) - } - return atomic.Casp1((*unsafe.Pointer)(noescape(unsafe.Pointer(ptr))), noescape(old), new) -} - // Like above, but implement in terms of sync/atomic's uintptr operations. // We cannot just call the runtime routines, because the race detector expects // to be able to intercept the sync/atomic forms but not the runtime forms. diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 32467715c4..c9cc7544b8 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1599,7 +1599,7 @@ func allocm(_p_ *p, fn func()) *m { // the following strategy: there is a stack of available m's // that can be stolen. Using compare-and-swap // to pop from the stack has ABA races, so we simulate -// a lock by doing an exchange (via casp) to steal the stack +// a lock by doing an exchange (via Casuintptr) to steal the stack // head and replace the top pointer with MLOCKED (1). // This serves as a simple spin lock that we can use even // without an m. The thread that locks the stack in this way diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index a0769bbb67..d5f78baded 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -145,7 +145,7 @@ func check() { h uint64 i, i1 float32 j, j1 float64 - k, k1 unsafe.Pointer + k unsafe.Pointer l *uint16 m [4]byte ) @@ -234,21 +234,6 @@ func check() { throw("cas6") } - k = unsafe.Pointer(uintptr(0xfedcb123)) - if sys.PtrSize == 8 { - k = unsafe.Pointer(uintptr(k) << 10) - } - if casp(&k, nil, nil) { - throw("casp1") - } - k1 = add(k, 1) - if !casp(&k, k, k1) { - throw("casp2") - } - if k != k1 { - throw("casp3") - } - m = [4]byte{1, 1, 1, 1} atomic.Or8(&m[1], 0xf0) if m[0] != 1 || m[1] != 0xf1 || m[2] != 1 || m[3] != 1 { -- cgit v1.3-5-g9baa From ad644d2e86bab85787879d41c2d2aebbd7c57db8 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Thu, 23 Aug 2018 05:06:47 +0000 Subject: all: fix typos detected by github.com/client9/misspell Change-Id: Iadb3c5de8ae9ea45855013997ed70f7929a88661 GitHub-Last-Rev: ae85bcf82be8fee533e2b9901c6133921382c70a GitHub-Pull-Request: golang/go#26920 Reviewed-on: https://go-review.googlesource.com/128955 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- misc/cgo/test/issue9400_linux.go | 2 +- misc/cgo/testshared/shared_test.go | 2 +- src/bytes/buffer_test.go | 2 +- src/cmd/asm/internal/asm/operand_test.go | 2 +- src/cmd/asm/internal/asm/testdata/amd64enc_extra.s | 2 +- src/cmd/compile/internal/gc/ssa.go | 2 +- src/cmd/compile/internal/ssa/deadstore.go | 2 +- src/cmd/go/internal/cache/default_unix_test.go | 2 +- src/cmd/go/internal/modload/load.go | 2 +- src/cmd/go/internal/work/exec.go | 2 +- src/cmd/trace/annotations.go | 1 + src/crypto/aes/gcm_arm64.s | 6 +++--- src/crypto/x509/verify.go | 2 +- src/internal/bytealg/index_arm64.s | 2 +- src/internal/trace/goroutines.go | 2 +- src/os/user/user.go | 2 +- src/runtime/asm_amd64.s | 2 +- src/runtime/sys_windows_amd64.s | 2 +- src/time/time.go | 2 +- test/fixedbugs/issue22662b.go | 2 +- test/live.go | 2 +- test/run.go | 2 +- 22 files changed, 24 insertions(+), 23 deletions(-) (limited to 'src/runtime') diff --git a/misc/cgo/test/issue9400_linux.go b/misc/cgo/test/issue9400_linux.go index 34eb4983a4..7719535d25 100644 --- a/misc/cgo/test/issue9400_linux.go +++ b/misc/cgo/test/issue9400_linux.go @@ -41,7 +41,7 @@ func test9400(t *testing.T) { // Grow the stack and put down a test pattern const pattern = 0x123456789abcdef - var big [1024]uint64 // len must match assmebly + var big [1024]uint64 // len must match assembly for i := range big { big[i] = pattern } diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index 846a27173e..529a2c692f 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -560,7 +560,7 @@ func TestNotes(t *testing.T) { abiHashNoteFound = true case 3: // ELF_NOTE_GODEPS_TAG if depsNoteFound { - t.Error("multiple depedency list notes") + t.Error("multiple dependency list notes") } testDepsNote(t, f, note) depsNoteFound = true diff --git a/src/bytes/buffer_test.go b/src/bytes/buffer_test.go index acbe5ca0c4..6e9d6952a5 100644 --- a/src/bytes/buffer_test.go +++ b/src/bytes/buffer_test.go @@ -293,7 +293,7 @@ func TestReadFromPanicReader(t *testing.T) { } check(t, "TestReadFromPanicReader (1)", &buf, "") - // Confirm that when Reader panics, the emtpy buffer remains empty + // Confirm that when Reader panics, the empty buffer remains empty var buf2 Buffer defer func() { recover() diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go index 1d1cf510cb..df60b71ebd 100644 --- a/src/cmd/asm/internal/asm/operand_test.go +++ b/src/cmd/asm/internal/asm/operand_test.go @@ -33,7 +33,7 @@ func newParser(goarch string) *Parser { // tryParse executes parse func in panicOnError=true context. // parse is expected to call any parsing methods that may panic. -// Returns error gathered from recover; nil if no parse errors occured. +// Returns error gathered from recover; nil if no parse errors occurred. // // For unexpected panics, calls t.Fatal. func tryParse(t *testing.T, parse func()) (err error) { diff --git a/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s b/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s index afd1dfd313..2f0d9ecf86 100644 --- a/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s +++ b/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s @@ -911,7 +911,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 VADDPD.BCST.Z (AX), Z2, K1, Z1 // 62f1edd95808 VMAXPD.BCST (AX), Z2, K1, Z1 // 62f1ed595f08 VMAXPD.BCST.Z (AX), Z2, K1, Z1 // 62f1edd95f08 - // EVEX: surpress all exceptions (SAE). + // EVEX: suppress all exceptions (SAE). VMAXPD.SAE Z3, Z2, K1, Z1 // 62f1ed595fcb or 62f1ed195fcb VMAXPD.SAE.Z Z3, Z2, K1, Z1 // 62f1edd95fcb or 62f1ed995fcb VMAXPD (AX), Z2, K1, Z1 // 62f1ed495f08 diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index bbd2a668a5..7292963799 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -5710,7 +5710,7 @@ func (n *Node) StorageClass() ssa.StorageClass { case PAUTO: return ssa.ClassAuto default: - Fatalf("untranslateable storage class for %v: %s", n, n.Class()) + Fatalf("untranslatable storage class for %v: %s", n, n.Class()) return 0 } } diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index ca6bce972e..1caa61a966 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -133,7 +133,7 @@ func dse(f *Func) { } } -// elimDeadAutosGeneric deletes autos that are never accessed. To acheive this +// elimDeadAutosGeneric deletes autos that are never accessed. To achieve this // we track the operations that the address of each auto reaches and if it only // reaches stores then we delete all the stores. The other operations will then // be eliminated by the dead code elimination pass. diff --git a/src/cmd/go/internal/cache/default_unix_test.go b/src/cmd/go/internal/cache/default_unix_test.go index a207497a42..1458201f4b 100644 --- a/src/cmd/go/internal/cache/default_unix_test.go +++ b/src/cmd/go/internal/cache/default_unix_test.go @@ -62,6 +62,6 @@ func TestDefaultDir(t *testing.T) { os.Setenv("HOME", "/") if _, showWarnings := defaultDir(); showWarnings { // https://golang.org/issue/26280 - t.Error("Cache initalization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /") + t.Error("Cache initialization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /") } } diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index e6340b8bfd..6c1525da9a 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -758,7 +758,7 @@ func (pkg *loadPkg) stackText() string { } // why returns the text to use in "go mod why" output about the given package. -// It is less ornate than the stackText but conatins the same information. +// It is less ornate than the stackText but contains the same information. func (pkg *loadPkg) why() string { var buf strings.Builder var stack []*loadPkg diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 42fa0e64ac..2822787e63 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -2858,7 +2858,7 @@ func useResponseFile(path string, argLen int) bool { } // On the Go build system, use response files about 10% of the - // time, just to excercise this codepath. + // time, just to exercise this codepath. isBuilder := os.Getenv("GO_BUILDER_NAME") != "" if isBuilder && rand.Intn(10) == 0 { return true diff --git a/src/cmd/trace/annotations.go b/src/cmd/trace/annotations.go index 96c109e0f2..8071ac8879 100644 --- a/src/cmd/trace/annotations.go +++ b/src/cmd/trace/annotations.go @@ -439,6 +439,7 @@ func (task *taskDesc) complete() bool { } // descendents returns all the task nodes in the subtree rooted from this task. +// TODO: the method name is misspelled func (task *taskDesc) decendents() []*taskDesc { if task == nil { return nil diff --git a/src/crypto/aes/gcm_arm64.s b/src/crypto/aes/gcm_arm64.s index 98e9f5bbe5..61c868cd0c 100644 --- a/src/crypto/aes/gcm_arm64.s +++ b/src/crypto/aes/gcm_arm64.s @@ -434,7 +434,7 @@ TEXT ·gcmAesEnc(SB),NOSPLIT,$0 VLD1 (tPtr), [ACC0.B16] VEOR ACC1.B16, ACC1.B16, ACC1.B16 VEOR ACCM.B16, ACCM.B16, ACCM.B16 - // Prepare intial counter, and the increment vector + // Prepare initial counter, and the increment vector VLD1 (ctrPtr), [CTR.B16] VEOR INC.B16, INC.B16, INC.B16 MOVD $1, H0 @@ -733,7 +733,7 @@ TEXT ·gcmAesDec(SB),NOSPLIT,$0 VLD1 (tPtr), [ACC0.B16] VEOR ACC1.B16, ACC1.B16, ACC1.B16 VEOR ACCM.B16, ACCM.B16, ACCM.B16 - // Prepare intial counter, and the increment vector + // Prepare initial counter, and the increment vector VLD1 (ctrPtr), [CTR.B16] VEOR INC.B16, INC.B16, INC.B16 MOVD $1, H0 @@ -969,7 +969,7 @@ tail: tailLast: VEOR KLAST.B16, B0.B16, B0.B16 - // Assuming it is safe to load past dstPtr due to the presense of the tag + // Assuming it is safe to load past dstPtr due to the presence of the tag VLD1 (srcPtr), [B5.B16] VEOR B5.B16, B0.B16, B0.B16 diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 210db4c1d0..4c2ff7b7c4 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -861,7 +861,7 @@ nextIntermediate: } // validHostname returns whether host is a valid hostname that can be matched or -// matched against according to RFC 6125 2.2, with some leniency to accomodate +// matched against according to RFC 6125 2.2, with some leniency to accommodate // legacy values. func validHostname(host string) bool { host = strings.TrimSuffix(host, ".") diff --git a/src/internal/bytealg/index_arm64.s b/src/internal/bytealg/index_arm64.s index 20d68ba9b8..3a551a72da 100644 --- a/src/internal/bytealg/index_arm64.s +++ b/src/internal/bytealg/index_arm64.s @@ -32,7 +32,7 @@ TEXT indexbody<>(SB),NOSPLIT,$0-56 // to avoid repeatedly re-load it again and again // for sebsequent substring comparisons SUB R3, R1, R4 - // R4 contains the start of last substring for comparsion + // R4 contains the start of last substring for comparison ADD R0, R4, R4 ADD $1, R0, R8 diff --git a/src/internal/trace/goroutines.go b/src/internal/trace/goroutines.go index 2d7d3aa3ae..a5fda489be 100644 --- a/src/internal/trace/goroutines.go +++ b/src/internal/trace/goroutines.go @@ -37,7 +37,7 @@ type UserRegionDesc struct { // Region end event. Normally EvUserRegion end event or nil, // but can be EvGoStop or EvGoEnd event if the goroutine - // terminated without explicitely ending the region. + // terminated without explicitly ending the region. End *Event GExecutionStat diff --git a/src/os/user/user.go b/src/os/user/user.go index 1f733b8023..c1b8101c86 100644 --- a/src/os/user/user.go +++ b/src/os/user/user.go @@ -11,7 +11,7 @@ parses /etc/passwd and /etc/group. The other is cgo-based and relies on the standard C library (libc) routines such as getpwuid_r and getgrnam_r. When cgo is available, cgo-based (libc-backed) code is used by default. -This can be overriden by using osusergo build tag, which enforces +This can be overridden by using osusergo build tag, which enforces the pure Go implementation. */ package user diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 6c65674b3b..2a15910aea 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -1472,7 +1472,7 @@ GLOBL debugCallFrameTooLarge<>(SB), RODATA, $0x14 // Size duplicated below // This function communicates back to the debugger by setting RAX and // invoking INT3 to raise a breakpoint signal. See the comments in the // implementation for the protocol the debugger is expected to -// follow. InjectDebugCall in the runtime tests demonstates this protocol. +// follow. InjectDebugCall in the runtime tests demonstrates this protocol. // // The debugger must ensure that any pointers passed to the function // obey escape analysis requirements. Specifically, it must not pass diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s index c1449dba60..c9127ac2d2 100644 --- a/src/runtime/sys_windows_amd64.s +++ b/src/runtime/sys_windows_amd64.s @@ -363,7 +363,7 @@ TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 // Layout new m scheduler stack on os stack. MOVQ SP, AX MOVQ AX, (g_stack+stack_hi)(DX) - SUBQ $(64*1024), AX // inital stack size (adjusted later) + SUBQ $(64*1024), AX // initial stack size (adjusted later) MOVQ AX, (g_stack+stack_lo)(DX) ADDQ $const__StackGuard, AX MOVQ AX, g_stackguard0(DX) diff --git a/src/time/time.go b/src/time/time.go index 5350d2e98b..f2da32dbad 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -1076,7 +1076,7 @@ func (t Time) Local() Time { return t } -// In returns a copy of t representating the same time instant, but +// In returns a copy of t representing the same time instant, but // with the copy's location information set to loc for display // purposes. // diff --git a/test/fixedbugs/issue22662b.go b/test/fixedbugs/issue22662b.go index 3594c0f4ef..2678383ab0 100644 --- a/test/fixedbugs/issue22662b.go +++ b/test/fixedbugs/issue22662b.go @@ -18,7 +18,7 @@ import ( ) // Each of these tests is expected to fail (missing package clause) -// at the position determined by the preceeding line directive. +// at the position determined by the preceding line directive. var tests = []struct { src, pos string }{ diff --git a/test/live.go b/test/live.go index 18611f5113..13bdc4aae1 100644 --- a/test/live.go +++ b/test/live.go @@ -465,7 +465,7 @@ func f29(b bool) { // copy of array of pointers should die at end of range loop var pstructarr [10]pstruct -// Struct size choosen to make pointer to element in pstructarr +// Struct size chosen to make pointer to element in pstructarr // not computable by strength reduction. type pstruct struct { intp *int diff --git a/test/run.go b/test/run.go index 99ef79feb1..82508d1c1f 100644 --- a/test/run.go +++ b/test/run.go @@ -435,7 +435,7 @@ func (ctxt *context) match(name string) bool { func init() { checkShouldTest() } // goGcflags returns the -gcflags argument to use with go build / go run. -// This must match the flags used for building the standard libary, +// This must match the flags used for building the standard library, // or else the commands will rebuild any needed packages (like runtime) // over and over. func goGcflags() string { -- cgit v1.3-5-g9baa From c9986d1452f3ef226bfd044fb0f128175a7dff03 Mon Sep 17 00:00:00 2001 From: Heschi Kreinick Date: Fri, 17 Aug 2018 16:32:02 -0400 Subject: runtime: fix use of wrong g in gentraceback gentraceback gets the currently running g to do some sanity checks, but should use gp everywhere to do its actual work. Some noncritical checks later accidentally used g instead of gp. This seems like it could be a problem in many different contexts, but I noticed in Windows profiling, where profilem calls gentraceback on a goroutine from a different thread. Change-Id: I3da27a43e833b257f6411ee6893bdece45a9323f Reviewed-on: https://go-review.googlesource.com/128895 Run-TryBot: Heschi Kreinick Reviewed-by: Austin Clements --- src/runtime/traceback.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index a1f32016b9..78589f5ea3 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -99,8 +99,9 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in if skip > 0 && callback != nil { throw("gentraceback callback cannot be used with non-zero skip") } - g := getg() - if g == gp && g == g.m.curg { + + // Don't call this "g"; it's too easy get "g" and "gp" confused. + if ourg := getg(); ourg == gp && ourg == ourg.m.curg { // The starting sp has been passed in as a uintptr, and the caller may // have other uintptr-typed stack references as well. // If during one of the calls that got us here or during one of the @@ -200,7 +201,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // g0, this systemstack is at the top of the stack. // if we're not on g0 or there's a no curg, then this is a regular call. sp := frame.sp - if flags&_TraceJumpStack != 0 && f.funcID == funcID_systemstack && gp == g.m.g0 && gp.m.curg != nil { + if flags&_TraceJumpStack != 0 && f.funcID == funcID_systemstack && gp == gp.m.g0 && gp.m.curg != nil { sp = gp.m.curg.sched.sp frame.sp = sp cgoCtxt = gp.m.curg.cgoCtxt @@ -425,7 +426,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in if frame.pc > f.entry { print(" +", hex(frame.pc-f.entry)) } - if g.m.throwing > 0 && gp == g.m.curg || level >= 2 { + if gp.m != nil && gp.m.throwing > 0 && gp == gp.m.curg || level >= 2 { print(" fp=", hex(frame.fp), " sp=", hex(frame.sp), " pc=", hex(frame.pc)) } print("\n") -- cgit v1.3-5-g9baa From ae6361e4bd78b85c284881a16b9b3f81133db1bb Mon Sep 17 00:00:00 2001 From: Heschi Kreinick Date: Fri, 17 Aug 2018 16:32:08 -0400 Subject: runtime: handle morestack system stack transition in gentraceback gentraceback handles system stack transitions, but only when they're done by systemstack(). Handle morestack() too. I tried to do this generically but systemstack and morestack are actually *very* different functions. Most notably, systemstack returns "normally", just messes with $sp along the way. morestack never returns at all -- it calls newstack, and newstack then jumps both stacks and functions back to whoever called morestack. I couldn't think of a way to handle both of them generically. So don't. The current implementation does not include systemstack on the generated traceback. That's partly because I don't know how to find its stack frame reliably, and partly because the current structure of the code wants to do the transition before the call, not after. If we're willing to assume that morestack's stack frame is 0 size, this could probably be fixed. For posterity's sake, alternatives tried: - Have morestack put a dummy function into schedbuf, like systemstack does. This actually worked (see patchset 1) but more by a series of coincidences than by deliberate design. The biggest coincidence was that because morestack_switch was a RET and its stack was 0 size, it actually worked to jump back to it at the end of newstack -- it would just return to the caller of morestack. Way too subtle for me, and also a little slower than just jumping directly. - Put morestack's PC and SP into schedbuf, so that gentraceback could treat it like a normal function except for the changing SP. This was a terrible idea and caused newstack to reenter morestack in a completely unreasonable state. To make testing possible I did a small redesign of testCPUProfile to take a callback that defines how to check if the conditions pass to it are satisfied. This seemed better than making the syntax of the "need" strings even more complicated. Updates #25943 Change-Id: I9271a30a976f80a093a3d4d1c7e9ec226faf74b4 Reviewed-on: https://go-review.googlesource.com/126795 Run-TryBot: Heschi Kreinick TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- src/runtime/pprof/pprof_test.go | 110 +++++++++++++++++++++++++++++++--------- src/runtime/traceback.go | 31 +++++++---- 2 files changed, 109 insertions(+), 32 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 44d514393e..095972fa68 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -73,14 +73,14 @@ func cpuHog2(x int) int { } func TestCPUProfile(t *testing.T) { - testCPUProfile(t, []string{"runtime/pprof.cpuHog1"}, func(dur time.Duration) { + testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1"}, func(dur time.Duration) { cpuHogger(cpuHog1, &salt1, dur) }) } func TestCPUProfileMultithreaded(t *testing.T) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) - testCPUProfile(t, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, func(dur time.Duration) { + testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, func(dur time.Duration) { c := make(chan int) go func() { cpuHogger(cpuHog1, &salt1, dur) @@ -92,7 +92,7 @@ func TestCPUProfileMultithreaded(t *testing.T) { } func TestCPUProfileInlining(t *testing.T) { - testCPUProfile(t, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, func(dur time.Duration) { + testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, func(dur time.Duration) { cpuHogger(inlinedCaller, &salt1, dur) }) } @@ -130,7 +130,9 @@ func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []*profile.Loca } } -func testCPUProfile(t *testing.T, need []string, f func(dur time.Duration)) { +// testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need, +// as interpreted by matches. +func testCPUProfile(t *testing.T, matches matchFunc, need []string, f func(dur time.Duration)) { switch runtime.GOOS { case "darwin": switch runtime.GOARCH { @@ -169,7 +171,7 @@ func testCPUProfile(t *testing.T, need []string, f func(dur time.Duration)) { f(duration) StopCPUProfile() - if profileOk(t, need, prof, duration) { + if profileOk(t, need, matches, prof, duration) { return } @@ -202,7 +204,21 @@ func contains(slice []string, s string) bool { return false } -func profileOk(t *testing.T, need []string, prof bytes.Buffer, duration time.Duration) (ok bool) { +// stackContains matches if a function named spec appears anywhere in the stack trace. +func stackContains(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool { + for _, loc := range stk { + for _, line := range loc.Line { + if strings.Contains(line.Function.Name, spec) { + return true + } + } + } + return false +} + +type matchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool + +func profileOk(t *testing.T, need []string, matches matchFunc, prof bytes.Buffer, duration time.Duration) (ok bool) { ok = true // Check that profile is well formed and contains need. @@ -213,20 +229,9 @@ func profileOk(t *testing.T, need []string, prof bytes.Buffer, duration time.Dur fmt.Fprintf(&buf, "%d:", count) fprintStack(&buf, stk) samples += count - for i, name := range need { - if semi := strings.Index(name, ";"); semi > -1 { - kv := strings.SplitN(name[semi+1:], "=", 2) - if len(kv) != 2 || !contains(labels[kv[0]], kv[1]) { - continue - } - name = name[:semi] - } - for _, loc := range stk { - for _, line := range loc.Line { - if strings.Contains(line.Function.Name, name) { - have[i] += count - } - } + for i, spec := range need { + if matches(spec, count, stk, labels) { + have[i] += count } } fmt.Fprintf(&buf, "\n") @@ -377,7 +382,7 @@ func fprintStack(w io.Writer, stk []*profile.Location) { // Test that profiling of division operations is okay, especially on ARM. See issue 6681. func TestMathBigDivide(t *testing.T) { - testCPUProfile(t, nil, func(duration time.Duration) { + testCPUProfile(t, nil, nil, func(duration time.Duration) { t := time.After(duration) pi := new(big.Int) for { @@ -395,6 +400,48 @@ func TestMathBigDivide(t *testing.T) { }) } +// stackContainsAll matches if all functions in spec (comma-separated) appear somewhere in the stack trace. +func stackContainsAll(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool { + for _, f := range strings.Split(spec, ",") { + if !stackContains(f, count, stk, labels) { + return false + } + } + return true +} + +func TestMorestack(t *testing.T) { + testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, func(duration time.Duration) { + t := time.After(duration) + c := make(chan bool) + for { + go func() { + growstack1() + c <- true + }() + select { + case <-t: + return + case <-c: + } + } + }) +} + +//go:noinline +func growstack1() { + growstack() +} + +//go:noinline +func growstack() { + var buf [8 << 10]byte + use(buf) +} + +//go:noinline +func use(x [8 << 10]byte) {} + func TestBlockProfile(t *testing.T) { type TestCase struct { name string @@ -848,8 +895,25 @@ func TestEmptyCallStack(t *testing.T) { } } +// stackContainsLabeled takes a spec like funcname;key=value and matches if the stack has that key +// and value and has funcname somewhere in the stack. +func stackContainsLabeled(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool { + semi := strings.Index(spec, ";") + if semi == -1 { + panic("no semicolon in key/value spec") + } + kv := strings.SplitN(spec[semi+1:], "=", 2) + if len(kv) != 2 { + panic("missing = in key/value spec") + } + if !contains(labels[kv[0]], kv[1]) { + return false + } + return stackContains(spec[:semi], count, stk, labels) +} + func TestCPUProfileLabel(t *testing.T) { - testCPUProfile(t, []string{"runtime/pprof.cpuHogger;key=value"}, func(dur time.Duration) { + testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, func(dur time.Duration) { Do(context.Background(), Labels("key", "value"), func(context.Context) { cpuHogger(cpuHog1, &salt1, dur) }) @@ -860,7 +924,7 @@ func TestLabelRace(t *testing.T) { // Test the race detector annotations for synchronization // between settings labels and consuming them from the // profile. - testCPUProfile(t, []string{"runtime/pprof.cpuHogger;key=value"}, func(dur time.Duration) { + testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, func(dur time.Duration) { start := time.Now() var wg sync.WaitGroup for time.Since(start) < dur { diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 78589f5ea3..8370fd7593 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -197,16 +197,29 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // Found an actual function. // Derive frame pointer and link register. if frame.fp == 0 { - // We want to jump over the systemstack switch. If we're running on the - // g0, this systemstack is at the top of the stack. - // if we're not on g0 or there's a no curg, then this is a regular call. - sp := frame.sp - if flags&_TraceJumpStack != 0 && f.funcID == funcID_systemstack && gp == gp.m.g0 && gp.m.curg != nil { - sp = gp.m.curg.sched.sp - frame.sp = sp - cgoCtxt = gp.m.curg.cgoCtxt + // Jump over system stack transitions. If we're on g0 and there's a user + // goroutine, try to jump. Otherwise this is a regular call. + if flags&_TraceJumpStack != 0 && gp == gp.m.g0 && gp.m.curg != nil { + switch f.funcID { + case funcID_morestack: + // morestack does not return normally -- newstack() + // gogo's to curg.sched. Match that. + // This keeps morestack() from showing up in the backtrace, + // but that makes some sense since it'll never be returned + // to. + frame.pc = gp.m.curg.sched.pc + frame.fn = findfunc(frame.pc) + f = frame.fn + frame.sp = gp.m.curg.sched.sp + cgoCtxt = gp.m.curg.cgoCtxt + case funcID_systemstack: + // systemstack returns normally, so just follow the + // stack transition. + frame.sp = gp.m.curg.sched.sp + cgoCtxt = gp.m.curg.cgoCtxt + } } - frame.fp = sp + uintptr(funcspdelta(f, frame.pc, &cache)) + frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc, &cache)) if !usesLR { // On x86, call instruction pushes return PC before entering new function. frame.fp += sys.RegSize -- cgit v1.3-5-g9baa From c15c04d9e85a6a2c46ae57cb830192e0eee276dc Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sat, 28 Jul 2018 10:56:48 +0200 Subject: runtime: use internal/cpu variables in assembler code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using internal/cpu variables has the benefit of avoiding false sharing (as those are padded) and allows memory and cache usage for these variables to be shared by multiple packages. Change-Id: I2bf68d03091bf52b466cf689230d5d25d5950037 Reviewed-on: https://go-review.googlesource.com/126599 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/asm_386.s | 2 +- src/runtime/cpuflags.go | 17 +++++++++++++++++ src/runtime/cpuflags_amd64.go | 6 ------ src/runtime/memclr_386.s | 3 ++- src/runtime/memclr_amd64.s | 2 +- src/runtime/memmove_386.s | 5 +++-- src/runtime/memmove_amd64.s | 3 ++- src/runtime/proc.go | 3 ++- src/runtime/runtime2.go | 5 ++--- 9 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 src/runtime/cpuflags.go (limited to 'src/runtime') diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index a6a81c3f63..725271eec4 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -881,7 +881,7 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0 // func cputicks() int64 TEXT runtime·cputicks(SB),NOSPLIT,$0-8 - CMPB runtime·support_sse2(SB), $1 + CMPB internal∕cpu·X86+const_offset_x86_HasSSE2(SB), $1 JNE done CMPB runtime·lfenceBeforeRdtsc(SB), $1 JNE mfence diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go new file mode 100644 index 0000000000..dee6116a90 --- /dev/null +++ b/src/runtime/cpuflags.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import ( + "internal/cpu" + "unsafe" +) + +// Offsets into internal/cpu records for use in assembly. +const ( + offset_x86_HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + offset_x86_HasERMS = unsafe.Offsetof(cpu.X86.HasERMS) + offset_x86_HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) +) diff --git a/src/runtime/cpuflags_amd64.go b/src/runtime/cpuflags_amd64.go index 10ab5f5b00..8cca4bca8f 100644 --- a/src/runtime/cpuflags_amd64.go +++ b/src/runtime/cpuflags_amd64.go @@ -6,12 +6,6 @@ package runtime import ( "internal/cpu" - "unsafe" -) - -// Offsets into internal/cpu records for use in assembly. -const ( - offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) ) var useAVXmemmove bool diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s index a6703b3641..318f883964 100644 --- a/src/runtime/memclr_386.s +++ b/src/runtime/memclr_386.s @@ -4,6 +4,7 @@ // +build !plan9 +#include "go_asm.h" #include "textflag.h" // NOTE: Windows externalthreadhandler expects memclr to preserve DX. @@ -28,7 +29,7 @@ tail: JBE _5through8 CMPL BX, $16 JBE _9through16 - CMPB runtime·support_sse2(SB), $1 + CMPB internal∕cpu·X86+const_offset_x86_HasSSE2(SB), $1 JNE nosse2 PXOR X0, X0 CMPL BX, $32 diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s index d79078fd00..b64b1477f9 100644 --- a/src/runtime/memclr_amd64.s +++ b/src/runtime/memclr_amd64.s @@ -38,7 +38,7 @@ tail: JBE _65through128 CMPQ BX, $256 JBE _129through256 - CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 + CMPB internal∕cpu·X86+const_offset_x86_HasAVX2(SB), $1 JE loop_preheader_avx2 // TODO: for really big clears, use MOVNTDQ, even without AVX2. diff --git a/src/runtime/memmove_386.s b/src/runtime/memmove_386.s index 172ea40820..85c622b6b6 100644 --- a/src/runtime/memmove_386.s +++ b/src/runtime/memmove_386.s @@ -25,6 +25,7 @@ // +build !plan9 +#include "go_asm.h" #include "textflag.h" // func memmove(to, from unsafe.Pointer, n uintptr) @@ -51,7 +52,7 @@ tail: JBE move_5through8 CMPL BX, $16 JBE move_9through16 - CMPB runtime·support_sse2(SB), $1 + CMPB internal∕cpu·X86+const_offset_x86_HasSSE2(SB), $1 JNE nosse2 CMPL BX, $32 JBE move_17through32 @@ -72,7 +73,7 @@ nosse2: */ forward: // If REP MOVSB isn't fast, don't use it - CMPB runtime·support_erms(SB), $1 // enhanced REP MOVSB/STOSB + CMPB internal∕cpu·X86+const_offset_x86_HasERMS(SB), $1 // enhanced REP MOVSB/STOSB JNE fwdBy4 // Check alignment diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s index cb5cd02e45..c5385a3d43 100644 --- a/src/runtime/memmove_amd64.s +++ b/src/runtime/memmove_amd64.s @@ -25,6 +25,7 @@ // +build !plan9 +#include "go_asm.h" #include "textflag.h" // func memmove(to, from unsafe.Pointer, n uintptr) @@ -83,7 +84,7 @@ forward: JLS move_256through2048 // If REP MOVSB isn't fast, don't use it - CMPB runtime·support_erms(SB), $1 // enhanced REP MOVSB/STOSB + CMPB internal∕cpu·X86+const_offset_x86_HasERMS(SB), $1 // enhanced REP MOVSB/STOSB JNE fwdBy8 // Check alignment diff --git a/src/runtime/proc.go b/src/runtime/proc.go index c9cc7544b8..75d309a9f6 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -507,7 +507,8 @@ func cpuinit() { cpu.Initialize(env) - support_erms = cpu.X86.HasERMS + // Support cpu feature variables are used in code generated by the compiler + // to guard execution of instructions that can not be assumed to be always supported. support_popcnt = cpu.X86.HasPOPCNT support_sse2 = cpu.X86.HasSSE2 support_sse41 = cpu.X86.HasSSE41 diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index bbbe1ee852..9311924942 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -836,16 +836,15 @@ var ( newprocs int32 // Information about what cpu features are available. - // Set on startup in runtime.cpuinit. // Packages outside the runtime should not use these // as they are not an external api. - // TODO: deprecate these; use internal/cpu directly. + // Set on startup in asm_{386,amd64,amd64p32}.s processorVersionInfo uint32 isIntel bool lfenceBeforeRdtsc bool // Set in runtime.cpuinit. - support_erms bool + // TODO: deprecate these; use internal/cpu directly. support_popcnt bool support_sse2 bool support_sse41 bool -- cgit v1.3-5-g9baa From 96dcc4457b9aad418abc0eb4316c21fefdbf08e1 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Fri, 1 Jun 2018 14:30:49 +0200 Subject: runtime: replace typedmemmmove with bulkBarrierPreWrite and memmove in growslice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bulkBarrierPreWrite together with a memmove as used in typedslicecopy is faster than a typedmemmove for each element of the old slice that needs to be copied to the new slice. typedslicecopy is not used here as runtime functions should not call other instrumented runtime functions and some conditions like dst == src or the destination slice not being large enought that are checked for in typedslicecopy can not happen in growslice. Append 13.5ns ± 6% 13.3ns ± 3% ~ (p=0.304 n=10+10) AppendGrowByte 1.18ms ± 2% 1.19ms ± 1% ~ (p=0.113 n=10+9) AppendGrowString 123ms ± 1% 73ms ± 1% -40.39% (p=0.000 n=9+8) AppendSlice/1Bytes 3.81ns ± 1% 3.78ns ± 1% ~ (p=0.116 n=10+10) AppendSlice/4Bytes 3.71ns ± 1% 3.70ns ± 0% ~ (p=0.095 n=10+9) AppendSlice/7Bytes 3.73ns ± 0% 3.75ns ± 1% ~ (p=0.442 n=10+10) AppendSlice/8Bytes 4.00ns ± 1% 4.01ns ± 1% ~ (p=0.330 n=10+10) AppendSlice/15Bytes 4.29ns ± 1% 4.28ns ± 1% ~ (p=0.536 n=10+10) AppendSlice/16Bytes 4.28ns ± 1% 4.31ns ± 1% +0.75% (p=0.019 n=10+10) AppendSlice/32Bytes 4.57ns ± 2% 4.58ns ± 2% ~ (p=0.236 n=10+10) AppendSliceLarge/1024Bytes 305ns ± 2% 306ns ± 1% ~ (p=0.236 n=10+10) AppendSliceLarge/4096Bytes 1.06µs ± 1% 1.06µs ± 0% ~ (p=1.000 n=9+10) AppendSliceLarge/16384Bytes 3.12µs ± 2% 3.11µs ± 1% ~ (p=0.493 n=10+10) AppendSliceLarge/65536Bytes 5.61µs ± 5% 5.36µs ± 2% -4.58% (p=0.003 n=10+8) AppendSliceLarge/262144Bytes 21.0µs ± 1% 19.5µs ± 1% -7.09% (p=0.000 n=8+10) AppendSliceLarge/1048576Bytes 78.4µs ± 1% 78.7µs ± 2% ~ (p=0.315 n=8+10) AppendStr/1Bytes 3.96ns ± 6% 3.99ns ± 9% ~ (p=0.591 n=10+10) AppendStr/4Bytes 3.98ns ± 1% 3.99ns ± 1% ~ (p=0.515 n=9+9) AppendStr/8Bytes 4.27ns ± 1% 4.27ns ± 1% ~ (p=0.633 n=10+10) AppendStr/16Bytes 4.56ns ± 2% 4.55ns ± 1% ~ (p=0.869 n=10+10) AppendStr/32Bytes 4.85ns ± 1% 4.89ns ± 1% +0.71% (p=0.003 n=10+8) AppendSpecialCase 18.7ns ± 1% 18.7ns ± 1% ~ (p=0.144 n=10+10) AppendInPlace/NoGrow/Byte 438ns ± 1% 439ns ± 1% ~ (p=0.135 n=10+8) AppendInPlace/NoGrow/1Ptr 1.05µs ± 2% 1.05µs ± 1% ~ (p=0.469 n=10+10) AppendInPlace/NoGrow/2Ptr 1.77µs ± 1% 1.78µs ± 2% ~ (p=0.469 n=10+10) AppendInPlace/NoGrow/3Ptr 1.94µs ± 1% 1.93µs ± 2% ~ (p=0.517 n=10+10) AppendInPlace/NoGrow/4Ptr 3.18µs ± 1% 3.17µs ± 0% ~ (p=0.483 n=10+9) AppendInPlace/Grow/Byte 382ns ± 2% 383ns ± 2% ~ (p=0.705 n=9+10) AppendInPlace/Grow/1Ptr 383ns ± 1% 384ns ± 1% ~ (p=0.844 n=10+10) AppendInPlace/Grow/2Ptr 459ns ± 2% 467ns ± 2% +1.74% (p=0.001 n=10+10) AppendInPlace/Grow/3Ptr 593ns ± 1% 597ns ± 2% ~ (p=0.195 n=10+10) AppendInPlace/Grow/4Ptr 583ns ± 2% 589ns ± 2% ~ (p=0.084 n=10+10) Change-Id: I629872f065a22b29267c1adbfc578aaedd36d365 Reviewed-on: https://go-review.googlesource.com/115755 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/mbarrier.go | 2 -- src/runtime/slice.go | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index b6c5ee0658..5142f4327a 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -226,8 +226,6 @@ func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr) { //go:nosplit func typedslicecopy(typ *_type, dst, src slice) int { - // TODO(rsc): If typedslicecopy becomes faster than calling - // typedmemmove repeatedly, consider using during func growslice. n := dst.len if n > src.len { n = src.len diff --git a/src/runtime/slice.go b/src/runtime/slice.go index fd5d08b52c..737aab5704 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -195,21 +195,17 @@ func growslice(et *_type, old slice, cap int) slice { var p unsafe.Pointer if et.kind&kindNoPointers != 0 { p = mallocgc(capmem, nil, false) - memmove(p, old.array, lenmem) // The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length). // Only clear the part that will not be overwritten. memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. p = mallocgc(capmem, et, true) - if !writeBarrier.enabled { - memmove(p, old.array, lenmem) - } else { - for i := uintptr(0); i < lenmem; i += et.size { - typedmemmove(et, add(p, i), add(old.array, i)) - } + if writeBarrier.enabled { + bulkBarrierPreWrite(uintptr(p), uintptr(old.array), lenmem) } } + memmove(p, old.array, lenmem) return slice{p, old.len, newcap} } -- cgit v1.3-5-g9baa From 4363c98f62e9e315ed20b12d2ce47021fd2bf7bc Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sun, 3 Jun 2018 13:00:19 +0200 Subject: runtime: do not execute write barrier on newly allocated slice in growslice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new slice created in growslice is cleared during malloc for element types containing pointers and therefore can only contain nil pointers. This change avoids executing write barriers for these nil pointers by adding and using a special bulkBarrierPreWriteSrcOnly function that does not enqueue pointers to slots in dst to the write barrier buffer. Change-Id: If9b18248bfeeb6a874b0132d19520adea593bfc4 Reviewed-on: https://go-review.googlesource.com/115996 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/mbitmap.go | 29 +++++++++++++++++++++++++++++ src/runtime/slice.go | 4 +++- 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'src/runtime') diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 75f23a16b4..e217e7695f 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -647,6 +647,35 @@ func bulkBarrierPreWrite(dst, src, size uintptr) { } } +// bulkBarrierPreWriteSrcOnly is like bulkBarrierPreWrite but +// does not execute write barriers for [dst, dst+size). +// +// In addition to the requirements of bulkBarrierPreWrite +// callers need to ensure [dst, dst+size) is zeroed. +// +// This is used for special cases where e.g. dst was just +// created and zeroed with malloc. +//go:nosplit +func bulkBarrierPreWriteSrcOnly(dst, src, size uintptr) { + if (dst|src|size)&(sys.PtrSize-1) != 0 { + throw("bulkBarrierPreWrite: unaligned arguments") + } + if !writeBarrier.needed { + return + } + buf := &getg().m.p.ptr().wbBuf + h := heapBitsForAddr(dst) + for i := uintptr(0); i < size; i += sys.PtrSize { + if h.isPointer() { + srcx := (*uintptr)(unsafe.Pointer(src + i)) + if !buf.putFast(0, *srcx) { + wbBufFlush(nil, 0) + } + } + h = h.next() + } +} + // bulkBarrierBitmap executes write barriers for copying from [src, // src+size) to [dst, dst+size) using a 1-bit pointer bitmap. src is // assumed to start maskOffset bytes into the data covered by the diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 737aab5704..4206f4384a 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -202,7 +202,9 @@ func growslice(et *_type, old slice, cap int) slice { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. p = mallocgc(capmem, et, true) if writeBarrier.enabled { - bulkBarrierPreWrite(uintptr(p), uintptr(old.array), lenmem) + // Only shade the pointers in old.array since we know the destination slice p + // only contains nil pointers because it has been cleared during alloc. + bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem) } } memmove(p, old.array, lenmem) -- cgit v1.3-5-g9baa From 2e8c31b3d2afce1c1c7b0c6af9cc4a9f296af299 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sun, 27 May 2018 08:49:36 +0200 Subject: runtime: move arm hardware division support detection to internal/cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assumes mandatory VFP and VFPv3 support to be present by default but not IDIVA if AT_HWCAP is not available. Adds GODEBUGCPU options to disable the use of code paths in the runtime that use hardware support for division. Change-Id: Ida02311bd9b9701de3fc120697e69445bf6c0853 Reviewed-on: https://go-review.googlesource.com/114826 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Keith Randall --- src/internal/cpu/cpu.go | 10 ++++++++++ src/internal/cpu/cpu_arm.go | 25 +++++++++++++++++++++++++ src/internal/cpu/cpu_no_init.go | 1 + src/runtime/cpuflags.go | 2 ++ src/runtime/os_darwin_arm.go | 2 -- src/runtime/os_freebsd.go | 1 + src/runtime/os_freebsd_arm.go | 25 +++++++++++++++++-------- src/runtime/os_linux_arm.go | 19 ++++++++++--------- src/runtime/os_nacl_arm.go | 2 -- src/runtime/os_netbsd_arm.go | 2 -- src/runtime/os_openbsd_arm.go | 2 -- src/runtime/os_plan9_arm.go | 2 -- src/runtime/vlop_arm.s | 2 +- 13 files changed, 67 insertions(+), 28 deletions(-) (limited to 'src/runtime') diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go index f2dfadbff8..2b5db91fe2 100644 --- a/src/internal/cpu/cpu.go +++ b/src/internal/cpu/cpu.go @@ -66,6 +66,16 @@ type ppc64 struct { _ CacheLinePad } +var ARM arm + +// The booleans in arm contain the correspondingly named cpu feature bit. +// The struct is padded to avoid false sharing. +type arm struct { + _ CacheLinePad + HasIDIVA bool + _ CacheLinePad +} + var ARM64 arm64 // The booleans in arm64 contain the correspondingly named cpu feature bit. diff --git a/src/internal/cpu/cpu_arm.go b/src/internal/cpu/cpu_arm.go index 078a6c3b80..b9baa44fea 100644 --- a/src/internal/cpu/cpu_arm.go +++ b/src/internal/cpu/cpu_arm.go @@ -5,3 +5,28 @@ package cpu const CacheLineSize = 32 + +// arm doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. +// These are linknamed in runtime/os_(linux|freebsd)_arm.go and are +// initialized by archauxv(). +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint + +// HWCAP/HWCAP2 bits. These are exposed by Linux and FreeBSD. +const ( + hwcap_IDIVA = 1 << 17 +) + +func doinit() { + options = []option{ + {"idiva", &ARM.HasIDIVA}, + } + + // HWCAP feature bits + ARM.HasIDIVA = isSet(HWCap, hwcap_IDIVA) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/src/internal/cpu/cpu_no_init.go b/src/internal/cpu/cpu_no_init.go index 1be4f29ddd..777ea9de8b 100644 --- a/src/internal/cpu/cpu_no_init.go +++ b/src/internal/cpu/cpu_no_init.go @@ -5,6 +5,7 @@ // +build !386 // +build !amd64 // +build !amd64p32 +// +build !arm // +build !arm64 // +build !ppc64 // +build !ppc64le diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go index dee6116a90..050168c2d7 100644 --- a/src/runtime/cpuflags.go +++ b/src/runtime/cpuflags.go @@ -14,4 +14,6 @@ const ( offset_x86_HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) offset_x86_HasERMS = unsafe.Offsetof(cpu.X86.HasERMS) offset_x86_HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) + + offset_arm_HasIDIVA = unsafe.Offsetof(cpu.ARM.HasIDIVA) ) diff --git a/src/runtime/os_darwin_arm.go b/src/runtime/os_darwin_arm.go index 8eb5655969..ee1bd174f1 100644 --- a/src/runtime/os_darwin_arm.go +++ b/src/runtime/os_darwin_arm.go @@ -4,8 +4,6 @@ package runtime -var hardDiv bool // TODO: set if a hardware divider is available - func checkgoarm() { // TODO(minux): FP checks like in os_linux_arm.go. diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index 631dc20ab4..08f7b0ecf0 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -389,6 +389,7 @@ const ( _AT_PAGESZ = 6 // Page size in bytes _AT_TIMEKEEP = 22 // Pointer to timehands. _AT_HWCAP = 25 // CPU feature flags + _AT_HWCAP2 = 26 // CPU feature flags 2 ) func sysauxv(auxv []uintptr) { diff --git a/src/runtime/os_freebsd_arm.go b/src/runtime/os_freebsd_arm.go index d2dc26f0c4..eb4de9bc21 100644 --- a/src/runtime/os_freebsd_arm.go +++ b/src/runtime/os_freebsd_arm.go @@ -4,22 +4,29 @@ package runtime +import "internal/cpu" + const ( _HWCAP_VFP = 1 << 6 _HWCAP_VFPv3 = 1 << 13 - _HWCAP_IDIVA = 1 << 17 ) -var hwcap = ^uint32(0) // set by archauxv -var hardDiv bool // set if a hardware divider is available +// AT_HWCAP is not available on FreeBSD-11.1-RELEASE or earlier. +// Default to mandatory VFP hardware support for arm being available. +// If AT_HWCAP is available goarmHWCap will be updated in archauxv. +// TODO(moehrmann) remove once all go supported FreeBSD versions support _AT_HWCAP. +var goarmHWCap uint = (_HWCAP_VFP | _HWCAP_VFPv3) func checkgoarm() { - if goarm > 5 && hwcap&_HWCAP_VFP == 0 { + // Update cpu.HWCap to match goarmHWCap in case they were not updated in archauxv. + cpu.HWCap = goarmHWCap + + if goarm > 5 && cpu.HWCap&_HWCAP_VFP == 0 { print("runtime: this CPU has no floating point hardware, so it cannot run\n") print("this GOARM=", goarm, " binary. Recompile using GOARM=5.\n") exit(1) } - if goarm > 6 && hwcap&_HWCAP_VFPv3 == 0 { + if goarm > 6 && cpu.HWCap&_HWCAP_VFPv3 == 0 { print("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n") print("this GOARM=", goarm, " binary. Recompile using GOARM=5 or GOARM=6.\n") exit(1) @@ -35,9 +42,11 @@ func checkgoarm() { func archauxv(tag, val uintptr) { switch tag { - case _AT_HWCAP: // CPU capability bit flags - hwcap = uint32(val) - hardDiv = (hwcap & _HWCAP_IDIVA) != 0 + case _AT_HWCAP: + cpu.HWCap = uint(val) + goarmHWCap = cpu.HWCap + case _AT_HWCAP2: + cpu.HWCap2 = uint(val) } } diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go index 14f1cfeaef..8f082ba6a0 100644 --- a/src/runtime/os_linux_arm.go +++ b/src/runtime/os_linux_arm.go @@ -4,20 +4,20 @@ package runtime -import "unsafe" +import ( + "internal/cpu" + "unsafe" +) const ( _AT_PLATFORM = 15 // introduced in at least 2.6.11 _HWCAP_VFP = 1 << 6 // introduced in at least 2.6.11 _HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30 - _HWCAP_IDIVA = 1 << 17 ) var randomNumber uint32 var armArch uint8 = 6 // we default to ARMv6 -var hwcap uint32 // set by archauxv -var hardDiv bool // set if a hardware divider is available func checkgoarm() { // On Android, /proc/self/auxv might be unreadable and hwcap won't @@ -26,12 +26,12 @@ func checkgoarm() { if GOOS == "android" { return } - if goarm > 5 && hwcap&_HWCAP_VFP == 0 { + if goarm > 5 && cpu.HWCap&_HWCAP_VFP == 0 { print("runtime: this CPU has no floating point hardware, so it cannot run\n") print("this GOARM=", goarm, " binary. Recompile using GOARM=5.\n") exit(1) } - if goarm > 6 && hwcap&_HWCAP_VFPv3 == 0 { + if goarm > 6 && cpu.HWCap&_HWCAP_VFPv3 == 0 { print("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n") print("this GOARM=", goarm, " binary. Recompile using GOARM=5 or GOARM=6.\n") exit(1) @@ -53,9 +53,10 @@ func archauxv(tag, val uintptr) { armArch = t - '0' } - case _AT_HWCAP: // CPU capability bit flags - hwcap = uint32(val) - hardDiv = (hwcap & _HWCAP_IDIVA) != 0 + case _AT_HWCAP: + cpu.HWCap = uint(val) + case _AT_HWCAP2: + cpu.HWCap2 = uint(val) } } diff --git a/src/runtime/os_nacl_arm.go b/src/runtime/os_nacl_arm.go index c64ebf31d3..8669ee75b4 100644 --- a/src/runtime/os_nacl_arm.go +++ b/src/runtime/os_nacl_arm.go @@ -4,8 +4,6 @@ package runtime -var hardDiv bool // TODO: set if a hardware divider is available - func checkgoarm() { // TODO(minux): FP checks like in os_linux_arm.go. diff --git a/src/runtime/os_netbsd_arm.go b/src/runtime/os_netbsd_arm.go index b02e36a73a..95603da643 100644 --- a/src/runtime/os_netbsd_arm.go +++ b/src/runtime/os_netbsd_arm.go @@ -6,8 +6,6 @@ package runtime import "unsafe" -var hardDiv bool // TODO: set if a hardware divider is available - func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { // Machine dependent mcontext initialisation for LWP. mc.__gregs[_REG_R15] = uint32(funcPC(lwp_tramp)) diff --git a/src/runtime/os_openbsd_arm.go b/src/runtime/os_openbsd_arm.go index c318578ab5..be2e1e9959 100644 --- a/src/runtime/os_openbsd_arm.go +++ b/src/runtime/os_openbsd_arm.go @@ -4,8 +4,6 @@ package runtime -var hardDiv bool // TODO: set if a hardware divider is available - func checkgoarm() { // TODO(minux): FP checks like in os_linux_arm.go. diff --git a/src/runtime/os_plan9_arm.go b/src/runtime/os_plan9_arm.go index 1ce0141ce2..fdce1e7a35 100644 --- a/src/runtime/os_plan9_arm.go +++ b/src/runtime/os_plan9_arm.go @@ -4,8 +4,6 @@ package runtime -var hardDiv bool // TODO: set if a hardware divider is available - func checkgoarm() { return // TODO(minux) } diff --git a/src/runtime/vlop_arm.s b/src/runtime/vlop_arm.s index d48e515d32..8df13abd98 100644 --- a/src/runtime/vlop_arm.s +++ b/src/runtime/vlop_arm.s @@ -44,7 +44,7 @@ // the RET instruction will clobber R12 on nacl, and the compiler's register // allocator needs to know. TEXT runtime·udiv(SB),NOSPLIT|NOFRAME,$0 - MOVBU runtime·hardDiv(SB), Ra + MOVBU internal∕cpu·ARM+const_offset_arm_HasIDIVA(SB), Ra CMP $0, Ra BNE udiv_hardware -- cgit v1.3-5-g9baa From 961eb13b6781907b5bfe4a7b22f68206020c4468 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Tue, 5 Jun 2018 08:14:57 +0200 Subject: runtime: replace sys.CacheLineSize by corresponding internal/cpu const and vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sys here is runtime/internal/sys. Replace uses of sys.CacheLineSize for padding by cpu.CacheLinePad or cpu.CacheLinePadSize. Replace other uses of sys.CacheLineSize by cpu.CacheLineSize. Remove now unused sys.CacheLineSize. Updates #25203 Change-Id: I1daf410fe8f6c0493471c2ceccb9ca0a5a75ed8f Reviewed-on: https://go-review.googlesource.com/126601 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/go/build/deps_test.go | 2 +- src/internal/cpu/cpu.go | 2 +- src/runtime/internal/atomic/atomic_arm.go | 4 ++-- src/runtime/internal/atomic/atomic_mipsx.go | 4 ++-- src/runtime/internal/sys/arch_386.go | 1 - src/runtime/internal/sys/arch_amd64.go | 1 - src/runtime/internal/sys/arch_amd64p32.go | 1 - src/runtime/internal/sys/arch_arm.go | 1 - src/runtime/internal/sys/arch_arm64.go | 1 - src/runtime/internal/sys/arch_mips.go | 1 - src/runtime/internal/sys/arch_mips64.go | 1 - src/runtime/internal/sys/arch_mips64le.go | 1 - src/runtime/internal/sys/arch_mipsle.go | 1 - src/runtime/internal/sys/arch_ppc64.go | 1 - src/runtime/internal/sys/arch_ppc64le.go | 1 - src/runtime/internal/sys/arch_s390x.go | 1 - src/runtime/internal/sys/arch_wasm.go | 1 - src/runtime/mgc.go | 10 +++++----- src/runtime/mgcsweepbuf.go | 5 +++-- src/runtime/mheap.go | 5 +++-- src/runtime/runtime2.go | 3 ++- src/runtime/sema.go | 4 ++-- src/runtime/time.go | 4 ++-- 23 files changed, 23 insertions(+), 33 deletions(-) (limited to 'src/runtime') diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 729d0db51f..244c745d41 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -38,7 +38,7 @@ var pkgDeps = map[string][]string{ "io": {"errors", "sync", "sync/atomic"}, "runtime": {"unsafe", "runtime/internal/atomic", "runtime/internal/sys", "internal/cpu", "internal/bytealg"}, "runtime/internal/sys": {}, - "runtime/internal/atomic": {"unsafe", "runtime/internal/sys"}, + "runtime/internal/atomic": {"unsafe", "internal/cpu"}, "internal/race": {"runtime", "unsafe"}, "sync": {"internal/race", "runtime", "sync/atomic", "unsafe"}, "sync/atomic": {"unsafe"}, diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go index 5363f11b90..bfb016c7f7 100644 --- a/src/internal/cpu/cpu.go +++ b/src/internal/cpu/cpu.go @@ -17,7 +17,7 @@ type CacheLinePad struct{ _ [CacheLinePadSize]byte } // CacheLineSize is the CPU's assumed cache line size. // There is currently no runtime detection of the real cache line size // so we use the constant per GOARCH CacheLinePadSize as an approximation. -var CacheLineSize = CacheLinePadSize +var CacheLineSize uintptr = CacheLinePadSize var X86 x86 diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go index 4ed7e991fe..1ecdb11db9 100644 --- a/src/runtime/internal/atomic/atomic_arm.go +++ b/src/runtime/internal/atomic/atomic_arm.go @@ -7,7 +7,7 @@ package atomic import ( - "runtime/internal/sys" + "internal/cpu" "unsafe" ) @@ -31,7 +31,7 @@ func (l *spinlock) unlock() { var locktab [57]struct { l spinlock - pad [sys.CacheLineSize - unsafe.Sizeof(spinlock{})]byte + pad [cpu.CacheLinePadSize - unsafe.Sizeof(spinlock{})]byte } func addrLock(addr *uint64) *spinlock { diff --git a/src/runtime/internal/atomic/atomic_mipsx.go b/src/runtime/internal/atomic/atomic_mipsx.go index 32be1c779d..55943f6925 100644 --- a/src/runtime/internal/atomic/atomic_mipsx.go +++ b/src/runtime/internal/atomic/atomic_mipsx.go @@ -7,14 +7,14 @@ package atomic import ( - "runtime/internal/sys" + "internal/cpu" "unsafe" ) // TODO implement lock striping var lock struct { state uint32 - pad [sys.CacheLineSize - 4]byte + pad [cpu.CacheLinePadSize - 4]byte } //go:noescape diff --git a/src/runtime/internal/sys/arch_386.go b/src/runtime/internal/sys/arch_386.go index 5fb1fba02b..5375701337 100644 --- a/src/runtime/internal/sys/arch_386.go +++ b/src/runtime/internal/sys/arch_386.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = I386 BigEndian = false - CacheLineSize = 64 DefaultPhysPageSize = GoosNacl*65536 + (1-GoosNacl)*4096 // 4k normally; 64k on NaCl PCQuantum = 1 Int64Align = 4 diff --git a/src/runtime/internal/sys/arch_amd64.go b/src/runtime/internal/sys/arch_amd64.go index 2f32bc469f..86fed4d531 100644 --- a/src/runtime/internal/sys/arch_amd64.go +++ b/src/runtime/internal/sys/arch_amd64.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = AMD64 BigEndian = false - CacheLineSize = 64 DefaultPhysPageSize = 4096 PCQuantum = 1 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_amd64p32.go b/src/runtime/internal/sys/arch_amd64p32.go index c560907c67..749d724809 100644 --- a/src/runtime/internal/sys/arch_amd64p32.go +++ b/src/runtime/internal/sys/arch_amd64p32.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = AMD64 BigEndian = false - CacheLineSize = 64 DefaultPhysPageSize = 65536*GoosNacl + 4096*(1-GoosNacl) PCQuantum = 1 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_arm.go b/src/runtime/internal/sys/arch_arm.go index f383d82027..2af09e0e35 100644 --- a/src/runtime/internal/sys/arch_arm.go +++ b/src/runtime/internal/sys/arch_arm.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = ARM BigEndian = false - CacheLineSize = 32 DefaultPhysPageSize = 65536 PCQuantum = 4 Int64Align = 4 diff --git a/src/runtime/internal/sys/arch_arm64.go b/src/runtime/internal/sys/arch_arm64.go index cb83ecc445..f13d2de129 100644 --- a/src/runtime/internal/sys/arch_arm64.go +++ b/src/runtime/internal/sys/arch_arm64.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = ARM64 BigEndian = false - CacheLineSize = 64 DefaultPhysPageSize = 65536 PCQuantum = 4 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_mips.go b/src/runtime/internal/sys/arch_mips.go index e12f32d0ee..e9bd69c928 100644 --- a/src/runtime/internal/sys/arch_mips.go +++ b/src/runtime/internal/sys/arch_mips.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = MIPS BigEndian = true - CacheLineSize = 32 DefaultPhysPageSize = 65536 PCQuantum = 4 Int64Align = 4 diff --git a/src/runtime/internal/sys/arch_mips64.go b/src/runtime/internal/sys/arch_mips64.go index 973ec10e17..5eb7b2b7b1 100644 --- a/src/runtime/internal/sys/arch_mips64.go +++ b/src/runtime/internal/sys/arch_mips64.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = MIPS64 BigEndian = true - CacheLineSize = 32 DefaultPhysPageSize = 16384 PCQuantum = 4 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_mips64le.go b/src/runtime/internal/sys/arch_mips64le.go index e96d962f36..14c804ed85 100644 --- a/src/runtime/internal/sys/arch_mips64le.go +++ b/src/runtime/internal/sys/arch_mips64le.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = MIPS64 BigEndian = false - CacheLineSize = 32 DefaultPhysPageSize = 16384 PCQuantum = 4 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_mipsle.go b/src/runtime/internal/sys/arch_mipsle.go index 25742ae9d3..91badb17d5 100644 --- a/src/runtime/internal/sys/arch_mipsle.go +++ b/src/runtime/internal/sys/arch_mipsle.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = MIPS BigEndian = false - CacheLineSize = 32 DefaultPhysPageSize = 65536 PCQuantum = 4 Int64Align = 4 diff --git a/src/runtime/internal/sys/arch_ppc64.go b/src/runtime/internal/sys/arch_ppc64.go index a538bbdec0..8cde4e18d0 100644 --- a/src/runtime/internal/sys/arch_ppc64.go +++ b/src/runtime/internal/sys/arch_ppc64.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = PPC64 BigEndian = true - CacheLineSize = 128 DefaultPhysPageSize = 65536 PCQuantum = 4 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_ppc64le.go b/src/runtime/internal/sys/arch_ppc64le.go index aa50689181..10c0066849 100644 --- a/src/runtime/internal/sys/arch_ppc64le.go +++ b/src/runtime/internal/sys/arch_ppc64le.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = PPC64 BigEndian = false - CacheLineSize = 128 DefaultPhysPageSize = 65536 PCQuantum = 4 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_s390x.go b/src/runtime/internal/sys/arch_s390x.go index e42c420a54..77fd4bf07d 100644 --- a/src/runtime/internal/sys/arch_s390x.go +++ b/src/runtime/internal/sys/arch_s390x.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = S390X BigEndian = true - CacheLineSize = 256 DefaultPhysPageSize = 4096 PCQuantum = 2 Int64Align = 8 diff --git a/src/runtime/internal/sys/arch_wasm.go b/src/runtime/internal/sys/arch_wasm.go index 5463f934d6..203fc2e472 100644 --- a/src/runtime/internal/sys/arch_wasm.go +++ b/src/runtime/internal/sys/arch_wasm.go @@ -7,7 +7,6 @@ package sys const ( ArchFamily = WASM BigEndian = false - CacheLineSize = 64 DefaultPhysPageSize = 65536 PCQuantum = 1 Int64Align = 8 diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index f54c8eb14f..c95b5ed37f 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -137,8 +137,8 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -414,7 +414,7 @@ type gcControllerState struct { // If this is zero, no fractional workers are needed. fractionalUtilizationGoal float64 - _ [sys.CacheLineSize]byte + _ cpu.CacheLinePad } // startCycle resets the GC controller's state and computes estimates @@ -919,9 +919,9 @@ const gcAssistTimeSlack = 5000 const gcOverAssistWork = 64 << 10 var work struct { - full lfstack // lock-free list of full blocks workbuf - empty lfstack // lock-free list of empty blocks workbuf - pad0 [sys.CacheLineSize]uint8 // prevents false-sharing between full/empty and nproc/nwait + full lfstack // lock-free list of full blocks workbuf + empty lfstack // lock-free list of empty blocks workbuf + pad0 cpu.CacheLinePad // prevents false-sharing between full/empty and nproc/nwait wbufSpans struct { lock mutex diff --git a/src/runtime/mgcsweepbuf.go b/src/runtime/mgcsweepbuf.go index 6c1118e385..0491f7ccf6 100644 --- a/src/runtime/mgcsweepbuf.go +++ b/src/runtime/mgcsweepbuf.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -83,7 +84,7 @@ retry: if newCap == 0 { newCap = gcSweepBufInitSpineCap } - newSpine := persistentalloc(newCap*sys.PtrSize, sys.CacheLineSize, &memstats.gc_sys) + newSpine := persistentalloc(newCap*sys.PtrSize, cpu.CacheLineSize, &memstats.gc_sys) if b.spineCap != 0 { // Blocks are allocated off-heap, so // no write barriers. @@ -102,7 +103,7 @@ retry: } // Allocate a new block and add it to the spine. - block = (*gcSweepBlock)(persistentalloc(unsafe.Sizeof(gcSweepBlock{}), sys.CacheLineSize, &memstats.gc_sys)) + block = (*gcSweepBlock)(persistentalloc(unsafe.Sizeof(gcSweepBlock{}), cpu.CacheLineSize, &memstats.gc_sys)) blockp := add(b.spine, sys.PtrSize*top) // Blocks are allocated off-heap, so no write barrier. atomic.StorepNoWB(blockp, unsafe.Pointer(block)) diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index b11853ca18..00ecfa2d66 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -9,6 +9,7 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -137,12 +138,12 @@ type mheap struct { // central free lists for small size classes. // the padding makes sure that the MCentrals are - // spaced CacheLineSize bytes apart, so that each MCentral.lock + // spaced CacheLinePadSize bytes apart, so that each MCentral.lock // gets its own cache line. // central is indexed by spanClass. central [numSpanClasses]struct { mcentral mcentral - pad [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte + pad [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte } spanalloc fixalloc // allocator for span* diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 9311924942..e4c6b3b52a 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -548,7 +549,7 @@ type p struct { runSafePointFn uint32 // if 1, run sched.safePointFn at next safe point - pad [sys.CacheLineSize]byte + pad cpu.CacheLinePad } type schedt struct { diff --git a/src/runtime/sema.go b/src/runtime/sema.go index aba9733127..18e0a398ba 100644 --- a/src/runtime/sema.go +++ b/src/runtime/sema.go @@ -20,8 +20,8 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -48,7 +48,7 @@ const semTabSize = 251 var semtable [semTabSize]struct { root semaRoot - pad [sys.CacheLineSize - unsafe.Sizeof(semaRoot{})]byte + pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte } //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire diff --git a/src/runtime/time.go b/src/runtime/time.go index 9de45f5e08..790819f259 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -7,7 +7,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/cpu" "unsafe" ) @@ -50,7 +50,7 @@ var timers [timersLen]struct { // The padding should eliminate false sharing // between timersBucket values. - pad [sys.CacheLineSize - unsafe.Sizeof(timersBucket{})%sys.CacheLineSize]byte + pad [cpu.CacheLinePadSize - unsafe.Sizeof(timersBucket{})%cpu.CacheLinePadSize]byte } func (t *timer) assignBucket() *timersBucket { -- cgit v1.3-5-g9baa From 05c02444eb2d8b8d3ecd949c4308d8e2323ae087 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Fri, 24 Aug 2018 11:02:00 +0200 Subject: all: align cpu feature variable offset naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an "offset_" prefix to all cpu feature variable offset constants to signify that they are not boolean cpu feature variables. Remove _ from offset constant names. Change-Id: I6e22a79ebcbe6e2ae54c4ac8764f9260bb3223ff Reviewed-on: https://go-review.googlesource.com/131215 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/internal/bytealg/bytealg.go | 11 ++++++----- src/internal/bytealg/compare_amd64.s | 2 +- src/internal/bytealg/count_amd64.s | 6 +++--- src/internal/bytealg/equal_386.s | 2 +- src/internal/bytealg/equal_amd64.s | 2 +- src/internal/bytealg/index_amd64.s | 2 +- src/internal/bytealg/indexbyte_amd64.s | 2 +- src/internal/bytealg/indexbyte_s390x.s | 2 +- src/runtime/asm_386.s | 2 +- src/runtime/cpuflags.go | 8 ++++---- src/runtime/memclr_386.s | 2 +- src/runtime/memclr_amd64.s | 2 +- src/runtime/memmove_386.s | 4 ++-- src/runtime/memmove_amd64.s | 2 +- src/runtime/vlop_arm.s | 2 +- 15 files changed, 26 insertions(+), 25 deletions(-) (limited to 'src/runtime') diff --git a/src/internal/bytealg/bytealg.go b/src/internal/bytealg/bytealg.go index 1ab7c30f4e..9ecd8eb004 100644 --- a/src/internal/bytealg/bytealg.go +++ b/src/internal/bytealg/bytealg.go @@ -11,11 +11,12 @@ import ( // Offsets into internal/cpu records for use in assembly. const ( - x86_HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) - x86_HasSSE42 = unsafe.Offsetof(cpu.X86.HasSSE42) - x86_HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) - x86_HasPOPCNT = unsafe.Offsetof(cpu.X86.HasPOPCNT) - s390x_HasVX = unsafe.Offsetof(cpu.S390X.HasVX) + offsetX86HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) + offsetX86HasSSE42 = unsafe.Offsetof(cpu.X86.HasSSE42) + offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + offsetX86HasPOPCNT = unsafe.Offsetof(cpu.X86.HasPOPCNT) + + offsetS390xHasVX = unsafe.Offsetof(cpu.S390X.HasVX) ) // MaxLen is the maximum length of the string to be searched for (argument b) in Index. diff --git a/src/internal/bytealg/compare_amd64.s b/src/internal/bytealg/compare_amd64.s index 277d77c545..05bef4aad9 100644 --- a/src/internal/bytealg/compare_amd64.s +++ b/src/internal/bytealg/compare_amd64.s @@ -47,7 +47,7 @@ TEXT cmpbody<>(SB),NOSPLIT,$0-0 CMPQ R8, $63 JBE loop - CMPB internal∕cpu·X86+const_x86_HasAVX2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JEQ big_loop_avx2 JMP big_loop loop: diff --git a/src/internal/bytealg/count_amd64.s b/src/internal/bytealg/count_amd64.s index cecba11cf9..fa864c4c76 100644 --- a/src/internal/bytealg/count_amd64.s +++ b/src/internal/bytealg/count_amd64.s @@ -6,7 +6,7 @@ #include "textflag.h" TEXT ·Count(SB),NOSPLIT,$0-40 - CMPB internal∕cpu·X86+const_x86_HasPOPCNT(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasPOPCNT(SB), $1 JEQ 2(PC) JMP ·countGeneric(SB) MOVQ b_base+0(FP), SI @@ -16,7 +16,7 @@ TEXT ·Count(SB),NOSPLIT,$0-40 JMP countbody<>(SB) TEXT ·CountString(SB),NOSPLIT,$0-32 - CMPB internal∕cpu·X86+const_x86_HasPOPCNT(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasPOPCNT(SB), $1 JEQ 2(PC) JMP ·countGenericString(SB) MOVQ s_base+0(FP), SI @@ -151,7 +151,7 @@ endofpage: RET avx2: - CMPB internal∕cpu·X86+const_x86_HasAVX2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JNE sse MOVD AX, X0 LEAQ -32(SI)(BX*1), R11 diff --git a/src/internal/bytealg/equal_386.s b/src/internal/bytealg/equal_386.s index c048b6cebc..273389284e 100644 --- a/src/internal/bytealg/equal_386.s +++ b/src/internal/bytealg/equal_386.s @@ -80,7 +80,7 @@ TEXT memeqbody<>(SB),NOSPLIT,$0-0 hugeloop: CMPL BX, $64 JB bigloop - CMPB internal∕cpu·X86+const_x86_HasSSE2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 JNE bigloop MOVOU (SI), X0 MOVOU (DI), X1 diff --git a/src/internal/bytealg/equal_amd64.s b/src/internal/bytealg/equal_amd64.s index cbc62dc1d8..5263d3040d 100644 --- a/src/internal/bytealg/equal_amd64.s +++ b/src/internal/bytealg/equal_amd64.s @@ -77,7 +77,7 @@ TEXT memeqbody<>(SB),NOSPLIT,$0-0 JB small CMPQ BX, $64 JB bigloop - CMPB internal∕cpu·X86+const_x86_HasAVX2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JE hugeloop_avx2 // 64 bytes at a time using xmm registers diff --git a/src/internal/bytealg/index_amd64.s b/src/internal/bytealg/index_amd64.s index f7297c0cab..4459820801 100644 --- a/src/internal/bytealg/index_amd64.s +++ b/src/internal/bytealg/index_amd64.s @@ -233,7 +233,7 @@ success_avx2: VZEROUPPER JMP success sse42: - CMPB internal∕cpu·X86+const_x86_HasSSE42(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasSSE42(SB), $1 JNE no_sse42 CMPQ AX, $12 // PCMPESTRI is slower than normal compare, diff --git a/src/internal/bytealg/indexbyte_amd64.s b/src/internal/bytealg/indexbyte_amd64.s index 359f38904b..5bf8866476 100644 --- a/src/internal/bytealg/indexbyte_amd64.s +++ b/src/internal/bytealg/indexbyte_amd64.s @@ -139,7 +139,7 @@ endofpage: RET avx2: - CMPB internal∕cpu·X86+const_x86_HasAVX2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JNE sse MOVD AX, X0 LEAQ -32(SI)(BX*1), R11 diff --git a/src/internal/bytealg/indexbyte_s390x.s b/src/internal/bytealg/indexbyte_s390x.s index 15fd2935b4..24f5ce17fa 100644 --- a/src/internal/bytealg/indexbyte_s390x.s +++ b/src/internal/bytealg/indexbyte_s390x.s @@ -64,7 +64,7 @@ notfound: RET large: - MOVBZ internal∕cpu·S390X+const_s390x_HasVX(SB), R1 + MOVBZ internal∕cpu·S390X+const_offsetS390xHasVX(SB), R1 CMPBNE R1, $0, vectorimpl srstimpl: // no vector facility diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 725271eec4..7761415ecd 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -881,7 +881,7 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0 // func cputicks() int64 TEXT runtime·cputicks(SB),NOSPLIT,$0-8 - CMPB internal∕cpu·X86+const_offset_x86_HasSSE2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 JNE done CMPB runtime·lfenceBeforeRdtsc(SB), $1 JNE mfence diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go index 050168c2d7..b65523766a 100644 --- a/src/runtime/cpuflags.go +++ b/src/runtime/cpuflags.go @@ -11,9 +11,9 @@ import ( // Offsets into internal/cpu records for use in assembly. const ( - offset_x86_HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) - offset_x86_HasERMS = unsafe.Offsetof(cpu.X86.HasERMS) - offset_x86_HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) + offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + offsetX86HasERMS = unsafe.Offsetof(cpu.X86.HasERMS) + offsetX86HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) - offset_arm_HasIDIVA = unsafe.Offsetof(cpu.ARM.HasIDIVA) + offsetARMHasIDIVA = unsafe.Offsetof(cpu.ARM.HasIDIVA) ) diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s index 318f883964..65f7196312 100644 --- a/src/runtime/memclr_386.s +++ b/src/runtime/memclr_386.s @@ -29,7 +29,7 @@ tail: JBE _5through8 CMPL BX, $16 JBE _9through16 - CMPB internal∕cpu·X86+const_offset_x86_HasSSE2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 JNE nosse2 PXOR X0, X0 CMPL BX, $32 diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s index b64b1477f9..d79078fd00 100644 --- a/src/runtime/memclr_amd64.s +++ b/src/runtime/memclr_amd64.s @@ -38,7 +38,7 @@ tail: JBE _65through128 CMPQ BX, $256 JBE _129through256 - CMPB internal∕cpu·X86+const_offset_x86_HasAVX2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JE loop_preheader_avx2 // TODO: for really big clears, use MOVNTDQ, even without AVX2. diff --git a/src/runtime/memmove_386.s b/src/runtime/memmove_386.s index 85c622b6b6..7b54070f59 100644 --- a/src/runtime/memmove_386.s +++ b/src/runtime/memmove_386.s @@ -52,7 +52,7 @@ tail: JBE move_5through8 CMPL BX, $16 JBE move_9through16 - CMPB internal∕cpu·X86+const_offset_x86_HasSSE2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 JNE nosse2 CMPL BX, $32 JBE move_17through32 @@ -73,7 +73,7 @@ nosse2: */ forward: // If REP MOVSB isn't fast, don't use it - CMPB internal∕cpu·X86+const_offset_x86_HasERMS(SB), $1 // enhanced REP MOVSB/STOSB + CMPB internal∕cpu·X86+const_offsetX86HasERMS(SB), $1 // enhanced REP MOVSB/STOSB JNE fwdBy4 // Check alignment diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s index c5385a3d43..b4243a833b 100644 --- a/src/runtime/memmove_amd64.s +++ b/src/runtime/memmove_amd64.s @@ -84,7 +84,7 @@ forward: JLS move_256through2048 // If REP MOVSB isn't fast, don't use it - CMPB internal∕cpu·X86+const_offset_x86_HasERMS(SB), $1 // enhanced REP MOVSB/STOSB + CMPB internal∕cpu·X86+const_offsetX86HasERMS(SB), $1 // enhanced REP MOVSB/STOSB JNE fwdBy8 // Check alignment diff --git a/src/runtime/vlop_arm.s b/src/runtime/vlop_arm.s index 8df13abd98..729653488f 100644 --- a/src/runtime/vlop_arm.s +++ b/src/runtime/vlop_arm.s @@ -44,7 +44,7 @@ // the RET instruction will clobber R12 on nacl, and the compiler's register // allocator needs to know. TEXT runtime·udiv(SB),NOSPLIT|NOFRAME,$0 - MOVBU internal∕cpu·ARM+const_offset_arm_HasIDIVA(SB), Ra + MOVBU internal∕cpu·ARM+const_offsetARMHasIDIVA(SB), Ra CMP $0, Ra BNE udiv_hardware -- cgit v1.3-5-g9baa From 97f153528513e9a7ededf7e0aca7a4e30a3f4fe7 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 24 Aug 2018 11:44:55 -0700 Subject: runtime: mark sigInitIgnored nosplit The sigInitIgnored function can be called by initsig before a shared library is initialized, before the runtime is initialized. Fixes #27183 Change-Id: I7073767938fc011879d47ea951d63a14d1cce878 Reviewed-on: https://go-review.googlesource.com/131277 Run-TryBot: Ian Lance Taylor Reviewed-by: Austin Clements TryBot-Result: Gobot Gobot --- src/runtime/sigqueue.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/sigqueue.go b/src/runtime/sigqueue.go index 9f53240954..a425433b20 100644 --- a/src/runtime/sigqueue.go +++ b/src/runtime/sigqueue.go @@ -237,8 +237,10 @@ func signal_ignore(s uint32) { atomic.Store(&sig.ignored[s/32], i) } -// sigInitIgnored marks the signal as already ignored. This is called at -// program start by siginit. +// sigInitIgnored marks the signal as already ignored. This is called at +// program start by initsig. In a shared library initsig is called by +// libpreinit, so the runtime may not be initialized yet. +//go:nosplit func sigInitIgnored(s uint32) { i := sig.ignored[s/32] i |= 1 << (s & 31) -- cgit v1.3-5-g9baa From eae5fc88c13d6902a7fe9c595fb02155eb037cbe Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sat, 25 Aug 2018 16:53:23 +0200 Subject: internal/bytealg: replace use of runtime.support_sse2 with cpu.X86.HasSSE2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the runtime.support_sse2 variable unused so it is removed in this CL too. Change-Id: Ia8b9ffee7ac97128179f74ef244b10315e44c234 Reviewed-on: https://go-review.googlesource.com/131455 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/internal/bytealg/compare_386.s | 2 +- src/runtime/proc.go | 1 - src/runtime/runtime2.go | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src/runtime') diff --git a/src/internal/bytealg/compare_386.s b/src/internal/bytealg/compare_386.s index 89296e1690..f73e3f8b35 100644 --- a/src/internal/bytealg/compare_386.s +++ b/src/internal/bytealg/compare_386.s @@ -45,7 +45,7 @@ TEXT cmpbody<>(SB),NOSPLIT,$0-0 JEQ allsame CMPL BP, $4 JB small - CMPB runtime·support_sse2(SB), $1 + CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 JNE mediumloop largeloop: CMPL BP, $16 diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 75d309a9f6..73b4a1d9d6 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -510,7 +510,6 @@ func cpuinit() { // Support cpu feature variables are used in code generated by the compiler // to guard execution of instructions that can not be assumed to be always supported. support_popcnt = cpu.X86.HasPOPCNT - support_sse2 = cpu.X86.HasSSE2 support_sse41 = cpu.X86.HasSSE41 arm64_support_atomics = cpu.ARM64.HasATOMICS diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index e4c6b3b52a..259bb376ae 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -847,7 +847,6 @@ var ( // Set in runtime.cpuinit. // TODO: deprecate these; use internal/cpu directly. support_popcnt bool - support_sse2 bool support_sse41 bool arm64_support_atomics bool -- cgit v1.3-5-g9baa From 8f4fd3f34e8e218cb90435b5a8c6ba9be23a1e1e Mon Sep 17 00:00:00 2001 From: Zheng Xu Date: Wed, 29 Aug 2018 14:55:03 +0800 Subject: build: support frame-pointer for arm64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supporting frame-pointer makes Linux's perf and other profilers much more useful because it lets them gather a stack trace efficiently on profiling events. Major changes include: 1. save FP on the word below where RSP is pointing to (proposed by Cherry and Austin) 2. adjust some specific offsets in runtime assembly and wrapper code 3. add support to FP in goroutine scheduler 4. adjust link stack overflow check to take the extra word into account 5. adjust nosplit test cases to enable frame sizes which are 16 bytes aligned Performance impacts on go1 benchmarks: Enable frame-pointer (by default) name old time/op new time/op delta BinaryTree17-46 5.94s ± 0% 6.00s ± 0% +1.03% (p=0.029 n=4+4) Fannkuch11-46 2.84s ± 1% 2.77s ± 0% -2.58% (p=0.008 n=5+5) FmtFprintfEmpty-46 55.0ns ± 1% 58.9ns ± 1% +7.06% (p=0.008 n=5+5) FmtFprintfString-46 102ns ± 0% 105ns ± 0% +2.94% (p=0.008 n=5+5) FmtFprintfInt-46 118ns ± 0% 117ns ± 1% -1.19% (p=0.000 n=4+5) FmtFprintfIntInt-46 181ns ± 0% 182ns ± 1% ~ (p=0.444 n=5+5) FmtFprintfPrefixedInt-46 215ns ± 1% 214ns ± 0% ~ (p=0.254 n=5+4) FmtFprintfFloat-46 292ns ± 0% 296ns ± 0% +1.46% (p=0.029 n=4+4) FmtManyArgs-46 720ns ± 0% 732ns ± 0% +1.72% (p=0.008 n=5+5) GobDecode-46 9.82ms ± 1% 10.03ms ± 2% +2.10% (p=0.008 n=5+5) GobEncode-46 8.14ms ± 0% 8.72ms ± 1% +7.14% (p=0.008 n=5+5) Gzip-46 420ms ± 0% 424ms ± 0% +0.92% (p=0.008 n=5+5) Gunzip-46 48.2ms ± 0% 48.4ms ± 0% +0.41% (p=0.008 n=5+5) HTTPClientServer-46 201µs ± 4% 201µs ± 0% ~ (p=0.730 n=5+4) JSONEncode-46 17.1ms ± 0% 17.7ms ± 1% +3.80% (p=0.008 n=5+5) JSONDecode-46 88.0ms ± 0% 90.1ms ± 0% +2.42% (p=0.008 n=5+5) Mandelbrot200-46 5.06ms ± 0% 5.07ms ± 0% ~ (p=0.310 n=5+5) GoParse-46 5.04ms ± 0% 5.12ms ± 0% +1.53% (p=0.008 n=5+5) RegexpMatchEasy0_32-46 117ns ± 0% 117ns ± 0% ~ (all equal) RegexpMatchEasy0_1K-46 332ns ± 0% 329ns ± 0% -0.78% (p=0.008 n=5+5) RegexpMatchEasy1_32-46 104ns ± 0% 113ns ± 0% +8.65% (p=0.029 n=4+4) RegexpMatchEasy1_1K-46 563ns ± 0% 569ns ± 0% +1.10% (p=0.008 n=5+5) RegexpMatchMedium_32-46 167ns ± 2% 177ns ± 1% +5.74% (p=0.008 n=5+5) RegexpMatchMedium_1K-46 49.5µs ± 0% 53.4µs ± 0% +7.81% (p=0.008 n=5+5) RegexpMatchHard_32-46 2.56µs ± 1% 2.72µs ± 0% +6.01% (p=0.008 n=5+5) RegexpMatchHard_1K-46 77.0µs ± 0% 81.8µs ± 0% +6.24% (p=0.016 n=5+4) Revcomp-46 631ms ± 1% 627ms ± 1% ~ (p=0.095 n=5+5) Template-46 81.8ms ± 0% 86.3ms ± 0% +5.55% (p=0.008 n=5+5) TimeParse-46 423ns ± 0% 432ns ± 0% +2.32% (p=0.008 n=5+5) TimeFormat-46 478ns ± 2% 497ns ± 1% +3.89% (p=0.008 n=5+5) [Geo mean] 71.6µs 73.3µs +2.45% name old speed new speed delta GobDecode-46 78.1MB/s ± 1% 76.6MB/s ± 2% -2.04% (p=0.008 n=5+5) GobEncode-46 94.3MB/s ± 0% 88.0MB/s ± 1% -6.67% (p=0.008 n=5+5) Gzip-46 46.2MB/s ± 0% 45.8MB/s ± 0% -0.91% (p=0.008 n=5+5) Gunzip-46 403MB/s ± 0% 401MB/s ± 0% -0.41% (p=0.008 n=5+5) JSONEncode-46 114MB/s ± 0% 109MB/s ± 1% -3.66% (p=0.008 n=5+5) JSONDecode-46 22.0MB/s ± 0% 21.5MB/s ± 0% -2.35% (p=0.008 n=5+5) GoParse-46 11.5MB/s ± 0% 11.3MB/s ± 0% -1.51% (p=0.008 n=5+5) RegexpMatchEasy0_32-46 272MB/s ± 0% 272MB/s ± 1% ~ (p=0.190 n=4+5) RegexpMatchEasy0_1K-46 3.08GB/s ± 0% 3.11GB/s ± 0% +0.77% (p=0.008 n=5+5) RegexpMatchEasy1_32-46 306MB/s ± 0% 283MB/s ± 0% -7.63% (p=0.029 n=4+4) RegexpMatchEasy1_1K-46 1.82GB/s ± 0% 1.80GB/s ± 0% -1.07% (p=0.008 n=5+5) RegexpMatchMedium_32-46 5.99MB/s ± 0% 5.64MB/s ± 1% -5.77% (p=0.016 n=4+5) RegexpMatchMedium_1K-46 20.7MB/s ± 0% 19.2MB/s ± 0% -7.25% (p=0.008 n=5+5) RegexpMatchHard_32-46 12.5MB/s ± 1% 11.8MB/s ± 0% -5.66% (p=0.008 n=5+5) RegexpMatchHard_1K-46 13.3MB/s ± 0% 12.5MB/s ± 1% -6.01% (p=0.008 n=5+5) Revcomp-46 402MB/s ± 1% 405MB/s ± 1% ~ (p=0.095 n=5+5) Template-46 23.7MB/s ± 0% 22.5MB/s ± 0% -5.25% (p=0.008 n=5+5) [Geo mean] 82.2MB/s 79.6MB/s -3.26% Disable frame-pointer (GOEXPERIMENT=noframepointer) name old time/op new time/op delta BinaryTree17-46 5.94s ± 0% 5.96s ± 0% +0.39% (p=0.029 n=4+4) Fannkuch11-46 2.84s ± 1% 2.79s ± 1% -1.68% (p=0.008 n=5+5) FmtFprintfEmpty-46 55.0ns ± 1% 55.2ns ± 3% ~ (p=0.794 n=5+5) FmtFprintfString-46 102ns ± 0% 103ns ± 0% +0.98% (p=0.016 n=5+4) FmtFprintfInt-46 118ns ± 0% 115ns ± 0% -2.54% (p=0.029 n=4+4) FmtFprintfIntInt-46 181ns ± 0% 179ns ± 0% -1.10% (p=0.000 n=5+4) FmtFprintfPrefixedInt-46 215ns ± 1% 213ns ± 0% ~ (p=0.143 n=5+4) FmtFprintfFloat-46 292ns ± 0% 300ns ± 0% +2.83% (p=0.029 n=4+4) FmtManyArgs-46 720ns ± 0% 739ns ± 0% +2.64% (p=0.008 n=5+5) GobDecode-46 9.82ms ± 1% 9.78ms ± 1% ~ (p=0.151 n=5+5) GobEncode-46 8.14ms ± 0% 8.12ms ± 1% ~ (p=0.690 n=5+5) Gzip-46 420ms ± 0% 420ms ± 0% ~ (p=0.548 n=5+5) Gunzip-46 48.2ms ± 0% 48.0ms ± 0% -0.33% (p=0.032 n=5+5) HTTPClientServer-46 201µs ± 4% 199µs ± 3% ~ (p=0.548 n=5+5) JSONEncode-46 17.1ms ± 0% 17.2ms ± 0% ~ (p=0.056 n=5+5) JSONDecode-46 88.0ms ± 0% 88.6ms ± 0% +0.64% (p=0.008 n=5+5) Mandelbrot200-46 5.06ms ± 0% 5.07ms ± 0% ~ (p=0.548 n=5+5) GoParse-46 5.04ms ± 0% 5.07ms ± 0% +0.65% (p=0.008 n=5+5) RegexpMatchEasy0_32-46 117ns ± 0% 112ns ± 4% -4.27% (p=0.016 n=4+5) RegexpMatchEasy0_1K-46 332ns ± 0% 330ns ± 1% ~ (p=0.095 n=5+5) RegexpMatchEasy1_32-46 104ns ± 0% 110ns ± 1% +5.29% (p=0.029 n=4+4) RegexpMatchEasy1_1K-46 563ns ± 0% 567ns ± 2% ~ (p=0.151 n=5+5) RegexpMatchMedium_32-46 167ns ± 2% 166ns ± 0% ~ (p=0.333 n=5+4) RegexpMatchMedium_1K-46 49.5µs ± 0% 49.6µs ± 0% ~ (p=0.841 n=5+5) RegexpMatchHard_32-46 2.56µs ± 1% 2.49µs ± 0% -2.81% (p=0.008 n=5+5) RegexpMatchHard_1K-46 77.0µs ± 0% 75.8µs ± 0% -1.55% (p=0.008 n=5+5) Revcomp-46 631ms ± 1% 628ms ± 0% ~ (p=0.095 n=5+5) Template-46 81.8ms ± 0% 84.3ms ± 1% +3.05% (p=0.008 n=5+5) TimeParse-46 423ns ± 0% 425ns ± 0% +0.52% (p=0.008 n=5+5) TimeFormat-46 478ns ± 2% 478ns ± 1% ~ (p=1.000 n=5+5) [Geo mean] 71.6µs 71.6µs -0.01% name old speed new speed delta GobDecode-46 78.1MB/s ± 1% 78.5MB/s ± 1% ~ (p=0.151 n=5+5) GobEncode-46 94.3MB/s ± 0% 94.5MB/s ± 1% ~ (p=0.690 n=5+5) Gzip-46 46.2MB/s ± 0% 46.2MB/s ± 0% ~ (p=0.571 n=5+5) Gunzip-46 403MB/s ± 0% 404MB/s ± 0% +0.33% (p=0.032 n=5+5) JSONEncode-46 114MB/s ± 0% 113MB/s ± 0% ~ (p=0.056 n=5+5) JSONDecode-46 22.0MB/s ± 0% 21.9MB/s ± 0% -0.64% (p=0.008 n=5+5) GoParse-46 11.5MB/s ± 0% 11.4MB/s ± 0% -0.64% (p=0.008 n=5+5) RegexpMatchEasy0_32-46 272MB/s ± 0% 285MB/s ± 4% +4.74% (p=0.016 n=4+5) RegexpMatchEasy0_1K-46 3.08GB/s ± 0% 3.10GB/s ± 1% ~ (p=0.151 n=5+5) RegexpMatchEasy1_32-46 306MB/s ± 0% 290MB/s ± 1% -5.21% (p=0.029 n=4+4) RegexpMatchEasy1_1K-46 1.82GB/s ± 0% 1.81GB/s ± 2% ~ (p=0.151 n=5+5) RegexpMatchMedium_32-46 5.99MB/s ± 0% 6.02MB/s ± 1% ~ (p=0.063 n=4+5) RegexpMatchMedium_1K-46 20.7MB/s ± 0% 20.7MB/s ± 0% ~ (p=0.659 n=5+5) RegexpMatchHard_32-46 12.5MB/s ± 1% 12.8MB/s ± 0% +2.88% (p=0.008 n=5+5) RegexpMatchHard_1K-46 13.3MB/s ± 0% 13.5MB/s ± 0% +1.58% (p=0.008 n=5+5) Revcomp-46 402MB/s ± 1% 405MB/s ± 0% ~ (p=0.095 n=5+5) Template-46 23.7MB/s ± 0% 23.0MB/s ± 1% -2.95% (p=0.008 n=5+5) [Geo mean] 82.2MB/s 82.3MB/s +0.04% Frame-pointer is enabled on Linux by default but can be disabled by setting: GOEXPERIMENT=noframepointer. Fixes #10110 Change-Id: I1bfaca6dba29a63009d7c6ab04ed7a1413d9479e Reviewed-on: https://go-review.googlesource.com/61511 Reviewed-by: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/arm64/ggen.go | 8 +- src/cmd/compile/internal/gc/pgen.go | 6 +- src/cmd/internal/obj/arm64/asm7.go | 10 +- src/cmd/internal/obj/arm64/obj7.go | 183 ++++++++++++++++++++++++++++++--- src/cmd/internal/objabi/util.go | 2 +- src/cmd/link/internal/ld/lib.go | 4 + src/runtime/asm_arm64.s | 34 ++++-- src/runtime/cgocall.go | 3 +- src/runtime/rt0_darwin_arm64.s | 2 + src/runtime/rt0_linux_arm64.s | 2 + src/runtime/sys_linux_arm64.s | 8 +- src/runtime/traceback.go | 2 +- test/codegen/stack.go | 10 +- test/nosplit.go | 26 ++--- 14 files changed, 243 insertions(+), 57 deletions(-) (limited to 'src/runtime') diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go index f7b3851398..204391fef1 100644 --- a/src/cmd/compile/internal/arm64/ggen.go +++ b/src/cmd/compile/internal/arm64/ggen.go @@ -14,10 +14,10 @@ import ( var darwin = objabi.GOOS == "darwin" func padframe(frame int64) int64 { - // arm64 requires that the frame size (not counting saved LR) - // be empty or be 8 mod 16. If not, pad it. - if frame != 0 && frame%16 != 8 { - frame += 8 + // arm64 requires that the frame size (not counting saved FP&LR) + // be 16 bytes aligned. If not, pad it. + if frame%16 != 0 { + frame += 16 - (frame % 16) } return frame } diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 7f20643ab5..563eb9e966 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -427,7 +427,8 @@ func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool if Ctxt.FixedFrameSize() == 0 { offs -= int64(Widthptr) } - if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" { + // There is a word space for FP on ARM64 even if the frame pointer is disabled offs -= int64(Widthptr) } @@ -607,7 +608,8 @@ func stackOffset(slot ssa.LocalSlot) int32 { if Ctxt.FixedFrameSize() == 0 { base -= int64(Widthptr) } - if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" { + // There is a word space for FP on ARM64 even if the frame pointer is disabled base -= int64(Widthptr) } case PPARAM, PPARAMOUT: diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index ad4f172544..2abb8c2c77 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -49,6 +49,7 @@ type ctxt7 struct { blitrl *obj.Prog elitrl *obj.Prog autosize int32 + extrasize int32 instoffset int64 pc int64 pool struct { @@ -777,7 +778,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Diag("arm64 ops not initialized, call arm64.buildop first") } - c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset&0xffffffff) + 8} + c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset & 0xffffffff), extrasize: int32(p.To.Offset >> 32)} + p.To.Offset &= 0xffffffff // extrasize is no longer needed bflag := 1 pc := int64(0) @@ -1436,7 +1438,8 @@ func (c *ctxt7) aclass(a *obj.Addr) int { // a.Offset is still relative to pseudo-SP. a.Reg = obj.REG_NONE } - c.instoffset = int64(c.autosize) + a.Offset + // The frame top 8 or 16 bytes are for FP + c.instoffset = int64(c.autosize) + a.Offset - int64(c.extrasize) return autoclass(c.instoffset) case obj.NAME_PARAM: @@ -1536,7 +1539,8 @@ func (c *ctxt7) aclass(a *obj.Addr) int { // a.Offset is still relative to pseudo-SP. a.Reg = obj.REG_NONE } - c.instoffset = int64(c.autosize) + a.Offset + // The frame top 8 or 16 bytes are for FP + c.instoffset = int64(c.autosize) + a.Offset - int64(c.extrasize) case obj.NAME_PARAM: if a.Reg == REGSP { diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go index 0d832387d7..97b8f70c9b 100644 --- a/src/cmd/internal/obj/arm64/obj7.go +++ b/src/cmd/internal/obj/arm64/obj7.go @@ -542,22 +542,28 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { c.autosize += 8 } - if c.autosize != 0 && c.autosize&(16-1) != 0 { - // The frame includes an LR. - // If the frame size is 8, it's only an LR, - // so there's no potential for breaking references to - // local variables by growing the frame size, - // because there are no local variables. - // But otherwise, if there is a non-empty locals section, - // the author of the code is responsible for making sure - // that the frame size is 8 mod 16. - if c.autosize == 8 { - c.autosize += 8 - c.cursym.Func.Locals += 8 + if c.autosize != 0 { + extrasize := int32(0) + if c.autosize%16 == 8 { + // Allocate extra 8 bytes on the frame top to save FP + extrasize = 8 + } else if c.autosize&(16-1) == 0 { + // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16 + extrasize = 16 } else { - c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8) + c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8) } + c.autosize += extrasize + c.cursym.Func.Locals += extrasize + + // low 32 bits for autosize + // high 32 bits for extrasize + p.To.Offset = int64(c.autosize) | int64(extrasize)<<32 + } else { + // NOFRAME + p.To.Offset = 0 } + if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { if c.ctxt.Debugvlog { c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name) @@ -565,9 +571,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { c.cursym.Func.Text.Mark |= LEAF } - // FP offsets need an updated p.To.Offset. - p.To.Offset = int64(c.autosize) - 8 - if cursym.Func.Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { @@ -631,6 +634,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q1.Spadj = aoffset } + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + q1 = obj.Appendp(q1, c.newprog) + q1.Pos = p.Pos + q1.As = AMOVD + q1.From.Type = obj.TYPE_REG + q1.From.Reg = REGFP + q1.To.Type = obj.TYPE_MEM + q1.To.Reg = REGSP + q1.To.Offset = -8 + + q1 = obj.Appendp(q1, c.newprog) + q1.Pos = p.Pos + q1.As = ASUB + q1.From.Type = obj.TYPE_CONST + q1.From.Offset = 8 + q1.Reg = REGSP + q1.To.Type = obj.TYPE_REG + q1.To.Reg = REGFP + } + if c.cursym.Func.Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // @@ -753,9 +776,30 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Type = obj.TYPE_REG p.To.Reg = REGSP p.Spadj = -c.autosize + + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + p = obj.Appendp(p, c.newprog) + p.As = ASUB + p.From.Type = obj.TYPE_CONST + p.From.Offset = 8 + p.Reg = REGSP + p.To.Type = obj.TYPE_REG + p.To.Reg = REGFP + } } } else { /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ + + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Reg = REGSP + p.From.Offset = -8 + p.To.Type = obj.TYPE_REG + p.To.Reg = REGFP + p = obj.Appendp(p, c.newprog) + } + aoffset := c.autosize if aoffset > 0xF0 { @@ -814,7 +858,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.Spadj = int32(+p.From.Offset) } } - break case obj.AGETCALLERPC: if cursym.Leaf() { @@ -828,6 +871,112 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.From.Type = obj.TYPE_MEM p.From.Reg = REGSP } + + case obj.ADUFFCOPY: + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + // ADR ret_addr, R27 + // STP (FP, R27), -24(SP) + // SUB 24, SP, FP + // DUFFCOPY + // ret_addr: + // SUB 8, SP, FP + + q1 := p + // copy DUFFCOPY from q1 to q4 + q4 := obj.Appendp(p, c.newprog) + q4.Pos = p.Pos + q4.As = obj.ADUFFCOPY + q4.To = p.To + + q1.As = AADR + q1.From.Type = obj.TYPE_BRANCH + q1.To.Type = obj.TYPE_REG + q1.To.Reg = REG_R27 + + q2 := obj.Appendp(q1, c.newprog) + q2.Pos = p.Pos + q2.As = ASTP + q2.From.Type = obj.TYPE_REGREG + q2.From.Reg = REGFP + q2.From.Offset = int64(REG_R27) + q2.To.Type = obj.TYPE_MEM + q2.To.Reg = REGSP + q2.To.Offset = -24 + + // maintaine FP for DUFFCOPY + q3 := obj.Appendp(q2, c.newprog) + q3.Pos = p.Pos + q3.As = ASUB + q3.From.Type = obj.TYPE_CONST + q3.From.Offset = 24 + q3.Reg = REGSP + q3.To.Type = obj.TYPE_REG + q3.To.Reg = REGFP + + q5 := obj.Appendp(q4, c.newprog) + q5.Pos = p.Pos + q5.As = ASUB + q5.From.Type = obj.TYPE_CONST + q5.From.Offset = 8 + q5.Reg = REGSP + q5.To.Type = obj.TYPE_REG + q5.To.Reg = REGFP + q1.Pcond = q5 + p = q5 + } + + case obj.ADUFFZERO: + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + // ADR ret_addr, R27 + // STP (FP, R27), -24(SP) + // SUB 24, SP, FP + // DUFFZERO + // ret_addr: + // SUB 8, SP, FP + + q1 := p + // copy DUFFZERO from q1 to q4 + q4 := obj.Appendp(p, c.newprog) + q4.Pos = p.Pos + q4.As = obj.ADUFFZERO + q4.To = p.To + + q1.As = AADR + q1.From.Type = obj.TYPE_BRANCH + q1.To.Type = obj.TYPE_REG + q1.To.Reg = REG_R27 + + q2 := obj.Appendp(q1, c.newprog) + q2.Pos = p.Pos + q2.As = ASTP + q2.From.Type = obj.TYPE_REGREG + q2.From.Reg = REGFP + q2.From.Offset = int64(REG_R27) + q2.To.Type = obj.TYPE_MEM + q2.To.Reg = REGSP + q2.To.Offset = -24 + + // maintaine FP for DUFFZERO + q3 := obj.Appendp(q2, c.newprog) + q3.Pos = p.Pos + q3.As = ASUB + q3.From.Type = obj.TYPE_CONST + q3.From.Offset = 24 + q3.Reg = REGSP + q3.To.Type = obj.TYPE_REG + q3.To.Reg = REGFP + + q5 := obj.Appendp(q4, c.newprog) + q5.Pos = p.Pos + q5.As = ASUB + q5.From.Type = obj.TYPE_CONST + q5.From.Offset = 8 + q5.Reg = REGSP + q5.To.Type = obj.TYPE_REG + q5.To.Reg = REGFP + q1.Pcond = q5 + p = q5 + } } } } diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index a47e2f93a1..ffd1c04d39 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -76,7 +76,7 @@ func init() { } func Framepointer_enabled(goos, goarch string) bool { - return framepointer_enabled != 0 && goarch == "amd64" && goos != "nacl" + return framepointer_enabled != 0 && (goarch == "amd64" && goos != "nacl" || goarch == "arm64" && goos == "linux") } func addexp(s string) { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 1b6d5d1704..ba03cb707b 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1815,6 +1815,10 @@ func (ctxt *Link) dostkcheck() { ch.up = nil ch.limit = objabi.StackLimit - callsize(ctxt) + if objabi.GOARCH == "arm64" { + // need extra 8 bytes below SP to save FP + ch.limit -= 8 + } // Check every function, but do the nosplit functions in a first pass, // to make the printed failure chains as short as possible. diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index af389be9fe..6a6a699241 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -39,10 +39,9 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0 #endif MOVD $setg_gcc<>(SB), R1 // arg 1: setg MOVD g, R0 // arg 0: G + SUB $16, RSP // reserve 16 bytes for sp-8 where fp may be saved. BL (R12) - MOVD _cgo_init(SB), R12 - CMP $0, R12 - BEQ nocgo + ADD $16, RSP nocgo: // update stackguard after _cgo_init @@ -107,6 +106,7 @@ TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8 MOVD buf+0(FP), R3 MOVD RSP, R0 MOVD R0, gobuf_sp(R3) + MOVD R29, gobuf_bp(R3) MOVD LR, gobuf_pc(R3) MOVD g, gobuf_g(R3) MOVD ZR, gobuf_lr(R3) @@ -128,10 +128,12 @@ TEXT runtime·gogo(SB), NOSPLIT, $24-8 MOVD 0(g), R4 // make sure g is not nil MOVD gobuf_sp(R5), R0 MOVD R0, RSP + MOVD gobuf_bp(R5), R29 MOVD gobuf_lr(R5), LR MOVD gobuf_ret(R5), R0 MOVD gobuf_ctxt(R5), R26 MOVD $0, gobuf_sp(R5) + MOVD $0, gobuf_bp(R5) MOVD $0, gobuf_ret(R5) MOVD $0, gobuf_lr(R5) MOVD $0, gobuf_ctxt(R5) @@ -147,6 +149,7 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 // Save caller state in g->sched MOVD RSP, R0 MOVD R0, (g_sched+gobuf_sp)(g) + MOVD R29, (g_sched+gobuf_bp)(g) MOVD LR, (g_sched+gobuf_pc)(g) MOVD $0, (g_sched+gobuf_lr)(g) MOVD g, (g_sched+gobuf_g)(g) @@ -163,6 +166,7 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 MOVD 0(R26), R4 // code pointer MOVD (g_sched+gobuf_sp)(g), R0 MOVD R0, RSP // sp = m->g0->sched.sp + MOVD (g_sched+gobuf_bp)(g), R29 MOVD R3, -8(RSP) MOVD $0, -16(RSP) SUB $16, RSP @@ -211,6 +215,7 @@ switch: MOVD R6, (g_sched+gobuf_pc)(g) MOVD RSP, R0 MOVD R0, (g_sched+gobuf_sp)(g) + MOVD R29, (g_sched+gobuf_bp)(g) MOVD $0, (g_sched+gobuf_lr)(g) MOVD g, (g_sched+gobuf_g)(g) @@ -224,6 +229,7 @@ switch: MOVD $runtime·mstart(SB), R4 MOVD R4, 0(R3) MOVD R3, RSP + MOVD (g_sched+gobuf_bp)(g), R29 // call target function MOVD 0(R26), R3 // code pointer @@ -235,7 +241,9 @@ switch: BL runtime·save_g(SB) MOVD (g_sched+gobuf_sp)(g), R0 MOVD R0, RSP + MOVD (g_sched+gobuf_bp)(g), R29 MOVD $0, (g_sched+gobuf_sp)(g) + MOVD $0, (g_sched+gobuf_bp)(g) RET noswitch: @@ -244,6 +252,7 @@ noswitch: // at an intermediate systemstack. MOVD 0(R26), R3 // code pointer MOVD.P 16(RSP), R30 // restore LR + SUB $8, RSP, R29 // restore FP B (R3) /* @@ -278,6 +287,7 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 // Set g->sched to context in f MOVD RSP, R0 MOVD R0, (g_sched+gobuf_sp)(g) + MOVD R29, (g_sched+gobuf_bp)(g) MOVD LR, (g_sched+gobuf_pc)(g) MOVD R3, (g_sched+gobuf_lr)(g) MOVD R26, (g_sched+gobuf_ctxt)(g) @@ -294,6 +304,7 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 BL runtime·save_g(SB) MOVD (g_sched+gobuf_sp)(g), R0 MOVD R0, RSP + MOVD (g_sched+gobuf_bp)(g), R29 MOVD.W $0, -16(RSP) // create a call frame on g0 (saved LR; keep 16-aligned) BL runtime·newstack(SB) @@ -843,8 +854,9 @@ TEXT runtime·jmpdefer(SB), NOSPLIT|NOFRAME, $0-16 // Save state of caller into g->sched. Smashes R0. TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0 MOVD LR, (g_sched+gobuf_pc)(g) - MOVD RSP, R0 + MOVD RSP, R0 MOVD R0, (g_sched+gobuf_sp)(g) + MOVD R29, (g_sched+gobuf_bp)(g) MOVD $0, (g_sched+gobuf_lr)(g) MOVD $0, (g_sched+gobuf_ret)(g) // Assert ctxt is zero. See func save. @@ -885,6 +897,7 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 BL runtime·save_g(SB) MOVD (g_sched+gobuf_sp)(g), R0 MOVD R0, RSP + MOVD (g_sched+gobuf_bp)(g), R29 MOVD R9, R0 // Now on a scheduling stack (a pthread-created stack). @@ -996,6 +1009,7 @@ needm: MOVD m_g0(R8), R3 MOVD RSP, R0 MOVD R0, (g_sched+gobuf_sp)(R3) + MOVD R29, (g_sched+gobuf_bp)(R3) havem: // Now there's a valid m, and we're running on its m->g0. @@ -1003,7 +1017,7 @@ havem: // Save current sp in m->g0->sched.sp in preparation for // switch back to m->curg stack. // NOTE: unwindm knows that the saved g->sched.sp is at 16(RSP) aka savedsp-16(SP). - // Beware that the frame size is actually 32. + // Beware that the frame size is actually 32+16. MOVD m_g0(R8), R3 MOVD (g_sched+gobuf_sp)(R3), R4 MOVD R4, savedsp-16(SP) @@ -1030,10 +1044,12 @@ havem: BL runtime·save_g(SB) MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 MOVD (g_sched+gobuf_pc)(g), R5 - MOVD R5, -(24+8)(R4) + MOVD R5, -48(R4) + MOVD (g_sched+gobuf_bp)(g), R5 + MOVD R5, -56(R4) MOVD ctxt+24(FP), R0 - MOVD R0, -(16+8)(R4) - MOVD $-(24+8)(R4), R0 // maintain 16-byte SP alignment + MOVD R0, -40(R4) + MOVD $-48(R4), R0 // maintain 16-byte SP alignment MOVD R0, RSP BL runtime·cgocallbackg(SB) @@ -1041,7 +1057,7 @@ havem: MOVD 0(RSP), R5 MOVD R5, (g_sched+gobuf_pc)(g) MOVD RSP, R4 - ADD $(24+8), R4, R4 + ADD $48, R4, R4 MOVD R4, (g_sched+gobuf_sp)(g) // Switch back to m->g0's stack and restore m->g0->sched.sp. diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index c85033f4bc..86bd2fb01c 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -268,7 +268,8 @@ func cgocallbackg1(ctxt uintptr) { case "arm64": // On arm64, stack frame is four words and there's a saved LR between // SP and the stack frame and between the stack frame and the arguments. - cb = (*args)(unsafe.Pointer(sp + 5*sys.PtrSize)) + // Additional two words (16-byte alignment) are for saving FP. + cb = (*args)(unsafe.Pointer(sp + 7*sys.PtrSize)) case "amd64": // On amd64, stack frame is two words, plus caller PC. if framepointer_enabled { diff --git a/src/runtime/rt0_darwin_arm64.s b/src/runtime/rt0_darwin_arm64.s index d039a8e0ab..e3972f4924 100644 --- a/src/runtime/rt0_darwin_arm64.s +++ b/src/runtime/rt0_darwin_arm64.s @@ -49,7 +49,9 @@ TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$168 MOVD _cgo_sys_thread_create(SB), R4 MOVD $_rt0_arm64_darwin_lib_go(SB), R0 MOVD $0, R1 + SUB $16, RSP // reserve 16 bytes for sp-8 where fp may be saved. BL (R4) + ADD $16, RSP // Restore callee-save registers. MOVD 24(RSP), R19 diff --git a/src/runtime/rt0_linux_arm64.s b/src/runtime/rt0_linux_arm64.s index 458f082159..a6bc99df56 100644 --- a/src/runtime/rt0_linux_arm64.s +++ b/src/runtime/rt0_linux_arm64.s @@ -48,7 +48,9 @@ TEXT _rt0_arm64_linux_lib(SB),NOSPLIT,$184 BEQ nocgo MOVD $_rt0_arm64_linux_lib_go(SB), R0 MOVD $0, R1 + SUB $16, RSP // reserve 16 bytes for sp-8 where fp may be saved. BL (R4) + ADD $16, RSP B restore nocgo: diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s index c6afd76a65..1c8fce3db6 100644 --- a/src/runtime/sys_linux_arm64.s +++ b/src/runtime/sys_linux_arm64.s @@ -239,7 +239,7 @@ TEXT runtime·nanotime(SB),NOSPLIT,$24-8 MOVD (g_sched+gobuf_sp)(R3), R1 // Set RSP to g0 stack noswitch: - SUB $16, R1 + SUB $32, R1 BIC $15, R1 MOVD R1, RSP @@ -298,7 +298,9 @@ TEXT runtime·callCgoSigaction(SB),NOSPLIT,$0 MOVD new+8(FP), R1 MOVD old+16(FP), R2 MOVD _cgo_sigaction(SB), R3 + SUB $16, RSP // reserve 16 bytes for sp-8 where fp may be saved. BL R3 + ADD $16, RSP MOVW R0, ret+24(FP) RET @@ -361,7 +363,9 @@ TEXT runtime·callCgoMmap(SB),NOSPLIT,$0 MOVW fd+24(FP), R4 MOVW off+28(FP), R5 MOVD _cgo_mmap(SB), R9 + SUB $16, RSP // reserve 16 bytes for sp-8 where fp may be saved. BL R9 + ADD $16, RSP MOVD R0, ret+32(FP) RET @@ -382,7 +386,9 @@ TEXT runtime·callCgoMunmap(SB),NOSPLIT,$0 MOVD addr+0(FP), R0 MOVD n+8(FP), R1 MOVD _cgo_munmap(SB), R9 + SUB $16, RSP // reserve 16 bytes for sp-8 where fp may be saved. BL R9 + ADD $16, RSP RET TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0 diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 8370fd7593..4c2010493a 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -285,7 +285,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // If framepointer_enabled and there's a frame, then // there's a saved bp here. - if framepointer_enabled && GOARCH == "amd64" && frame.varp > frame.sp { + if frame.varp > frame.sp && (framepointer_enabled && GOARCH == "amd64" || GOARCH == "arm64") { frame.varp -= sys.RegSize } diff --git a/test/codegen/stack.go b/test/codegen/stack.go index 7e12dbc0eb..0f2f6178c7 100644 --- a/test/codegen/stack.go +++ b/test/codegen/stack.go @@ -16,7 +16,7 @@ import "runtime" // 386:"TEXT\t.*, [$]0-" // amd64:"TEXT\t.*, [$]0-" // arm:"TEXT\t.*, [$]-4-" -// arm64:"TEXT\t.*, [$]-8-" +// arm64:"TEXT\t.*, [$]0-" // mips:"TEXT\t.*, [$]-4-" // ppc64le:"TEXT\t.*, [$]0-" // s390x:"TEXT\t.*, [$]0-" @@ -35,7 +35,7 @@ type T struct { // 386:"TEXT\t.*, [$]0-" // amd64:"TEXT\t.*, [$]0-" // arm:"TEXT\t.*, [$]0-" (spills return address) -// arm64:"TEXT\t.*, [$]-8-" +// arm64:"TEXT\t.*, [$]0-" // mips:"TEXT\t.*, [$]-4-" // ppc64le:"TEXT\t.*, [$]0-" // s390x:"TEXT\t.*, [$]0-" @@ -50,7 +50,7 @@ func ZeroLargeStruct(x *T) { // - 386 fails due to spilling a register // amd64:"TEXT\t.*, [$]0-" // arm:"TEXT\t.*, [$]0-" (spills return address) -// arm64:"TEXT\t.*, [$]-8-" +// arm64:"TEXT\t.*, [$]0-" // ppc64le:"TEXT\t.*, [$]0-" // s390x:"TEXT\t.*, [$]0-" // Note: that 386 currently has to spill a register. @@ -64,7 +64,7 @@ func KeepWanted(t *T) { // - 386 fails due to spilling a register // - arm & mips fail due to softfloat calls // amd64:"TEXT\t.*, [$]0-" -// arm64:"TEXT\t.*, [$]-8-" +// arm64:"TEXT\t.*, [$]0-" // ppc64le:"TEXT\t.*, [$]0-" // s390x:"TEXT\t.*, [$]0-" func ArrayAdd64(a, b [4]float64) [4]float64 { @@ -76,7 +76,7 @@ func ArrayAdd64(a, b [4]float64) [4]float64 { // 386:"TEXT\t.*, [$]0-" // amd64:"TEXT\t.*, [$]0-" // arm:"TEXT\t.*, [$]0-" (spills return address) -// arm64:"TEXT\t.*, [$]-8-" +// arm64:"TEXT\t.*, [$]0-" // mips:"TEXT\t.*, [$]-4-" // ppc64le:"TEXT\t.*, [$]0-" // s390x:"TEXT\t.*, [$]0-" diff --git a/test/nosplit.go b/test/nosplit.go index e6cd04e563..8b61c9e96d 100644 --- a/test/nosplit.go +++ b/test/nosplit.go @@ -118,11 +118,11 @@ main 136 # (CallSize is 32 on ppc64, 8 on amd64 for frame pointer.) main 96 nosplit main 100 nosplit; REJECT ppc64 ppc64le -main 104 nosplit; REJECT ppc64 ppc64le +main 104 nosplit; REJECT ppc64 ppc64le arm64 main 108 nosplit; REJECT ppc64 ppc64le -main 112 nosplit; REJECT ppc64 ppc64le +main 112 nosplit; REJECT ppc64 ppc64le arm64 main 116 nosplit; REJECT ppc64 ppc64le -main 120 nosplit; REJECT ppc64 ppc64le amd64 +main 120 nosplit; REJECT ppc64 ppc64le amd64 arm64 main 124 nosplit; REJECT ppc64 ppc64le amd64 main 128 nosplit; REJECT main 132 nosplit; REJECT @@ -136,11 +136,11 @@ main 136 nosplit; REJECT # Because AMD64 uses frame pointer, it has 8 fewer bytes. main 96 nosplit call f; f 0 nosplit main 100 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le -main 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le +main 104 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le arm64 main 108 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le -main 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 +main 112 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64 main 116 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 -main 120 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 +main 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 arm64 main 124 nosplit call f; f 0 nosplit; REJECT ppc64 ppc64le amd64 386 main 128 nosplit call f; f 0 nosplit; REJECT main 132 nosplit call f; f 0 nosplit; REJECT @@ -152,11 +152,11 @@ main 136 nosplit call f; f 0 nosplit; REJECT # Architectures differ in the same way as before. main 96 nosplit call f; f 0 call f main 100 nosplit call f; f 0 call f; REJECT ppc64 ppc64le -main 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 +main 104 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64 main 108 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 -main 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 +main 112 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 arm64 main 116 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 -main 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 +main 120 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 arm64 main 124 nosplit call f; f 0 call f; REJECT ppc64 ppc64le amd64 386 main 128 nosplit call f; f 0 call f; REJECT main 132 nosplit call f; f 0 call f; REJECT @@ -165,11 +165,11 @@ main 136 nosplit call f; f 0 call f; REJECT # Indirect calls are assumed to be splitting functions. main 96 nosplit callind main 100 nosplit callind; REJECT ppc64 ppc64le -main 104 nosplit callind; REJECT ppc64 ppc64le amd64 +main 104 nosplit callind; REJECT ppc64 ppc64le amd64 arm64 main 108 nosplit callind; REJECT ppc64 ppc64le amd64 -main 112 nosplit callind; REJECT ppc64 ppc64le amd64 +main 112 nosplit callind; REJECT ppc64 ppc64le amd64 arm64 main 116 nosplit callind; REJECT ppc64 ppc64le amd64 -main 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 +main 120 nosplit callind; REJECT ppc64 ppc64le amd64 386 arm64 main 124 nosplit callind; REJECT ppc64 ppc64le amd64 386 main 128 nosplit callind; REJECT main 132 nosplit callind; REJECT @@ -319,7 +319,7 @@ TestCases: } } - if size%ptrSize == 4 || goarch == "arm64" && size != 0 && (size+8)%16 != 0 { + if size%ptrSize == 4 { continue TestCases } nosplit := m[3] -- cgit v1.3-5-g9baa From f9a4ae018d99c5afb0e4f128545ff26e01d7b498 Mon Sep 17 00:00:00 2001 From: Alexey Alexandrov Date: Fri, 27 Jul 2018 00:05:50 -0700 Subject: runtime/pprof: compute memory profile block size using sampled values Fixes #26638. Change-Id: I3c18d1298d99af8ea8c00916303efd2b5a5effc7 Reviewed-on: https://go-review.googlesource.com/126336 Reviewed-by: Hyang-Ah Hana Kim Run-TryBot: Hyang-Ah Hana Kim TryBot-Result: Gobot Gobot --- src/runtime/pprof/protomem.go | 4 ++-- src/runtime/pprof/protomem_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/pprof/protomem.go b/src/runtime/pprof/protomem.go index 82565d5245..1c88aae43a 100644 --- a/src/runtime/pprof/protomem.go +++ b/src/runtime/pprof/protomem.go @@ -56,8 +56,8 @@ func writeHeapProto(w io.Writer, p []runtime.MemProfileRecord, rate int64, defau values[0], values[1] = scaleHeapSample(r.AllocObjects, r.AllocBytes, rate) values[2], values[3] = scaleHeapSample(r.InUseObjects(), r.InUseBytes(), rate) var blockSize int64 - if values[0] > 0 { - blockSize = values[1] / values[0] + if r.AllocObjects > 0 { + blockSize = r.AllocBytes / r.AllocObjects } b.pbSample(values, locs, func() { if blockSize != 0 { diff --git a/src/runtime/pprof/protomem_test.go b/src/runtime/pprof/protomem_test.go index 315d5f0b4d..471b1ae9c3 100644 --- a/src/runtime/pprof/protomem_test.go +++ b/src/runtime/pprof/protomem_test.go @@ -48,7 +48,7 @@ func TestConvertMemProfile(t *testing.T) { {ID: 3, Mapping: map2, Address: addr2 + 1}, {ID: 4, Mapping: map2, Address: addr2 + 2}, }, - NumLabel: map[string][]int64{"bytes": {829411}}, + NumLabel: map[string][]int64{"bytes": {512 * 1024}}, }, { Value: []int64{1, 829411, 0, 0}, @@ -57,7 +57,7 @@ func TestConvertMemProfile(t *testing.T) { {ID: 6, Mapping: map1, Address: addr1 + 2}, {ID: 7, Mapping: map2, Address: addr2 + 3}, }, - NumLabel: map[string][]int64{"bytes": {829411}}, + NumLabel: map[string][]int64{"bytes": {512 * 1024}}, }, } for _, tc := range []struct { -- cgit v1.3-5-g9baa From dbd8af74723d2c98cbdcc70f7e2801f69b57ac5b Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Seo Date: Thu, 23 Aug 2018 20:16:19 -0300 Subject: runtime: add support for VDSO on ppc64x for use in walltime/nanotime This change adds support for VDSO on ppc64x, making it possible to avoid a syscall in walltime and nanotime. BenchmarkClockVDSOAndFallbackPaths/vDSO-192 20000000 66.0 ns/op BenchmarkClockVDSOAndFallbackPaths/Fallback-192 1000000 1456 ns/op Change-Id: I3373bd804b6f122961de3ae9d034e6ccf35748e6 Reviewed-on: https://go-review.googlesource.com/131135 Run-TryBot: Lynn Boger TryBot-Result: Gobot Gobot Reviewed-by: Lynn Boger --- src/runtime/os_linux_novdso.go | 2 +- src/runtime/sys_linux_ppc64x.s | 94 +++++++++++++++++++++++++++++++++++----- src/runtime/vdso_elf64.go | 2 +- src/runtime/vdso_in_none.go | 2 +- src/runtime/vdso_linux.go | 7 ++- src/runtime/vdso_linux_ppc64x.go | 25 +++++++++++ src/runtime/vdso_linux_test.go | 2 +- 7 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 src/runtime/vdso_linux_ppc64x.go (limited to 'src/runtime') diff --git a/src/runtime/os_linux_novdso.go b/src/runtime/os_linux_novdso.go index ee4a7a95c2..e54c1c4dc1 100644 --- a/src/runtime/os_linux_novdso.go +++ b/src/runtime/os_linux_novdso.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build linux -// +build !386,!amd64,!arm,!arm64 +// +build !386,!amd64,!arm,!arm64,!ppc64,!ppc64le package runtime diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index 483cb8ef9a..075adf2368 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -154,21 +154,87 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 // func walltime() (sec int64, nsec int32) TEXT runtime·walltime(SB),NOSPLIT,$16 - MOVD $0, R3 // CLOCK_REALTIME - MOVD $0(R1), R4 - SYSCALL $SYS_clock_gettime - MOVD 0(R1), R3 // sec - MOVD 8(R1), R5 // nsec + MOVD R1, R15 // R15 is unchanged by C code + MOVD g_m(g), R21 // R21 = m + + MOVD $0, R3 // CLOCK_REALTIME + + MOVD runtime·vdsoClockgettimeSym(SB), R12 // Check for VDSO availability + CMP R12, R0 + BEQ fallback + + // Set vdsoPC and vdsoSP for SIGPROF traceback. + MOVD LR, R14 + MOVD R14, m_vdsoPC(R21) + MOVD R15, m_vdsoSP(R21) + + MOVD m_curg(R21), R6 + CMP g, R6 + BNE noswitch + + MOVD m_g0(R21), R7 + MOVD (g_sched+gobuf_sp)(R7), R1 // Set SP to g0 stack + +noswitch: + SUB $16, R1 // Space for results + RLDICR $0, R1, $59, R1 // Align for C code + MOVD R12, CTR + MOVD R1, R4 + BL (CTR) // Call from VDSO + MOVD $0, R0 // Restore R0 + MOVD R0, m_vdsoSP(R21) // Clear vdsoSP + MOVD 0(R1), R3 // sec + MOVD 8(R1), R5 // nsec + MOVD R15, R1 // Restore SP + +finish: MOVD R3, sec+0(FP) MOVW R5, nsec+8(FP) RET + // Syscall fallback +fallback: + ADD $32, R1, R4 + SYSCALL $SYS_clock_gettime + MOVD 32(R1), R3 + MOVD 40(R1), R5 + JMP finish + TEXT runtime·nanotime(SB),NOSPLIT,$16 - MOVW $1, R3 // CLOCK_MONOTONIC - MOVD $0(R1), R4 - SYSCALL $SYS_clock_gettime - MOVD 0(R1), R3 // sec - MOVD 8(R1), R5 // nsec + MOVD $1, R3 // CLOCK_MONOTONIC + + MOVD R1, R15 // R15 is unchanged by C code + MOVD g_m(g), R21 // R21 = m + + MOVD runtime·vdsoClockgettimeSym(SB), R12 // Check for VDSO availability + CMP R12, R0 + BEQ fallback + + // Set vdsoPC and vdsoSP for SIGPROF traceback. + MOVD LR, R14 // R14 is unchanged by C code + MOVD R14, m_vdsoPC(R21) + MOVD R15, m_vdsoSP(R21) + + MOVD m_curg(R21), R6 + CMP g, R6 + BNE noswitch + + MOVD m_g0(R21), R7 + MOVD (g_sched+gobuf_sp)(R7), R1 // Set SP to g0 stack + +noswitch: + SUB $16, R1 // Space for results + RLDICR $0, R1, $59, R1 // Align for C code + MOVD R12, CTR + MOVD R1, R4 + BL (CTR) // Call from VDSO + MOVD $0, R0 // Restore R0 + MOVD $0, m_vdsoSP(R21) // Clear vdsoSP + MOVD 0(R1), R3 // sec + MOVD 8(R1), R5 // nsec + MOVD R15, R1 // Restore SP + +finish: // sec is in R3, nsec in R5 // return nsec in R3 MOVD $1000000000, R4 @@ -177,6 +243,14 @@ TEXT runtime·nanotime(SB),NOSPLIT,$16 MOVD R3, ret+0(FP) RET + // Syscall fallback +fallback: + ADD $32, R1, R4 + SYSCALL $SYS_clock_gettime + MOVD 32(R1), R3 + MOVD 48(R1), R5 + JMP finish + TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 MOVW how+0(FP), R3 MOVD new+8(FP), R4 diff --git a/src/runtime/vdso_elf64.go b/src/runtime/vdso_elf64.go index 8510250065..7c9bd96277 100644 --- a/src/runtime/vdso_elf64.go +++ b/src/runtime/vdso_elf64.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build linux -// +build amd64 arm64 +// +build amd64 arm64 ppc64 ppc64le package runtime diff --git a/src/runtime/vdso_in_none.go b/src/runtime/vdso_in_none.go index 34cfac56d1..f2d6bb55d9 100644 --- a/src/runtime/vdso_in_none.go +++ b/src/runtime/vdso_in_none.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux,!386,!amd64,!arm,!arm64 !linux +// +build linux,!386,!amd64,!arm,!arm64,!ppc64,!ppc64le !linux package runtime diff --git a/src/runtime/vdso_linux.go b/src/runtime/vdso_linux.go index f6a285efd7..9827874bea 100644 --- a/src/runtime/vdso_linux.go +++ b/src/runtime/vdso_linux.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build linux -// +build 386 amd64 arm arm64 +// +build 386 amd64 arm arm64 ppc64 ppc64le package runtime @@ -42,6 +42,8 @@ const ( _STT_FUNC = 2 /* Symbol is a code object */ + _STT_NOTYPE = 0 /* Symbol type is not specified */ + _STB_GLOBAL = 1 /* Global symbol */ _STB_WEAK = 2 /* Weak symbol */ @@ -212,7 +214,8 @@ func vdsoParseSymbols(info *vdsoInfo, version int32) { sym := &info.symtab[symIndex] typ := _ELF_ST_TYPE(sym.st_info) bind := _ELF_ST_BIND(sym.st_info) - if typ != _STT_FUNC || bind != _STB_GLOBAL && bind != _STB_WEAK || sym.st_shndx == _SHN_UNDEF { + // On ppc64x, VDSO functions are of type _STT_NOTYPE. + if typ != _STT_FUNC && typ != _STT_NOTYPE || bind != _STB_GLOBAL && bind != _STB_WEAK || sym.st_shndx == _SHN_UNDEF { return false } if k.name != gostringnocopy(&info.symstrings[sym.st_name]) { diff --git a/src/runtime/vdso_linux_ppc64x.go b/src/runtime/vdso_linux_ppc64x.go new file mode 100644 index 0000000000..f30946e4c5 --- /dev/null +++ b/src/runtime/vdso_linux_ppc64x.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux +// +build ppc64 ppc64le + +package runtime + +const ( + // vdsoArrayMax is the byte-size of a maximally sized array on this architecture. + // See cmd/compile/internal/ppc64/galign.go arch.MAXWIDTH initialization. + vdsoArrayMax = 1<<50 - 1 +) + +var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6.15", 0x75fcba5} + +var vdsoSymbolKeys = []vdsoSymbolKey{ + {"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym}, +} + +// initialize with vsyscall fallbacks +var ( + vdsoClockgettimeSym uintptr = 0 +) diff --git a/src/runtime/vdso_linux_test.go b/src/runtime/vdso_linux_test.go index b5221f90b7..ad083c61b4 100644 --- a/src/runtime/vdso_linux_test.go +++ b/src/runtime/vdso_linux_test.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build linux -// +build 386 amd64 arm arm64 +// +build 386 amd64 arm arm64 ppc64 ppc64le package runtime_test -- cgit v1.3-5-g9baa From 88206b89313bd7c143bc0d4946543969255ecc2b Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Fri, 27 Jul 2018 07:21:24 -0400 Subject: test: improve runtime/pprof tests for gccgo In the CPU profile tests for gccgo, check to make sure that the runtime's sigprof handler itself doesn't appear in the profile. Add a "skip if gccgo" guard to one testpoint. Updates #26595 Change-Id: I92a44161d61f17b9305ce09532134edd229745a7 Reviewed-on: https://go-review.googlesource.com/126316 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/runtime/pprof/pprof_test.go | 53 ++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 11 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 095972fa68..126ba50054 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -72,15 +72,24 @@ func cpuHog2(x int) int { return foo } +// Return a list of functions that we don't want to ever appear in CPU +// profiles. For gccgo, that list includes the sigprof handler itself. +func avoidFunctions() []string { + if runtime.Compiler == "gccgo" { + return []string{"runtime.sigprof"} + } + return nil +} + func TestCPUProfile(t *testing.T) { - testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1"}, func(dur time.Duration) { + testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions(), func(dur time.Duration) { cpuHogger(cpuHog1, &salt1, dur) }) } func TestCPUProfileMultithreaded(t *testing.T) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) - testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, func(dur time.Duration) { + testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, avoidFunctions(), func(dur time.Duration) { c := make(chan int) go func() { cpuHogger(cpuHog1, &salt1, dur) @@ -92,7 +101,7 @@ func TestCPUProfileMultithreaded(t *testing.T) { } func TestCPUProfileInlining(t *testing.T) { - testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, func(dur time.Duration) { + testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, avoidFunctions(), func(dur time.Duration) { cpuHogger(inlinedCaller, &salt1, dur) }) } @@ -132,7 +141,7 @@ func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []*profile.Loca // testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need, // as interpreted by matches. -func testCPUProfile(t *testing.T, matches matchFunc, need []string, f func(dur time.Duration)) { +func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) { switch runtime.GOOS { case "darwin": switch runtime.GOARCH { @@ -171,7 +180,7 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, f func(dur t f(duration) StopCPUProfile() - if profileOk(t, need, matches, prof, duration) { + if profileOk(t, matches, need, avoid, prof, duration) { return } @@ -218,11 +227,13 @@ func stackContains(spec string, count uintptr, stk []*profile.Location, labels m type matchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool -func profileOk(t *testing.T, need []string, matches matchFunc, prof bytes.Buffer, duration time.Duration) (ok bool) { +func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, prof bytes.Buffer, duration time.Duration) (ok bool) { ok = true - // Check that profile is well formed and contains need. + // Check that profile is well formed, contains 'need', and does not contain + // anything from 'avoid'. have := make([]uintptr, len(need)) + avoidSamples := make([]uintptr, len(avoid)) var samples uintptr var buf bytes.Buffer parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, labels map[string][]string) { @@ -234,6 +245,15 @@ func profileOk(t *testing.T, need []string, matches matchFunc, prof bytes.Buffer have[i] += count } } + for i, name := range avoid { + for _, loc := range stk { + for _, line := range loc.Line { + if strings.Contains(line.Function.Name, name) { + avoidSamples[i] += count + } + } + } + } fmt.Fprintf(&buf, "\n") }) t.Logf("total %d CPU profile samples collected:\n%s", samples, buf.String()) @@ -256,6 +276,14 @@ func profileOk(t *testing.T, need []string, matches matchFunc, prof bytes.Buffer ok = false } + for i, name := range avoid { + bad := avoidSamples[i] + if bad != 0 { + t.Logf("found %d samples in avoid-function %s\n", bad, name) + ok = false + } + } + if len(need) == 0 { return ok } @@ -323,6 +351,9 @@ func TestCPUProfileWithFork(t *testing.T) { // If it did, it would see inconsistent state and would either record an incorrect stack // or crash because the stack was malformed. func TestGoroutineSwitch(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("not applicable for gccgo") + } // How much to try. These defaults take about 1 seconds // on a 2012 MacBook Pro. The ones in short mode take // about 0.1 seconds. @@ -382,7 +413,7 @@ func fprintStack(w io.Writer, stk []*profile.Location) { // Test that profiling of division operations is okay, especially on ARM. See issue 6681. func TestMathBigDivide(t *testing.T) { - testCPUProfile(t, nil, nil, func(duration time.Duration) { + testCPUProfile(t, nil, nil, nil, func(duration time.Duration) { t := time.After(duration) pi := new(big.Int) for { @@ -411,7 +442,7 @@ func stackContainsAll(spec string, count uintptr, stk []*profile.Location, label } func TestMorestack(t *testing.T) { - testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, func(duration time.Duration) { + testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions(), func(duration time.Duration) { t := time.After(duration) c := make(chan bool) for { @@ -913,7 +944,7 @@ func stackContainsLabeled(spec string, count uintptr, stk []*profile.Location, l } func TestCPUProfileLabel(t *testing.T) { - testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, func(dur time.Duration) { + testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) { Do(context.Background(), Labels("key", "value"), func(context.Context) { cpuHogger(cpuHog1, &salt1, dur) }) @@ -924,7 +955,7 @@ func TestLabelRace(t *testing.T) { // Test the race detector annotations for synchronization // between settings labels and consuming them from the // profile. - testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, func(dur time.Duration) { + testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, nil, func(dur time.Duration) { start := time.Now() var wg sync.WaitGroup for time.Since(start) < dur { -- cgit v1.3-5-g9baa From b794ca64d29f3e584cbdf49bde7141d3c12dd2ab Mon Sep 17 00:00:00 2001 From: Charles Kenney Date: Mon, 3 Sep 2018 07:02:03 +0000 Subject: runtime/trace: fix syntax errors in NewTask doc example Fixes #27406 Change-Id: I9c6f5bac5b26558fa7628233c74a62faf676e811 GitHub-Last-Rev: 29d19f719316b486224a15a50556465811985edf GitHub-Pull-Request: golang/go#27437 Reviewed-on: https://go-review.googlesource.com/132775 Reviewed-by: Emmanuel Odeke Run-TryBot: Emmanuel Odeke TryBot-Result: Gobot Gobot --- src/runtime/trace/annotation.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/trace/annotation.go b/src/runtime/trace/annotation.go index 3545ef3bba..d5a7d003fe 100644 --- a/src/runtime/trace/annotation.go +++ b/src/runtime/trace/annotation.go @@ -24,13 +24,13 @@ type traceContextKey struct{} // If the end function is called multiple times, only the first // call is used in the latency measurement. // -// ctx, task := trace.NewTask(ctx, "awesome task") -// trace.WithRegion(ctx, prepWork) +// ctx, task := trace.NewTask(ctx, "awesomeTask") +// trace.WithRegion(ctx, "preparation", prepWork) // // preparation of the task // go func() { // continue processing the task in a separate goroutine. // defer task.End() -// trace.WithRegion(ctx, remainingWork) -// } +// trace.WithRegion(ctx, "remainingWork", remainingWork) +// }() func NewTask(pctx context.Context, taskType string) (ctx context.Context, task *Task) { pid := fromContext(pctx).id id := newID() -- cgit v1.3-5-g9baa