aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorChressie Himpel <chressie@google.com>2022-02-03 19:10:54 +0100
committerChressie Himpel <chressie@google.com>2022-02-03 19:30:02 +0100
commite14fee553a4646d15123caa04f7ca6ddb7b48362 (patch)
tree26086b9981918546f946d12547d097eb0974cc2e /src/runtime
parentd382493a20cf005c6631032ebb410a7c2bc768eb (diff)
parent8384fe86a5b7f579a50c7ad423d4dd4eb2d1f117 (diff)
downloadgo-e14fee553a4646d15123caa04f7ca6ddb7b48362.tar.xz
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: I18dbf4f9fa7e2334fccedd862a523126cf38164e
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/abi_test.go2
-rw-r--r--src/runtime/alg.go2
-rw-r--r--src/runtime/asan.go30
-rw-r--r--src/runtime/asan/asan.go31
-rw-r--r--src/runtime/asan_amd64.s21
-rw-r--r--src/runtime/asan_arm64.s25
-rw-r--r--src/runtime/asm_amd64.s136
-rw-r--r--src/runtime/asm_mips64x.s1
-rw-r--r--src/runtime/asm_mipsx.s1
-rw-r--r--src/runtime/asm_ppc64x.s1
-rw-r--r--src/runtime/asm_riscv64.s5
-rw-r--r--src/runtime/asm_s390x.s10
-rw-r--r--src/runtime/atomic_mips64x.s1
-rw-r--r--src/runtime/atomic_mipsx.s1
-rw-r--r--src/runtime/atomic_ppc64x.s1
-rw-r--r--src/runtime/cgo.go2
-rw-r--r--src/runtime/cgo/gcc_amd64.S2
-rw-r--r--src/runtime/cgo/handle.go39
-rw-r--r--src/runtime/cgo/handle_test.go6
-rw-r--r--src/runtime/cgocall.go4
-rw-r--r--src/runtime/chan_test.go2
-rw-r--r--src/runtime/checkptr_test.go12
-rw-r--r--src/runtime/cpuprof.go13
-rw-r--r--src/runtime/crash_cgo_test.go76
-rw-r--r--src/runtime/crash_nonunix_test.go13
-rw-r--r--src/runtime/crash_test.go111
-rw-r--r--src/runtime/crash_unix_test.go17
-rw-r--r--src/runtime/debug/garbage_test.go69
-rw-r--r--src/runtime/debug/mod.go24
-rw-r--r--src/runtime/debug_test.go34
-rw-r--r--src/runtime/debugcall.go2
-rw-r--r--src/runtime/debuglog.go2
-rw-r--r--src/runtime/defer_test.go3
-rw-r--r--src/runtime/defs_linux_riscv64.go8
-rw-r--r--src/runtime/duff_mips64x.s1
-rw-r--r--src/runtime/duff_ppc64x.s1
-rw-r--r--src/runtime/error.go4
-rw-r--r--src/runtime/export_debug_test.go14
-rw-r--r--src/runtime/export_debuglog_test.go18
-rw-r--r--src/runtime/export_futex_test.go19
-rw-r--r--src/runtime/export_test.go35
-rw-r--r--src/runtime/futex_test.go87
-rw-r--r--src/runtime/gc_test.go17
-rw-r--r--src/runtime/gcinfo_test.go28
-rw-r--r--src/runtime/hash_test.go2
-rw-r--r--src/runtime/iface.go10
-rw-r--r--src/runtime/iface_test.go16
-rw-r--r--src/runtime/internal/atomic/atomic_mips64x.s1
-rw-r--r--src/runtime/internal/atomic/atomic_mipsx.s1
-rw-r--r--src/runtime/internal/atomic/atomic_ppc64x.s1
-rw-r--r--src/runtime/internal/atomic/bench_test.go2
-rw-r--r--src/runtime/internal/atomic/sys_nonlinux_arm.s1
-rw-r--r--src/runtime/lfstack_test.go2
-rw-r--r--src/runtime/libfuzzer_amd64.s1
-rw-r--r--src/runtime/libfuzzer_arm64.s1
-rw-r--r--src/runtime/malloc.go20
-rw-r--r--src/runtime/malloc_test.go19
-rw-r--r--src/runtime/map_benchmark_test.go6
-rw-r--r--src/runtime/map_test.go6
-rw-r--r--src/runtime/mbitmap.go4
-rw-r--r--src/runtime/mem_aix.go1
-rw-r--r--src/runtime/mem_bsd.go1
-rw-r--r--src/runtime/mem_darwin.go1
-rw-r--r--src/runtime/mem_linux.go1
-rw-r--r--src/runtime/memclr_386.s1
-rw-r--r--src/runtime/memclr_amd64.s1
-rw-r--r--src/runtime/memclr_mips64x.s1
-rw-r--r--src/runtime/memclr_mipsx.s1
-rw-r--r--src/runtime/memclr_ppc64x.s1
-rw-r--r--src/runtime/memmove_386.s1
-rw-r--r--src/runtime/memmove_amd64.s1
-rw-r--r--src/runtime/memmove_mips64x.s1
-rw-r--r--src/runtime/memmove_mipsx.s1
-rw-r--r--src/runtime/memmove_ppc64x.s1
-rw-r--r--src/runtime/mfinal.go6
-rw-r--r--src/runtime/mfinal_test.go16
-rw-r--r--src/runtime/mgc.go18
-rw-r--r--src/runtime/mgcmark.go18
-rw-r--r--src/runtime/mgcpacer.go38
-rw-r--r--src/runtime/mgcscavenge.go14
-rw-r--r--src/runtime/mgcscavenge_test.go5
-rw-r--r--src/runtime/mgcsweep.go2
-rw-r--r--src/runtime/mkpreempt.go2
-rw-r--r--src/runtime/mpagealloc_32bit.go8
-rw-r--r--src/runtime/mpagealloc_64bit.go4
-rw-r--r--src/runtime/mpagealloc_test.go9
-rw-r--r--src/runtime/mpagecache_test.go5
-rw-r--r--src/runtime/mprof.go6
-rw-r--r--src/runtime/msan_amd64.s1
-rw-r--r--src/runtime/msan_arm64.s1
-rw-r--r--src/runtime/netpoll.go169
-rw-r--r--src/runtime/netpoll_aix.go5
-rw-r--r--src/runtime/netpoll_epoll.go5
-rw-r--r--src/runtime/netpoll_kqueue.go5
-rw-r--r--src/runtime/netpoll_solaris.go2
-rw-r--r--src/runtime/os_windows.go46
-rw-r--r--src/runtime/panic.go15
-rw-r--r--src/runtime/plugin.go6
-rw-r--r--src/runtime/pprof/mprof_test.go2
-rw-r--r--src/runtime/pprof/pprof.go8
-rw-r--r--src/runtime/pprof/pprof_test.go433
-rw-r--r--src/runtime/pprof/proto.go22
-rw-r--r--src/runtime/pprof/proto_test.go16
-rw-r--r--src/runtime/pprof/uname_linux_test.go61
-rw-r--r--src/runtime/pprof/uname_other_test.go15
-rw-r--r--src/runtime/preempt_mips64x.s1
-rw-r--r--src/runtime/preempt_mipsx.s1
-rw-r--r--src/runtime/preempt_ppc64x.s1
-rw-r--r--src/runtime/proc.go43
-rw-r--r--src/runtime/proc_test.go18
-rw-r--r--src/runtime/race/README4
-rw-r--r--src/runtime/race/output_test.go4
-rw-r--r--src/runtime/race/race_darwin_amd64.sysobin451280 -> 455944 bytes
-rw-r--r--src/runtime/race/race_darwin_arm64.sysobin438936 -> 438560 bytes
-rw-r--r--src/runtime/race/race_test.go2
-rw-r--r--src/runtime/race/testdata/issue12664_test.go2
-rw-r--r--src/runtime/race/testdata/mop_test.go18
-rw-r--r--src/runtime/race/testdata/pool_test.go4
-rw-r--r--src/runtime/race_amd64.s1
-rw-r--r--src/runtime/race_arm64.s1
-rw-r--r--src/runtime/race_ppc64le.s1
-rw-r--r--src/runtime/rt0_linux_mips64x.s2
-rw-r--r--src/runtime/rt0_linux_mipsx.s2
-rw-r--r--src/runtime/runtime-gdb_test.go6
-rw-r--r--src/runtime/runtime2.go8
-rw-r--r--src/runtime/runtime_test.go80
-rw-r--r--src/runtime/rwmutex_test.go5
-rw-r--r--src/runtime/semasleep_test.go63
-rw-r--r--src/runtime/signal_unix.go31
-rw-r--r--src/runtime/signal_windows.go87
-rw-r--r--src/runtime/sizeof_test.go6
-rw-r--r--src/runtime/softfloat64_test.go2
-rw-r--r--src/runtime/stack.go2
-rw-r--r--src/runtime/stack_test.go72
-rw-r--r--src/runtime/symtab.go2
-rw-r--r--src/runtime/sys_aix_ppc64.s12
-rw-r--r--src/runtime/sys_darwin.go96
-rw-r--r--src/runtime/sys_darwin_arm64.go4
-rw-r--r--src/runtime/sys_linux_arm.s2
-rw-r--r--src/runtime/sys_linux_mips64x.s2
-rw-r--r--src/runtime/sys_linux_mipsx.s2
-rw-r--r--src/runtime/sys_linux_ppc64x.s2
-rw-r--r--src/runtime/sys_netbsd_arm64.s4
-rw-r--r--src/runtime/sys_openbsd.go22
-rw-r--r--src/runtime/sys_openbsd1.go5
-rw-r--r--src/runtime/sys_openbsd2.go54
-rw-r--r--src/runtime/sys_openbsd_386.s7
-rw-r--r--src/runtime/sys_openbsd_amd64.s7
-rw-r--r--src/runtime/sys_openbsd_arm.s8
-rw-r--r--src/runtime/sys_openbsd_arm64.s8
-rw-r--r--src/runtime/syscall_solaris.go2
-rw-r--r--src/runtime/syscall_windows.go4
-rw-r--r--src/runtime/syscall_windows_test.go6
-rw-r--r--src/runtime/testdata/testprog/badtraceback.go3
-rw-r--r--src/runtime/testdata/testprog/gc.go143
-rw-r--r--src/runtime/testdata/testprog/preempt.go4
-rw-r--r--src/runtime/testdata/testprog/signal.go1
-rw-r--r--src/runtime/testdata/testprog/sleep.go7
-rw-r--r--src/runtime/testdata/testprog/syscalls_none.go1
-rw-r--r--src/runtime/testdata/testprogcgo/callback.go3
-rw-r--r--src/runtime/testdata/testprogcgo/catchpanic.go1
-rw-r--r--src/runtime/testdata/testprogcgo/dropm.go1
-rw-r--r--src/runtime/testdata/testprogcgo/eintr.go1
-rw-r--r--src/runtime/testdata/testprogcgo/exec.go1
-rw-r--r--src/runtime/testdata/testprogcgo/gprof.go46
-rw-r--r--src/runtime/testdata/testprogcgo/gprof_c.c30
-rw-r--r--src/runtime/testdata/testprogcgo/lockosthread.go1
-rw-r--r--src/runtime/testdata/testprogcgo/needmdeadlock.go1
-rw-r--r--src/runtime/testdata/testprogcgo/numgoroutine.go1
-rw-r--r--src/runtime/testdata/testprogcgo/pprof.go11
-rw-r--r--src/runtime/testdata/testprogcgo/raceprof.go1
-rw-r--r--src/runtime/testdata/testprogcgo/racesig.go1
-rw-r--r--src/runtime/testdata/testprogcgo/segv.go1
-rw-r--r--src/runtime/testdata/testprogcgo/sigstack.go1
-rw-r--r--src/runtime/testdata/testprogcgo/threadpanic.go1
-rw-r--r--src/runtime/testdata/testprogcgo/threadpprof.go25
-rw-r--r--src/runtime/testdata/testprogcgo/threadprof.go17
-rw-r--r--src/runtime/testdata/testprognet/signal.go1
-rw-r--r--src/runtime/testdata/testprognet/signalexec.go1
-rw-r--r--src/runtime/testdata/testwinlib/main.c5
-rw-r--r--src/runtime/testdata/testwinlib/main.go1
-rw-r--r--src/runtime/time.go10
-rw-r--r--src/runtime/time_fake.go4
-rw-r--r--src/runtime/time_linux_amd64.s1
-rw-r--r--src/runtime/time_windows_386.s1
-rw-r--r--src/runtime/time_windows_amd64.s1
-rw-r--r--src/runtime/time_windows_arm.s1
-rw-r--r--src/runtime/time_windows_arm64.s1
-rw-r--r--src/runtime/tls_arm.s1
-rw-r--r--src/runtime/tls_mips64x.s1
-rw-r--r--src/runtime/tls_mipsx.s1
-rw-r--r--src/runtime/tls_ppc64x.s1
-rw-r--r--src/runtime/trace.go3
-rw-r--r--src/runtime/trace/annotation.go2
-rw-r--r--src/runtime/traceback.go14
-rw-r--r--src/runtime/zcallback_windows.s1
196 files changed, 1895 insertions, 1176 deletions
diff --git a/src/runtime/abi_test.go b/src/runtime/abi_test.go
index 5c1f1f4067..f9e8d701ce 100644
--- a/src/runtime/abi_test.go
+++ b/src/runtime/abi_test.go
@@ -78,7 +78,7 @@ func TestFinalizerRegisterABI(t *testing.T) {
tests := []struct {
name string
- fin interface{}
+ fin any
confirmValue int
}{
{"Pointer", regFinalizerPointer, -1},
diff --git a/src/runtime/alg.go b/src/runtime/alg.go
index 978a3b85dc..5d7d1c77f4 100644
--- a/src/runtime/alg.go
+++ b/src/runtime/alg.go
@@ -290,7 +290,7 @@ func int64Hash(i uint64, seed uintptr) uintptr {
return memhash64(noescape(unsafe.Pointer(&i)), seed)
}
-func efaceHash(i interface{}, seed uintptr) uintptr {
+func efaceHash(i any, seed uintptr) uintptr {
return nilinterhash(noescape(unsafe.Pointer(&i)), seed)
}
diff --git a/src/runtime/asan.go b/src/runtime/asan.go
index a22b56bb07..5f1e6370d2 100644
--- a/src/runtime/asan.go
+++ b/src/runtime/asan.go
@@ -11,23 +11,43 @@ import (
)
// Public address sanitizer API.
-
func ASanRead(addr unsafe.Pointer, len int) {
- asanread(addr, uintptr(len))
+ sp := getcallersp()
+ pc := getcallerpc()
+ doasanread(addr, uintptr(len), sp, pc)
}
func ASanWrite(addr unsafe.Pointer, len int) {
- asanwrite(addr, uintptr(len))
+ sp := getcallersp()
+ pc := getcallerpc()
+ doasanwrite(addr, uintptr(len), sp, pc)
}
// Private interface for the runtime.
const asanenabled = true
+// asan{read,write} are nosplit because they may be called between
+// fork and exec, when the stack must not grow. See issue #50391.
+
+//go:nosplit
+func asanread(addr unsafe.Pointer, sz uintptr) {
+ sp := getcallersp()
+ pc := getcallerpc()
+ doasanread(addr, sz, sp, pc)
+}
+
+//go:nosplit
+func asanwrite(addr unsafe.Pointer, sz uintptr) {
+ sp := getcallersp()
+ pc := getcallerpc()
+ doasanwrite(addr, sz, sp, pc)
+}
+
//go:noescape
-func asanread(addr unsafe.Pointer, sz uintptr)
+func doasanread(addr unsafe.Pointer, sz, sp, pc uintptr)
//go:noescape
-func asanwrite(addr unsafe.Pointer, sz uintptr)
+func doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr)
//go:noescape
func asanunpoison(addr unsafe.Pointer, sz uintptr)
diff --git a/src/runtime/asan/asan.go b/src/runtime/asan/asan.go
index eb66b3aab5..bab2362c51 100644
--- a/src/runtime/asan/asan.go
+++ b/src/runtime/asan/asan.go
@@ -14,38 +14,15 @@ package asan
#include <stdint.h>
#include <sanitizer/asan_interface.h>
-extern void __asan_report_load1(void*);
-extern void __asan_report_load2(void*);
-extern void __asan_report_load4(void*);
-extern void __asan_report_load8(void*);
-extern void __asan_report_load_n(void*, uintptr_t);
-extern void __asan_report_store1(void*);
-extern void __asan_report_store2(void*);
-extern void __asan_report_store4(void*);
-extern void __asan_report_store8(void*);
-extern void __asan_report_store_n(void*, uintptr_t);
-
-void __asan_read_go(void *addr, uintptr_t sz) {
+void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc) {
if (__asan_region_is_poisoned(addr, sz)) {
- switch (sz) {
- case 1: __asan_report_load1(addr); break;
- case 2: __asan_report_load2(addr); break;
- case 4: __asan_report_load4(addr); break;
- case 8: __asan_report_load8(addr); break;
- default: __asan_report_load_n(addr, sz); break;
- }
+ __asan_report_error(pc, 0, sp, addr, false, sz);
}
}
-void __asan_write_go(void *addr, uintptr_t sz) {
+void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc) {
if (__asan_region_is_poisoned(addr, sz)) {
- switch (sz) {
- case 1: __asan_report_store1(addr); break;
- case 2: __asan_report_store2(addr); break;
- case 4: __asan_report_store4(addr); break;
- case 8: __asan_report_store8(addr); break;
- default: __asan_report_store_n(addr, sz); break;
- }
+ __asan_report_error(pc, 0, sp, addr, true, sz);
}
}
diff --git a/src/runtime/asan_amd64.s b/src/runtime/asan_amd64.s
index 01bd612dc3..3857350020 100644
--- a/src/runtime/asan_amd64.s
+++ b/src/runtime/asan_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build asan
-// +build asan
#include "go_asm.h"
#include "go_tls.h"
@@ -16,25 +15,33 @@
#ifdef GOOS_windows
#define RARG0 CX
#define RARG1 DX
+#define RARG2 R8
+#define RARG3 R9
#else
#define RARG0 DI
#define RARG1 SI
+#define RARG2 DX
+#define RARG3 CX
#endif
// Called from intrumented code.
-// func runtime·asanread(addr unsafe.Pointer, sz uintptr)
-TEXT runtime·asanread(SB), NOSPLIT, $0-16
+// func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr)
+TEXT runtime·doasanread(SB), NOSPLIT, $0-32
MOVQ addr+0(FP), RARG0
MOVQ size+8(FP), RARG1
- // void __asan_read_go(void *addr, uintptr_t sz);
+ MOVQ sp+16(FP), RARG2
+ MOVQ pc+24(FP), RARG3
+ // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc);
MOVQ $__asan_read_go(SB), AX
JMP asancall<>(SB)
-// func runtime·asanwrite(addr unsafe.Pointer, sz uintptr)
-TEXT runtime·asanwrite(SB), NOSPLIT, $0-16
+// func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr)
+TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32
MOVQ addr+0(FP), RARG0
MOVQ size+8(FP), RARG1
- // void __asan_write_go(void *addr, uintptr_t sz);
+ MOVQ sp+16(FP), RARG2
+ MOVQ pc+24(FP), RARG3
+ // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc);
MOVQ $__asan_write_go(SB), AX
JMP asancall<>(SB)
diff --git a/src/runtime/asan_arm64.s b/src/runtime/asan_arm64.s
index eb0f9bd71e..5ed03c932b 100644
--- a/src/runtime/asan_arm64.s
+++ b/src/runtime/asan_arm64.s
@@ -3,29 +3,34 @@
// license that can be found in the LICENSE file.
//go:build asan
-// +build asan
#include "go_asm.h"
#include "textflag.h"
#define RARG0 R0
#define RARG1 R1
-#define FARG R3
+#define RARG2 R2
+#define RARG3 R3
+#define FARG R4
// Called from instrumented code.
-// func runtime·asanread(addr unsafe.Pointer, sz uintptr)
-TEXT runtime·asanread(SB), NOSPLIT, $0-16
+// func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr)
+TEXT runtime·doasanread(SB), NOSPLIT, $0-32
MOVD addr+0(FP), RARG0
MOVD size+8(FP), RARG1
- // void __asan_read_go(void *addr, uintptr_t sz);
+ MOVD sp+16(FP), RARG2
+ MOVD pc+24(FP), RARG3
+ // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc);
MOVD $__asan_read_go(SB), FARG
JMP asancall<>(SB)
-// func runtime·asanwrite(addr unsafe.Pointer, sz uintptr)
-TEXT runtime·asanwrite(SB), NOSPLIT, $0-16
+// func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr)
+TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32
MOVD addr+0(FP), RARG0
MOVD size+8(FP), RARG1
- // void __asan_write_go(void *addr, uintptr_t sz);
+ MOVD sp+16(FP), RARG2
+ MOVD pc+24(FP), RARG3
+ // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc);
MOVD $__asan_write_go(SB), FARG
JMP asancall<>(SB)
@@ -54,8 +59,8 @@ TEXT asancall<>(SB), NOSPLIT, $0-0
CMP R11, g
BEQ g0stack
- MOVD (g_sched+gobuf_sp)(R11), R4
- MOVD R4, RSP
+ MOVD (g_sched+gobuf_sp)(R11), R5
+ MOVD R5, RSP
g0stack:
BL (FARG)
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 0f0e5be21a..c08ae610fb 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -145,28 +145,14 @@ GLOBL bad_cpu_msg<>(SB), RODATA, $84
#endif
-#ifdef GOAMD64_v1
-#define SKIP_GOAMD64_CHECK
-#endif
-
-#ifndef GOAMD64_v1
-#ifndef GOAMD64_v2
-#ifndef GOAMD64_v3
-#ifndef GOAMD64_v4
-#define SKIP_GOAMD64_CHECK
-#endif
-#endif
-#endif
-#endif
-
TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0
// copy arguments forward on an even stack
MOVQ DI, AX // argc
MOVQ SI, BX // argv
- SUBQ $(4*8+7), SP // 2args 2auto
+ SUBQ $(5*8), SP // 3args 2auto
ANDQ $~15, SP
- MOVQ AX, 16(SP)
- MOVQ BX, 24(SP)
+ MOVQ AX, 24(SP)
+ MOVQ BX, 32(SP)
// create istack out of the given (operating system) stack.
// _cgo_init may update stackguard.
@@ -181,23 +167,8 @@ TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0
MOVL $0, AX
CPUID
CMPL AX, $0
-#ifdef SKIP_GOAMD64_CHECK
JE nocpuinfo
-#else
- JNE has_cpuinfo
-
-bad_cpu: // show that the program requires a certain microarchitecture level.
- MOVQ $2, 0(SP)
- MOVQ $bad_cpu_msg<>(SB), AX
- MOVQ AX, 8(SP)
- MOVQ $84, 16(SP)
- CALL runtime·write(SB)
- MOVQ $1, 0(SP)
- CALL runtime·exit(SB)
- CALL runtime·abort(SB)
-#endif
-has_cpuinfo:
CMPL BX, $0x756E6547 // "Genu"
JNE notintel
CMPL DX, $0x49656E69 // "ineI"
@@ -212,44 +183,6 @@ notintel:
CPUID
MOVL AX, runtime·processorVersionInfo(SB)
-#ifdef NEED_FEATURES_CX
- ANDL $NEED_FEATURES_CX, CX
- CMPL CX, $NEED_FEATURES_CX
- JNE bad_cpu
-#endif
-
-#ifdef NEED_MAX_CPUID
- MOVL $0x80000000, AX
- CPUID
- CMPL AX, $NEED_MAX_CPUID
- JL bad_cpu
-#endif
-
-#ifdef NEED_EXT_FEATURES_BX
- MOVL $7, AX
- MOVL $0, CX
- CPUID
- ANDL $NEED_EXT_FEATURES_BX, BX
- CMPL BX, $NEED_EXT_FEATURES_BX
- JNE bad_cpu
-#endif
-
-#ifdef NEED_EXT_FEATURES_CX
- MOVL $0x80000001, AX
- CPUID
- ANDL $NEED_EXT_FEATURES_CX, CX
- CMPL CX, $NEED_EXT_FEATURES_CX
- JNE bad_cpu
-#endif
-
-#ifdef NEED_OS_SUPPORT_AX
- XORL CX, CX
- XGETBV
- ANDL $NEED_OS_SUPPORT_AX, AX
- CMPL AX, $NEED_OS_SUPPORT_AX
- JNE bad_cpu
-#endif
-
nocpuinfo:
// if there is an _cgo_init, call it.
MOVQ _cgo_init(SB), AX
@@ -330,11 +263,59 @@ ok:
MOVQ AX, g_m(CX)
CLD // convention is D is always left cleared
+
+ // Check GOAMD64 reqirements
+ // We need to do this after setting up TLS, so that
+ // we can report an error if there is a failure. See issue 49586.
+#ifdef NEED_FEATURES_CX
+ MOVL $0, AX
+ CPUID
+ CMPL AX, $0
+ JE bad_cpu
+ MOVL $1, AX
+ CPUID
+ ANDL $NEED_FEATURES_CX, CX
+ CMPL CX, $NEED_FEATURES_CX
+ JNE bad_cpu
+#endif
+
+#ifdef NEED_MAX_CPUID
+ MOVL $0x80000000, AX
+ CPUID
+ CMPL AX, $NEED_MAX_CPUID
+ JL bad_cpu
+#endif
+
+#ifdef NEED_EXT_FEATURES_BX
+ MOVL $7, AX
+ MOVL $0, CX
+ CPUID
+ ANDL $NEED_EXT_FEATURES_BX, BX
+ CMPL BX, $NEED_EXT_FEATURES_BX
+ JNE bad_cpu
+#endif
+
+#ifdef NEED_EXT_FEATURES_CX
+ MOVL $0x80000001, AX
+ CPUID
+ ANDL $NEED_EXT_FEATURES_CX, CX
+ CMPL CX, $NEED_EXT_FEATURES_CX
+ JNE bad_cpu
+#endif
+
+#ifdef NEED_OS_SUPPORT_AX
+ XORL CX, CX
+ XGETBV
+ ANDL $NEED_OS_SUPPORT_AX, AX
+ CMPL AX, $NEED_OS_SUPPORT_AX
+ JNE bad_cpu
+#endif
+
CALL runtime·check(SB)
- MOVL 16(SP), AX // copy argc
+ MOVL 24(SP), AX // copy argc
MOVL AX, 0(SP)
- MOVQ 24(SP), AX // copy argv
+ MOVQ 32(SP), AX // copy argv
MOVQ AX, 8(SP)
CALL runtime·args(SB)
CALL runtime·osinit(SB)
@@ -352,6 +333,17 @@ ok:
CALL runtime·abort(SB) // mstart should never return
RET
+bad_cpu: // show that the program requires a certain microarchitecture level.
+ MOVQ $2, 0(SP)
+ MOVQ $bad_cpu_msg<>(SB), AX
+ MOVQ AX, 8(SP)
+ MOVQ $84, 16(SP)
+ CALL runtime·write(SB)
+ MOVQ $1, 0(SP)
+ CALL runtime·exit(SB)
+ CALL runtime·abort(SB)
+ RET
+
// Prevent dead-code elimination of debugCallV2, which is
// intended to be called by debuggers.
MOVQ $runtime·debugCallV2<ABIInternal>(SB), AX
diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s
index e0e5cbb704..3597ebec57 100644
--- a/src/runtime/asm_mips64x.s
+++ b/src/runtime/asm_mips64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/asm_mipsx.s b/src/runtime/asm_mipsx.s
index 1b550719d1..4a086b8eb3 100644
--- a/src/runtime/asm_mipsx.s
+++ b/src/runtime/asm_mipsx.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips || mipsle
-// +build mips mipsle
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s
index 0e7ef7b2b8..ae14213999 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s
index 0e813189d4..2a4837b399 100644
--- a/src/runtime/asm_riscv64.s
+++ b/src/runtime/asm_riscv64.s
@@ -81,7 +81,10 @@ TEXT setg_gcc<>(SB),NOSPLIT,$0-0
// func cputicks() int64
TEXT runtime·cputicks(SB),NOSPLIT,$0-8
- RDCYCLE A0
+ // RDTIME to emulate cpu ticks
+ // RDCYCLE reads counter that is per HART(core) based
+ // according to the riscv manual, see issue 46737
+ RDTIME A0
MOV A0, ret+0(FP)
RET
diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s
index 5894fe5783..9159a67372 100644
--- a/src/runtime/asm_s390x.s
+++ b/src/runtime/asm_s390x.s
@@ -777,13 +777,12 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1
// gcWriteBarrier does NOT follow the Go ABI. It takes two arguments:
// - R2 is the destination of the write
// - R3 is the value being written at R2.
-// It clobbers R10 (the temp register).
+// It clobbers R10 (the temp register) and R1 (used by PLT stub).
// It does not clobber any other general-purpose registers,
// but may clobber others (e.g., floating point registers).
-TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$104
+TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$96
// Save the registers clobbered by the fast path.
- MOVD R1, 96(R15)
- MOVD R4, 104(R15)
+ MOVD R4, 96(R15)
MOVD g_m(g), R1
MOVD m_p(R1), R1
// Increment wbBuf.next position.
@@ -798,8 +797,7 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$104
// Is the buffer full?
CMPBEQ R4, R1, flush
ret:
- MOVD 96(R15), R1
- MOVD 104(R15), R4
+ MOVD 96(R15), R4
// Do the write.
MOVD R3, (R2)
RET
diff --git a/src/runtime/atomic_mips64x.s b/src/runtime/atomic_mips64x.s
index e2118e6a20..dd6380ce40 100644
--- a/src/runtime/atomic_mips64x.s
+++ b/src/runtime/atomic_mips64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "textflag.h"
diff --git a/src/runtime/atomic_mipsx.s b/src/runtime/atomic_mipsx.s
index 1eacd273b4..ac255fe7e6 100644
--- a/src/runtime/atomic_mipsx.s
+++ b/src/runtime/atomic_mipsx.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips || mipsle
-// +build mips mipsle
#include "textflag.h"
diff --git a/src/runtime/atomic_ppc64x.s b/src/runtime/atomic_ppc64x.s
index b63de2dbd3..4742b6cf56 100644
--- a/src/runtime/atomic_ppc64x.s
+++ b/src/runtime/atomic_ppc64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "textflag.h"
diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go
index 395d54a66e..d90468240d 100644
--- a/src/runtime/cgo.go
+++ b/src/runtime/cgo.go
@@ -42,7 +42,7 @@ var cgoHasExtraM bool
// 2) they keep the argument alive until the call site; the call is emitted after
// the end of the (presumed) use of the argument by C.
// cgoUse should not actually be called (see cgoAlwaysFalse).
-func cgoUse(interface{}) { throw("cgoUse should not be called") }
+func cgoUse(any) { throw("cgoUse should not be called") }
// cgoAlwaysFalse is a boolean value that is always false.
// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }.
diff --git a/src/runtime/cgo/gcc_amd64.S b/src/runtime/cgo/gcc_amd64.S
index d75f864666..46699d1d9c 100644
--- a/src/runtime/cgo/gcc_amd64.S
+++ b/src/runtime/cgo/gcc_amd64.S
@@ -12,7 +12,7 @@
#endif
/*
- * void crosscall_amd64(void (*fn)(void))
+ * void crosscall_amd64(void (*fn)(void), void (*setg_gcc)(void*), void *g)
*
* Calling into the 6c tool chain, where all registers are caller save.
* Called from standard x86-64 ABI, where %rbx, %rbp, %r12-%r15
diff --git a/src/runtime/cgo/handle.go b/src/runtime/cgo/handle.go
index 720acca802..d711900d79 100644
--- a/src/runtime/cgo/handle.go
+++ b/src/runtime/cgo/handle.go
@@ -59,6 +59,41 @@ import (
// void myprint(uintptr_t handle) {
// MyGoPrint(handle);
// }
+//
+// Some C functions accept a void* argument that points to an arbitrary
+// data value supplied by the caller. It is not safe to coerce a cgo.Handle
+// (an integer) to a Go unsafe.Pointer, but instead we can pass the address
+// of the cgo.Handle to the void* parameter, as in this variant of the
+// previous example:
+//
+// package main
+//
+// /*
+// extern void MyGoPrint(void *context);
+// static inline void myprint(void *context) {
+// MyGoPrint(context);
+// }
+// */
+// import "C"
+// import (
+// "runtime/cgo"
+// "unsafe"
+// )
+//
+// //export MyGoPrint
+// func MyGoPrint(context unsafe.Pointer) {
+// h := *(*cgo.Handle)(context)
+// val := h.Value().(string)
+// println(val)
+// h.Delete()
+// }
+//
+// func main() {
+// val := "hello Go"
+// h := cgo.NewHandle(val)
+// C.myprint(unsafe.Pointer(&h))
+// // Output: hello Go
+// }
type Handle uintptr
// NewHandle returns a handle for a given value.
@@ -70,7 +105,7 @@ type Handle uintptr
//
// The intended use is to pass the returned handle to C code, which
// passes it back to Go, which calls Value.
-func NewHandle(v interface{}) Handle {
+func NewHandle(v any) Handle {
h := atomic.AddUintptr(&handleIdx, 1)
if h == 0 {
panic("runtime/cgo: ran out of handle space")
@@ -83,7 +118,7 @@ func NewHandle(v interface{}) Handle {
// Value returns the associated Go value for a valid handle.
//
// The method panics if the handle is invalid.
-func (h Handle) Value() interface{} {
+func (h Handle) Value() any {
v, ok := handles.Load(uintptr(h))
if !ok {
panic("runtime/cgo: misuse of an invalid Handle")
diff --git a/src/runtime/cgo/handle_test.go b/src/runtime/cgo/handle_test.go
index 738051a0ea..b341c8e1e4 100644
--- a/src/runtime/cgo/handle_test.go
+++ b/src/runtime/cgo/handle_test.go
@@ -13,8 +13,8 @@ func TestHandle(t *testing.T) {
v := 42
tests := []struct {
- v1 interface{}
- v2 interface{}
+ v1 any
+ v2 any
}{
{v1: v, v2: v},
{v1: &v, v2: &v},
@@ -44,7 +44,7 @@ func TestHandle(t *testing.T) {
}
siz := 0
- handles.Range(func(k, v interface{}) bool {
+ handles.Range(func(k, v any) bool {
siz++
return true
})
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index 694b3e66cd..a0c9560fd0 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -389,7 +389,7 @@ var racecgosync uint64 // represents possible synchronization in C code
// cgoCheckPointer checks if the argument contains a Go pointer that
// points to a Go pointer, and panics if it does.
-func cgoCheckPointer(ptr interface{}, arg interface{}) {
+func cgoCheckPointer(ptr any, arg any) {
if debug.cgocheck == 0 {
return
}
@@ -628,7 +628,7 @@ func cgoInRange(p unsafe.Pointer, start, end uintptr) bool {
// cgoCheckResult is called to check the result parameter of an
// exported Go function. It panics if the result is or contains a Go
// pointer.
-func cgoCheckResult(val interface{}) {
+func cgoCheckResult(val any) {
if debug.cgocheck == 0 {
return
}
diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go
index 355267c5e3..9471d4596c 100644
--- a/src/runtime/chan_test.go
+++ b/src/runtime/chan_test.go
@@ -494,7 +494,7 @@ func TestSelectFairness(t *testing.T) {
func TestChanSendInterface(t *testing.T) {
type mt struct{}
m := &mt{}
- c := make(chan interface{}, 1)
+ c := make(chan any, 1)
c <- m
select {
case c <- m:
diff --git a/src/runtime/checkptr_test.go b/src/runtime/checkptr_test.go
index b3aea079c6..15011ec494 100644
--- a/src/runtime/checkptr_test.go
+++ b/src/runtime/checkptr_test.go
@@ -12,6 +12,12 @@ import (
)
func TestCheckPtr(t *testing.T) {
+ // This test requires rebuilding packages with -d=checkptr=1,
+ // so it's somewhat slow.
+ if testing.Short() {
+ t.Skip("skipping test in -short mode")
+ }
+
t.Parallel()
testenv.MustHaveGoRun(t)
@@ -57,6 +63,12 @@ func TestCheckPtr(t *testing.T) {
}
func TestCheckPtr2(t *testing.T) {
+ // This test requires rebuilding packages with -d=checkptr=2,
+ // so it's somewhat slow.
+ if testing.Short() {
+ t.Skip("skipping test in -short mode")
+ }
+
t.Parallel()
testenv.MustHaveGoRun(t)
diff --git a/src/runtime/cpuprof.go b/src/runtime/cpuprof.go
index c81ab710c2..48cef46fe9 100644
--- a/src/runtime/cpuprof.go
+++ b/src/runtime/cpuprof.go
@@ -89,7 +89,7 @@ func SetCPUProfileRate(hz int) {
// held at the time of the signal, nor can it use substantial amounts
// of stack.
//go:nowritebarrierrec
-func (p *cpuProfile) add(gp *g, stk []uintptr) {
+func (p *cpuProfile) add(tagPtr *unsafe.Pointer, stk []uintptr) {
// Simple cas-lock to coordinate with setcpuprofilerate.
for !atomic.Cas(&prof.signalLock, 0, 1) {
osyield()
@@ -104,15 +104,6 @@ func (p *cpuProfile) add(gp *g, stk []uintptr) {
// because otherwise its write barrier behavior may not
// be correct. See the long comment there before
// changing the argument here.
- //
- // Note: it can happen on Windows, where we are calling
- // p.add with a gp that is not the current g, that gp is nil,
- // meaning we interrupted a system thread with no g.
- // Avoid faulting in that case.
- var tagPtr *unsafe.Pointer
- if gp != nil {
- tagPtr = &gp.labels
- }
cpuprof.log.write(tagPtr, nanotime(), hdr[:], stk)
}
@@ -209,6 +200,8 @@ func runtime_pprof_runtime_cyclesPerSecond() int64 {
// If profiling is turned off and all the profile data accumulated while it was
// on has been returned, readProfile returns eof=true.
// The caller must save the returned data and tags before calling readProfile again.
+// The returned data contains a whole number of records, and tags contains
+// exactly one entry per record.
//
//go:linkname runtime_pprof_readProfile runtime/pprof.readProfile
func runtime_pprof_readProfile() ([]uint64, []unsafe.Pointer, bool) {
diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go
index e6d1742a38..9444554d37 100644
--- a/src/runtime/crash_cgo_test.go
+++ b/src/runtime/crash_cgo_test.go
@@ -7,7 +7,6 @@
package runtime_test
import (
- "bytes"
"fmt"
"internal/testenv"
"os"
@@ -64,6 +63,10 @@ func TestCgoCallbackGC(t *testing.T) {
t.Skip("too slow for mips64x builders")
}
}
+ if testenv.Builder() == "darwin-amd64-10_14" {
+ // TODO(#23011): When the 10.14 builders are gone, remove this skip.
+ t.Skip("skipping due to platform bug on macOS 10.14; see https://golang.org/issue/43926")
+ }
got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
want := "OK\n"
if got != want {
@@ -90,23 +93,9 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
case "plan9", "windows":
t.Skipf("no pthreads on %s", runtime.GOOS)
}
- if runtime.GOARCH == "ppc64" && runtime.GOOS == "linux" {
- // TODO(austin) External linking not implemented on
- // linux/ppc64 (issue #8912)
- t.Skipf("no external linking on ppc64")
- }
- exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
- if err != nil {
- t.Fatal(err)
- }
-
- got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
- if err != nil {
- t.Fatalf("exit status: %v\n%s", err, got)
- }
-
- if want := "OK\n"; string(got) != want {
+ got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF", "GO_START_SIGPROF_THREAD=1")
+ if want := "OK\n"; got != want {
t.Fatalf("expected %q, but got:\n%s", want, got)
}
}
@@ -119,18 +108,8 @@ func TestCgoExternalThreadSignal(t *testing.T) {
t.Skipf("no pthreads on %s", runtime.GOOS)
}
- exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
- if err != nil {
- t.Fatal(err)
- }
-
- got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
- if err != nil {
- t.Fatalf("exit status: %v\n%s", err, got)
- }
-
- want := []byte("OK\n")
- if !bytes.Equal(got, want) {
+ got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
+ if want := "OK\n"; got != want {
t.Fatalf("expected %q, but got:\n%s", want, got)
}
}
@@ -302,12 +281,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
t.Fatal(err)
}
- // pprofCgoTraceback is called whenever CGO code is executing and a signal
- // is received. Disable signal preemption to increase the likelihood at
- // least one SIGPROF signal fired to capture a sample. See issue #37201.
cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
- cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1")
-
got, err := cmd.CombinedOutput()
if err != nil {
if testenv.Builder() == "linux-amd64-alpine" {
@@ -320,7 +294,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
defer os.Remove(fn)
for try := 0; try < 2; try++ {
- cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-traces"))
+ cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-tagignore=ignore", "-traces"))
// Check that pprof works both with and without explicit executable on command line.
if try == 0 {
cmd.Args = append(cmd.Args, exe, fn)
@@ -628,15 +602,25 @@ func TestSegv(t *testing.T) {
// No runtime errors like "runtime: unknown pc".
switch runtime.GOOS {
case "darwin", "illumos", "solaris":
- // TODO(golang.org/issue/49182): Skip, runtime
- // throws while attempting to generate
- // traceback.
- default:
- nowant := "runtime: "
- if strings.Contains(got, nowant) {
- t.Errorf("unexpectedly saw %q in output", nowant)
+ // Runtime sometimes throws when generating the traceback.
+ testenv.SkipFlaky(t, 49182)
+ case "linux":
+ if runtime.GOARCH == "386" {
+ // Runtime throws when generating a traceback from
+ // a VDSO call via asmcgocall.
+ testenv.SkipFlaky(t, 50504)
+ }
+ if testenv.Builder() == "linux-mips64le-mengzhuo" && strings.Contains(got, "runtime: unknown pc") {
+ // Runtime sometimes throw "unknown pc" when generating the traceback.
+ // Curiously, that doesn't seem to happen on the linux-mips64le-rtrk
+ // builder.
+ testenv.SkipFlaky(t, 50605)
}
}
+ nowant := "runtime: "
+ if strings.Contains(got, nowant) {
+ t.Errorf("unexpectedly saw %q in output", nowant)
+ }
})
}
}
@@ -702,3 +686,11 @@ func TestNeedmDeadlock(t *testing.T) {
t.Fatalf("want %s, got %s\n", want, output)
}
}
+
+func TestCgoTracebackGoroutineProfile(t *testing.T) {
+ output := runTestProg(t, "testprogcgo", "GoroutineProfile")
+ want := "OK\n"
+ if output != want {
+ t.Fatalf("want %s, got %s\n", want, output)
+ }
+}
diff --git a/src/runtime/crash_nonunix_test.go b/src/runtime/crash_nonunix_test.go
deleted file mode 100644
index 73c1cd3101..0000000000
--- a/src/runtime/crash_nonunix_test.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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.
-
-//go:build windows || plan9 || (js && wasm)
-
-package runtime_test
-
-import "os"
-
-// sigquit is the signal to send to kill a hanging testdata program.
-// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
-var sigquit = os.Kill
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index e0c0bac892..9b9ab4f3e1 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
"bytes"
+ "errors"
"flag"
"fmt"
"internal/testenv"
@@ -14,11 +15,9 @@ import (
"path/filepath"
"regexp"
"runtime"
- "strconv"
"strings"
"sync"
"testing"
- "time"
)
var toRemove []string
@@ -34,12 +33,13 @@ func TestMain(m *testing.M) {
var testprog struct {
sync.Mutex
dir string
- target map[string]buildexe
+ target map[string]*buildexe
}
type buildexe struct {
- exe string
- err error
+ once sync.Once
+ exe string
+ err error
}
func runTestProg(t *testing.T, binary, name string, env ...string) string {
@@ -69,52 +69,19 @@ func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
if testing.Short() {
cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
}
- var b bytes.Buffer
- cmd.Stdout = &b
- cmd.Stderr = &b
- if err := cmd.Start(); err != nil {
- t.Fatalf("starting %s %s: %v", exe, name, err)
- }
-
- // If the process doesn't complete within 1 minute,
- // assume it is hanging and kill it to get a stack trace.
- p := cmd.Process
- done := make(chan bool)
- go func() {
- scale := 1
- // This GOARCH/GOOS test is copied from cmd/dist/test.go.
- // TODO(iant): Have cmd/dist update the environment variable.
- if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
- scale = 2
- }
- if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
- if sc, err := strconv.Atoi(s); err == nil {
- scale = sc
- }
- }
-
- select {
- case <-done:
- case <-time.After(time.Duration(scale) * time.Minute):
- p.Signal(sigquit)
- }
- }()
-
- if err := cmd.Wait(); err != nil {
- t.Logf("%s %s exit status: %v", exe, name, err)
- }
- close(done)
-
- return b.String()
+ out, _ := testenv.RunWithTimeout(t, cmd)
+ return string(out)
}
+var serializeBuild = make(chan bool, 2)
+
func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
if *flagQuick {
t.Skip("-quick")
}
+ testenv.MustHaveGoBuild(t)
testprog.Lock()
- defer testprog.Unlock()
if testprog.dir == "" {
dir, err := os.MkdirTemp("", "go-build")
if err != nil {
@@ -125,29 +92,48 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error)
}
if testprog.target == nil {
- testprog.target = make(map[string]buildexe)
+ testprog.target = make(map[string]*buildexe)
}
name := binary
if len(flags) > 0 {
name += "_" + strings.Join(flags, "_")
}
target, ok := testprog.target[name]
- if ok {
- return target.exe, target.err
- }
-
- exe := filepath.Join(testprog.dir, name+".exe")
- cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
- cmd.Dir = "testdata/" + binary
- out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
- if err != nil {
- target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
+ if !ok {
+ target = &buildexe{}
testprog.target[name] = target
- return "", target.err
}
- target.exe = exe
- testprog.target[name] = target
- return exe, nil
+
+ dir := testprog.dir
+
+ // Unlock testprog while actually building, so that other
+ // tests can look up executables that were already built.
+ testprog.Unlock()
+
+ target.once.Do(func() {
+ // Only do two "go build"'s at a time,
+ // to keep load from getting too high.
+ serializeBuild <- true
+ defer func() { <-serializeBuild }()
+
+ // Don't get confused if testenv.GoToolPath calls t.Skip.
+ target.err = errors.New("building test called t.Skip")
+
+ exe := filepath.Join(dir, name+".exe")
+
+ t.Logf("running go build -o %s %s", exe, strings.Join(flags, " "))
+ cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
+ cmd.Dir = "testdata/" + binary
+ out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
+ if err != nil {
+ target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
+ } else {
+ target.exe = exe
+ target.err = nil
+ }
+ })
+
+ return target.exe, target.err
}
func TestVDSO(t *testing.T) {
@@ -417,7 +403,7 @@ func TestRuntimePanicWithRuntimeError(t *testing.T) {
}
}
-func panicValue(fn func()) (recovered interface{}) {
+func panicValue(fn func()) (recovered any) {
defer func() {
recovered = recover()
}()
@@ -691,6 +677,13 @@ func TestBadTraceback(t *testing.T) {
}
func TestTimePprof(t *testing.T) {
+ // This test is unreliable on any system in which nanotime
+ // calls into libc.
+ switch runtime.GOOS {
+ case "aix", "darwin", "illumos", "openbsd", "solaris":
+ t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
+ }
+
// Pass GOTRACEBACK for issue #41120 to try to get more
// information on timeout.
fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go
index 0d9d22aa49..1eb10f9b60 100644
--- a/src/runtime/crash_unix_test.go
+++ b/src/runtime/crash_unix_test.go
@@ -13,7 +13,7 @@ import (
"os"
"os/exec"
"runtime"
- "strings"
+ "runtime/debug"
"sync"
"syscall"
"testing"
@@ -21,16 +21,12 @@ import (
"unsafe"
)
-// sigquit is the signal to send to kill a hanging testdata program.
-// Send SIGQUIT to get a stack trace.
-var sigquit = syscall.SIGQUIT
-
func init() {
if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
// We can't use SIGQUIT to kill subprocesses because
// it's blocked. Use SIGKILL instead. See issue
// #19196 for an example of when this happens.
- sigquit = syscall.SIGKILL
+ testenv.Sigquit = syscall.SIGKILL
}
}
@@ -211,6 +207,11 @@ func TestPanicSystemstack(t *testing.T) {
func init() {
if len(os.Args) >= 2 && os.Args[1] == "testPanicSystemstackInternal" {
+ // Complete any in-flight GCs and disable future ones. We're going to
+ // block goroutines on runtime locks, which aren't ever preemptible for the
+ // GC to scan them.
+ runtime.GC()
+ debug.SetGCPercent(-1)
// Get two threads running on the system stack with
// something recognizable in the stack trace.
runtime.GOMAXPROCS(2)
@@ -244,9 +245,7 @@ func TestSignalExitStatus(t *testing.T) {
func TestSignalIgnoreSIGTRAP(t *testing.T) {
if runtime.GOOS == "openbsd" {
- if bn := testenv.Builder(); strings.HasSuffix(bn, "-62") || strings.HasSuffix(bn, "-64") {
- testenv.SkipFlaky(t, 17496)
- }
+ testenv.SkipFlaky(t, 49725)
}
output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP")
diff --git a/src/runtime/debug/garbage_test.go b/src/runtime/debug/garbage_test.go
index 69e769ecf2..7213bbe641 100644
--- a/src/runtime/debug/garbage_test.go
+++ b/src/runtime/debug/garbage_test.go
@@ -6,6 +6,7 @@ package debug_test
import (
"internal/testenv"
+ "os"
"runtime"
. "runtime/debug"
"testing"
@@ -87,27 +88,71 @@ func TestReadGCStats(t *testing.T) {
}
}
-var big = make([]byte, 1<<20)
+var big []byte
func TestFreeOSMemory(t *testing.T) {
- var ms1, ms2 runtime.MemStats
+ // Tests FreeOSMemory by making big susceptible to collection
+ // and checking that at least that much memory is returned to
+ // the OS after.
- if big == nil {
- t.Skip("test is not reliable when run multiple times")
- }
- big = nil
+ const bigBytes = 32 << 20
+ big = make([]byte, bigBytes)
+
+ // Make sure any in-progress GCs are complete.
runtime.GC()
- runtime.ReadMemStats(&ms1)
+
+ var before runtime.MemStats
+ runtime.ReadMemStats(&before)
+
+ // Clear the last reference to the big allocation, making it
+ // susceptible to collection.
+ big = nil
+
+ // FreeOSMemory runs a GC cycle before releasing memory,
+ // so it's fine to skip a GC here.
+ //
+ // It's possible the background scavenger runs concurrently
+ // with this function and does most of the work for it.
+ // If that happens, it's OK. What we want is a test that fails
+ // often if FreeOSMemory does not work correctly, and a test
+ // that passes every time if it does.
FreeOSMemory()
- runtime.ReadMemStats(&ms2)
- if ms1.HeapReleased >= ms2.HeapReleased {
- t.Errorf("released before=%d; released after=%d; did not go up", ms1.HeapReleased, ms2.HeapReleased)
+
+ var after runtime.MemStats
+ runtime.ReadMemStats(&after)
+
+ // Check to make sure that the big allocation (now freed)
+ // had its memory shift into HeapReleased as a result of that
+ // FreeOSMemory.
+ if after.HeapReleased <= before.HeapReleased {
+ t.Fatalf("no memory released: %d -> %d", before.HeapReleased, after.HeapReleased)
+ }
+
+ // Check to make sure bigBytes was released, plus some slack. Pages may get
+ // allocated in between the two measurements above for a variety for reasons,
+ // most commonly for GC work bufs. Since this can get fairly high, depending
+ // on scheduling and what GOMAXPROCS is, give a lot of slack up-front.
+ //
+ // Add a little more slack too if the page size is bigger than the runtime page size.
+ // "big" could end up unaligned on its ends, forcing the scavenger to skip at worst
+ // 2x pages.
+ slack := uint64(bigBytes / 2)
+ pageSize := uint64(os.Getpagesize())
+ if pageSize > 8<<10 {
+ slack += pageSize * 2
+ }
+ if slack > bigBytes {
+ // We basically already checked this.
+ return
+ }
+ if after.HeapReleased-before.HeapReleased < bigBytes-slack {
+ t.Fatalf("less than %d released: %d -> %d", bigBytes, before.HeapReleased, after.HeapReleased)
}
}
var (
- setGCPercentBallast interface{}
- setGCPercentSink interface{}
+ setGCPercentBallast any
+ setGCPercentSink any
)
func TestSetGCPercent(t *testing.T) {
diff --git a/src/runtime/debug/mod.go b/src/runtime/debug/mod.go
index 14b99f5735..14a496a8eb 100644
--- a/src/runtime/debug/mod.go
+++ b/src/runtime/debug/mod.go
@@ -57,8 +57,9 @@ type Module struct {
// BuildSetting describes a setting that may be used to understand how the
// binary was built. For example, VCS commit and dirty status is stored here.
type BuildSetting struct {
- // Key and Value describe the build setting. They must not contain tabs
- // or newlines.
+ // Key and Value describe the build setting.
+ // Key must not contain an equals sign, space, tab, or newline.
+ // Value must not contain newlines ('\n').
Key, Value string
}
@@ -97,10 +98,13 @@ func (bi *BuildInfo) MarshalText() ([]byte, error) {
formatMod("dep", *dep)
}
for _, s := range bi.Settings {
- if strings.ContainsAny(s.Key, "\n\t") || strings.ContainsAny(s.Value, "\n\t") {
- return nil, fmt.Errorf("build setting %q contains tab or newline", s.Key)
+ if strings.ContainsAny(s.Key, "= \t\n") {
+ return nil, fmt.Errorf("invalid build setting key %q", s.Key)
}
- fmt.Fprintf(buf, "build\t%s\t%s\n", s.Key, s.Value)
+ if strings.Contains(s.Value, "\n") {
+ return nil, fmt.Errorf("invalid build setting value for key %q: contains newline", s.Value)
+ }
+ fmt.Fprintf(buf, "build\t%s=%s\n", s.Key, s.Value)
}
return buf.Bytes(), nil
@@ -185,14 +189,14 @@ func (bi *BuildInfo) UnmarshalText(data []byte) (err error) {
}
last = nil
case bytes.HasPrefix(line, buildLine):
- elem := bytes.Split(line[len(buildLine):], tab)
- if len(elem) != 2 {
- return fmt.Errorf("expected 2 columns for build setting; got %d", len(elem))
+ key, val, ok := strings.Cut(string(line[len(buildLine):]), "=")
+ if !ok {
+ return fmt.Errorf("invalid build line")
}
- if len(elem[0]) == 0 {
+ if key == "" {
return fmt.Errorf("empty key")
}
- bi.Settings = append(bi.Settings, BuildSetting{Key: string(elem[0]), Value: string(elem[1])})
+ bi.Settings = append(bi.Settings, BuildSetting{Key: key, Value: val})
}
lineNum++
}
diff --git a/src/runtime/debug_test.go b/src/runtime/debug_test.go
index b5db7a55f1..5bb0c5cee3 100644
--- a/src/runtime/debug_test.go
+++ b/src/runtime/debug_test.go
@@ -34,12 +34,22 @@ func startDebugCallWorker(t *testing.T) (g *runtime.G, after func()) {
skipUnderDebugger(t)
// This can deadlock if there aren't enough threads or if a GC
- // tries to interrupt an atomic loop (see issue #10958). We
- // use 8 Ps so there's room for the debug call worker,
+ // tries to interrupt an atomic loop (see issue #10958). Execute
+ // an extra GC to ensure even the sweep phase is done (out of
+ // caution to prevent #49370 from happening).
+ // TODO(mknyszek): This extra GC cycle is likely unnecessary
+ // because preemption (which may happen during the sweep phase)
+ // isn't much of an issue anymore thanks to asynchronous preemption.
+ // The biggest risk is having a write barrier in the debug call
+ // injection test code fire, because it runs in a signal handler
+ // and may not have a P.
+ //
+ // We use 8 Ps so there's room for the debug call worker,
// something that's trying to preempt the call worker, and the
// goroutine that's trying to stop the call worker.
ogomaxprocs := runtime.GOMAXPROCS(8)
ogcpercent := debug.SetGCPercent(-1)
+ runtime.GC()
// ready is a buffered channel so debugCallWorker won't block
// on sending to it. This makes it less likely we'll catch
@@ -143,6 +153,7 @@ func TestDebugCall(t *testing.T) {
x1: 42.0,
}
}
+
if _, err := runtime.InjectDebugCall(g, fn, &regs, args, debugCallTKill, false); err != nil {
t.Fatal(err)
}
@@ -233,6 +244,12 @@ func TestDebugCallUnsafePoint(t *testing.T) {
// This can deadlock if there aren't enough threads or if a GC
// tries to interrupt an atomic loop (see issue #10958).
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
+
+ // InjectDebugCall cannot be executed while a GC is actively in
+ // progress. Wait until the current GC is done, and turn it off.
+ //
+ // See #49370.
+ runtime.GC()
defer debug.SetGCPercent(debug.SetGCPercent(-1))
// Test that the runtime refuses call injection at unsafe points.
@@ -256,6 +273,19 @@ func TestDebugCallPanic(t *testing.T) {
// This can deadlock if there aren't enough threads.
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
+ // InjectDebugCall cannot be executed while a GC is actively in
+ // progress. Wait until the current GC is done, and turn it off.
+ //
+ // See #10958 and #49370.
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // TODO(mknyszek): This extra GC cycle is likely unnecessary
+ // because preemption (which may happen during the sweep phase)
+ // isn't much of an issue anymore thanks to asynchronous preemption.
+ // The biggest risk is having a write barrier in the debug call
+ // injection test code fire, because it runs in a signal handler
+ // and may not have a P.
+ runtime.GC()
+
ready := make(chan *runtime.G)
var stop uint32
defer atomic.StoreUint32(&stop, 1)
diff --git a/src/runtime/debugcall.go b/src/runtime/debugcall.go
index 005a259f28..205971c428 100644
--- a/src/runtime/debugcall.go
+++ b/src/runtime/debugcall.go
@@ -16,7 +16,7 @@ const (
)
func debugCallV2()
-func debugCallPanicked(val interface{})
+func debugCallPanicked(val any)
// debugCallCheck checks whether it is safe to inject a debugger
// function call with return PC pc. If not, it returns a string
diff --git a/src/runtime/debuglog.go b/src/runtime/debuglog.go
index 588b54d1f5..75b91c4216 100644
--- a/src/runtime/debuglog.go
+++ b/src/runtime/debuglog.go
@@ -266,7 +266,7 @@ func (l *dlogger) hex(x uint64) *dlogger {
}
//go:nosplit
-func (l *dlogger) p(x interface{}) *dlogger {
+func (l *dlogger) p(x any) *dlogger {
if !dlogEnabled {
return l
}
diff --git a/src/runtime/defer_test.go b/src/runtime/defer_test.go
index 1d5745d60b..3a54951c31 100644
--- a/src/runtime/defer_test.go
+++ b/src/runtime/defer_test.go
@@ -433,7 +433,7 @@ func TestIssue43921(t *testing.T) {
}()
}
-func expect(t *testing.T, n int, err interface{}) {
+func expect(t *testing.T, n int, err any) {
if n != err {
t.Fatalf("have %v, want %v", err, n)
}
@@ -467,7 +467,6 @@ func TestIssue43920(t *testing.T) {
}
func step(t *testing.T, steps *int, want int) {
- println("step", want)
*steps++
if *steps != want {
t.Fatalf("have %v, want %v", *steps, want)
diff --git a/src/runtime/defs_linux_riscv64.go b/src/runtime/defs_linux_riscv64.go
index 332720a8c8..747e26bc4b 100644
--- a/src/runtime/defs_linux_riscv64.go
+++ b/src/runtime/defs_linux_riscv64.go
@@ -122,10 +122,12 @@ func (tv *timeval) set_usec(x int32) {
}
type sigactiont struct {
- sa_handler uintptr
- sa_flags uint64
+ sa_handler uintptr
+ sa_flags uint64
+ sa_mask uint64
+ // Linux on riscv64 does not have the sa_restorer field, but the setsig
+ // function references it (for x86). Not much harm to include it at the end.
sa_restorer uintptr
- sa_mask uint64
}
type siginfoFields struct {
diff --git a/src/runtime/duff_mips64x.s b/src/runtime/duff_mips64x.s
index a897d7fd9b..3a8524c78b 100644
--- a/src/runtime/duff_mips64x.s
+++ b/src/runtime/duff_mips64x.s
@@ -3,7 +3,6 @@
// See mkduff.go for comments.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "textflag.h"
diff --git a/src/runtime/duff_ppc64x.s b/src/runtime/duff_ppc64x.s
index eeecf13df1..a3caaa8817 100644
--- a/src/runtime/duff_ppc64x.s
+++ b/src/runtime/duff_ppc64x.s
@@ -3,7 +3,6 @@
// See mkduff.go for comments.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "textflag.h"
diff --git a/src/runtime/error.go b/src/runtime/error.go
index 91f83ae126..43114f092e 100644
--- a/src/runtime/error.go
+++ b/src/runtime/error.go
@@ -210,7 +210,7 @@ type stringer interface {
// printany prints an argument passed to panic.
// If panic is called with a value that has a String or Error method,
// it has already been converted into a string by preprintpanics.
-func printany(i interface{}) {
+func printany(i any) {
switch v := i.(type) {
case nil:
print("nil")
@@ -253,7 +253,7 @@ func printany(i interface{}) {
}
}
-func printanycustomtype(i interface{}) {
+func printanycustomtype(i any) {
eface := efaceOf(&i)
typestring := eface._type.string()
diff --git a/src/runtime/export_debug_test.go b/src/runtime/export_debug_test.go
index 032a9b9725..19a9ec135f 100644
--- a/src/runtime/export_debug_test.go
+++ b/src/runtime/export_debug_test.go
@@ -22,7 +22,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 interface{}, regArgs *abi.RegArgs, stackArgs interface{}, tkill func(tid int) error, returnOnUnsafePoint bool) (interface{}, error) {
+func InjectDebugCall(gp *g, fn any, regArgs *abi.RegArgs, stackArgs any, tkill func(tid int) error, returnOnUnsafePoint bool) (any, error) {
if gp.lockedm == 0 {
return nil, plainError("goroutine not locked to thread")
}
@@ -96,7 +96,7 @@ type debugCallHandler struct {
regArgs *abi.RegArgs
argp unsafe.Pointer
argSize uintptr
- panic interface{}
+ panic any
handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool
@@ -107,6 +107,10 @@ type debugCallHandler struct {
}
func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
+ // TODO(49370): This code is riddled with write barriers, but called from
+ // a signal handler. Add the go:nowritebarrierrec annotation and restructure
+ // this to avoid write barriers.
+
switch h.gp.atomicstatus {
case _Grunning:
if getg().m != h.mp {
@@ -141,7 +145,11 @@ func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
}
func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
- // Sanity check.
+ // TODO(49370): This code is riddled with write barriers, but called from
+ // a signal handler. Add the go:nowritebarrierrec annotation and restructure
+ // this to avoid write barriers.
+
+ // Double-check m.
if getg().m != h.mp {
println("trap on wrong M", getg().m, h.mp)
return false
diff --git a/src/runtime/export_debuglog_test.go b/src/runtime/export_debuglog_test.go
index 8cd943b438..1a9074e646 100644
--- a/src/runtime/export_debuglog_test.go
+++ b/src/runtime/export_debuglog_test.go
@@ -14,15 +14,15 @@ const DebugLogStringLimit = debugLogStringLimit
var Dlog = dlog
-func (l *dlogger) End() { l.end() }
-func (l *dlogger) B(x bool) *dlogger { return l.b(x) }
-func (l *dlogger) I(x int) *dlogger { return l.i(x) }
-func (l *dlogger) I16(x int16) *dlogger { return l.i16(x) }
-func (l *dlogger) U64(x uint64) *dlogger { return l.u64(x) }
-func (l *dlogger) Hex(x uint64) *dlogger { return l.hex(x) }
-func (l *dlogger) P(x interface{}) *dlogger { return l.p(x) }
-func (l *dlogger) S(x string) *dlogger { return l.s(x) }
-func (l *dlogger) PC(x uintptr) *dlogger { return l.pc(x) }
+func (l *dlogger) End() { l.end() }
+func (l *dlogger) B(x bool) *dlogger { return l.b(x) }
+func (l *dlogger) I(x int) *dlogger { return l.i(x) }
+func (l *dlogger) I16(x int16) *dlogger { return l.i16(x) }
+func (l *dlogger) U64(x uint64) *dlogger { return l.u64(x) }
+func (l *dlogger) Hex(x uint64) *dlogger { return l.hex(x) }
+func (l *dlogger) P(x any) *dlogger { return l.p(x) }
+func (l *dlogger) S(x string) *dlogger { return l.s(x) }
+func (l *dlogger) PC(x uintptr) *dlogger { return l.pc(x) }
func DumpDebugLog() string {
g := getg()
diff --git a/src/runtime/export_futex_test.go b/src/runtime/export_futex_test.go
deleted file mode 100644
index 03157d8eed..0000000000
--- a/src/runtime/export_futex_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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.
-
-//go:build dragonfly || freebsd || linux
-
-package runtime
-
-var Futexwakeup = futexwakeup
-
-//go:nosplit
-func Futexsleep(addr *uint32, val uint32, ns int64) {
- // Temporarily disable preemption so that a preemption signal
- // doesn't interrupt the system call.
- poff := debug.asyncpreemptoff
- debug.asyncpreemptoff = 1
- futexsleep(addr, val, ns)
- debug.asyncpreemptoff = poff
-}
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index b2e64f14ad..0f21838721 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -75,7 +75,7 @@ func Netpoll(delta int64) {
})
}
-func GCMask(x interface{}) (ret []byte) {
+func GCMask(x any) (ret []byte) {
systemstack(func() {
ret = getgcmask(x)
})
@@ -218,7 +218,7 @@ func SetEnvs(e []string) { envs = e }
// For benchmarking.
-func BenchSetType(n int, x interface{}) {
+func BenchSetType(n int, x any) {
e := *efaceOf(&x)
t := e._type
var size uintptr
@@ -546,7 +546,7 @@ func MapTombstoneCheck(m map[int]int) {
// We should have a series of filled and emptyOne cells, followed by
// a series of emptyRest cells.
h := *(**hmap)(unsafe.Pointer(&m))
- i := interface{}(m)
+ i := any(m)
t := *(**maptype)(unsafe.Pointer(&i))
for x := 0; x < 1<<h.B; x++ {
@@ -1048,7 +1048,19 @@ func FreePageAlloc(pp *PageAlloc) {
//
// This should not be higher than 0x100*pallocChunkBytes to support
// mips and mipsle, which only have 31-bit address spaces.
-var BaseChunkIdx = ChunkIdx(chunkIndex(((0xc000*pageAlloc64Bit + 0x100*pageAlloc32Bit) * pallocChunkBytes) + arenaBaseOffset*goos.IsAix))
+var BaseChunkIdx = func() ChunkIdx {
+ var prefix uintptr
+ if pageAlloc64Bit != 0 {
+ prefix = 0xc000
+ } else {
+ prefix = 0x100
+ }
+ baseAddr := prefix * pallocChunkBytes
+ if goos.IsAix != 0 {
+ baseAddr += arenaBaseOffset
+ }
+ return ChunkIdx(chunkIndex(baseAddr))
+}()
// PageBase returns an address given a chunk index and a page index
// relative to that chunk.
@@ -1299,11 +1311,22 @@ func (c *GCController) EndCycle(bytesMarked uint64, assistTime, elapsed int64, g
c.commit(triggerRatio)
}
-var escapeSink interface{}
+var escapeSink any
//go:noinline
-func escape(x interface{}) interface{} {
+func escape(x any) any {
escapeSink = x
escapeSink = nil
return x
}
+
+// Acquirem blocks preemption.
+func Acquirem() {
+ acquirem()
+}
+
+func Releasem() {
+ releasem(getg().m)
+}
+
+var Timediv = timediv
diff --git a/src/runtime/futex_test.go b/src/runtime/futex_test.go
deleted file mode 100644
index 188d0c6525..0000000000
--- a/src/runtime/futex_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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.
-
-// Futex is only available on DragonFly BSD, FreeBSD and Linux.
-// The race detector emits calls to split stack functions so it breaks
-// the test.
-
-//go:build (dragonfly || freebsd || linux) && !race
-
-package runtime_test
-
-import (
- "runtime"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-)
-
-type futexsleepTest struct {
- mtx uint32
- ns int64
- msg string
- ch chan *futexsleepTest
-}
-
-var futexsleepTests = []futexsleepTest{
- beforeY2038: {mtx: 0, ns: 86400 * 1e9, msg: "before the year 2038"},
- afterY2038: {mtx: 0, ns: (1<<31 + 100) * 1e9, msg: "after the year 2038"},
-}
-
-const (
- beforeY2038 = iota
- afterY2038
-)
-
-func TestFutexsleep(t *testing.T) {
- if runtime.GOMAXPROCS(0) > 1 {
- // futexsleep doesn't handle EINTR or other signals,
- // so spurious wakeups may happen.
- t.Skip("skipping; GOMAXPROCS>1")
- }
-
- start := time.Now()
- var wg sync.WaitGroup
- for i := range futexsleepTests {
- tt := &futexsleepTests[i]
- tt.mtx = 0
- tt.ch = make(chan *futexsleepTest, 1)
- wg.Add(1)
- go func(tt *futexsleepTest) {
- runtime.Entersyscall()
- runtime.Futexsleep(&tt.mtx, 0, tt.ns)
- runtime.Exitsyscall()
- tt.ch <- tt
- wg.Done()
- }(tt)
- }
-loop:
- for {
- select {
- case tt := <-futexsleepTests[beforeY2038].ch:
- t.Errorf("futexsleep test %q finished early after %s", tt.msg, time.Since(start))
- break loop
- case tt := <-futexsleepTests[afterY2038].ch:
- // Looks like FreeBSD 10 kernel has changed
- // the semantics of timedwait on userspace
- // mutex to make broken stuff look broken.
- switch {
- case runtime.GOOS == "freebsd" && runtime.GOARCH == "386":
- t.Log("freebsd/386 may not work correctly after the year 2038, see golang.org/issue/7194")
- default:
- t.Errorf("futexsleep test %q finished early after %s", tt.msg, time.Since(start))
- break loop
- }
- case <-time.After(time.Second):
- break loop
- }
- }
- for i := range futexsleepTests {
- tt := &futexsleepTests[i]
- atomic.StoreUint32(&tt.mtx, 1)
- runtime.Futexwakeup(&tt.mtx, 1)
- }
- wg.Wait()
-}
diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go
index 7b979afd55..9743dbbe2b 100644
--- a/src/runtime/gc_test.go
+++ b/src/runtime/gc_test.go
@@ -136,7 +136,7 @@ func TestGcLastTime(t *testing.T) {
}
}
-var hugeSink interface{}
+var hugeSink any
func TestHugeGCInfo(t *testing.T) {
// The test ensures that compiler can chew these huge types even on weakest machines.
@@ -196,7 +196,10 @@ func TestPeriodicGC(t *testing.T) {
func TestGcZombieReporting(t *testing.T) {
// This test is somewhat sensitive to how the allocator works.
- got := runTestProg(t, "testprog", "GCZombie")
+ // Pointers in zombies slice may cross-span, thus we
+ // add invalidptr=0 for avoiding the badPointer check.
+ // See issue https://golang.org/issues/49613/
+ got := runTestProg(t, "testprog", "GCZombie", "GODEBUG=invalidptr=0")
want := "found pointer to free object"
if !strings.Contains(got, want) {
t.Fatalf("expected %q in output, but got %q", want, got)
@@ -454,7 +457,7 @@ func BenchmarkSetTypeNode1024Slice(b *testing.B) {
benchSetType(b, make([]Node1024, 32))
}
-func benchSetType(b *testing.B, x interface{}) {
+func benchSetType(b *testing.B, x any) {
v := reflect.ValueOf(x)
t := v.Type()
switch t.Kind() {
@@ -520,7 +523,7 @@ func TestPrintGC(t *testing.T) {
close(done)
}
-func testTypeSwitch(x interface{}) error {
+func testTypeSwitch(x any) error {
switch y := x.(type) {
case nil:
// ok
@@ -530,14 +533,14 @@ func testTypeSwitch(x interface{}) error {
return nil
}
-func testAssert(x interface{}) error {
+func testAssert(x any) error {
if y, ok := x.(error); ok {
return y
}
return nil
}
-func testAssertVar(x interface{}) error {
+func testAssertVar(x any) error {
var y, ok = x.(error)
if ok {
return y
@@ -548,7 +551,7 @@ func testAssertVar(x interface{}) error {
var a bool
//go:noinline
-func testIfaceEqual(x interface{}) {
+func testIfaceEqual(x any) {
if x == "abc" {
a = true
}
diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go
index 0808b416f0..f2c88ef1ab 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -66,7 +66,7 @@ func TestGCInfo(t *testing.T) {
runtime.KeepAlive(x)
}
{
- var x interface{}
+ var x any
verifyGCInfo(t, "stack eface", &x, infoEface)
runtime.KeepAlive(x)
}
@@ -84,12 +84,12 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), trimDead(infoPtrScalar))
verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), trimDead(infoBigStruct()))
verifyGCInfo(t, "heap string", escape(new(string)), trimDead(infoString))
- verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface))
+ verifyGCInfo(t, "heap eface", escape(new(any)), trimDead(infoEface))
verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface))
}
}
-func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
+func verifyGCInfo(t *testing.T, name string, p any, mask0 []byte) {
mask := runtime.GCMask(p)
if !bytes.Equal(mask, mask0) {
t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask)
@@ -104,9 +104,9 @@ func trimDead(mask []byte) []byte {
return mask
}
-var gcinfoSink interface{}
+var gcinfoSink any
-func escape(p interface{}) interface{} {
+func escape(p any) any {
gcinfoSink = p
return p
}
@@ -194,18 +194,18 @@ var (
bssBigStruct BigStruct
bssString string
bssSlice []string
- bssEface interface{}
+ bssEface any
bssIface Iface
// DATA
- dataPtr = Ptr{new(byte)}
- dataScalarPtr = ScalarPtr{q: 1}
- dataPtrScalar = PtrScalar{w: 1}
- dataBigStruct = BigStruct{w: 1}
- dataString = "foo"
- dataSlice = []string{"foo"}
- dataEface interface{} = 42
- dataIface Iface = IfaceImpl(42)
+ dataPtr = Ptr{new(byte)}
+ dataScalarPtr = ScalarPtr{q: 1}
+ dataPtrScalar = PtrScalar{w: 1}
+ dataBigStruct = BigStruct{w: 1}
+ dataString = "foo"
+ dataSlice = []string{"foo"}
+ dataEface any = 42
+ dataIface Iface = IfaceImpl(42)
infoString = []byte{typePointer, typeScalar}
infoSlice = []byte{typePointer, typeScalar, typeScalar}
diff --git a/src/runtime/hash_test.go b/src/runtime/hash_test.go
index 7048874a71..cf56c57a5f 100644
--- a/src/runtime/hash_test.go
+++ b/src/runtime/hash_test.go
@@ -382,7 +382,7 @@ func (k *Int64Key) name() string {
}
type EfaceKey struct {
- i interface{}
+ i any
}
func (k *EfaceKey) clear() {
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index e2bec10948..a4d56dd33b 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -296,11 +296,11 @@ type (
)
var (
- uint16Eface interface{} = uint16InterfacePtr(0)
- uint32Eface interface{} = uint32InterfacePtr(0)
- uint64Eface interface{} = uint64InterfacePtr(0)
- stringEface interface{} = stringInterfacePtr("")
- sliceEface interface{} = sliceInterfacePtr(nil)
+ uint16Eface any = uint16InterfacePtr(0)
+ uint32Eface any = uint32InterfacePtr(0)
+ uint64Eface any = uint64InterfacePtr(0)
+ stringEface any = stringInterfacePtr("")
+ sliceEface any = sliceInterfacePtr(nil)
uint16Type *_type = efaceOf(&uint16Eface)._type
uint32Type *_type = efaceOf(&uint32Eface)._type
diff --git a/src/runtime/iface_test.go b/src/runtime/iface_test.go
index 4fab6c968a..06f6eeb952 100644
--- a/src/runtime/iface_test.go
+++ b/src/runtime/iface_test.go
@@ -44,8 +44,8 @@ func (Tstr) Method1() {}
func (Tslice) Method1() {}
var (
- e interface{}
- e_ interface{}
+ e any
+ e_ any
i1 I1
i2 I2
ts TS
@@ -196,7 +196,7 @@ func BenchmarkAssertI2I(b *testing.B) {
func BenchmarkAssertI2E(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
- e = i1.(interface{})
+ e = i1.(any)
}
}
@@ -224,33 +224,33 @@ func BenchmarkAssertE2T2Blank(b *testing.B) {
func BenchmarkAssertI2E2(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
- e, ok = i1.(interface{})
+ e, ok = i1.(any)
}
}
func BenchmarkAssertI2E2Blank(b *testing.B) {
i1 = tm
for i := 0; i < b.N; i++ {
- _, ok = i1.(interface{})
+ _, ok = i1.(any)
}
}
func BenchmarkAssertE2E2(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
- e_, ok = e.(interface{})
+ e_, ok = e.(any)
}
}
func BenchmarkAssertE2E2Blank(b *testing.B) {
e = tm
for i := 0; i < b.N; i++ {
- _, ok = e.(interface{})
+ _, ok = e.(any)
}
}
func TestNonEscapingConvT2E(t *testing.T) {
- m := make(map[interface{}]bool)
+ m := make(map[any]bool)
m[42] = true
if !m[42] {
t.Fatalf("42 is not present in the map")
diff --git a/src/runtime/internal/atomic/atomic_mips64x.s b/src/runtime/internal/atomic/atomic_mips64x.s
index fedfc4a175..b4411d87da 100644
--- a/src/runtime/internal/atomic/atomic_mips64x.s
+++ b/src/runtime/internal/atomic/atomic_mips64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "textflag.h"
diff --git a/src/runtime/internal/atomic/atomic_mipsx.s b/src/runtime/internal/atomic/atomic_mipsx.s
index c0835d66ed..390e9ce7ac 100644
--- a/src/runtime/internal/atomic/atomic_mipsx.s
+++ b/src/runtime/internal/atomic/atomic_mipsx.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips || mipsle
-// +build mips mipsle
#include "textflag.h"
diff --git a/src/runtime/internal/atomic/atomic_ppc64x.s b/src/runtime/internal/atomic/atomic_ppc64x.s
index 226b3b6216..04f0eadd06 100644
--- a/src/runtime/internal/atomic/atomic_ppc64x.s
+++ b/src/runtime/internal/atomic/atomic_ppc64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "textflag.h"
diff --git a/src/runtime/internal/atomic/bench_test.go b/src/runtime/internal/atomic/bench_test.go
index 2476c06c52..efc0531a0d 100644
--- a/src/runtime/internal/atomic/bench_test.go
+++ b/src/runtime/internal/atomic/bench_test.go
@@ -9,7 +9,7 @@ import (
"testing"
)
-var sink interface{}
+var sink any
func BenchmarkAtomicLoad64(b *testing.B) {
var x uint64
diff --git a/src/runtime/internal/atomic/sys_nonlinux_arm.s b/src/runtime/internal/atomic/sys_nonlinux_arm.s
index 04036ca970..b55bf908a2 100644
--- a/src/runtime/internal/atomic/sys_nonlinux_arm.s
+++ b/src/runtime/internal/atomic/sys_nonlinux_arm.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !linux
-// +build !linux
#include "textflag.h"
diff --git a/src/runtime/lfstack_test.go b/src/runtime/lfstack_test.go
index fb4b45992d..d0a1b6ba06 100644
--- a/src/runtime/lfstack_test.go
+++ b/src/runtime/lfstack_test.go
@@ -24,7 +24,7 @@ func toMyNode(node *LFNode) *MyNode {
return (*MyNode)(unsafe.Pointer(node))
}
-var global interface{}
+var global any
func TestLFStack(t *testing.T) {
stack := new(uint64)
diff --git a/src/runtime/libfuzzer_amd64.s b/src/runtime/libfuzzer_amd64.s
index 13645fc7af..253fe15198 100644
--- a/src/runtime/libfuzzer_amd64.s
+++ b/src/runtime/libfuzzer_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build libfuzzer
-// +build libfuzzer
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/libfuzzer_arm64.s b/src/runtime/libfuzzer_arm64.s
index 4ad8242804..ae0efd8c9b 100644
--- a/src/runtime/libfuzzer_arm64.s
+++ b/src/runtime/libfuzzer_arm64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build libfuzzer
-// +build libfuzzer
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index e267e2df23..6ed6ceade2 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -201,15 +201,21 @@ const (
// we further limit it to 31 bits.
//
// On ios/arm64, although 64-bit pointers are presumably
- // available, pointers are truncated to 33 bits. Furthermore,
- // only the top 4 GiB of the address space are actually available
- // to the application, but we allow the whole 33 bits anyway for
- // simplicity.
- // TODO(mknyszek): Consider limiting it to 32 bits and using
- // arenaBaseOffset to offset into the top 4 GiB.
+ // available, pointers are truncated to 33 bits in iOS <14.
+ // Furthermore, only the top 4 GiB of the address space are
+ // actually available to the application. In iOS >=14, more
+ // of the address space is available, and the OS can now
+ // provide addresses outside of those 33 bits. Pick 40 bits
+ // as a reasonable balance between address space usage by the
+ // page allocator, and flexibility for what mmap'd regions
+ // we'll accept for the heap. We can't just move to the full
+ // 48 bits because this uses too much address space for older
+ // iOS versions.
+ // TODO(mknyszek): Once iOS <14 is deprecated, promote ios/arm64
+ // to a 48-bit address space like every other arm64 platform.
//
// WebAssembly currently has a limit of 4GB linear memory.
- heapAddrBits = (_64bit*(1-goarch.IsWasm)*(1-goos.IsIos*goarch.IsArm64))*48 + (1-_64bit+goarch.IsWasm)*(32-(goarch.IsMips+goarch.IsMipsle)) + 33*goos.IsIos*goarch.IsArm64
+ heapAddrBits = (_64bit*(1-goarch.IsWasm)*(1-goos.IsIos*goarch.IsArm64))*48 + (1-_64bit+goarch.IsWasm)*(32-(goarch.IsMips+goarch.IsMipsle)) + 40*goos.IsIos*goarch.IsArm64
// maxAlloc is the maximum size of an allocation. On 64-bit,
// it's theoretically possible to allocate 1<<heapAddrBits bytes. On
diff --git a/src/runtime/malloc_test.go b/src/runtime/malloc_test.go
index e028554b23..8ff88687bd 100644
--- a/src/runtime/malloc_test.go
+++ b/src/runtime/malloc_test.go
@@ -33,14 +33,14 @@ func TestMemStats(t *testing.T) {
st := new(MemStats)
ReadMemStats(st)
- nz := func(x interface{}) error {
+ nz := func(x any) error {
if x != reflect.Zero(reflect.TypeOf(x)).Interface() {
return nil
}
return fmt.Errorf("zero value")
}
- le := func(thresh float64) func(interface{}) error {
- return func(x interface{}) error {
+ le := func(thresh float64) func(any) error {
+ return func(x any) error {
// These sanity tests aren't necessarily valid
// with high -test.count values, so only run
// them once.
@@ -54,8 +54,8 @@ func TestMemStats(t *testing.T) {
return fmt.Errorf("insanely high value (overflow?); want <= %v", thresh)
}
}
- eq := func(x interface{}) func(interface{}) error {
- return func(y interface{}) error {
+ eq := func(x any) func(any) error {
+ return func(y any) error {
if x == y {
return nil
}
@@ -64,7 +64,7 @@ func TestMemStats(t *testing.T) {
}
// Of the uint fields, HeapReleased, HeapIdle can be 0.
// PauseTotalNs can be 0 if timer resolution is poor.
- fields := map[string][]func(interface{}) error{
+ fields := map[string][]func(any) error{
"Alloc": {nz, le(1e10)}, "TotalAlloc": {nz, le(1e11)}, "Sys": {nz, le(1e10)},
"Lookups": {eq(uint64(0))}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)},
"HeapAlloc": {nz, le(1e10)}, "HeapSys": {nz, le(1e10)}, "HeapIdle": {le(1e10)},
@@ -198,6 +198,10 @@ func TestTinyAllocIssue37262(t *testing.T) {
runtime.GC()
runtime.GC()
+ // Disable preemption so we stay on one P's tiny allocator and
+ // nothing else allocates from it.
+ runtime.Acquirem()
+
// Make 1-byte allocations until we get a fresh tiny slot.
aligned := false
for i := 0; i < 16; i++ {
@@ -208,6 +212,7 @@ func TestTinyAllocIssue37262(t *testing.T) {
}
}
if !aligned {
+ runtime.Releasem()
t.Fatal("unable to get a fresh tiny slot")
}
@@ -229,6 +234,8 @@ func TestTinyAllocIssue37262(t *testing.T) {
tinyByteSink = nil
tinyUint32Sink = nil
tinyObj12Sink = nil
+
+ runtime.Releasem()
}
func TestPageCacheLeak(t *testing.T) {
diff --git a/src/runtime/map_benchmark_test.go b/src/runtime/map_benchmark_test.go
index d0becc9ddb..b46d2a4727 100644
--- a/src/runtime/map_benchmark_test.go
+++ b/src/runtime/map_benchmark_test.go
@@ -488,20 +488,20 @@ func BenchmarkMapStringConversion(b *testing.B) {
var BoolSink bool
func BenchmarkMapInterfaceString(b *testing.B) {
- m := map[interface{}]bool{}
+ m := map[any]bool{}
for i := 0; i < 100; i++ {
m[fmt.Sprintf("%d", i)] = true
}
- key := (interface{})("A")
+ key := (any)("A")
b.ResetTimer()
for i := 0; i < b.N; i++ {
BoolSink = m[key]
}
}
func BenchmarkMapInterfacePtr(b *testing.B) {
- m := map[interface{}]bool{}
+ m := map[any]bool{}
for i := 0; i < 100; i++ {
i := i
diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go
index f78cad5a77..0c83dd4ddf 100644
--- a/src/runtime/map_test.go
+++ b/src/runtime/map_test.go
@@ -1050,7 +1050,7 @@ func BenchmarkMapDelete(b *testing.B) {
func TestDeferDeleteSlow(t *testing.T) {
ks := []complex128{0, 1, 2, 3}
- m := make(map[interface{}]int)
+ m := make(map[any]int)
for i, k := range ks {
m[k] = i
}
@@ -1193,14 +1193,14 @@ func TestMapInterfaceKey(t *testing.T) {
c64 complex64
c128 complex128
s string
- i0 interface{}
+ i0 any
i1 interface {
String() string
}
a [4]string
}
- m := map[interface{}]bool{}
+ m := map[any]bool{}
// Put a bunch of data in m, so that a bad hash is likely to
// lead to a bad bucket, which will lead to a missed lookup.
for i := 0; i < 1000; i++ {
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 3330ddd62e..1c6f3f959f 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -1952,7 +1952,7 @@ func getgcmaskcb(frame *stkframe, ctxt unsafe.Pointer) bool {
// gcbits returns the GC type info for x, for testing.
// The result is the bitmap entries (0 or 1), one entry per byte.
//go:linkname reflect_gcbits reflect.gcbits
-func reflect_gcbits(x interface{}) []byte {
+func reflect_gcbits(x any) []byte {
ret := getgcmask(x)
typ := (*ptrtype)(unsafe.Pointer(efaceOf(&x)._type)).elem
nptr := typ.ptrdata / goarch.PtrSize
@@ -1965,7 +1965,7 @@ func reflect_gcbits(x interface{}) []byte {
// Returns GC type info for the pointer stored in ep for testing.
// If ep points to the stack, only static live information will be returned
// (i.e. not for objects which are only dynamically live stack objects).
-func getgcmask(ep interface{}) (mask []byte) {
+func getgcmask(ep any) (mask []byte) {
e := *efaceOf(&ep)
p := e.data
t := e._type
diff --git a/src/runtime/mem_aix.go b/src/runtime/mem_aix.go
index 957aa4dcc2..489d7928e1 100644
--- a/src/runtime/mem_aix.go
+++ b/src/runtime/mem_aix.go
@@ -72,6 +72,7 @@ func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
throw("runtime: out of memory")
}
if err != 0 {
+ print("runtime: mprotect(", v, ", ", n, ") returned ", err, "\n")
throw("runtime: cannot map pages in arena address space")
}
}
diff --git a/src/runtime/mem_bsd.go b/src/runtime/mem_bsd.go
index b152571792..49337eafbf 100644
--- a/src/runtime/mem_bsd.go
+++ b/src/runtime/mem_bsd.go
@@ -73,6 +73,7 @@ func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
throw("runtime: out of memory")
}
if p != v || err != 0 {
+ print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n")
throw("runtime: cannot map pages in arena address space")
}
}
diff --git a/src/runtime/mem_darwin.go b/src/runtime/mem_darwin.go
index 7fccd2bb8e..9f836c0818 100644
--- a/src/runtime/mem_darwin.go
+++ b/src/runtime/mem_darwin.go
@@ -66,6 +66,7 @@ func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
throw("runtime: out of memory")
}
if p != v || err != 0 {
+ print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n")
throw("runtime: cannot map pages in arena address space")
}
}
diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go
index f8f9c53170..f8333014c2 100644
--- a/src/runtime/mem_linux.go
+++ b/src/runtime/mem_linux.go
@@ -189,6 +189,7 @@ func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
throw("runtime: out of memory")
}
if p != v || err != 0 {
+ print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n")
throw("runtime: cannot map pages in arena address space")
}
}
diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s
index 2627792ced..a72e5f228d 100644
--- a/src/runtime/memclr_386.s
+++ b/src/runtime/memclr_386.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !plan9
-// +build !plan9
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s
index 918a4b9e0e..700bbd7b9b 100644
--- a/src/runtime/memclr_amd64.s
+++ b/src/runtime/memclr_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !plan9
-// +build !plan9
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/memclr_mips64x.s b/src/runtime/memclr_mips64x.s
index bc037013fe..cf3a9c4ab4 100644
--- a/src/runtime/memclr_mips64x.s
+++ b/src/runtime/memclr_mips64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/memclr_mipsx.s b/src/runtime/memclr_mipsx.s
index 3d21c3c414..ee3009d46b 100644
--- a/src/runtime/memclr_mipsx.s
+++ b/src/runtime/memclr_mipsx.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips || mipsle
-// +build mips mipsle
#include "textflag.h"
diff --git a/src/runtime/memclr_ppc64x.s b/src/runtime/memclr_ppc64x.s
index 91aa417ca2..64132cee96 100644
--- a/src/runtime/memclr_ppc64x.s
+++ b/src/runtime/memclr_ppc64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "textflag.h"
diff --git a/src/runtime/memmove_386.s b/src/runtime/memmove_386.s
index 389ef88477..6d7e17fcbc 100644
--- a/src/runtime/memmove_386.s
+++ b/src/runtime/memmove_386.s
@@ -24,7 +24,6 @@
// THE SOFTWARE.
//go:build !plan9
-// +build !plan9
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s
index fa0c0e414f..eeb5033fd9 100644
--- a/src/runtime/memmove_amd64.s
+++ b/src/runtime/memmove_amd64.s
@@ -24,7 +24,6 @@
// THE SOFTWARE.
//go:build !plan9
-// +build !plan9
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/memmove_mips64x.s b/src/runtime/memmove_mips64x.s
index fef3c6be82..b69178ccd3 100644
--- a/src/runtime/memmove_mips64x.s
+++ b/src/runtime/memmove_mips64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "textflag.h"
diff --git a/src/runtime/memmove_mipsx.s b/src/runtime/memmove_mipsx.s
index cd02fc25c4..494288cf33 100644
--- a/src/runtime/memmove_mipsx.s
+++ b/src/runtime/memmove_mipsx.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips || mipsle
-// +build mips mipsle
#include "textflag.h"
diff --git a/src/runtime/memmove_ppc64x.s b/src/runtime/memmove_ppc64x.s
index b36b23f8ef..e69e71a4a1 100644
--- a/src/runtime/memmove_ppc64x.s
+++ b/src/runtime/memmove_ppc64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "textflag.h"
diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go
index 3cdb81e2fb..e2ac5d4993 100644
--- a/src/runtime/mfinal.go
+++ b/src/runtime/mfinal.go
@@ -200,7 +200,7 @@ func runfinq() {
framesz = f.nret
} else {
// Need to pass arguments on the stack too.
- framesz = unsafe.Sizeof((interface{})(nil)) + f.nret
+ framesz = unsafe.Sizeof((any)(nil)) + f.nret
}
if framecap < framesz {
// The frame does not contain pointers interesting for GC,
@@ -329,7 +329,7 @@ func runfinq() {
// A single goroutine runs all finalizers for a program, sequentially.
// If a finalizer must run for a long time, it should do so by starting
// a new goroutine.
-func SetFinalizer(obj interface{}, finalizer interface{}) {
+func SetFinalizer(obj any, finalizer any) {
if debug.sbrk != 0 {
// debug.sbrk never frees memory, so no finalizers run
// (and we don't have the data structures to record them).
@@ -470,7 +470,7 @@ okarg:
// Note: KeepAlive should only be used to prevent finalizers from
// running prematurely. In particular, when used with unsafe.Pointer,
// the rules for valid uses of unsafe.Pointer still apply.
-func KeepAlive(x interface{}) {
+func KeepAlive(x any) {
// Introduce a use of x that the compiler can't eliminate.
// This makes sure x is alive on entry. We need x to be alive
// on entry for "defer runtime.KeepAlive(x)"; see issue 21402.
diff --git a/src/runtime/mfinal_test.go b/src/runtime/mfinal_test.go
index 3ca8d31c60..04ba7a6830 100644
--- a/src/runtime/mfinal_test.go
+++ b/src/runtime/mfinal_test.go
@@ -34,14 +34,14 @@ func TestFinalizerType(t *testing.T) {
}
var finalizerTests = []struct {
- convert func(*int) interface{}
- finalizer interface{}
+ convert func(*int) any
+ finalizer any
}{
- {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }},
- {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }},
- {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }},
- {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
- {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
+ {func(x *int) any { return x }, func(v *int) { finalize(v) }},
+ {func(x *int) any { return Tintptr(x) }, func(v Tintptr) { finalize(v) }},
+ {func(x *int) any { return Tintptr(x) }, func(v *int) { finalize(v) }},
+ {func(x *int) any { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
+ {func(x *int) any { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
}
for i, tt := range finalizerTests {
@@ -85,7 +85,7 @@ func TestFinalizerInterfaceBig(t *testing.T) {
go func() {
v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
old := *v
- runtime.SetFinalizer(v, func(v interface{}) {
+ runtime.SetFinalizer(v, func(v any) {
i, ok := v.(*bigValue)
if !ok {
t.Errorf("finalizer called with type %T, want *bigValue", v)
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 96f4157b59..44b96154e7 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -320,11 +320,20 @@ var work struct {
nwait uint32
// Number of roots of various root types. Set by gcMarkRootPrepare.
+ //
+ // nStackRoots == len(stackRoots), but we have nStackRoots for
+ // consistency.
nDataRoots, nBSSRoots, nSpanRoots, nStackRoots int
// Base indexes of each root type. Set by gcMarkRootPrepare.
baseData, baseBSS, baseSpans, baseStacks, baseEnd uint32
+ // stackRoots is a snapshot of all of the Gs that existed
+ // before the beginning of concurrent marking. The backing
+ // store of this must not be modified because it might be
+ // shared with allgs.
+ stackRoots []*g
+
// Each type of GC state transition is protected by a lock.
// Since multiple threads can simultaneously detect the state
// transition condition, any thread that detects a transition
@@ -545,7 +554,7 @@ func (t gcTrigger) test() bool {
// own write.
return gcController.heapLive >= gcController.trigger
case gcTriggerTime:
- if atomic.Loadint32(&gcController.gcPercent) < 0 {
+ if gcController.gcPercent.Load() < 0 {
return false
}
lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime))
@@ -1314,7 +1323,7 @@ func gcBgMarkWorker() {
// point, signal the main GC goroutine.
if incnwait == work.nproc && !gcMarkWorkAvailable(nil) {
// We don't need the P-local buffers here, allow
- // preemption becuse we may schedule like a regular
+ // preemption because we may schedule like a regular
// goroutine in gcMarkDone (block on locks, etc).
releasem(node.m.ptr())
node.m.set(nil)
@@ -1368,6 +1377,11 @@ func gcMark(startTime int64) {
throw("work.full != 0")
}
+ // Drop allg snapshot. allgs may have grown, in which case
+ // this is the only reference to the old backing store and
+ // there's no need to keep it around.
+ work.stackRoots = nil
+
// Clear out buffers and double-check that all gcWork caches
// are empty. This should be ensured by gcMarkDone before we
// enter mark termination.
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index a5129bd1ee..0bf044e314 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -102,7 +102,8 @@ func gcMarkRootPrepare() {
// ignore them because they begin life without any roots, so
// there's nothing to scan, and any roots they create during
// the concurrent phase will be caught by the write barrier.
- work.nStackRoots = int(atomic.Loaduintptr(&allglen))
+ work.stackRoots = allGsSnapshot()
+ work.nStackRoots = len(work.stackRoots)
work.markrootNext = 0
work.markrootJobs = uint32(fixedRootCount + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nStackRoots)
@@ -194,15 +195,12 @@ func markroot(gcw *gcWork, i uint32, flushBgCredit bool) int64 {
default:
// the rest is scanning goroutine stacks
workCounter = &gcController.stackScanWork
- var gp *g
- if work.baseStacks <= i && i < work.baseEnd {
- // N.B. Atomic read of allglen in gcMarkRootPrepare
- // acts as a barrier to ensure that allgs must be large
- // enough to contain all relevant Gs.
- gp = allgs[i-work.baseStacks]
- } else {
+ if i < work.baseStacks || work.baseEnd <= i {
+ printlock()
+ print("runtime: markroot index ", i, " not in stack roots range [", work.baseStacks, ", ", work.baseEnd, ")\n")
throw("markroot: bad index")
}
+ gp := work.stackRoots[i-work.baseStacks]
// remember when we've first observed the G blocked
// needed only to output in traceback
@@ -403,7 +401,7 @@ func markrootSpans(gcw *gcWork, shard int) {
}
// gcAssistAlloc performs GC work to make gp's assist debt positive.
-// gp must be the calling user gorountine.
+// gp must be the calling user goroutine.
//
// This must be called with preemption enabled.
func gcAssistAlloc(gp *g) {
@@ -1563,7 +1561,7 @@ func gcmarknewobject(span *mspan, obj, size, scanSize uintptr) {
if !goexperiment.PacerRedesign {
// The old pacer counts newly allocated memory toward
// heapScanWork because heapScan is continuously updated
- // throughout the GC cyle with newly allocated memory. However,
+ // throughout the GC cycle with newly allocated memory. However,
// these objects are never actually scanned, so we need
// to account for them in heapScanWork here, "faking" their work.
// Otherwise the pacer will think it's always behind, potentially
diff --git a/src/runtime/mgcpacer.go b/src/runtime/mgcpacer.go
index 5b699cb298..6df8af45a8 100644
--- a/src/runtime/mgcpacer.go
+++ b/src/runtime/mgcpacer.go
@@ -53,8 +53,8 @@ const (
gcOverAssistWork = 64 << 10
// defaultHeapMinimum is the value of heapMinimum for GOGC==100.
- defaultHeapMinimum = goexperiment.PacerRedesignInt*(512<<10) +
- (1-goexperiment.PacerRedesignInt)*(4<<20)
+ defaultHeapMinimum = (goexperiment.HeapMinimum512KiBInt)*(512<<10) +
+ (1-goexperiment.HeapMinimum512KiBInt)*(4<<20)
// scannableStackSizeSlack is the bytes of stack space allocated or freed
// that can accumulate on a P before updating gcController.stackSize.
@@ -84,12 +84,9 @@ func init() {
var gcController gcControllerState
type gcControllerState struct {
- // Initialized from $GOGC. GOGC=off means no GC.
- //
- // Updated atomically with mheap_.lock held or during a STW.
- // Safe to read atomically at any time, or non-atomically with
- // mheap_.lock or STW.
- gcPercent int32
+
+ // Initialized from GOGC. GOGC=off means no GC.
+ gcPercent atomic.Int32
_ uint32 // padding so following 64-bit values are 8-byte aligned
@@ -479,7 +476,7 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int) {
// is when assists are enabled and the necessary statistics are
// available).
func (c *gcControllerState) revise() {
- gcPercent := atomic.Loadint32(&c.gcPercent)
+ gcPercent := c.gcPercent.Load()
if gcPercent < 0 {
// If GC is disabled but we're running a forced GC,
// act like GOGC is huge for the below calculations.
@@ -969,8 +966,8 @@ func (c *gcControllerState) commit(triggerRatio float64) {
// has grown by GOGC/100 over where it started the last cycle,
// plus additional runway for non-heap sources of GC work.
goal := ^uint64(0)
- if c.gcPercent >= 0 {
- goal = c.heapMarked + (c.heapMarked+atomic.Load64(&c.stackScan)+atomic.Load64(&c.globalsScan))*uint64(c.gcPercent)/100
+ if gcPercent := c.gcPercent.Load(); gcPercent >= 0 {
+ goal = c.heapMarked + (c.heapMarked+atomic.Load64(&c.stackScan)+atomic.Load64(&c.globalsScan))*uint64(gcPercent)/100
}
// Don't trigger below the minimum heap size.
@@ -1084,17 +1081,19 @@ func (c *gcControllerState) commit(triggerRatio float64) {
//
// For !goexperiment.PacerRedesign.
func (c *gcControllerState) oldCommit(triggerRatio float64) {
+ gcPercent := c.gcPercent.Load()
+
// Compute the next GC goal, which is when the allocated heap
// has grown by GOGC/100 over the heap marked by the last
// cycle.
goal := ^uint64(0)
- if c.gcPercent >= 0 {
- goal = c.heapMarked + c.heapMarked*uint64(c.gcPercent)/100
+ if gcPercent >= 0 {
+ goal = c.heapMarked + c.heapMarked*uint64(gcPercent)/100
}
// Set the trigger ratio, capped to reasonable bounds.
- if c.gcPercent >= 0 {
- scalingFactor := float64(c.gcPercent) / 100
+ if gcPercent >= 0 {
+ scalingFactor := float64(gcPercent) / 100
// Ensure there's always a little margin so that the
// mutator assist ratio isn't infinity.
maxTriggerRatio := 0.95 * scalingFactor
@@ -1134,7 +1133,7 @@ func (c *gcControllerState) oldCommit(triggerRatio float64) {
// We trigger the next GC cycle when the allocated heap has
// grown by the trigger ratio over the marked heap size.
trigger := ^uint64(0)
- if c.gcPercent >= 0 {
+ if gcPercent >= 0 {
trigger = uint64(float64(c.heapMarked) * (1 + triggerRatio))
// Don't trigger below the minimum heap size.
minTrigger := c.heapMinimum
@@ -1210,13 +1209,12 @@ func (c *gcControllerState) setGCPercent(in int32) int32 {
assertWorldStoppedOrLockHeld(&mheap_.lock)
}
- out := c.gcPercent
+ out := c.gcPercent.Load()
if in < 0 {
in = -1
}
- // Write it atomically so readers like revise() can read it safely.
- atomic.Storeint32(&c.gcPercent, in)
- c.heapMinimum = defaultHeapMinimum * uint64(c.gcPercent) / 100
+ c.heapMinimum = defaultHeapMinimum * uint64(in) / 100
+ c.gcPercent.Store(in)
// Update pacing in response to gcPercent change.
c.commit(c.triggerRatio)
diff --git a/src/runtime/mgcscavenge.go b/src/runtime/mgcscavenge.go
index 4a7f2465fd..c27e189af9 100644
--- a/src/runtime/mgcscavenge.go
+++ b/src/runtime/mgcscavenge.go
@@ -263,7 +263,7 @@ func bgscavenge(c chan int) {
scavenge.parked = true
scavenge.timer = new(timer)
- scavenge.timer.f = func(_ interface{}, _ uintptr) {
+ scavenge.timer.f = func(_ any, _ uintptr) {
wakeScavenger()
}
@@ -326,8 +326,8 @@ func bgscavenge(c chan int) {
// Accumulate the amount of time spent scavenging.
start := nanotime()
- released = mheap_.pages.scavenge(scavengeQuantum)
- atomic.Xadduintptr(&mheap_.pages.scav.released, released)
+ r := mheap_.pages.scavenge(scavengeQuantum)
+ atomic.Xadduintptr(&mheap_.pages.scav.released, r)
end := nanotime()
// On some platforms we may see end >= start if the time it takes to scavenge
@@ -339,10 +339,16 @@ func bgscavenge(c chan int) {
// on timing.
const approxCritNSPerPhysicalPage = 10e3
if end <= start {
- crit += approxCritNSPerPhysicalPage * float64(released/physPageSize)
+ crit += approxCritNSPerPhysicalPage * float64(r/physPageSize)
} else {
crit += float64(end - start)
}
+ released += r
+
+ // When using fake time just do one loop.
+ if faketime != 0 {
+ break
+ }
}
if released == 0 {
diff --git a/src/runtime/mgcscavenge_test.go b/src/runtime/mgcscavenge_test.go
index b186cad2f4..0659293c60 100644
--- a/src/runtime/mgcscavenge_test.go
+++ b/src/runtime/mgcscavenge_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
"fmt"
+ "internal/goos"
"math/rand"
. "runtime"
"testing"
@@ -408,7 +409,9 @@ func TestPageAllocScavenge(t *testing.T) {
},
},
}
- if PageAlloc64Bit != 0 {
+ // Disable these tests on iOS since we have a small address space.
+ // See #46860.
+ if PageAlloc64Bit != 0 && goos.IsIos == 0 {
tests["ScavAllVeryDiscontiguous"] = setup{
beforeAlloc: map[ChunkIdx][]BitRange{
BaseChunkIdx: {},
diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go
index fdbec30cf1..a46f4ec2c6 100644
--- a/src/runtime/mgcsweep.go
+++ b/src/runtime/mgcsweep.go
@@ -393,7 +393,7 @@ func sweepone() uintptr {
// The scavenger is signaled by the last sweeper because once
// sweeping is done, we will definitely have useful work for
// the scavenger to do, since the scavenger only runs over the
- // heap once per GC cyle. This update is not done during sweep
+ // heap once per GC cycle. This update is not done during sweep
// termination because in some cases there may be a long delay
// between sweep done and sweep termination (e.g. not enough
// allocations to trigger a GC) which would be nice to fill in
diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go
index e8c23d485a..17c9b75d69 100644
--- a/src/runtime/mkpreempt.go
+++ b/src/runtime/mkpreempt.go
@@ -129,7 +129,7 @@ func header(arch string) {
fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n")
}
-func p(f string, args ...interface{}) {
+func p(f string, args ...any) {
fmted := fmt.Sprintf(f, args...)
fmt.Fprintf(out, "\t%s\n", strings.ReplaceAll(fmted, "\n", "\n\t"))
}
diff --git a/src/runtime/mpagealloc_32bit.go b/src/runtime/mpagealloc_32bit.go
index 1d863f2fda..8c83b93412 100644
--- a/src/runtime/mpagealloc_32bit.go
+++ b/src/runtime/mpagealloc_32bit.go
@@ -2,19 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build 386 || arm || mips || mipsle || wasm || (ios && arm64)
+//go:build 386 || arm || mips || mipsle || wasm
// wasm is a treated as a 32-bit architecture for the purposes of the page
// allocator, even though it has 64-bit pointers. This is because any wasm
// pointer always has its top 32 bits as zero, so the effective heap address
// space is only 2^32 bytes in size (see heapAddrBits).
-// ios/arm64 is treated as a 32-bit architecture for the purposes of the
-// page allocator, even though it has 64-bit pointers and a 33-bit address
-// space (see heapAddrBits). The 33 bit address space cannot be rounded up
-// to 64 bits because there are too many summary levels to fit in just 33
-// bits.
-
package runtime
import "unsafe"
diff --git a/src/runtime/mpagealloc_64bit.go b/src/runtime/mpagealloc_64bit.go
index 782628c91d..1bacfbe0fa 100644
--- a/src/runtime/mpagealloc_64bit.go
+++ b/src/runtime/mpagealloc_64bit.go
@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build amd64 || (!ios && arm64) || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x
-
-// See mpagealloc_32bit.go for why ios/arm64 is excluded here.
+//go:build amd64 || arm64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x
package runtime
diff --git a/src/runtime/mpagealloc_test.go b/src/runtime/mpagealloc_test.go
index 5d979fa95b..f2b82e3f50 100644
--- a/src/runtime/mpagealloc_test.go
+++ b/src/runtime/mpagealloc_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
"fmt"
+ "internal/goos"
. "runtime"
"testing"
)
@@ -165,7 +166,9 @@ func TestPageAllocGrow(t *testing.T) {
},
},
}
- if PageAlloc64Bit != 0 {
+ // Disable these tests on iOS since we have a small address space.
+ // See #46860.
+ if PageAlloc64Bit != 0 && goos.IsIos == 0 {
tests["ExtremelyDiscontiguous"] = test{
chunks: []ChunkIdx{
BaseChunkIdx,
@@ -571,7 +574,9 @@ func TestPageAllocAlloc(t *testing.T) {
},
},
}
- if PageAlloc64Bit != 0 {
+ // Disable these tests on iOS since we have a small address space.
+ // See #46860.
+ if PageAlloc64Bit != 0 && goos.IsIos == 0 {
const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
// This test attempts to trigger a bug wherein we look at unmapped summary
diff --git a/src/runtime/mpagecache_test.go b/src/runtime/mpagecache_test.go
index 69084f9a84..6cb0620f7b 100644
--- a/src/runtime/mpagecache_test.go
+++ b/src/runtime/mpagecache_test.go
@@ -5,6 +5,7 @@
package runtime_test
import (
+ "internal/goos"
"math/rand"
. "runtime"
"testing"
@@ -372,7 +373,9 @@ func TestPageAllocAllocToCache(t *testing.T) {
},
},
}
- if PageAlloc64Bit != 0 {
+ // Disable these tests on iOS since we have a small address space.
+ // See #46860.
+ if PageAlloc64Bit != 0 && goos.IsIos == 0 {
const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
// This test is similar to the one with the same name for
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index b4de8f53a9..569c17f0a7 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -805,7 +805,11 @@ func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int
// truncated profile than to crash the entire process.
return
}
- saveg(^uintptr(0), ^uintptr(0), gp1, &r[0])
+ // saveg calls gentraceback, which may call cgo traceback functions.
+ // The world is stopped, so it cannot use cgocall (which will be
+ // blocked at exitsyscall). Do it on the system stack so it won't
+ // call into the schedular (see traceback.go:cgoContextPCs).
+ systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) })
if labels != nil {
lbl[0] = gp1.labels
lbl = lbl[1:]
diff --git a/src/runtime/msan_amd64.s b/src/runtime/msan_amd64.s
index 1bb57a3b7e..89ed3048d0 100644
--- a/src/runtime/msan_amd64.s
+++ b/src/runtime/msan_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build msan
-// +build msan
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/msan_arm64.s b/src/runtime/msan_arm64.s
index 93ade8dd89..b9eff34ab6 100644
--- a/src/runtime/msan_arm64.s
+++ b/src/runtime/msan_arm64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build msan
-// +build msan
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go
index f60e62dec7..bb3dd35317 100644
--- a/src/runtime/netpoll.go
+++ b/src/runtime/netpoll.go
@@ -71,31 +71,99 @@ const pollBlockSize = 4 * 1024
//go:notinheap
type pollDesc struct {
link *pollDesc // in pollcache, protected by pollcache.lock
+ fd uintptr // constant for pollDesc usage lifetime
+
+ // atomicInfo holds bits from closing, rd, and wd,
+ // which are only ever written while holding the lock,
+ // summarized for use by netpollcheckerr,
+ // which cannot acquire the lock.
+ // After writing these fields under lock in a way that
+ // might change the summary, code must call publishInfo
+ // before releasing the lock.
+ // Code that changes fields and then calls netpollunblock
+ // (while still holding the lock) must call publishInfo
+ // before calling netpollunblock, because publishInfo is what
+ // stops netpollblock from blocking anew
+ // (by changing the result of netpollcheckerr).
+ // atomicInfo also holds the eventErr bit,
+ // recording whether a poll event on the fd got an error;
+ // atomicInfo is the only source of truth for that bit.
+ atomicInfo atomic.Uint32 // atomic pollInfo
+
+ // rg, wg are accessed atomically and hold g pointers.
+ // (Using atomic.Uintptr here is similar to using guintptr elsewhere.)
+ rg atomic.Uintptr // pdReady, pdWait, G waiting for read or nil
+ wg atomic.Uintptr // pdReady, pdWait, G waiting for write or nil
- // The lock protects pollOpen, pollSetDeadline, pollUnblock and deadlineimpl operations.
- // This fully covers seq, rt and wt variables. fd is constant throughout the PollDesc lifetime.
- // pollReset, pollWait, pollWaitCanceled and runtime·netpollready (IO readiness notification)
- // proceed w/o taking the lock. So closing, everr, rg, rd, wg and wd are manipulated
- // in a lock-free way by all operations.
- // TODO(golang.org/issue/49008): audit these lock-free fields for continued correctness.
- // NOTE(dvyukov): the following code uses uintptr to store *g (rg/wg),
- // that will blow up when GC starts moving objects.
lock mutex // protects the following fields
- fd uintptr
closing bool
- everr bool // marks event scanning error happened
user uint32 // user settable cookie
rseq uintptr // protects from stale read timers
- rg uintptr // pdReady, pdWait, G waiting for read or nil. Accessed atomically.
rt timer // read deadline timer (set if rt.f != nil)
- rd int64 // read deadline
+ rd int64 // read deadline (a nanotime in the future, -1 when expired)
wseq uintptr // protects from stale write timers
- wg uintptr // pdReady, pdWait, G waiting for write or nil. Accessed atomically.
wt timer // write deadline timer
- wd int64 // write deadline
+ wd int64 // write deadline (a nanotime in the future, -1 when expired)
self *pollDesc // storage for indirect interface. See (*pollDesc).makeArg.
}
+// pollInfo is the bits needed by netpollcheckerr, stored atomically,
+// mostly duplicating state that is manipulated under lock in pollDesc.
+// The one exception is the pollEventErr bit, which is maintained only
+// in the pollInfo.
+type pollInfo uint32
+
+const (
+ pollClosing = 1 << iota
+ pollEventErr
+ pollExpiredReadDeadline
+ pollExpiredWriteDeadline
+)
+
+func (i pollInfo) closing() bool { return i&pollClosing != 0 }
+func (i pollInfo) eventErr() bool { return i&pollEventErr != 0 }
+func (i pollInfo) expiredReadDeadline() bool { return i&pollExpiredReadDeadline != 0 }
+func (i pollInfo) expiredWriteDeadline() bool { return i&pollExpiredWriteDeadline != 0 }
+
+// info returns the pollInfo corresponding to pd.
+func (pd *pollDesc) info() pollInfo {
+ return pollInfo(pd.atomicInfo.Load())
+}
+
+// publishInfo updates pd.atomicInfo (returned by pd.info)
+// using the other values in pd.
+// It must be called while holding pd.lock,
+// and it must be called after changing anything
+// that might affect the info bits.
+// In practice this means after changing closing
+// or changing rd or wd from < 0 to >= 0.
+func (pd *pollDesc) publishInfo() {
+ var info uint32
+ if pd.closing {
+ info |= pollClosing
+ }
+ if pd.rd < 0 {
+ info |= pollExpiredReadDeadline
+ }
+ if pd.wd < 0 {
+ info |= pollExpiredWriteDeadline
+ }
+
+ // Set all of x except the pollEventErr bit.
+ x := pd.atomicInfo.Load()
+ for !pd.atomicInfo.CompareAndSwap(x, (x&pollEventErr)|info) {
+ x = pd.atomicInfo.Load()
+ }
+}
+
+// setEventErr sets the result of pd.info().eventErr() to b.
+func (pd *pollDesc) setEventErr(b bool) {
+ x := pd.atomicInfo.Load()
+ for (x&pollEventErr != 0) != b && !pd.atomicInfo.CompareAndSwap(x, x^pollEventErr) {
+ x = pd.atomicInfo.Load()
+ }
+}
+
type pollCache struct {
lock mutex
first *pollDesc
@@ -147,24 +215,25 @@ func poll_runtime_isPollServerDescriptor(fd uintptr) bool {
func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
pd := pollcache.alloc()
lock(&pd.lock)
- wg := atomic.Loaduintptr(&pd.wg)
+ wg := pd.wg.Load()
if wg != 0 && wg != pdReady {
throw("runtime: blocked write on free polldesc")
}
- rg := atomic.Loaduintptr(&pd.rg)
+ rg := pd.rg.Load()
if rg != 0 && rg != pdReady {
throw("runtime: blocked read on free polldesc")
}
pd.fd = fd
pd.closing = false
- pd.everr = false
+ pd.setEventErr(false)
pd.rseq++
- atomic.Storeuintptr(&pd.rg, 0)
+ pd.rg.Store(0)
pd.rd = 0
pd.wseq++
- atomic.Storeuintptr(&pd.wg, 0)
+ pd.wg.Store(0)
pd.wd = 0
pd.self = pd
+ pd.publishInfo()
unlock(&pd.lock)
errno := netpollopen(fd, pd)
@@ -180,11 +249,11 @@ func poll_runtime_pollClose(pd *pollDesc) {
if !pd.closing {
throw("runtime: close polldesc w/o unblock")
}
- wg := atomic.Loaduintptr(&pd.wg)
+ wg := pd.wg.Load()
if wg != 0 && wg != pdReady {
throw("runtime: blocked write on closing polldesc")
}
- rg := atomic.Loaduintptr(&pd.rg)
+ rg := pd.rg.Load()
if rg != 0 && rg != pdReady {
throw("runtime: blocked read on closing polldesc")
}
@@ -209,9 +278,9 @@ func poll_runtime_pollReset(pd *pollDesc, mode int) int {
return errcode
}
if mode == 'r' {
- atomic.Storeuintptr(&pd.rg, 0)
+ pd.rg.Store(0)
} else if mode == 'w' {
- atomic.Storeuintptr(&pd.wg, 0)
+ pd.wg.Store(0)
}
return pollNoError
}
@@ -273,6 +342,7 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
if mode == 'w' || mode == 'r'+'w' {
pd.wd = d
}
+ pd.publishInfo()
combo := pd.rd > 0 && pd.rd == pd.wd
rtf := netpollReadDeadline
if combo {
@@ -314,15 +384,13 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
}
}
// If we set the new deadline in the past, unblock currently pending IO if any.
+ // Note that pd.publishInfo has already been called, above, immediately after modifying rd and wd.
var rg, wg *g
- if pd.rd < 0 || pd.wd < 0 {
- atomic.StorepNoWB(noescape(unsafe.Pointer(&wg)), nil) // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
- if pd.rd < 0 {
- rg = netpollunblock(pd, 'r', false)
- }
- if pd.wd < 0 {
- wg = netpollunblock(pd, 'w', false)
- }
+ if pd.rd < 0 {
+ rg = netpollunblock(pd, 'r', false)
+ }
+ if pd.wd < 0 {
+ wg = netpollunblock(pd, 'w', false)
}
unlock(&pd.lock)
if rg != nil {
@@ -343,7 +411,7 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
pd.rseq++
pd.wseq++
var rg, wg *g
- atomic.StorepNoWB(noescape(unsafe.Pointer(&rg)), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
+ pd.publishInfo()
rg = netpollunblock(pd, 'r', false)
wg = netpollunblock(pd, 'w', false)
if pd.rt.f != nil {
@@ -388,16 +456,17 @@ func netpollready(toRun *gList, pd *pollDesc, mode int32) {
}
func netpollcheckerr(pd *pollDesc, mode int32) int {
- if pd.closing {
+ info := pd.info()
+ if info.closing() {
return pollErrClosing
}
- if (mode == 'r' && pd.rd < 0) || (mode == 'w' && pd.wd < 0) {
+ if (mode == 'r' && info.expiredReadDeadline()) || (mode == 'w' && info.expiredWriteDeadline()) {
return pollErrTimeout
}
// Report an event scanning error only on a read event.
// An error on a write event will be captured in a subsequent
// write call that is able to report a more specific error.
- if mode == 'r' && pd.everr {
+ if mode == 'r' && info.eventErr() {
return pollErrNotPollable
}
return pollNoError
@@ -432,28 +501,28 @@ func netpollblock(pd *pollDesc, mode int32, waitio bool) bool {
// set the gpp semaphore to pdWait
for {
// Consume notification if already ready.
- if atomic.Casuintptr(gpp, pdReady, 0) {
+ if gpp.CompareAndSwap(pdReady, 0) {
return true
}
- if atomic.Casuintptr(gpp, 0, pdWait) {
+ if gpp.CompareAndSwap(0, pdWait) {
break
}
// Double check that this isn't corrupt; otherwise we'd loop
// forever.
- if v := atomic.Loaduintptr(gpp); v != pdReady && v != 0 {
+ if v := gpp.Load(); v != pdReady && v != 0 {
throw("runtime: double wait")
}
}
// need to recheck error states after setting gpp to pdWait
// this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
- // do the opposite: store to closing/rd/wd, membarrier, load of rg/wg
+ // do the opposite: store to closing/rd/wd, publishInfo, load of rg/wg
if waitio || netpollcheckerr(pd, mode) == pollNoError {
gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceEvGoBlockNet, 5)
}
// be careful to not lose concurrent pdReady notification
- old := atomic.Xchguintptr(gpp, 0)
+ old := gpp.Swap(0)
if old > pdWait {
throw("runtime: corrupted polldesc")
}
@@ -467,7 +536,7 @@ func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g {
}
for {
- old := atomic.Loaduintptr(gpp)
+ old := gpp.Load()
if old == pdReady {
return nil
}
@@ -480,7 +549,7 @@ func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g {
if ioready {
new = pdReady
}
- if atomic.Casuintptr(gpp, old, new) {
+ if gpp.CompareAndSwap(old, new) {
if old == pdWait {
old = 0
}
@@ -508,7 +577,7 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
throw("runtime: inconsistent read deadline")
}
pd.rd = -1
- atomic.StorepNoWB(unsafe.Pointer(&pd.rt.f), nil) // full memory barrier between store to rd and load of rg in netpollunblock
+ pd.publishInfo()
rg = netpollunblock(pd, 'r', false)
}
var wg *g
@@ -517,7 +586,7 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
throw("runtime: inconsistent write deadline")
}
pd.wd = -1
- atomic.StorepNoWB(unsafe.Pointer(&pd.wt.f), nil) // full memory barrier between store to wd and load of wg in netpollunblock
+ pd.publishInfo()
wg = netpollunblock(pd, 'w', false)
}
unlock(&pd.lock)
@@ -529,15 +598,15 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
}
}
-func netpollDeadline(arg interface{}, seq uintptr) {
+func netpollDeadline(arg any, seq uintptr) {
netpolldeadlineimpl(arg.(*pollDesc), seq, true, true)
}
-func netpollReadDeadline(arg interface{}, seq uintptr) {
+func netpollReadDeadline(arg any, seq uintptr) {
netpolldeadlineimpl(arg.(*pollDesc), seq, true, false)
}
-func netpollWriteDeadline(arg interface{}, seq uintptr) {
+func netpollWriteDeadline(arg any, seq uintptr) {
netpolldeadlineimpl(arg.(*pollDesc), seq, false, true)
}
@@ -570,7 +639,7 @@ func (c *pollCache) alloc() *pollDesc {
// a conversion requires an allocation because pointers to
// go:notinheap types (which pollDesc is) must be stored
// in interfaces indirectly. See issue 42076.
-func (pd *pollDesc) makeArg() (i interface{}) {
+func (pd *pollDesc) makeArg() (i any) {
x := (*eface)(unsafe.Pointer(&i))
x._type = pdType
x.data = unsafe.Pointer(&pd.self)
@@ -578,6 +647,6 @@ func (pd *pollDesc) makeArg() (i interface{}) {
}
var (
- pdEface interface{} = (*pollDesc)(nil)
- pdType *_type = efaceOf(&pdEface)._type
+ pdEface any = (*pollDesc)(nil)
+ pdType *_type = efaceOf(&pdEface)._type
)
diff --git a/src/runtime/netpoll_aix.go b/src/runtime/netpoll_aix.go
index 4590ed81a6..90950af444 100644
--- a/src/runtime/netpoll_aix.go
+++ b/src/runtime/netpoll_aix.go
@@ -212,10 +212,7 @@ retry:
pfd.events &= ^_POLLOUT
}
if mode != 0 {
- pds[i].everr = false
- if pfd.revents == _POLLERR {
- pds[i].everr = true
- }
+ pds[i].setEventErr(pfd.revents == _POLLERR)
netpollready(&toRun, pds[i], mode)
n--
}
diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go
index e0fb877d50..b7d6199965 100644
--- a/src/runtime/netpoll_epoll.go
+++ b/src/runtime/netpoll_epoll.go
@@ -168,10 +168,7 @@ retry:
}
if mode != 0 {
pd := *(**pollDesc)(unsafe.Pointer(&ev.data))
- pd.everr = false
- if ev.events == _EPOLLERR {
- pd.everr = true
- }
+ pd.setEventErr(ev.events == _EPOLLERR)
netpollready(&toRun, pd, mode)
}
}
diff --git a/src/runtime/netpoll_kqueue.go b/src/runtime/netpoll_kqueue.go
index 2f7f2848d2..1694753b6f 100644
--- a/src/runtime/netpoll_kqueue.go
+++ b/src/runtime/netpoll_kqueue.go
@@ -179,10 +179,7 @@ retry:
}
if mode != 0 {
pd := (*pollDesc)(unsafe.Pointer(ev.udata))
- pd.everr = false
- if ev.flags == _EV_ERROR {
- pd.everr = true
- }
+ pd.setEventErr(ev.flags == _EV_ERROR)
netpollready(&toRun, pd, mode)
}
}
diff --git a/src/runtime/netpoll_solaris.go b/src/runtime/netpoll_solaris.go
index d217d5b160..6e545b3d31 100644
--- a/src/runtime/netpoll_solaris.go
+++ b/src/runtime/netpoll_solaris.go
@@ -158,7 +158,7 @@ func netpollclose(fd uintptr) int32 {
// this call, port_getn will return one and only one event for that
// particular descriptor, so this function needs to be called again.
func netpollupdate(pd *pollDesc, set, clear uint32) {
- if pd.closing {
+ if pd.info().closing() {
return
}
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index 0e17e75e3e..c76add7802 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -40,7 +40,6 @@ const (
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
-//go:cgo_import_dynamic runtime._RaiseException RaiseException%4 "kernel32.dll"
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
@@ -94,7 +93,6 @@ var (
_PostQueuedCompletionStatus,
_QueryPerformanceCounter,
_QueryPerformanceFrequency,
- _RaiseException,
_ResumeThread,
_SetConsoleCtrlHandler,
_SetErrorMode,
@@ -122,7 +120,6 @@ var (
_AddVectoredContinueHandler,
_LoadLibraryExA,
_LoadLibraryExW,
- _WerSetFlags,
_ stdFunction
// Use RtlGenRandom to generate cryptographically random data.
@@ -257,7 +254,6 @@ func loadOptionalSyscalls() {
_AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000"))
_LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000"))
_LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000"))
- _WerSetFlags = windowsFindfunc(k32, []byte("WerSetFlags\000"))
useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)
var advapi32dll = []byte("advapi32.dll\000")
@@ -327,7 +323,7 @@ func monitorSuspendResume() {
if powerRegisterSuspendResumeNotification == nil {
return // Running on Windows 7, where we don't need it anyway.
}
- var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr {
+ var fn any = func(context uintptr, changeType uint32, setting uintptr) uintptr {
for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
if mp.resumesema != 0 {
stdcall1(_SetEvent, mp.resumesema)
@@ -686,7 +682,7 @@ func goenvs() {
// We call these all the way here, late in init, so that malloc works
// for the callback functions these generate.
- var fn interface{} = ctrlHandler
+ var fn any = ctrlHandler
ctrlHandlerPC := compileCallback(*efaceOf(&fn), true)
stdcall2(_SetConsoleCtrlHandler, ctrlHandlerPC, 1)
@@ -1198,8 +1194,10 @@ func ctrlHandler(_type uint32) uintptr {
if sigsend(s) {
if s == _SIGTERM {
// Windows terminates the process after this handler returns.
- // Block indefinitely to give signal handlers a chance to clean up.
- stdcall1(_Sleep, uintptr(_INFINITE))
+ // Block indefinitely to give signal handlers a chance to clean up,
+ // but make sure to be properly parked first, so the rest of the
+ // program can continue executing.
+ block()
}
return 1
}
@@ -1308,18 +1306,13 @@ func setThreadCPUProfiler(hz int32) {
atomic.Store((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz))
}
-const preemptMSupported = GOARCH == "386" || GOARCH == "amd64"
+const preemptMSupported = true
// suspendLock protects simultaneous SuspendThread operations from
// suspending each other.
var suspendLock mutex
func preemptM(mp *m) {
- if !preemptMSupported {
- // TODO: Implement call injection
- return
- }
-
if mp == getg().m {
throw("self-preempt")
}
@@ -1401,8 +1394,31 @@ func preemptM(mp *m) {
*(*uintptr)(unsafe.Pointer(sp)) = newpc
c.set_sp(sp)
c.set_ip(targetPC)
- }
+ case "arm":
+ // Push LR. The injected call is responsible
+ // for restoring LR. gentraceback is aware of
+ // this extra slot. See sigctxt.pushCall in
+ // signal_arm.go, which is similar except we
+ // subtract 1 from IP here.
+ sp := c.sp()
+ sp -= goarch.PtrSize
+ c.set_sp(sp)
+ *(*uint32)(unsafe.Pointer(sp)) = uint32(c.lr())
+ c.set_lr(newpc - 1)
+ c.set_ip(targetPC)
+
+ case "arm64":
+ // Push LR. The injected call is responsible
+ // for restoring LR. gentraceback is aware of
+ // this extra slot. See sigctxt.pushCall in
+ // signal_arm64.go.
+ sp := c.sp() - 16 // SP needs 16-byte alignment
+ c.set_sp(sp)
+ *(*uint64)(unsafe.Pointer(sp)) = uint64(c.lr())
+ c.set_lr(newpc)
+ c.set_ip(targetPC)
+ }
stdcall2(_SetThreadContext, thread, uintptr(unsafe.Pointer(c)))
}
}
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index eec69dfdc6..6600410cb6 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -755,7 +755,7 @@ func deferCallSave(p *_panic, fn func()) {
}
// The implementation of the predeclared function panic.
-func gopanic(e interface{}) {
+func gopanic(e any) {
gp := getg()
if gp.m.curg != gp {
print("panic: ")
@@ -957,7 +957,7 @@ func getargp() uintptr {
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
//go:nosplit
-func gorecover(argp uintptr) interface{} {
+func gorecover(argp uintptr) any {
// Must be in a function running as part of a deferred call during the panic.
// Must be called from the topmost function of the call
// (the function used in the defer statement).
@@ -1002,11 +1002,6 @@ var runningPanicDefers uint32
// panicking is incremented and decremented atomically.
var panicking uint32
-// tracebackprinted is zero before gopanic() prints the traceback. After
-// traceback is printed, it sets to 1 so that the subsequent exception handler
-// won't print the traceback again.
-var tracebackprinted uint32
-
// paniclk is held while printing the panic information and stack trace,
// so that two concurrent panics don't overlap their output.
var paniclk mutex
@@ -1050,9 +1045,6 @@ func fatalthrow() {
startpanic_m()
if dopanic_m(gp, pc, sp) {
- // At this point, traceback has already been printed.
- // Set tracebackprinted to 1 to avoid printing traceback again
- tracebackprinted = 1
// crash uses a decent amount of nosplit stack and we're already
// low on stack in throw, so crash on the system stack (unlike
// fatalpanic).
@@ -1094,9 +1086,6 @@ func fatalpanic(msgs *_panic) {
})
if docrash {
- // At this point, traceback has already been printed.
- // Set tracebackprinted to 1 to avoid printing traceback again
- tracebackprinted = 1
// By crashing outside the above systemstack call, debuggers
// will not be confused when generating a backtrace.
// Function crash is marked nosplit to avoid stack growth.
diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go
index f37854f915..a61dcc3b5d 100644
--- a/src/runtime/plugin.go
+++ b/src/runtime/plugin.go
@@ -7,7 +7,7 @@ package runtime
import "unsafe"
//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
-func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr string) {
+func plugin_lastmoduleinit() (path string, syms map[string]any, errstr string) {
var md *moduledata
for pmd := firstmoduledata.next; pmd != nil; pmd = pmd.next {
if pmd.bad {
@@ -76,11 +76,11 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr s
// Because functions are handled specially in the plugin package,
// function symbol names are prefixed here with '.' to avoid
// a dependency on the reflect package.
- syms = make(map[string]interface{}, len(md.ptab))
+ syms = make(map[string]any, len(md.ptab))
for _, ptab := range md.ptab {
symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
- var val interface{}
+ var val any
valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&val))
(*valp)[0] = unsafe.Pointer(t)
diff --git a/src/runtime/pprof/mprof_test.go b/src/runtime/pprof/mprof_test.go
index ab8341d32f..665487a7c4 100644
--- a/src/runtime/pprof/mprof_test.go
+++ b/src/runtime/pprof/mprof_test.go
@@ -17,7 +17,7 @@ import (
"unsafe"
)
-var memSink interface{}
+var memSink any
func allocateTransient1M() {
for i := 0; i < 1024; i++ {
diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go
index 000abf935c..d75efce7a8 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -134,7 +134,7 @@ import (
type Profile struct {
name string
mu sync.Mutex
- m map[interface{}][]uintptr
+ m map[any][]uintptr
count func() int
write func(io.Writer, int) error
}
@@ -217,7 +217,7 @@ func NewProfile(name string) *Profile {
}
p := &Profile{
name: name,
- m: map[interface{}][]uintptr{},
+ m: map[any][]uintptr{},
}
profiles.m[name] = p
return p
@@ -277,7 +277,7 @@ func (p *Profile) Count() int {
// Passing skip=0 begins the stack trace at the call to Add inside rpc.NewClient.
// Passing skip=1 begins the stack trace at the call to NewClient inside mypkg.Run.
//
-func (p *Profile) Add(value interface{}, skip int) {
+func (p *Profile) Add(value any, skip int) {
if p.name == "" {
panic("pprof: use of uninitialized Profile")
}
@@ -303,7 +303,7 @@ func (p *Profile) Add(value interface{}, skip int) {
// Remove removes the execution stack associated with value from the profile.
// It is a no-op if the value is not in the profile.
-func (p *Profile) Remove(value interface{}) {
+func (p *Profile) Remove(value any) {
p.mu.Lock()
defer p.mu.Unlock()
delete(p.m, value)
diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go
index 417d5034a6..1a44ab7ad7 100644
--- a/src/runtime/pprof/pprof_test.go
+++ b/src/runtime/pprof/pprof_test.go
@@ -79,10 +79,6 @@ func cpuHog2(x int) int {
return foo
}
-func cpuHog3(x int) int {
- return cpuHog0(x, 1e5)
-}
-
// 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 {
@@ -93,14 +89,16 @@ func avoidFunctions() []string {
}
func TestCPUProfile(t *testing.T) {
- testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions(), func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions())
+ testCPUProfile(t, matches, 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"}, avoidFunctions(), func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, avoidFunctions())
+ testCPUProfile(t, matches, func(dur time.Duration) {
c := make(chan int)
go func() {
cpuHogger(cpuHog1, &salt1, dur)
@@ -116,6 +114,30 @@ func TestCPUProfileMultithreadMagnitude(t *testing.T) {
t.Skip("issue 35057 is only confirmed on Linux")
}
+ // Linux [5.9,5.16) has a kernel bug that can break CPU timers on newly
+ // created threads, breaking our CPU accounting.
+ major, minor, patch, err := linuxKernelVersion()
+ if err != nil {
+ t.Errorf("Error determining kernel version: %v", err)
+ }
+ t.Logf("Running on Linux %d.%d.%d", major, minor, patch)
+ defer func() {
+ if t.Failed() {
+ t.Logf("Failure of this test may indicate that your system suffers from a known Linux kernel bug fixed on newer kernels. See https://golang.org/issue/49065.")
+ }
+ }()
+
+ // Disable on affected builders to avoid flakiness, but otherwise keep
+ // it enabled to potentially warn users that they are on a broken
+ // kernel.
+ if testenv.Builder() != "" && (runtime.GOARCH == "386" || runtime.GOARCH == "amd64") {
+ have59 := major > 5 || (major == 5 && minor >= 9)
+ have516 := major > 5 || (major == 5 && minor >= 16)
+ if have59 && !have516 {
+ testenv.SkipFlaky(t, 49065)
+ }
+ }
+
// Run a workload in a single goroutine, then run copies of the same
// workload in several goroutines. For both the serial and parallel cases,
// the CPU time the process measures with its own profiler should match the
@@ -132,90 +154,93 @@ func TestCPUProfileMultithreadMagnitude(t *testing.T) {
maxDiff = 0.40
}
- parallelism := runtime.GOMAXPROCS(0)
-
- // This test compares the process's total CPU time against the CPU
- // profiler's view of time spent in direct execution of user code.
- // Background work, especially from the garbage collector, adds noise to
- // that measurement. Disable automatic triggering of the GC, and then
- // request a complete GC cycle (up through sweep termination).
- defer debug.SetGCPercent(debug.SetGCPercent(-1))
- runtime.GC()
-
- var cpuTime1, cpuTimeN time.Duration
- p := testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog3"}, avoidFunctions(), func(dur time.Duration) {
- cpuTime1 = diffCPUTime(t, func() {
- // Consume CPU in one goroutine
- cpuHogger(cpuHog1, &salt1, dur)
- })
-
- cpuTimeN = diffCPUTime(t, func() {
- // Next, consume CPU in several goroutines
- var wg sync.WaitGroup
- var once sync.Once
- for i := 0; i < parallelism; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- var salt = 0
- cpuHogger(cpuHog3, &salt, dur)
- once.Do(func() { salt1 = salt })
- }()
- }
- wg.Wait()
- })
- })
-
- for i, unit := range []string{"count", "nanoseconds"} {
- if have, want := p.SampleType[i].Unit, unit; have != want {
- t.Errorf("pN SampleType[%d]; %q != %q", i, have, want)
+ compare := func(a, b time.Duration, maxDiff float64) error {
+ if a <= 0 || b <= 0 {
+ return fmt.Errorf("Expected both time reports to be positive")
}
- }
- var value1, valueN time.Duration
- for _, sample := range p.Sample {
- if stackContains("runtime/pprof.cpuHog1", uintptr(sample.Value[0]), sample.Location, sample.Label) {
- value1 += time.Duration(sample.Value[1]) * time.Nanosecond
+ if a < b {
+ a, b = b, a
}
- if stackContains("runtime/pprof.cpuHog3", uintptr(sample.Value[0]), sample.Location, sample.Label) {
- valueN += time.Duration(sample.Value[1]) * time.Nanosecond
+
+ diff := float64(a-b) / float64(a)
+ if diff > maxDiff {
+ return fmt.Errorf("CPU usage reports are too different (limit -%.1f%%, got -%.1f%%)", maxDiff*100, diff*100)
}
+
+ return nil
}
- compare := func(a, b time.Duration, maxDiff float64) func(*testing.T) {
- return func(t *testing.T) {
- t.Logf("compare %s vs %s", a, b)
- if a <= 0 || b <= 0 {
- t.Errorf("Expected both time reports to be positive")
- return
+ for _, tc := range []struct {
+ name string
+ workers int
+ }{
+ {
+ name: "serial",
+ workers: 1,
+ },
+ {
+ name: "parallel",
+ workers: runtime.GOMAXPROCS(0),
+ },
+ } {
+ // check that the OS's perspective matches what the Go runtime measures.
+ t.Run(tc.name, func(t *testing.T) {
+ t.Logf("Running with %d workers", tc.workers)
+
+ var cpuTime time.Duration
+ matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions())
+ p := testCPUProfile(t, matches, func(dur time.Duration) {
+ cpuTime = diffCPUTime(t, func() {
+ var wg sync.WaitGroup
+ var once sync.Once
+ for i := 0; i < tc.workers; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ var salt = 0
+ cpuHogger(cpuHog1, &salt, dur)
+ once.Do(func() { salt1 = salt })
+ }()
+ }
+ wg.Wait()
+ })
+ })
+
+ for i, unit := range []string{"count", "nanoseconds"} {
+ if have, want := p.SampleType[i].Unit, unit; have != want {
+ t.Errorf("pN SampleType[%d]; %q != %q", i, have, want)
+ }
}
- if a < b {
- a, b = b, a
+ // cpuHog1 called above is the primary source of CPU
+ // load, but there may be some background work by the
+ // runtime. Since the OS rusage measurement will
+ // include all work done by the process, also compare
+ // against all samples in our profile.
+ var value time.Duration
+ for _, sample := range p.Sample {
+ value += time.Duration(sample.Value[1]) * time.Nanosecond
}
- diff := float64(a-b) / float64(a)
- if diff > maxDiff {
- t.Errorf("CPU usage reports are too different (limit -%.1f%%, got -%.1f%%)", maxDiff*100, diff*100)
+ t.Logf("compare %s vs %s", cpuTime, value)
+ if err := compare(cpuTime, value, maxDiff); err != nil {
+ t.Errorf("compare got %v want nil", err)
}
- }
+ })
}
-
- // check that the OS's perspective matches what the Go runtime measures
- t.Run("serial execution OS vs pprof", compare(cpuTime1, value1, maxDiff))
- t.Run("parallel execution OS vs pprof", compare(cpuTimeN, valueN, maxDiff))
}
// containsInlinedCall reports whether the function body for the function f is
// known to contain an inlined function call within the first maxBytes bytes.
-func containsInlinedCall(f interface{}, maxBytes int) bool {
+func containsInlinedCall(f any, maxBytes int) bool {
_, found := findInlinedCall(f, maxBytes)
return found
}
// findInlinedCall returns the PC of an inlined function call within
// the function body for the function f if any.
-func findInlinedCall(f interface{}, maxBytes int) (pc uint64, found bool) {
+func findInlinedCall(f any, maxBytes int) (pc uint64, found bool) {
fFunc := runtime.FuncForPC(uintptr(abi.FuncPCABIInternal(f)))
if fFunc == nil || fFunc.Entry() == 0 {
panic("failed to locate function entry")
@@ -248,7 +273,8 @@ func TestCPUProfileInlining(t *testing.T) {
t.Skip("Can't determine whether inlinedCallee was inlined into inlinedCaller.")
}
- p := testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, avoidFunctions(), func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, avoidFunctions())
+ p := testCPUProfile(t, matches, func(dur time.Duration) {
cpuHogger(inlinedCaller, &salt1, dur)
})
@@ -298,7 +324,8 @@ func inlinedCalleeDump(pcs []uintptr) {
}
func TestCPUProfileRecursion(t *testing.T) {
- p := testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.recursionCallee", "runtime/pprof.recursionCaller"}, avoidFunctions(), func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.recursionCallee", "runtime/pprof.recursionCaller"}, avoidFunctions())
+ p := testCPUProfile(t, matches, func(dur time.Duration) {
cpuHogger(recursionCaller, &salt1, dur)
})
@@ -383,7 +410,7 @@ func cpuProfilingBroken() bool {
// testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need,
// as interpreted by matches, and returns the parsed profile.
-func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) *profile.Profile {
+func testCPUProfile(t *testing.T, matches profileMatchFunc, f func(dur time.Duration)) *profile.Profile {
switch runtime.GOOS {
case "darwin":
out, err := exec.Command("uname", "-a").CombinedOutput()
@@ -424,7 +451,7 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []stri
f(duration)
StopCPUProfile()
- if p, ok := profileOk(t, matches, need, avoid, prof, duration); ok {
+ if p, ok := profileOk(t, matches, prof, duration); ok {
return p
}
@@ -480,35 +507,18 @@ func stackContains(spec string, count uintptr, stk []*profile.Location, labels m
return false
}
-type matchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool
+type sampleMatchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool
-func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, prof bytes.Buffer, duration time.Duration) (_ *profile.Profile, ok bool) {
+func profileOk(t *testing.T, matches profileMatchFunc, prof bytes.Buffer, duration time.Duration) (_ *profile.Profile, ok bool) {
ok = true
- // 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
p := parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, labels map[string][]string) {
fmt.Fprintf(&buf, "%d:", count)
fprintStack(&buf, stk)
+ fmt.Fprintf(&buf, " labels: %v\n", labels)
samples += count
- for i, spec := range need {
- if matches(spec, count, stk, labels) {
- 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())
@@ -531,39 +541,77 @@ func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, p
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 matches != nil && !matches(t, p) {
+ ok = false
}
- if len(need) == 0 {
- return p, ok
- }
+ return p, ok
+}
- var total uintptr
- for i, name := range need {
- total += have[i]
- t.Logf("%s: %d\n", name, have[i])
- }
- if total == 0 {
- t.Logf("no samples in expected functions")
- ok = false
- }
- // We'd like to check a reasonable minimum, like
- // total / len(have) / smallconstant, but this test is
- // pretty flaky (see bug 7095). So we'll just test to
- // make sure we got at least one sample.
- min := uintptr(1)
- for i, name := range need {
- if have[i] < min {
- t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
+type profileMatchFunc func(*testing.T, *profile.Profile) bool
+
+func matchAndAvoidStacks(matches sampleMatchFunc, need []string, avoid []string) profileMatchFunc {
+ return func(t *testing.T, p *profile.Profile) (ok bool) {
+ ok = true
+
+ // 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))
+
+ for _, sample := range p.Sample {
+ count := uintptr(sample.Value[0])
+ for i, spec := range need {
+ if matches(spec, count, sample.Location, sample.Label) {
+ have[i] += count
+ }
+ }
+ for i, name := range avoid {
+ for _, loc := range sample.Location {
+ for _, line := range loc.Line {
+ if strings.Contains(line.Function.Name, name) {
+ avoidSamples[i] += count
+ }
+ }
+ }
+ }
+ }
+
+ 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
+ }
+
+ var total uintptr
+ for i, name := range need {
+ total += have[i]
+ t.Logf("%s: %d\n", name, have[i])
+ }
+ if total == 0 {
+ t.Logf("no samples in expected functions")
ok = false
}
+
+ // We'd like to check a reasonable minimum, like
+ // total / len(have) / smallconstant, but this test is
+ // pretty flaky (see bug 7095). So we'll just test to
+ // make sure we got at least one sample.
+ min := uintptr(1)
+ for i, name := range need {
+ if have[i] < min {
+ t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
+ ok = false
+ }
+ }
+ return
}
- return p, ok
}
// Fork can hang if preempted with signals frequently enough (see issue 5517).
@@ -675,12 +723,11 @@ func fprintStack(w io.Writer, stk []*profile.Location) {
}
fmt.Fprintf(w, ")")
}
- fmt.Fprintf(w, "\n")
}
// Test that profiling of division operations is okay, especially on ARM. See issue 6681.
func TestMathBigDivide(t *testing.T) {
- testCPUProfile(t, nil, nil, nil, func(duration time.Duration) {
+ testCPUProfile(t, nil, func(duration time.Duration) {
t := time.After(duration)
pi := new(big.Int)
for {
@@ -709,7 +756,8 @@ func stackContainsAll(spec string, count uintptr, stk []*profile.Location, label
}
func TestMorestack(t *testing.T) {
- testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions(), func(duration time.Duration) {
+ matches := matchAndAvoidStacks(stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions())
+ testCPUProfile(t, matches, func(duration time.Duration) {
t := time.After(duration)
c := make(chan bool)
for {
@@ -1340,7 +1388,8 @@ func stackContainsLabeled(spec string, count uintptr, stk []*profile.Location, l
}
func TestCPUProfileLabel(t *testing.T) {
- testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, avoidFunctions())
+ testCPUProfile(t, matches, func(dur time.Duration) {
Do(context.Background(), Labels("key", "value"), func(context.Context) {
cpuHogger(cpuHog1, &salt1, dur)
})
@@ -1351,7 +1400,8 @@ 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"}, nil, func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, nil)
+ testCPUProfile(t, matches, func(dur time.Duration) {
start := time.Now()
var wg sync.WaitGroup
for time.Since(start) < dur {
@@ -1370,6 +1420,126 @@ func TestLabelRace(t *testing.T) {
})
}
+// TestLabelSystemstack makes sure CPU profiler samples of goroutines running
+// on systemstack include the correct pprof labels. See issue #48577
+func TestLabelSystemstack(t *testing.T) {
+ // Grab and re-set the initial value before continuing to ensure
+ // GOGC doesn't actually change following the test.
+ gogc := debug.SetGCPercent(100)
+ debug.SetGCPercent(gogc)
+
+ matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime.systemstack;key=value"}, avoidFunctions())
+ p := testCPUProfile(t, matches, func(dur time.Duration) {
+ Do(context.Background(), Labels("key", "value"), func(ctx context.Context) {
+ parallelLabelHog(ctx, dur, gogc)
+ })
+ })
+
+ // Two conditions to check:
+ // * labelHog should always be labeled.
+ // * The label should _only_ appear on labelHog and the Do call above.
+ for _, s := range p.Sample {
+ isLabeled := s.Label != nil && contains(s.Label["key"], "value")
+ var (
+ mayBeLabeled bool
+ mustBeLabeled bool
+ mustNotBeLabeled bool
+ )
+ for _, loc := range s.Location {
+ for _, l := range loc.Line {
+ switch l.Function.Name {
+ case "runtime/pprof.labelHog", "runtime/pprof.parallelLabelHog", "runtime/pprof.parallelLabelHog.func1":
+ mustBeLabeled = true
+ case "runtime/pprof.Do":
+ // Do sets the labels, so samples may
+ // or may not be labeled depending on
+ // which part of the function they are
+ // at.
+ mayBeLabeled = true
+ case "runtime.bgsweep", "runtime.bgscavenge", "runtime.forcegchelper", "runtime.gcBgMarkWorker", "runtime.runfinq", "runtime.sysmon":
+ // Runtime system goroutines or threads
+ // (such as those identified by
+ // runtime.isSystemGoroutine). These
+ // should never be labeled.
+ mustNotBeLabeled = true
+ case "gogo", "gosave_systemstack_switch", "racecall":
+ // These are context switch/race
+ // critical that we can't do a full
+ // traceback from. Typically this would
+ // be covered by the runtime check
+ // below, but these symbols don't have
+ // the package name.
+ mayBeLabeled = true
+ }
+
+ if strings.HasPrefix(l.Function.Name, "runtime.") {
+ // There are many places in the runtime
+ // where we can't do a full traceback.
+ // Ideally we'd list them all, but
+ // barring that allow anything in the
+ // runtime, unless explicitly excluded
+ // above.
+ mayBeLabeled = true
+ }
+ }
+ }
+ if mustNotBeLabeled {
+ // If this must not be labeled, then mayBeLabeled hints
+ // are not relevant.
+ mayBeLabeled = false
+ }
+ if mustBeLabeled && !isLabeled {
+ var buf bytes.Buffer
+ fprintStack(&buf, s.Location)
+ t.Errorf("Sample labeled got false want true: %s", buf.String())
+ }
+ if mustNotBeLabeled && isLabeled {
+ var buf bytes.Buffer
+ fprintStack(&buf, s.Location)
+ t.Errorf("Sample labeled got true want false: %s", buf.String())
+ }
+ if isLabeled && !(mayBeLabeled || mustBeLabeled) {
+ var buf bytes.Buffer
+ fprintStack(&buf, s.Location)
+ t.Errorf("Sample labeled got true want false: %s", buf.String())
+ }
+ }
+}
+
+// labelHog is designed to burn CPU time in a way that a high number of CPU
+// samples end up running on systemstack.
+func labelHog(stop chan struct{}, gogc int) {
+ // Regression test for issue 50032. We must give GC an opportunity to
+ // be initially triggered by a labelled goroutine.
+ runtime.GC()
+
+ for i := 0; ; i++ {
+ select {
+ case <-stop:
+ return
+ default:
+ debug.SetGCPercent(gogc)
+ }
+ }
+}
+
+// parallelLabelHog runs GOMAXPROCS goroutines running labelHog.
+func parallelLabelHog(ctx context.Context, dur time.Duration, gogc int) {
+ var wg sync.WaitGroup
+ stop := make(chan struct{})
+ for i := 0; i < runtime.GOMAXPROCS(0); i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ labelHog(stop, gogc)
+ }()
+ }
+
+ time.Sleep(dur)
+ close(stop)
+ wg.Wait()
+}
+
// Check that there is no deadlock when the program receives SIGPROF while in
// 64bit atomics' critical section. Used to happen on mips{,le}. See #20146.
func TestAtomicLoadStore64(t *testing.T) {
@@ -1476,6 +1646,7 @@ func TestTryAdd(t *testing.T) {
testCases := []struct {
name string
input []uint64 // following the input format assumed by profileBuilder.addCPUData.
+ count int // number of records in input.
wantLocs [][]string // ordered location entries with function names.
wantSamples []*profile.Sample // ordered samples, we care only about Value and the profile location IDs.
}{{
@@ -1485,6 +1656,7 @@ func TestTryAdd(t *testing.T) {
3, 0, 500, // hz = 500. Must match the period.
5, 0, 50, inlinedCallerStack[0], inlinedCallerStack[1],
},
+ count: 2,
wantLocs: [][]string{
{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"},
},
@@ -1501,6 +1673,7 @@ func TestTryAdd(t *testing.T) {
7, 0, 10, inlinedCallerStack[0], inlinedCallerStack[1], inlinedCallerStack[0], inlinedCallerStack[1],
5, 0, 20, inlinedCallerStack[0], inlinedCallerStack[1],
},
+ count: 3,
wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
wantSamples: []*profile.Sample{
{Value: []int64{10, 10 * period}, Location: []*profile.Location{{ID: 1}, {ID: 1}}},
@@ -1514,6 +1687,7 @@ func TestTryAdd(t *testing.T) {
// entry. The "stk" entry is actually the count.
4, 0, 0, 4242,
},
+ count: 2,
wantLocs: [][]string{{"runtime/pprof.lostProfileEvent"}},
wantSamples: []*profile.Sample{
{Value: []int64{4242, 4242 * period}, Location: []*profile.Location{{ID: 1}}},
@@ -1532,6 +1706,7 @@ func TestTryAdd(t *testing.T) {
5, 0, 30, inlinedCallerStack[0], inlinedCallerStack[0],
4, 0, 40, inlinedCallerStack[0],
},
+ count: 3,
// inlinedCallerDump shows up here because
// runtime_expandFinalInlineFrame adds it to the stack frame.
wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump"}, {"runtime/pprof.inlinedCallerDump"}},
@@ -1545,6 +1720,7 @@ func TestTryAdd(t *testing.T) {
3, 0, 500, // hz = 500. Must match the period.
9, 0, 10, recursionStack[0], recursionStack[1], recursionStack[2], recursionStack[3], recursionStack[4], recursionStack[5],
},
+ count: 2,
wantLocs: [][]string{
{"runtime/pprof.recursionChainBottom"},
{
@@ -1568,6 +1744,7 @@ func TestTryAdd(t *testing.T) {
5, 0, 50, inlinedCallerStack[0], inlinedCallerStack[1],
4, 0, 60, inlinedCallerStack[0],
},
+ count: 3,
wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
wantSamples: []*profile.Sample{
{Value: []int64{50, 50 * period}, Location: []*profile.Location{{ID: 1}}},
@@ -1580,6 +1757,7 @@ func TestTryAdd(t *testing.T) {
4, 0, 70, inlinedCallerStack[0],
5, 0, 80, inlinedCallerStack[0], inlinedCallerStack[1],
},
+ count: 3,
wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
wantSamples: []*profile.Sample{
{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
@@ -1592,6 +1770,7 @@ func TestTryAdd(t *testing.T) {
3, 0, 500, // hz = 500. Must match the period.
4, 0, 70, inlinedCallerStack[0],
},
+ count: 2,
wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
wantSamples: []*profile.Sample{
{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
@@ -1607,6 +1786,7 @@ func TestTryAdd(t *testing.T) {
// from getting merged into above.
5, 0, 80, inlinedCallerStack[1], inlinedCallerStack[0],
},
+ count: 3,
wantLocs: [][]string{
{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"},
{"runtime/pprof.inlinedCallerDump"},
@@ -1619,7 +1799,7 @@ func TestTryAdd(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- p, err := translateCPUProfile(tc.input)
+ p, err := translateCPUProfile(tc.input, tc.count)
if err != nil {
t.Fatalf("translating profile: %v", err)
}
@@ -1662,7 +1842,8 @@ func TestTimeVDSO(t *testing.T) {
testenv.SkipFlaky(t, 48655)
}
- p := testCPUProfile(t, stackContains, []string{"time.now"}, avoidFunctions(), func(dur time.Duration) {
+ matches := matchAndAvoidStacks(stackContains, []string{"time.now"}, avoidFunctions())
+ p := testCPUProfile(t, matches, func(dur time.Duration) {
t0 := time.Now()
for {
t := time.Now()
diff --git a/src/runtime/pprof/proto.go b/src/runtime/pprof/proto.go
index 54e7a80183..073a076802 100644
--- a/src/runtime/pprof/proto.go
+++ b/src/runtime/pprof/proto.go
@@ -266,8 +266,9 @@ func newProfileBuilder(w io.Writer) *profileBuilder {
}
// addCPUData adds the CPU profiling data to the profile.
-// The data must be a whole number of records,
-// as delivered by the runtime.
+//
+// The data must be a whole number of records, as delivered by the runtime.
+// len(tags) must be equal to the number of records in data.
func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error {
if !b.havePeriod {
// first record is period
@@ -282,6 +283,9 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error
b.period = 1e9 / int64(data[2])
b.havePeriod = true
data = data[3:]
+ // Consume tag slot. Note that there isn't a meaningful tag
+ // value for this record.
+ tags = tags[1:]
}
// Parse CPU samples from the profile.
@@ -306,14 +310,14 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error
if data[0] < 3 || tags != nil && len(tags) < 1 {
return fmt.Errorf("malformed profile")
}
+ if len(tags) < 1 {
+ return fmt.Errorf("mismatched profile records and tags")
+ }
count := data[2]
stk := data[3:data[0]]
data = data[data[0]:]
- var tag unsafe.Pointer
- if tags != nil {
- tag = tags[0]
- tags = tags[1:]
- }
+ tag := tags[0]
+ tags = tags[1:]
if count == 0 && len(stk) == 1 {
// overflow record
@@ -327,6 +331,10 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error
}
b.m.lookup(stk, tag).count += int64(count)
}
+
+ if len(tags) != 0 {
+ return fmt.Errorf("mismatched profile records and tags")
+ }
return nil
}
diff --git a/src/runtime/pprof/proto_test.go b/src/runtime/pprof/proto_test.go
index 4a9749a83f..84a051a536 100644
--- a/src/runtime/pprof/proto_test.go
+++ b/src/runtime/pprof/proto_test.go
@@ -17,16 +17,20 @@ import (
"runtime"
"strings"
"testing"
+ "unsafe"
)
// translateCPUProfile parses binary CPU profiling stack trace data
// generated by runtime.CPUProfile() into a profile struct.
// This is only used for testing. Real conversions stream the
// data into the profileBuilder as it becomes available.
-func translateCPUProfile(data []uint64) (*profile.Profile, error) {
+//
+// count is the number of records in data.
+func translateCPUProfile(data []uint64, count int) (*profile.Profile, error) {
var buf bytes.Buffer
b := newProfileBuilder(&buf)
- if err := b.addCPUData(data, nil); err != nil {
+ tags := make([]unsafe.Pointer, count)
+ if err := b.addCPUData(data, tags); err != nil {
return nil, err
}
b.build()
@@ -36,7 +40,7 @@ func translateCPUProfile(data []uint64) (*profile.Profile, error) {
// fmtJSON returns a pretty-printed JSON form for x.
// It works reasonbly well for printing protocol-buffer
// data structures like profile.Profile.
-func fmtJSON(x interface{}) string {
+func fmtJSON(x any) string {
js, _ := json.MarshalIndent(x, "", "\t")
return string(js)
}
@@ -46,7 +50,7 @@ func TestConvertCPUProfileEmpty(t *testing.T) {
var buf bytes.Buffer
b := []uint64{3, 0, 500} // empty profile at 500 Hz (2ms sample period)
- p, err := translateCPUProfile(b)
+ p, err := translateCPUProfile(b, 1)
if err != nil {
t.Fatalf("translateCPUProfile: %v", err)
}
@@ -120,7 +124,7 @@ func TestConvertCPUProfile(t *testing.T) {
5, 0, 40, uint64(addr2 + 1), uint64(addr2 + 2), // 40 samples in addr2
5, 0, 10, uint64(addr1 + 1), uint64(addr1 + 2), // 10 samples in addr1
}
- p, err := translateCPUProfile(b)
+ p, err := translateCPUProfile(b, 4)
if err != nil {
t.Fatalf("translating profile: %v", err)
}
@@ -429,7 +433,7 @@ func TestEmptyStack(t *testing.T) {
3, 0, 500, // hz = 500
3, 0, 10, // 10 samples with an empty stack trace
}
- _, err := translateCPUProfile(b)
+ _, err := translateCPUProfile(b, 2)
if err != nil {
t.Fatalf("translating profile: %v", err)
}
diff --git a/src/runtime/pprof/uname_linux_test.go b/src/runtime/pprof/uname_linux_test.go
new file mode 100644
index 0000000000..8374c83f74
--- /dev/null
+++ b/src/runtime/pprof/uname_linux_test.go
@@ -0,0 +1,61 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux
+
+package pprof
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "syscall"
+)
+
+var versionRe = regexp.MustCompile(`^(\d+)(?:\.(\d+)(?:\.(\d+))).*$`)
+
+func linuxKernelVersion() (major, minor, patch int, err error) {
+ var uname syscall.Utsname
+ if err := syscall.Uname(&uname); err != nil {
+ return 0, 0, 0, err
+ }
+
+ buf := make([]byte, 0, len(uname.Release))
+ for _, b := range uname.Release {
+ if b == 0 {
+ break
+ }
+ buf = append(buf, byte(b))
+ }
+ rl := string(buf)
+
+ m := versionRe.FindStringSubmatch(rl)
+ if m == nil {
+ return 0, 0, 0, fmt.Errorf("error matching version number in %q", rl)
+ }
+
+ v, err := strconv.ParseInt(m[1], 10, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("error parsing major version %q in %s: %w", m[1], rl, err)
+ }
+ major = int(v)
+
+ if len(m) >= 3 {
+ v, err := strconv.ParseInt(m[2], 10, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("error parsing minor version %q in %s: %w", m[2], rl, err)
+ }
+ minor = int(v)
+ }
+
+ if len(m) >= 4 {
+ v, err := strconv.ParseInt(m[3], 10, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("error parsing patch version %q in %s: %w", m[3], rl, err)
+ }
+ patch = int(v)
+ }
+
+ return
+}
diff --git a/src/runtime/pprof/uname_other_test.go b/src/runtime/pprof/uname_other_test.go
new file mode 100644
index 0000000000..327640755b
--- /dev/null
+++ b/src/runtime/pprof/uname_other_test.go
@@ -0,0 +1,15 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !linux
+
+package pprof
+
+import (
+ "errors"
+)
+
+func linuxKernelVersion() (major, minor, patch int, err error) {
+ return 0, 0, 0, errors.New("not running on linux")
+}
diff --git a/src/runtime/preempt_mips64x.s b/src/runtime/preempt_mips64x.s
index c1249e382e..996b592ae0 100644
--- a/src/runtime/preempt_mips64x.s
+++ b/src/runtime/preempt_mips64x.s
@@ -1,7 +1,6 @@
// Code generated by mkpreempt.go; DO NOT EDIT.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/preempt_mipsx.s b/src/runtime/preempt_mipsx.s
index 70b79e05b9..7b169acd99 100644
--- a/src/runtime/preempt_mipsx.s
+++ b/src/runtime/preempt_mipsx.s
@@ -1,7 +1,6 @@
// Code generated by mkpreempt.go; DO NOT EDIT.
//go:build mips || mipsle
-// +build mips mipsle
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/preempt_ppc64x.s b/src/runtime/preempt_ppc64x.s
index 7ed4021dde..2c4d02edfe 100644
--- a/src/runtime/preempt_ppc64x.s
+++ b/src/runtime/preempt_ppc64x.s
@@ -1,7 +1,6 @@
// Code generated by mkpreempt.go; DO NOT EDIT.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index bf5fa8e4fc..1be7a60830 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -547,6 +547,20 @@ func allgadd(gp *g) {
unlock(&allglock)
}
+// allGsSnapshot returns a snapshot of the slice of all Gs.
+//
+// The world must be stopped or allglock must be held.
+func allGsSnapshot() []*g {
+ assertWorldStoppedOrLockHeld(&allglock)
+
+ // Because the world is stopped or allglock is held, allgadd
+ // cannot happen concurrently with this. allgs grows
+ // monotonically and existing entries never change, so we can
+ // simply return a copy of the slice header. For added safety,
+ // we trim everything past len because that can still change.
+ return allgs[:len(allgs):len(allgs)]
+}
+
// atomicAllG returns &allgs[0] and len(allgs) for use with atomicAllGIndex.
func atomicAllG() (**g, uintptr) {
length := atomic.Loaduintptr(&allglen)
@@ -980,17 +994,18 @@ func casgstatus(gp *g, oldval, newval uint32) {
gp.trackingSeq++
}
if gp.tracking {
- now := nanotime()
if oldval == _Grunnable {
// We transitioned out of runnable, so measure how much
// time we spent in this state and add it to
// runnableTime.
+ now := nanotime()
gp.runnableTime += now - gp.runnableStamp
gp.runnableStamp = 0
}
if newval == _Grunnable {
// We just transitioned into runnable, so record what
// time that happened.
+ now := nanotime()
gp.runnableStamp = now
} else if newval == _Grunning {
// We're transitioning into running, so turn off
@@ -4285,11 +4300,13 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
newg.gopc = callerpc
newg.ancestors = saveAncestors(callergp)
newg.startpc = fn.fn
- if _g_.m.curg != nil {
- newg.labels = _g_.m.curg.labels
- }
if isSystemGoroutine(newg, false) {
atomic.Xadd(&sched.ngsys, +1)
+ } else {
+ // Only user goroutines inherit pprof labels.
+ if _g_.m.curg != nil {
+ newg.labels = _g_.m.curg.labels
+ }
}
// Track initial transition?
newg.trackingSeq = uint8(fastrand())
@@ -4654,7 +4671,6 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
getg().m.mallocing++
var stk [maxCPUProfStack]uintptr
- flags := uint(_TraceJumpStack)
n := 0
if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 {
cgoOff := 0
@@ -4672,12 +4688,12 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
}
// Collect Go stack that leads to the cgo call.
- n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[cgoOff], len(stk)-cgoOff, nil, nil, flags)
+ n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[cgoOff], len(stk)-cgoOff, nil, nil, 0)
if n > 0 {
n += cgoOff
}
} else {
- n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap|flags)
+ n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap|_TraceJumpStack)
}
if n <= 0 {
@@ -4687,10 +4703,10 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
if usesLibcall() && mp.libcallg != 0 && mp.libcallpc != 0 && mp.libcallsp != 0 {
// Libcall, i.e. runtime syscall on windows.
// Collect Go stack that leads to the call.
- n = gentraceback(mp.libcallpc, mp.libcallsp, 0, mp.libcallg.ptr(), 0, &stk[0], len(stk), nil, nil, flags)
+ n = gentraceback(mp.libcallpc, mp.libcallsp, 0, mp.libcallg.ptr(), 0, &stk[0], len(stk), nil, nil, 0)
}
if n == 0 && mp != nil && mp.vdsoSP != 0 {
- n = gentraceback(mp.vdsoPC, mp.vdsoSP, 0, gp, 0, &stk[0], len(stk), nil, nil, flags)
+ n = gentraceback(mp.vdsoPC, mp.vdsoSP, 0, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap|_TraceJumpStack)
}
if n == 0 {
// If all of the above has failed, account it against abstract "System" or "GC".
@@ -4711,7 +4727,14 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
}
if prof.hz != 0 {
- cpuprof.add(gp, stk[:n])
+ // Note: it can happen on Windows that we interrupted a system thread
+ // with no g, so gp could nil. The other nil checks are done out of
+ // caution, but not expected to be nil in practice.
+ var tagPtr *unsafe.Pointer
+ if gp != nil && gp.m != nil && gp.m.curg != nil {
+ tagPtr = &gp.m.curg.labels
+ }
+ cpuprof.add(tagPtr, stk[:n])
}
getg().m.mallocing--
}
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go
index 53cafe8907..719d0d1aee 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -119,6 +119,10 @@ func TestGoroutineParallelism(t *testing.T) {
// since the goroutines can't be stopped/preempted.
// Disable GC for this test (see issue #10958).
defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // SetGCPercent waits until the mark phase is over, but the runtime
+ // also preempts at the start of the sweep phase, so make sure that's
+ // done too. See #45867.
+ runtime.GC()
for try := 0; try < N; try++ {
done := make(chan bool)
x := uint32(0)
@@ -163,6 +167,10 @@ func testGoroutineParallelism2(t *testing.T, load, netpoll bool) {
// since the goroutines can't be stopped/preempted.
// Disable GC for this test (see issue #10958).
defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // SetGCPercent waits until the mark phase is over, but the runtime
+ // also preempts at the start of the sweep phase, so make sure that's
+ // done too. See #45867.
+ runtime.GC()
for try := 0; try < N; try++ {
if load {
// Create P goroutines and wait until they all run.
@@ -623,6 +631,10 @@ func TestSchedLocalQueueEmpty(t *testing.T) {
// If runtime triggers a forced GC during this test then it will deadlock,
// since the goroutines can't be stopped/preempted during spin wait.
defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // SetGCPercent waits until the mark phase is over, but the runtime
+ // also preempts at the start of the sweep phase, so make sure that's
+ // done too. See #45867.
+ runtime.GC()
iters := int(1e5)
if testing.Short() {
@@ -1032,7 +1044,7 @@ func testPreemptionAfterSyscall(t *testing.T, syscallDuration time.Duration) {
interations = 1
}
const (
- maxDuration = 3 * time.Second
+ maxDuration = 5 * time.Second
nroutines = 8
)
@@ -1068,6 +1080,10 @@ func testPreemptionAfterSyscall(t *testing.T, syscallDuration time.Duration) {
}
func TestPreemptionAfterSyscall(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ testenv.SkipFlaky(t, 41015)
+ }
+
for _, i := range []time.Duration{10, 100, 1000} {
d := i * time.Microsecond
t.Run(fmt.Sprint(d), func(t *testing.T) {
diff --git a/src/runtime/race/README b/src/runtime/race/README
index 3b188a0361..d3c55182ef 100644
--- a/src/runtime/race/README
+++ b/src/runtime/race/README
@@ -4,12 +4,12 @@ the LLVM project (https://github.com/llvm/llvm-project/tree/main/compiler-rt).
To update the .syso files use golang.org/x/build/cmd/racebuild.
-race_darwin_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
+race_darwin_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 with https://reviews.llvm.org/D114825 applied and Go 7ccbcc90560468937f02609a43cb39a6e13ff797.
race_freebsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
race_linux_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
race_linux_ppc64le.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
race_netbsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
race_windows_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
race_linux_arm64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b.
-race_darwin_arm64.syso built with LLVM 00da38ce2d36c07f12c287dc515d37bb7bc410e9 and Go fe70a3a0fd31441bcbb9932ecab11a6083cf2119.
+race_darwin_arm64.syso built with LLVM 00da38ce2d36c07f12c287dc515d37bb7bc410e9 with https://reviews.llvm.org/D114825 applied and Go 7ccbcc90560468937f02609a43cb39a6e13ff797.
race_openbsd_amd64.syso built with LLVM fcf6ae2f070eba73074b6ec8d8281e54d29dbeeb and Go 8f2db14cd35bbd674cb2988a508306de6655e425.
diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go
index 46cdfcd0e9..0dcdabe641 100644
--- a/src/runtime/race/output_test.go
+++ b/src/runtime/race/output_test.go
@@ -207,7 +207,7 @@ func TestFail(t *testing.T) {
}
`, []string{`
==================
---- FAIL: TestFail \(0...s\)
+--- FAIL: TestFail \([0-9.]+s\)
.*main_test.go:14: true
.*testing.go:.*: race detected during execution of test
FAIL`}},
@@ -363,7 +363,7 @@ func TestPass(t *testing.T) {
}
`, []string{`
==================
---- FAIL: TestFail \(0...s\)
+--- FAIL: TestFail \([0-9.]+s\)
.*testing.go:.*: race detected during execution of test
FAIL`}},
{"mutex", "run", "", "atexit_sleep_ms=0", `
diff --git a/src/runtime/race/race_darwin_amd64.syso b/src/runtime/race/race_darwin_amd64.syso
index 3f95ecc8ee..6fbe140026 100644
--- a/src/runtime/race/race_darwin_amd64.syso
+++ b/src/runtime/race/race_darwin_amd64.syso
Binary files differ
diff --git a/src/runtime/race/race_darwin_arm64.syso b/src/runtime/race/race_darwin_arm64.syso
index f6eaa62ae3..207099eb1d 100644
--- a/src/runtime/race/race_darwin_arm64.syso
+++ b/src/runtime/race/race_darwin_arm64.syso
Binary files differ
diff --git a/src/runtime/race/race_test.go b/src/runtime/race/race_test.go
index 1677e13986..4fe61683eb 100644
--- a/src/runtime/race/race_test.go
+++ b/src/runtime/race/race_test.go
@@ -187,7 +187,7 @@ func runTests(t *testing.T) ([]byte, error) {
func TestIssue8102(t *testing.T) {
// If this compiles with -race, the test passes.
type S struct {
- x interface{}
+ x any
i int
}
c := make(chan int)
diff --git a/src/runtime/race/testdata/issue12664_test.go b/src/runtime/race/testdata/issue12664_test.go
index c9f790edc8..714e83d318 100644
--- a/src/runtime/race/testdata/issue12664_test.go
+++ b/src/runtime/race/testdata/issue12664_test.go
@@ -56,7 +56,7 @@ func TestRaceIssue12664_3(t *testing.T) {
close(c)
}()
var r MyT
- var i interface{} = r
+ var i any = r
issue12664_3 = i.(MyT)
<-c
}
diff --git a/src/runtime/race/testdata/mop_test.go b/src/runtime/race/testdata/mop_test.go
index b60cabfe86..94b6e58de0 100644
--- a/src/runtime/race/testdata/mop_test.go
+++ b/src/runtime/race/testdata/mop_test.go
@@ -255,7 +255,7 @@ func TestRaceCaseIssue6418(t *testing.T) {
func TestRaceCaseType(t *testing.T) {
var x, y int
- var i interface{} = x
+ var i any = x
c := make(chan int, 1)
go func() {
switch i.(type) {
@@ -270,7 +270,7 @@ func TestRaceCaseType(t *testing.T) {
func TestRaceCaseTypeBody(t *testing.T) {
var x, y int
- var i interface{} = &x
+ var i any = &x
c := make(chan int, 1)
go func() {
switch i := i.(type) {
@@ -288,8 +288,8 @@ func TestRaceCaseTypeIssue5890(t *testing.T) {
// spurious extra instrumentation of the initial interface
// value.
var x, y int
- m := make(map[int]map[int]interface{})
- m[0] = make(map[int]interface{})
+ m := make(map[int]map[int]any)
+ m[0] = make(map[int]any)
c := make(chan int, 1)
go func() {
switch i := m[0][1].(type) {
@@ -758,7 +758,7 @@ func TestRaceStructFieldRW3(t *testing.T) {
}
func TestRaceEfaceWW(t *testing.T) {
- var a, b interface{}
+ var a, b any
ch := make(chan bool, 1)
go func() {
a = 1
@@ -810,7 +810,7 @@ func TestRaceEfaceConv(t *testing.T) {
c := make(chan bool)
v := 0
go func() {
- go func(x interface{}) {
+ go func(x any) {
}(v)
c <- true
}()
@@ -1127,7 +1127,7 @@ func TestRaceRune(t *testing.T) {
func TestRaceEmptyInterface1(t *testing.T) {
c := make(chan bool)
- var x interface{}
+ var x any
go func() {
x = nil
c <- true
@@ -1138,7 +1138,7 @@ func TestRaceEmptyInterface1(t *testing.T) {
func TestRaceEmptyInterface2(t *testing.T) {
c := make(chan bool)
- var x interface{}
+ var x any
go func() {
x = &Point{}
c <- true
@@ -1579,7 +1579,7 @@ func TestRaceAddrExpr(t *testing.T) {
func TestRaceTypeAssert(t *testing.T) {
c := make(chan bool, 1)
x := 0
- var i interface{} = x
+ var i any = x
go func() {
y := 0
i = y
diff --git a/src/runtime/race/testdata/pool_test.go b/src/runtime/race/testdata/pool_test.go
index 161f4b7c23..a96913e381 100644
--- a/src/runtime/race/testdata/pool_test.go
+++ b/src/runtime/race/testdata/pool_test.go
@@ -15,7 +15,7 @@ func TestRacePool(t *testing.T) {
// Repeat so that at least one iteration gets reuse.
for i := 0; i < 10; i++ {
c := make(chan int)
- p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }}
+ p := &sync.Pool{New: func() any { return make([]byte, 10) }}
x := p.Get().([]byte)
x[0] = 1
p.Put(x)
@@ -31,7 +31,7 @@ func TestRacePool(t *testing.T) {
func TestNoRacePool(t *testing.T) {
for i := 0; i < 10; i++ {
- p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }}
+ p := &sync.Pool{New: func() any { return make([]byte, 10) }}
x := p.Get().([]byte)
x[0] = 1
p.Put(x)
diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s
index d42e415dca..f055acf77d 100644
--- a/src/runtime/race_amd64.s
+++ b/src/runtime/race_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build race
-// +build race
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s
index 2b2413b6b7..798e23294a 100644
--- a/src/runtime/race_arm64.s
+++ b/src/runtime/race_arm64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build race
-// +build race
#include "go_asm.h"
#include "funcdata.h"
diff --git a/src/runtime/race_ppc64le.s b/src/runtime/race_ppc64le.s
index 625c81a255..68cc5c8805 100644
--- a/src/runtime/race_ppc64le.s
+++ b/src/runtime/race_ppc64le.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build race
-// +build race
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/rt0_linux_mips64x.s b/src/runtime/rt0_linux_mips64x.s
index fabd8570b5..e9328b7326 100644
--- a/src/runtime/rt0_linux_mips64x.s
+++ b/src/runtime/rt0_linux_mips64x.s
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && (mips64 || mips64le)
-// +build linux
-// +build mips64 mips64le
#include "textflag.h"
diff --git a/src/runtime/rt0_linux_mipsx.s b/src/runtime/rt0_linux_mipsx.s
index 9f5842b51a..3cbb7fc377 100644
--- a/src/runtime/rt0_linux_mipsx.s
+++ b/src/runtime/rt0_linux_mipsx.s
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && (mips || mipsle)
-// +build linux
-// +build mips mipsle
#include "textflag.h"
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index 4a0f489c2f..7e8723e15f 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -153,8 +153,8 @@ func TestGdbPython(t *testing.T) {
}
func TestGdbPythonCgo(t *testing.T) {
- if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" || runtime.GOARCH == "mips64" {
- testenv.SkipFlaky(t, 18784)
+ if strings.HasPrefix(runtime.GOARCH, "mips") {
+ testenv.SkipFlaky(t, 37794)
}
testGdbPython(t, true)
}
@@ -424,7 +424,7 @@ func TestGdbBacktrace(t *testing.T) {
"-ex", "continue",
filepath.Join(dir, "a.exe"),
}
- got, err := exec.Command("gdb", args...).CombinedOutput()
+ got, err := testenv.RunWithTimeout(t, exec.Command("gdb", args...))
t.Logf("gdb output:\n%s", got)
if err != nil {
t.Fatalf("gdb exited with error: %v", err)
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index bfd857e8d5..3eada37840 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -209,7 +209,7 @@ type eface struct {
data unsafe.Pointer
}
-func efaceOf(ep *interface{}) *eface {
+func efaceOf(ep *any) *eface {
return (*eface)(unsafe.Pointer(ep))
}
@@ -255,6 +255,8 @@ func efaceOf(ep *interface{}) *eface {
// so I can't see them ever moving. If we did want to start moving data
// in the GC, we'd need to allocate the goroutine structs from an
// alternate arena. Using guintptr doesn't make that problem any worse.
+// Note that pollDesc.rg, pollDesc.wg also store g in uintptr form,
+// so they would need to be updated too if g's start moving.
type guintptr uintptr
//go:nosplit
@@ -939,7 +941,7 @@ func extendRandom(r []byte, n int) {
}
// A _defer holds an entry on the list of deferred calls.
-// If you add a field here, add code to clear it in freedefer and deferProcStack
+// If you add a field here, add code to clear it in deferProcStack.
// This struct must match the code in cmd/compile/internal/ssagen/ssa.go:deferstruct
// and cmd/compile/internal/ssagen/ssa.go:(*state).call.
// Some defers will be allocated on the stack and some on the heap.
@@ -982,7 +984,7 @@ type _defer struct {
// adjustment takes care of them.
type _panic struct {
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
- arg interface{} // argument to panic
+ arg any // argument to panic
link *_panic // link to earlier panic
pc uintptr // where to return to in runtime if this panic is bypassed
sp unsafe.Pointer // where to return to in runtime if this panic is bypassed
diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go
index 4572a25195..12f261bdd2 100644
--- a/src/runtime/runtime_test.go
+++ b/src/runtime/runtime_test.go
@@ -6,6 +6,7 @@ package runtime_test
import (
"flag"
+ "fmt"
"io"
. "runtime"
"runtime/debug"
@@ -53,8 +54,8 @@ func BenchmarkIfaceCmpNil100(b *testing.B) {
}
}
-var efaceCmp1 interface{}
-var efaceCmp2 interface{}
+var efaceCmp1 any
+var efaceCmp2 any
func BenchmarkEfaceCmpDiff(b *testing.B) {
x := 5
@@ -362,3 +363,78 @@ func TestVersion(t *testing.T) {
t.Fatalf("cr/nl in version: %q", vers)
}
}
+
+func TestTimediv(t *testing.T) {
+ for _, tc := range []struct {
+ num int64
+ div int32
+ ret int32
+ rem int32
+ }{
+ {
+ num: 8,
+ div: 2,
+ ret: 4,
+ rem: 0,
+ },
+ {
+ num: 9,
+ div: 2,
+ ret: 4,
+ rem: 1,
+ },
+ {
+ // Used by runtime.check.
+ num: 12345*1000000000 + 54321,
+ div: 1000000000,
+ ret: 12345,
+ rem: 54321,
+ },
+ {
+ num: 1<<32 - 1,
+ div: 2,
+ ret: 1<<31 - 1, // no overflow.
+ rem: 1,
+ },
+ {
+ num: 1 << 32,
+ div: 2,
+ ret: 1<<31 - 1, // overflow.
+ rem: 0,
+ },
+ {
+ num: 1 << 40,
+ div: 2,
+ ret: 1<<31 - 1, // overflow.
+ rem: 0,
+ },
+ {
+ num: 1<<40 + 1,
+ div: 1 << 10,
+ ret: 1 << 30,
+ rem: 1,
+ },
+ } {
+ name := fmt.Sprintf("%d div %d", tc.num, tc.div)
+ t.Run(name, func(t *testing.T) {
+ // Double check that the inputs make sense using
+ // standard 64-bit division.
+ ret64 := tc.num / int64(tc.div)
+ rem64 := tc.num % int64(tc.div)
+ if ret64 != int64(int32(ret64)) {
+ // Simulate timediv overflow value.
+ ret64 = 1<<31 - 1
+ rem64 = 0
+ }
+ if ret64 != int64(tc.ret) {
+ t.Errorf("%d / %d got ret %d rem %d want ret %d rem %d", tc.num, tc.div, ret64, rem64, tc.ret, tc.rem)
+ }
+
+ var rem int32
+ ret := Timediv(tc.num, tc.div, &rem)
+ if ret != tc.ret || rem != tc.rem {
+ t.Errorf("timediv %d / %d got ret %d rem %d want ret %d rem %d", tc.num, tc.div, ret, rem, tc.ret, tc.rem)
+ }
+ })
+ }
+}
diff --git a/src/runtime/rwmutex_test.go b/src/runtime/rwmutex_test.go
index 291a32ea5e..f15d367b32 100644
--- a/src/runtime/rwmutex_test.go
+++ b/src/runtime/rwmutex_test.go
@@ -55,6 +55,11 @@ func TestParallelRWMutexReaders(t *testing.T) {
// since the goroutines can't be stopped/preempted.
// Disable GC for this test (see issue #10958).
defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // SetGCPercent waits until the mark phase is over, but the runtime
+ // also preempts at the start of the sweep phase, so make sure that's
+ // done too.
+ GC()
+
doTestParallelReaders(1)
doTestParallelReaders(3)
doTestParallelReaders(4)
diff --git a/src/runtime/semasleep_test.go b/src/runtime/semasleep_test.go
index cf4ef18208..d56733c0cf 100644
--- a/src/runtime/semasleep_test.go
+++ b/src/runtime/semasleep_test.go
@@ -7,6 +7,7 @@
package runtime_test
import (
+ "io"
"os/exec"
"syscall"
"testing"
@@ -20,43 +21,83 @@ func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) {
if *flagQuick {
t.Skip("-quick")
}
+ t.Parallel() // Waits for a program to sleep for 1s.
exe, err := buildTestProg(t, "testprog")
if err != nil {
t.Fatal(err)
}
- start := time.Now()
cmd := exec.Command(exe, "After1")
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ t.Fatalf("StdoutPipe: %v", err)
+ }
+ beforeStart := time.Now()
if err := cmd.Start(); err != nil {
t.Fatalf("Failed to start command: %v", err)
}
doneCh := make(chan error, 1)
go func() {
doneCh <- cmd.Wait()
+ close(doneCh)
}()
+ t.Cleanup(func() {
+ cmd.Process.Kill()
+ <-doneCh
+ })
+
+ // Wait for After1 to close its stdout so that we know the runtime's SIGIO
+ // handler is registered.
+ b, err := io.ReadAll(stdout)
+ if len(b) > 0 {
+ t.Logf("read from testprog stdout: %s", b)
+ }
+ if err != nil {
+ t.Fatalf("error reading from testprog: %v", err)
+ }
+
+ // Wait for an arbitrary timeout longer than one second. The subprocess itself
+ // attempts to sleep for one second, but if the machine running the test is
+ // heavily loaded that subprocess may not schedule very quickly even if the
+ // bug remains fixed. (This is fine, because if the bug really is unfixed we
+ // can keep the process hung indefinitely, as long as we signal it often
+ // enough.)
+ timeout := 10 * time.Second
+
+ // The subprocess begins sleeping for 1s after it writes to stdout, so measure
+ // the timeout from here (not from when we started creating the process).
+ // That should reduce noise from process startup overhead.
+ ready := time.Now()
// With the repro running, we can continuously send to it
- // a non-terminal signal such as SIGIO, to spuriously
- // wakeup pthread_cond_timedwait_relative_np.
- unfixedTimer := time.NewTimer(2 * time.Second)
+ // a signal that the runtime considers non-terminal,
+ // such as SIGIO, to spuriously wake up
+ // pthread_cond_timedwait_relative_np.
+ ticker := time.NewTicker(200 * time.Millisecond)
+ defer ticker.Stop()
for {
select {
- case <-time.After(200 * time.Millisecond):
+ case now := <-ticker.C:
+ if now.Sub(ready) > timeout {
+ t.Error("Program failed to return on time and has to be killed, issue #27520 still exists")
+ // Send SIGQUIT to get a goroutine dump.
+ // Stop sending SIGIO so that the program can clean up and actually terminate.
+ cmd.Process.Signal(syscall.SIGQUIT)
+ return
+ }
+
// Send the pesky signal that toggles spinning
// indefinitely if #27520 is not fixed.
cmd.Process.Signal(syscall.SIGIO)
- case <-unfixedTimer.C:
- t.Error("Program failed to return on time and has to be killed, issue #27520 still exists")
- cmd.Process.Signal(syscall.SIGKILL)
- return
-
case err := <-doneCh:
if err != nil {
t.Fatalf("The program returned but unfortunately with an error: %v", err)
}
- if time.Since(start) < 100*time.Millisecond {
+ if time.Since(beforeStart) < 1*time.Second {
+ // The program was supposed to sleep for a full (monotonic) second;
+ // it should not return before that has elapsed.
t.Fatalf("The program stopped too quickly.")
}
return
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index dbcbfc67bc..08f266cc67 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -271,7 +271,24 @@ func setProcessCPUProfilerTimer(hz int32) {
if hz != 0 {
// Enable the Go signal handler if not enabled.
if atomic.Cas(&handlingSig[_SIGPROF], 0, 1) {
- atomic.Storeuintptr(&fwdSig[_SIGPROF], getsig(_SIGPROF))
+ h := getsig(_SIGPROF)
+ // If no signal handler was installed before, then we record
+ // _SIG_IGN here. When we turn off profiling (below) we'll start
+ // ignoring SIGPROF signals. We do this, rather than change
+ // to SIG_DFL, because there may be a pending SIGPROF
+ // signal that has not yet been delivered to some other thread.
+ // If we change to SIG_DFL when turning off profiling, the
+ // program will crash when that SIGPROF is delivered. We assume
+ // that programs that use profiling don't want to crash on a
+ // stray SIGPROF. See issue 19320.
+ // We do the change here instead of when turning off profiling,
+ // because there we may race with a signal handler running
+ // concurrently, in particular, sigfwdgo may observe _SIG_DFL and
+ // die. See issue 43828.
+ if h == _SIG_DFL {
+ h = _SIG_IGN
+ }
+ atomic.Storeuintptr(&fwdSig[_SIGPROF], h)
setsig(_SIGPROF, abi.FuncPCABIInternal(sighandler))
}
@@ -288,21 +305,9 @@ func setProcessCPUProfilerTimer(hz int32) {
// when we enabled profiling. We don't try to handle the case
// of a program that changes the SIGPROF handler while Go
// profiling is enabled.
- //
- // If no signal handler was installed before, then start
- // ignoring SIGPROF signals. We do this, rather than change
- // to SIG_DFL, because there may be a pending SIGPROF
- // signal that has not yet been delivered to some other thread.
- // If we change to SIG_DFL here, the program will crash
- // when that SIGPROF is delivered. We assume that programs
- // that use profiling don't want to crash on a stray SIGPROF.
- // See issue 19320.
if !sigInstallGoHandler(_SIGPROF) {
if atomic.Cas(&handlingSig[_SIGPROF], 1, 0) {
h := atomic.Loaduintptr(&fwdSig[_SIGPROF])
- if h == _SIG_DFL {
- h = _SIG_IGN
- }
setsig(_SIGPROF, h)
}
}
diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go
index b036f3c965..16c36d07f1 100644
--- a/src/runtime/signal_windows.go
+++ b/src/runtime/signal_windows.go
@@ -22,38 +22,6 @@ func disableWER() {
stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
}
-// isWin7 returns true on Windows 7. Otherwise it returns false.
-//
-//go:nosplit
-func isWin7() bool {
- var maj, min, build uint32
- stdcall3(_RtlGetNtVersionNumbers, uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)), uintptr(unsafe.Pointer(&build)))
- return maj < 6 || (maj == 6 && min <= 1)
-}
-
-// enableWERNoUI re-enables Windows error reporting without fault reporting UI.
-//
-// This is marked nosplit since it is used during crash.
-//
-//go:nosplit
-func enableWERNoUI() bool {
- if _WerSetFlags == nil {
- return false
- }
-
- // Disable Fault reporting UI
- const (
- WER_FAULT_REPORTING_NO_UI = 0x0020
- )
- if stdcall1(_WerSetFlags, WER_FAULT_REPORTING_NO_UI) != 0 {
- return false
- }
-
- // re-enable Windows Error Reporting
- stdcall1(_SetErrorMode, 0)
- return true
-}
-
// in sys_windows_386.s and sys_windows_amd64.s
func exceptiontramp()
func firstcontinuetramp()
@@ -140,7 +108,6 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
// Don't go through any more of the Windows handler chain.
// Crash now.
winthrow(info, r, gp)
- exit(2)
}
// After this point, it is safe to grow the stack.
@@ -229,20 +196,6 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
}
winthrow(info, r, gp)
-
- _, _, docrash := gotraceback()
- if docrash {
- // Windows 7 apears to ignore WER_FAULT_REPORTING_NO_UI
- // WerSetFlags API flag. So do not call enableWERNoUI
- // on Windows 7.
- if !isWin7() {
- // trigger crash dump creation
- if enableWERNoUI() {
- return _EXCEPTION_CONTINUE_SEARCH
- }
- }
- }
- exit(2)
return 0 // not reached
}
@@ -250,6 +203,11 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
func winthrow(info *exceptionrecord, r *context, gp *g) {
_g_ := getg()
+ if panicking != 0 { // traceback already printed
+ exit(2)
+ }
+ 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.
@@ -271,16 +229,18 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
_g_.m.throwing = 1
_g_.m.caughtsig.set(gp)
- level, _, _ := gotraceback()
+ level, _, docrash := gotraceback()
if level > 0 {
- // only print traceback when it hasn't been printed
- if tracebackprinted == 0 {
- tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
- tracebackothers(gp)
- tracebackprinted = 1
- }
+ tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
+ tracebackothers(gp)
dumpregs(r)
}
+
+ if docrash {
+ crash()
+ }
+
+ exit(2)
}
func sigpanic() {
@@ -352,17 +312,14 @@ func signame(sig uint32) string {
//go:nosplit
func crash() {
- // When GOTRACEBACK==crash, raise the same exception
- // from kernel32.dll, so that Windows gets a chance
- // to handle the exception by creating a crash dump.
-
- // Get the Exception code that caused the crash
- gp := getg()
- exceptionCode := gp.sig
-
- // RaiseException() here will not be handled in exceptionhandler()
- // because it comes from kernel32.dll
- stdcall4(_RaiseException, uintptr(unsafe.Pointer(&exceptionCode)), 0, 0, 0)
+ // TODO: This routine should do whatever is needed
+ // to make the Windows program abort/crash as it
+ // would if Go was not intercepting signals.
+ // On Unix the routine would remove the custom signal
+ // handler and then raise a signal (like SIGABRT).
+ // Something like that should happen here.
+ // It's okay to leave this empty for now: if crash returns
+ // the ordinary exit-after-panic happens.
}
// gsignalStack is unused on Windows.
diff --git a/src/runtime/sizeof_test.go b/src/runtime/sizeof_test.go
index bbbd1becf7..ebf544ad3b 100644
--- a/src/runtime/sizeof_test.go
+++ b/src/runtime/sizeof_test.go
@@ -17,9 +17,9 @@ func TestSizeof(t *testing.T) {
const _64bit = unsafe.Sizeof(uintptr(0)) == 8
var tests = []struct {
- val interface{} // type as a value
- _32bit uintptr // size on 32bit platforms
- _64bit uintptr // size on 64bit platforms
+ val any // type as a value
+ _32bit uintptr // size on 32bit platforms
+ _64bit uintptr // size on 64bit platforms
}{
{runtime.G{}, 236, 392}, // g, but exported for testing
{runtime.Sudog{}, 56, 88}, // sudog, but exported for testing
diff --git a/src/runtime/softfloat64_test.go b/src/runtime/softfloat64_test.go
index 7347aff8f1..3f53e8bc55 100644
--- a/src/runtime/softfloat64_test.go
+++ b/src/runtime/softfloat64_test.go
@@ -127,7 +127,7 @@ func fromint64sw(f float64) float64 {
var nerr int
-func err(t *testing.T, format string, args ...interface{}) {
+func err(t *testing.T, format string, args ...any) {
t.Errorf(format, args...)
// cut errors off after a while.
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 25a6f5bbb4..edc37d4878 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -1358,7 +1358,7 @@ func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args
var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit
func stkobjinit() {
- var abiRegArgsEface interface{} = abi.RegArgs{}
+ var abiRegArgsEface any = abi.RegArgs{}
abiRegArgsType := efaceOf(&abiRegArgsEface)._type
if abiRegArgsType.kind&kindGCProg != 0 {
throw("abiRegArgsType needs GC Prog, update methodValueCallFrameObjs")
diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go
index 3f02243a1e..1a59086901 100644
--- a/src/runtime/stack_test.go
+++ b/src/runtime/stack_test.go
@@ -7,11 +7,9 @@ package runtime_test
import (
"bytes"
"fmt"
- "os"
"reflect"
"regexp"
. "runtime"
- "strconv"
"strings"
"sync"
"sync/atomic"
@@ -83,12 +81,7 @@ func TestStackGrowth(t *testing.T) {
t.Skip("-quick")
}
- if GOARCH == "wasm" {
- t.Skip("fails on wasm (too slow?)")
- }
-
- // Don't make this test parallel as this makes the 20 second
- // timeout unreliable on slow builders. (See issue #19381.)
+ t.Parallel()
var wg sync.WaitGroup
@@ -102,6 +95,7 @@ func TestStackGrowth(t *testing.T) {
growDuration = time.Since(start)
}()
wg.Wait()
+ t.Log("first growStack took", growDuration)
// in locked goroutine
wg.Add(1)
@@ -114,48 +108,38 @@ func TestStackGrowth(t *testing.T) {
wg.Wait()
// in finalizer
+ var finalizerStart time.Time
+ var started, progress uint32
wg.Add(1)
- go func() {
+ s := new(string) // Must be of a type that avoids the tiny allocator, or else the finalizer might not run.
+ SetFinalizer(s, func(ss *string) {
defer wg.Done()
- done := make(chan bool)
- var startTime time.Time
- var started, progress uint32
- go func() {
- s := new(string)
- SetFinalizer(s, func(ss *string) {
- startTime = time.Now()
- atomic.StoreUint32(&started, 1)
- growStack(&progress)
- done <- true
- })
- s = nil
- done <- true
- }()
- <-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)
- }
- }
+ finalizerStart = time.Now()
+ atomic.StoreUint32(&started, 1)
+ growStack(&progress)
+ })
+ setFinalizerTime := time.Now()
+ s = nil
- select {
- case <-done:
- case <-time.After(timeout):
+ if d, ok := t.Deadline(); ok {
+ // Pad the timeout by an arbitrary 5% to give the AfterFunc time to run.
+ timeout := time.Until(d) * 19 / 20
+ timer := time.AfterFunc(timeout, func() {
+ // Panic — instead of calling t.Error and returning from the test — so
+ // that we get a useful goroutine dump if the test times out, especially
+ // if GOTRACEBACK=system or GOTRACEBACK=crash is set.
if atomic.LoadUint32(&started) == 0 {
- t.Log("finalizer did not start")
+ panic("finalizer did not start")
} else {
- t.Logf("finalizer started %s ago and finished %d iterations", time.Since(startTime), atomic.LoadUint32(&progress))
+ panic(fmt.Sprintf("finalizer started %s ago (%s after registration) and ran %d iterations, but did not return", time.Since(finalizerStart), finalizerStart.Sub(setFinalizerTime), atomic.LoadUint32(&progress)))
}
- t.Log("first growStack took", growDuration)
- t.Error("finalizer did not run")
- return
- }
- }()
+ })
+ defer timer.Stop()
+ }
+
+ GC()
wg.Wait()
+ t.Logf("finalizer started after %s and ran %d iterations in %v", finalizerStart.Sub(setFinalizerTime), atomic.LoadUint32(&progress), time.Since(finalizerStart))
}
// ... and in init
@@ -879,7 +863,7 @@ func deferHeapAndStack(n int) (r int) {
}
// Pass a value to escapeMe to force it to escape.
-var escapeMe = func(x interface{}) {}
+var escapeMe = func(x any) {}
// Test that when F -> G is inlined and F is excluded from stack
// traces, G still appears.
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 3237a6b708..21dd95a397 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -408,7 +408,7 @@ type pcHeader struct {
// moduledata records information about the layout of the executable
// image. It is written by the linker. Any changes here must be
-// matched changes to the code in cmd/internal/ld/symtab.go:symtab.
+// matched changes to the code in cmd/link/internal/ld/symtab.go:symtab.
// moduledata is stored in statically allocated non-pointer memory;
// none of the pointers here are visible to the garbage collector.
type moduledata struct {
diff --git a/src/runtime/sys_aix_ppc64.s b/src/runtime/sys_aix_ppc64.s
index c171c191c0..217ebb8878 100644
--- a/src/runtime/sys_aix_ppc64.s
+++ b/src/runtime/sys_aix_ppc64.s
@@ -25,7 +25,12 @@ TEXT callCfunction<>(SB), NOSPLIT|NOFRAME,$0
// stored in libcall_fn and store the results in libcall struture
// Up to 6 arguments can be passed to this C function
// Called by runtime.asmcgocall
-// It reserves a stack of 288 bytes for the C function.
+// It reserves a stack of 288 bytes for the C function. It must
+// follow AIX convention, thus the first local variable must
+// be stored at the offset 112, after the linker area (48 bytes)
+// and the argument area (64).
+// The AIX convention is described here:
+// https://www.ibm.com/docs/en/aix/7.2?topic=overview-runtime-process-stack
// NOT USING GO CALLING CONVENTION
// runtime.asmsyscall6 is a function descriptor to the real asmsyscall6.
DATA runtime·asmsyscall6+0(SB)/8, $asmsyscall6<>(SB)
@@ -34,7 +39,8 @@ DATA runtime·asmsyscall6+16(SB)/8, $0
GLOBL runtime·asmsyscall6(SB), NOPTR, $24
TEXT asmsyscall6<>(SB),NOSPLIT,$256
- MOVD R3, 48(R1) // Save libcall for later
+ // Save libcall for later
+ MOVD R3, 112(R1)
MOVD libcall_fn(R3), R12
MOVD libcall_args(R3), R9
MOVD 0(R9), R3
@@ -50,7 +56,7 @@ TEXT asmsyscall6<>(SB),NOSPLIT,$256
MOVD 40(R1), R2
// Store result in libcall
- MOVD 48(R1), R5
+ MOVD 112(R1), R5
MOVD R3, (libcall_r1)(R5)
MOVD $-1, R6
CMP R6, R3
diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go
index 0f91685d6c..80dd1a0378 100644
--- a/src/runtime/sys_darwin.go
+++ b/src/runtime/sys_darwin.go
@@ -105,28 +105,38 @@ func syscallNoErr()
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_init(attr *pthreadattr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ return ret
}
func pthread_attr_init_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_getstacksize(attr *pthreadattr, size *uintptr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ KeepAlive(size)
+ return ret
}
func pthread_attr_getstacksize_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_setdetachstate(attr *pthreadattr, state int) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ return ret
}
func pthread_attr_setdetachstate_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ KeepAlive(arg) // Just for consistency. Arg of course needs to be kept alive for the start function.
+ return ret
}
func pthread_create_trampoline()
@@ -156,7 +166,7 @@ func pthread_kill_trampoline()
// mmap is used to do low-level memory allocation via mmap. Don't allow stack
// splits, since this function (used by sysAlloc) is called in a lot of low-level
// parts of the runtime and callers often assume it won't acquire any locks.
-// go:nosplit
+//go:nosplit
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
args := struct {
addr unsafe.Pointer
@@ -175,6 +185,7 @@ func mmap_trampoline()
//go:cgo_unsafe_args
func munmap(addr unsafe.Pointer, n uintptr) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(munmap_trampoline)), unsafe.Pointer(&addr))
+ KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address.
}
func munmap_trampoline()
@@ -182,6 +193,7 @@ func munmap_trampoline()
//go:cgo_unsafe_args
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(madvise_trampoline)), unsafe.Pointer(&addr))
+ KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address.
}
func madvise_trampoline()
@@ -189,13 +201,16 @@ func madvise_trampoline()
//go:cgo_unsafe_args
func mlock(addr unsafe.Pointer, n uintptr) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(mlock_trampoline)), unsafe.Pointer(&addr))
+ KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address.
}
func mlock_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func read(fd int32, p unsafe.Pointer, n int32) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd))
+ KeepAlive(p)
+ return ret
}
func read_trampoline()
@@ -239,14 +254,18 @@ func usleep_no_g(usec uint32) {
//go:nosplit
//go:cgo_unsafe_args
func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd))
+ KeepAlive(p)
+ return ret
}
func write_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func open(name *byte, mode, perm int32) (ret int32) {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name))
+ ret = libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name))
+ KeepAlive(name)
+ return
}
func open_trampoline()
@@ -285,6 +304,8 @@ func walltime_trampoline()
//go:cgo_unsafe_args
func sigaction(sig uint32, new *usigactiont, old *usigactiont) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaction_trampoline)), unsafe.Pointer(&sig))
+ KeepAlive(new)
+ KeepAlive(old)
}
func sigaction_trampoline()
@@ -292,6 +313,8 @@ func sigaction_trampoline()
//go:cgo_unsafe_args
func sigprocmask(how uint32, new *sigset, old *sigset) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(sigprocmask_trampoline)), unsafe.Pointer(&how))
+ KeepAlive(new)
+ KeepAlive(old)
}
func sigprocmask_trampoline()
@@ -306,6 +329,8 @@ func sigaltstack(new *stackt, old *stackt) {
new.ss_size = 32768
}
libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaltstack_trampoline)), unsafe.Pointer(&new))
+ KeepAlive(new)
+ KeepAlive(old)
}
func sigaltstack_trampoline()
@@ -320,20 +345,32 @@ func raiseproc_trampoline()
//go:cgo_unsafe_args
func setitimer(mode int32, new, old *itimerval) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(setitimer_trampoline)), unsafe.Pointer(&mode))
+ KeepAlive(new)
+ KeepAlive(old)
}
func setitimer_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func sysctl(mib *uint32, miblen uint32, oldp *byte, oldlenp *uintptr, newp *byte, newlen uintptr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib))
+ KeepAlive(mib)
+ KeepAlive(oldp)
+ KeepAlive(oldlenp)
+ KeepAlive(newp)
+ return ret
}
func sysctl_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func sysctlbyname(name *byte, oldp *byte, oldlenp *uintptr, newp *byte, newlen uintptr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctlbyname_trampoline)), unsafe.Pointer(&name))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctlbyname_trampoline)), unsafe.Pointer(&name))
+ KeepAlive(name)
+ KeepAlive(oldp)
+ KeepAlive(oldlenp)
+ KeepAlive(newp)
+ return ret
}
func sysctlbyname_trampoline()
@@ -355,56 +392,79 @@ func kqueue_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq))
+ KeepAlive(ch)
+ KeepAlive(ev)
+ KeepAlive(ts)
+ return ret
}
func kevent_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_mutex_init(m *pthreadmutex, attr *pthreadmutexattr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_init_trampoline)), unsafe.Pointer(&m))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_init_trampoline)), unsafe.Pointer(&m))
+ KeepAlive(m)
+ KeepAlive(attr)
+ return ret
}
func pthread_mutex_init_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_mutex_lock(m *pthreadmutex) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_lock_trampoline)), unsafe.Pointer(&m))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_lock_trampoline)), unsafe.Pointer(&m))
+ KeepAlive(m)
+ return ret
}
func pthread_mutex_lock_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_mutex_unlock(m *pthreadmutex) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_unlock_trampoline)), unsafe.Pointer(&m))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_unlock_trampoline)), unsafe.Pointer(&m))
+ KeepAlive(m)
+ return ret
}
func pthread_mutex_unlock_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_cond_init(c *pthreadcond, attr *pthreadcondattr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_init_trampoline)), unsafe.Pointer(&c))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_init_trampoline)), unsafe.Pointer(&c))
+ KeepAlive(c)
+ KeepAlive(attr)
+ return ret
}
func pthread_cond_init_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_cond_wait(c *pthreadcond, m *pthreadmutex) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_wait_trampoline)), unsafe.Pointer(&c))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_wait_trampoline)), unsafe.Pointer(&c))
+ KeepAlive(c)
+ KeepAlive(m)
+ return ret
}
func pthread_cond_wait_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_cond_timedwait_relative_np(c *pthreadcond, m *pthreadmutex, t *timespec) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_timedwait_relative_np_trampoline)), unsafe.Pointer(&c))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_timedwait_relative_np_trampoline)), unsafe.Pointer(&c))
+ KeepAlive(c)
+ KeepAlive(m)
+ KeepAlive(t)
+ return ret
}
func pthread_cond_timedwait_relative_np_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_cond_signal(c *pthreadcond) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_signal_trampoline)), unsafe.Pointer(&c))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_signal_trampoline)), unsafe.Pointer(&c))
+ KeepAlive(c)
+ return ret
}
func pthread_cond_signal_trampoline()
diff --git a/src/runtime/sys_darwin_arm64.go b/src/runtime/sys_darwin_arm64.go
index e6d4c1be48..6170f4fdda 100644
--- a/src/runtime/sys_darwin_arm64.go
+++ b/src/runtime/sys_darwin_arm64.go
@@ -15,7 +15,9 @@ import (
//go:nosplit
//go:cgo_unsafe_args
func g0_pthread_key_create(k *pthreadkey, destructor uintptr) int32 {
- return asmcgocall(unsafe.Pointer(abi.FuncPCABI0(pthread_key_create_trampoline)), unsafe.Pointer(&k))
+ ret := asmcgocall(unsafe.Pointer(abi.FuncPCABI0(pthread_key_create_trampoline)), unsafe.Pointer(&k))
+ KeepAlive(k)
+ return ret
}
func pthread_key_create_trampoline()
diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s
index 65935de99f..ca443b699f 100644
--- a/src/runtime/sys_linux_arm.s
+++ b/src/runtime/sys_linux_arm.s
@@ -456,7 +456,7 @@ finish:
MOVW $1000000000, R3
MULLU R0, R3, (R1, R0)
ADD.S R2, R0
- ADC R4, R1
+ ADC $0, R1 // Add carry bit to upper half.
MOVW R0, ret_lo+0(FP)
MOVW R1, ret_hi+4(FP)
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
index 08e44d671b..0df2597993 100644
--- a/src/runtime/sys_linux_mips64x.s
+++ b/src/runtime/sys_linux_mips64x.s
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && (mips64 || mips64le)
-// +build linux
-// +build mips64 mips64le
//
// System calls and other sys.stuff for mips64, Linux
diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s
index c828431899..2207e9ab98 100644
--- a/src/runtime/sys_linux_mipsx.s
+++ b/src/runtime/sys_linux_mipsx.s
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && (mips || mipsle)
-// +build linux
-// +build mips mipsle
//
// System calls and other sys.stuff for mips, Linux
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index 9347afaf19..dc3d89fae7 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && (ppc64 || ppc64le)
-// +build linux
-// +build ppc64 ppc64le
//
// System calls and other sys.stuff for ppc64, Linux
diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s
index 2d0b894d47..8a0496e807 100644
--- a/src/runtime/sys_netbsd_arm64.s
+++ b/src/runtime/sys_netbsd_arm64.s
@@ -279,8 +279,8 @@ fail:
TEXT sigreturn_tramp<>(SB),NOSPLIT,$-8
MOVD g, R0
SVC $SYS_setcontext
- MOVD $0x4242, R0 // Something failed, return magic number
- SVC $SYS_exit
+ MOVD $0, R0
+ MOVD R0, (R0) // crash
TEXT runtime·sigaction(SB),NOSPLIT,$-8
MOVW sig+0(FP), R0 // arg 1 - signum
diff --git a/src/runtime/sys_openbsd.go b/src/runtime/sys_openbsd.go
index 9f3a25fcf8..c4b8489612 100644
--- a/src/runtime/sys_openbsd.go
+++ b/src/runtime/sys_openbsd.go
@@ -17,35 +17,47 @@ import (
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_init(attr *pthreadattr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ return ret
}
func pthread_attr_init_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_destroy(attr *pthreadattr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_destroy_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_destroy_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ return ret
}
func pthread_attr_destroy_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_getstacksize(attr *pthreadattr, size *uintptr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ KeepAlive(size)
+ return ret
}
func pthread_attr_getstacksize_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_setdetachstate(attr *pthreadattr, state int) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ return ret
}
func pthread_attr_setdetachstate_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr))
+ KeepAlive(attr)
+ KeepAlive(arg) // Just for consistency. Arg of course needs to be kept alive for the start function.
+ return ret
}
func pthread_create_trampoline()
diff --git a/src/runtime/sys_openbsd1.go b/src/runtime/sys_openbsd1.go
index 4b80f60226..d852e3c58a 100644
--- a/src/runtime/sys_openbsd1.go
+++ b/src/runtime/sys_openbsd1.go
@@ -14,7 +14,10 @@ import (
//go:nosplit
//go:cgo_unsafe_args
func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(thrsleep_trampoline)), unsafe.Pointer(&ident))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(thrsleep_trampoline)), unsafe.Pointer(&ident))
+ KeepAlive(tsp)
+ KeepAlive(abort)
+ return ret
}
func thrsleep_trampoline()
diff --git a/src/runtime/sys_openbsd2.go b/src/runtime/sys_openbsd2.go
index 7024cfa86d..4d50b4f6b1 100644
--- a/src/runtime/sys_openbsd2.go
+++ b/src/runtime/sys_openbsd2.go
@@ -45,7 +45,7 @@ func thrkill_trampoline()
// mmap is used to do low-level memory allocation via mmap. Don't allow stack
// splits, since this function (used by sysAlloc) is called in a lot of low-level
// parts of the runtime and callers often assume it won't acquire any locks.
-// go:nosplit
+//go:nosplit
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
args := struct {
addr unsafe.Pointer
@@ -56,6 +56,7 @@ func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (un
ret2 int
}{addr, n, prot, flags, fd, off, nil, 0}
libcCall(unsafe.Pointer(abi.FuncPCABI0(mmap_trampoline)), unsafe.Pointer(&args))
+ KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address.
return args.ret1, args.ret2
}
func mmap_trampoline()
@@ -64,6 +65,7 @@ func mmap_trampoline()
//go:cgo_unsafe_args
func munmap(addr unsafe.Pointer, n uintptr) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(munmap_trampoline)), unsafe.Pointer(&addr))
+ KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address.
}
func munmap_trampoline()
@@ -71,13 +73,16 @@ func munmap_trampoline()
//go:cgo_unsafe_args
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(madvise_trampoline)), unsafe.Pointer(&addr))
+ KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address.
}
func madvise_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func open(name *byte, mode, perm int32) (ret int32) {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name))
+ ret = libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name))
+ KeepAlive(name)
+ return
}
func open_trampoline()
@@ -91,14 +96,18 @@ func close_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func read(fd int32, p unsafe.Pointer, n int32) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd))
+ KeepAlive(p)
+ return ret
}
func read_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd))
+ KeepAlive(p)
+ return ret
}
func write_trampoline()
@@ -121,6 +130,8 @@ func pipe2_trampoline()
//go:cgo_unsafe_args
func setitimer(mode int32, new, old *itimerval) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(setitimer_trampoline)), unsafe.Pointer(&mode))
+ KeepAlive(new)
+ KeepAlive(old)
}
func setitimer_trampoline()
@@ -140,7 +151,12 @@ func usleep_no_g(usec uint32) {
//go:nosplit
//go:cgo_unsafe_args
func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib))
+ KeepAlive(mib)
+ KeepAlive(out)
+ KeepAlive(size)
+ KeepAlive(dst)
+ return ret
}
func sysctl_trampoline()
@@ -158,7 +174,13 @@ func nanotime1() int64 {
clock_id int32
tp unsafe.Pointer
}{_CLOCK_MONOTONIC, unsafe.Pointer(&ts)}
- libcCall(unsafe.Pointer(abi.FuncPCABI0(clock_gettime_trampoline)), unsafe.Pointer(&args))
+ if errno := libcCall(unsafe.Pointer(abi.FuncPCABI0(clock_gettime_trampoline)), unsafe.Pointer(&args)); errno < 0 {
+ // Avoid growing the nosplit stack.
+ systemstack(func() {
+ println("runtime: errno", -errno)
+ throw("clock_gettime failed")
+ })
+ }
return ts.tv_sec*1e9 + int64(ts.tv_nsec)
}
func clock_gettime_trampoline()
@@ -170,7 +192,13 @@ func walltime() (int64, int32) {
clock_id int32
tp unsafe.Pointer
}{_CLOCK_REALTIME, unsafe.Pointer(&ts)}
- libcCall(unsafe.Pointer(abi.FuncPCABI0(clock_gettime_trampoline)), unsafe.Pointer(&args))
+ if errno := libcCall(unsafe.Pointer(abi.FuncPCABI0(clock_gettime_trampoline)), unsafe.Pointer(&args)); errno < 0 {
+ // Avoid growing the nosplit stack.
+ systemstack(func() {
+ println("runtime: errno", -errno)
+ throw("clock_gettime failed")
+ })
+ }
return ts.tv_sec, int32(ts.tv_nsec)
}
@@ -184,7 +212,11 @@ func kqueue_trampoline()
//go:nosplit
//go:cgo_unsafe_args
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 {
- return libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq))
+ ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq))
+ KeepAlive(ch)
+ KeepAlive(ev)
+ KeepAlive(ts)
+ return ret
}
func kevent_trampoline()
@@ -192,6 +224,8 @@ func kevent_trampoline()
//go:cgo_unsafe_args
func sigaction(sig uint32, new *sigactiont, old *sigactiont) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaction_trampoline)), unsafe.Pointer(&sig))
+ KeepAlive(new)
+ KeepAlive(old)
}
func sigaction_trampoline()
@@ -201,6 +235,8 @@ func sigprocmask(how uint32, new *sigset, old *sigset) {
// sigprocmask is called from sigsave, which is called from needm.
// As such, we have to be able to run with no g here.
asmcgocall_no_g(unsafe.Pointer(abi.FuncPCABI0(sigprocmask_trampoline)), unsafe.Pointer(&how))
+ KeepAlive(new)
+ KeepAlive(old)
}
func sigprocmask_trampoline()
@@ -208,6 +244,8 @@ func sigprocmask_trampoline()
//go:cgo_unsafe_args
func sigaltstack(new *stackt, old *stackt) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaltstack_trampoline)), unsafe.Pointer(&new))
+ KeepAlive(new)
+ KeepAlive(old)
}
func sigaltstack_trampoline()
diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s
index 7830b61b7d..890b96b673 100644
--- a/src/runtime/sys_openbsd_386.s
+++ b/src/runtime/sys_openbsd_386.s
@@ -520,8 +520,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0
MOVL BX, 4(SP) // arg 2 - clock_id
CALL libc_clock_gettime(SB)
CMPL AX, $-1
- JNE 2(PC)
- MOVL $0xf1, 0xf1 // crash on failure
+ JNE noerr
+ CALL libc_errno(SB)
+ MOVL (AX), AX
+ NEGL AX // caller expects negative errno
+noerr:
MOVL BP, SP
POPL BP
RET
diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s
index fc89ee6cbb..fc6d5dc387 100644
--- a/src/runtime/sys_openbsd_amd64.s
+++ b/src/runtime/sys_openbsd_amd64.s
@@ -369,8 +369,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0
MOVL 0(DI), DI // arg 1 clock_id
CALL libc_clock_gettime(SB)
TESTL AX, AX
- JEQ 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ JEQ noerr
+ CALL libc_errno(SB)
+ MOVL (AX), AX // errno
+ NEGL AX // caller expects negative errno value
+noerr:
POPQ BP
RET
diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s
index 143fcf0518..a9cb1fbafe 100644
--- a/src/runtime/sys_openbsd_arm.s
+++ b/src/runtime/sys_openbsd_arm.s
@@ -407,9 +407,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0
MOVW 0(R0), R0 // arg 1 clock_id
BL libc_clock_gettime(SB)
CMP $-1, R0
- BNE 3(PC)
- MOVW $0, R8 // crash on failure
- MOVW R8, (R8)
+ BNE noerr
+ BL libc_errno(SB)
+ MOVW (R0), R0 // errno
+ RSB.CS $0, R0 // caller expects negative errno
+noerr:
MOVW R9, R13
RET
diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s
index 9b4acc90a5..3fa7e1ede2 100644
--- a/src/runtime/sys_openbsd_arm64.s
+++ b/src/runtime/sys_openbsd_arm64.s
@@ -359,9 +359,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0
MOVD 0(R0), R0 // arg 1 - clock_id
CALL libc_clock_gettime(SB)
CMP $-1, R0
- BNE 3(PC)
- MOVD $0, R0 // crash on failure
- MOVD R0, (R0)
+ BNE noerr
+ CALL libc_errno(SB)
+ MOVW (R0), R0 // errno
+ NEG R0, R0 // caller expects negative errno value
+noerr:
RET
TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0
diff --git a/src/runtime/syscall_solaris.go b/src/runtime/syscall_solaris.go
index 15be8e1c61..e270e271c0 100644
--- a/src/runtime/syscall_solaris.go
+++ b/src/runtime/syscall_solaris.go
@@ -312,6 +312,8 @@ func syscall_wait4(pid uintptr, wstatus *uint32, options uintptr, rusage unsafe.
entersyscallblock()
asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&call))
exitsyscall()
+ KeepAlive(wstatus)
+ KeepAlive(rusage)
return int(call.r1), call.err
}
diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go
index da181f2a8d..e76b403ade 100644
--- a/src/runtime/syscall_windows.go
+++ b/src/runtime/syscall_windows.go
@@ -422,6 +422,8 @@ func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (hand
}
cgocall(asmstdcallAddr, unsafe.Pointer(c))
+ KeepAlive(filename)
+ KeepAlive(absoluteFilepath)
handle = c.r1
if handle == 0 {
err = c.err
@@ -441,6 +443,7 @@ func syscall_loadlibrary(filename *uint16) (handle, err uintptr) {
c.n = 1
c.args = uintptr(noescape(unsafe.Pointer(&filename)))
cgocall(asmstdcallAddr, unsafe.Pointer(c))
+ KeepAlive(filename)
handle = c.r1
if handle == 0 {
err = c.err
@@ -459,6 +462,7 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint
c.n = 2
c.args = uintptr(noescape(unsafe.Pointer(&handle)))
cgocall(asmstdcallAddr, unsafe.Pointer(c))
+ KeepAlive(procname)
outhandle = c.r1
if outhandle == 0 {
err = c.err
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index 65f74b32fb..034a1d84db 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -288,7 +288,7 @@ func TestCallbackInAnotherThread(t *testing.T) {
}
type cbFunc struct {
- goFunc interface{}
+ goFunc any
}
func (f cbFunc) cName(cdecl bool) string {
@@ -628,6 +628,9 @@ func TestOutputDebugString(t *testing.T) {
}
func TestRaiseException(t *testing.T) {
+ if testenv.Builder() == "windows-amd64-2012" {
+ testenv.SkipFlaky(t, 49681)
+ }
o := runTestProg(t, "testprog", "RaiseException")
if strings.Contains(o, "RaiseException should not return") {
t.Fatalf("RaiseException did not crash program: %v", o)
@@ -770,6 +773,7 @@ func TestSyscallN(t *testing.T) {
for arglen := 0; arglen <= runtime.MaxArgs; arglen++ {
arglen := arglen
t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
+ t.Parallel()
args := make([]string, arglen)
rets := make([]string, arglen+1)
params := make([]uintptr, arglen)
diff --git a/src/runtime/testdata/testprog/badtraceback.go b/src/runtime/testdata/testprog/badtraceback.go
index d558adceec..09aa2b877e 100644
--- a/src/runtime/testdata/testprog/badtraceback.go
+++ b/src/runtime/testdata/testprog/badtraceback.go
@@ -17,6 +17,9 @@ func init() {
func BadTraceback() {
// Disable GC to prevent traceback at unexpected time.
debug.SetGCPercent(-1)
+ // Out of an abundance of caution, also make sure that there are
+ // no GCs actively in progress.
+ runtime.GC()
// Run badLR1 on its own stack to minimize the stack size and
// exercise the stack bounds logic in the hex dump.
diff --git a/src/runtime/testdata/testprog/gc.go b/src/runtime/testdata/testprog/gc.go
index 74732cd9f4..215228ea05 100644
--- a/src/runtime/testdata/testprog/gc.go
+++ b/src/runtime/testdata/testprog/gc.go
@@ -90,7 +90,7 @@ func GCFairness2() {
runtime.GOMAXPROCS(1)
debug.SetGCPercent(1)
var count [3]int64
- var sink [3]interface{}
+ var sink [3]any
for i := range count {
go func(i int) {
for {
@@ -132,81 +132,88 @@ func GCFairness2() {
func GCPhys() {
// This test ensures that heap-growth scavenging is working as intended.
//
- // It sets up a specific scenario: it allocates two pairs of objects whose
- // sizes sum to size. One object in each pair is "small" (though must be
- // large enough to be considered a large object by the runtime) and one is
- // large. The small objects are kept while the large objects are freed,
- // creating two large unscavenged holes in the heap. The heap goal should
- // also be small as a result (so size must be at least as large as the
- // minimum heap size). We then allocate one large object, bigger than both
- // pairs of objects combined. This allocation, because it will tip
- // HeapSys-HeapReleased well above the heap goal, should trigger heap-growth
- // scavenging and scavenge most, if not all, of the large holes we created
- // earlier.
+ // It attempts to construct a sizeable "swiss cheese" heap, with many
+ // allocChunk-sized holes. Then, it triggers a heap growth by trying to
+ // allocate as much memory as would fit in those holes.
+ //
+ // The heap growth should cause a large number of those holes to be
+ // returned to the OS.
+
const (
- // Size must be also large enough to be considered a large
- // object (not in any size-segregated span).
- size = 4 << 20
- split = 64 << 10
- objects = 2
+ // The total amount of memory we're willing to allocate.
+ allocTotal = 32 << 20
// The page cache could hide 64 8-KiB pages from the scavenger today.
maxPageCache = (8 << 10) * 64
-
- // Reduce GOMAXPROCS down to 4 if it's greater. We need to bound the amount
- // of memory held in the page cache because the scavenger can't reach it.
- // The page cache will hold at most maxPageCache of memory per-P, so this
- // bounds the amount of memory hidden from the scavenger to 4*maxPageCache
- // at most.
- maxProcs = 4
)
- // Set GOGC so that this test operates under consistent assumptions.
- debug.SetGCPercent(100)
- procs := runtime.GOMAXPROCS(-1)
- if procs > maxProcs {
- defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
- procs = runtime.GOMAXPROCS(-1)
+
+ // How big the allocations are needs to depend on the page size.
+ // If the page size is too big and the allocations are too small,
+ // they might not be aligned to the physical page size, so the scavenger
+ // will gloss over them.
+ pageSize := os.Getpagesize()
+ var allocChunk int
+ if pageSize <= 8<<10 {
+ allocChunk = 64 << 10
+ } else {
+ allocChunk = 512 << 10
}
- // Save objects which we want to survive, and condemn objects which we don't.
- // Note that we condemn objects in this way and release them all at once in
- // order to avoid having the GC start freeing up these objects while the loop
- // is still running and filling in the holes we intend to make.
- saved := make([][]byte, 0, objects+1)
- condemned := make([][]byte, 0, objects)
- for i := 0; i < 2*objects; i++ {
+ allocs := allocTotal / allocChunk
+
+ // Set GC percent just so this test is a little more consistent in the
+ // face of varying environments.
+ debug.SetGCPercent(100)
+
+ // Set GOMAXPROCS to 1 to minimize the amount of memory held in the page cache,
+ // and to reduce the chance that the background scavenger gets scheduled.
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+
+ // Allocate allocTotal bytes of memory in allocChunk byte chunks.
+ // Alternate between whether the chunk will be held live or will be
+ // condemned to GC to create holes in the heap.
+ saved := make([][]byte, allocs/2+1)
+ condemned := make([][]byte, allocs/2)
+ for i := 0; i < allocs; i++ {
+ b := make([]byte, allocChunk)
if i%2 == 0 {
- saved = append(saved, make([]byte, split))
+ saved = append(saved, b)
} else {
- condemned = append(condemned, make([]byte, size-split))
+ condemned = append(condemned, b)
}
}
- condemned = nil
- // Clean up the heap. This will free up every other object created above
- // (i.e. everything in condemned) creating holes in the heap.
- // Also, if the condemned objects are still being swept, its possible that
- // the scavenging that happens as a result of the next allocation won't see
- // the holes at all. We call runtime.GC() twice here so that when we allocate
- // our large object there's no race with sweeping.
- runtime.GC()
+
+ // Run a GC cycle just so we're at a consistent state.
runtime.GC()
- // Perform one big allocation which should also scavenge any holes.
- //
- // The heap goal will rise after this object is allocated, so it's very
- // important that we try to do all the scavenging in a single allocation
- // that exceeds the heap goal. Otherwise the rising heap goal could foil our
- // test.
- saved = append(saved, make([]byte, objects*size))
- // Clean up the heap again just to put it in a known state.
+
+ // Drop the only reference to all the condemned memory.
+ condemned = nil
+
+ // Clear the condemned memory.
runtime.GC()
+
+ // At this point, the background scavenger is likely running
+ // and could pick up the work, so the next line of code doesn't
+ // end up doing anything. That's fine. What's important is that
+ // this test fails somewhat regularly if the runtime doesn't
+ // scavenge on heap growth, and doesn't fail at all otherwise.
+
+ // Make a large allocation that in theory could fit, but won't
+ // because we turned the heap into swiss cheese.
+ saved = append(saved, make([]byte, allocTotal/2))
+
// heapBacked is an estimate of the amount of physical memory used by
// this test. HeapSys is an estimate of the size of the mapped virtual
// address space (which may or may not be backed by physical pages)
// whereas HeapReleased is an estimate of the amount of bytes returned
// to the OS. Their difference then roughly corresponds to the amount
// of virtual address space that is backed by physical pages.
+ //
+ // heapBacked also subtracts out maxPageCache bytes of memory because
+ // this is memory that may be hidden from the scavenger per-P. Since
+ // GOMAXPROCS=1 here, subtracting it out once is fine.
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
- heapBacked := stats.HeapSys - stats.HeapReleased
+ heapBacked := stats.HeapSys - stats.HeapReleased - maxPageCache
// If heapBacked does not exceed the heap goal by more than retainExtraPercent
// then the scavenger is working as expected; the newly-created holes have been
// scavenged immediately as part of the allocations which cannot fit in the holes.
@@ -216,19 +223,14 @@ func GCPhys() {
// to other allocations that happen during this test we may still see some physical
// memory over-use.
overuse := (float64(heapBacked) - float64(stats.HeapAlloc)) / float64(stats.HeapAlloc)
- // Compute the threshold.
+ // Check against our overuse threshold, which is what the scavenger always reserves
+ // to encourage allocation of memory that doesn't need to be faulted in.
//
- // In theory, this threshold should just be zero, but that's not possible in practice.
- // Firstly, the runtime's page cache can hide up to maxPageCache of free memory from the
- // scavenger per P. To account for this, we increase the threshold by the ratio between the
- // total amount the runtime could hide from the scavenger to the amount of memory we expect
- // to be able to scavenge here, which is (size-split)*objects. This computation is the crux
- // GOMAXPROCS above; if GOMAXPROCS is too high the threshold just becomes 100%+ since the
- // amount of memory being allocated is fixed. Then we add 5% to account for noise, such as
- // other allocations this test may have performed that we don't explicitly account for The
- // baseline threshold here is around 11% for GOMAXPROCS=1, capping out at around 30% for
- // GOMAXPROCS=4.
- threshold := 0.05 + float64(procs)*maxPageCache/float64((size-split)*objects)
+ // Add additional slack in case the page size is large and the scavenger
+ // can't reach that memory because it doesn't constitute a complete aligned
+ // physical page. Assume the worst case: a full physical page out of each
+ // allocation.
+ threshold := 0.1 + float64(pageSize)/float64(allocChunk)
if overuse <= threshold {
fmt.Println("OK")
return
@@ -243,6 +245,7 @@ func GCPhys() {
"(alloc: %d, goal: %d, sys: %d, rel: %d, objs: %d)\n", threshold*100, overuse*100,
stats.HeapAlloc, stats.NextGC, stats.HeapSys, stats.HeapReleased, len(saved))
runtime.KeepAlive(saved)
+ runtime.KeepAlive(condemned)
}
// Test that defer closure is correctly scanned when the stack is scanned.
@@ -263,9 +266,9 @@ func DeferLiveness() {
}
//go:noinline
-func escape(x interface{}) { sink2 = x; sink2 = nil }
+func escape(x any) { sink2 = x; sink2 = nil }
-var sink2 interface{}
+var sink2 any
// Test zombie object detection and reporting.
func GCZombie() {
diff --git a/src/runtime/testdata/testprog/preempt.go b/src/runtime/testdata/testprog/preempt.go
index 1c74d0e435..fb6755a372 100644
--- a/src/runtime/testdata/testprog/preempt.go
+++ b/src/runtime/testdata/testprog/preempt.go
@@ -20,6 +20,10 @@ func AsyncPreempt() {
runtime.GOMAXPROCS(1)
// Disable GC so we have complete control of what we're testing.
debug.SetGCPercent(-1)
+ // Out of an abundance of caution, also make sure that there are
+ // no GCs actively in progress. The sweep phase of a GC cycle
+ // for instance tries to preempt Ps at the very beginning.
+ runtime.GC()
// Start a goroutine with no sync safe-points.
var ready, ready2 uint32
diff --git a/src/runtime/testdata/testprog/signal.go b/src/runtime/testdata/testprog/signal.go
index 417e105c68..cc5ac8af58 100644
--- a/src/runtime/testdata/testprog/signal.go
+++ b/src/runtime/testdata/testprog/signal.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows && !plan9
// +build !windows,!plan9
package main
diff --git a/src/runtime/testdata/testprog/sleep.go b/src/runtime/testdata/testprog/sleep.go
index 86e2f6cfe6..b230e60181 100644
--- a/src/runtime/testdata/testprog/sleep.go
+++ b/src/runtime/testdata/testprog/sleep.go
@@ -4,7 +4,10 @@
package main
-import "time"
+import (
+ "os"
+ "time"
+)
// for golang.org/issue/27250
@@ -13,5 +16,7 @@ func init() {
}
func After1() {
+ os.Stdout.WriteString("ready\n")
+ os.Stdout.Close()
<-time.After(1 * time.Second)
}
diff --git a/src/runtime/testdata/testprog/syscalls_none.go b/src/runtime/testdata/testprog/syscalls_none.go
index 7f8ded3994..068bb59af3 100644
--- a/src/runtime/testdata/testprog/syscalls_none.go
+++ b/src/runtime/testdata/testprog/syscalls_none.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !linux
// +build !linux
package main
diff --git a/src/runtime/testdata/testprogcgo/callback.go b/src/runtime/testdata/testprogcgo/callback.go
index be0409f39d..25f07159b8 100644
--- a/src/runtime/testdata/testprogcgo/callback.go
+++ b/src/runtime/testdata/testprogcgo/callback.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
@@ -65,7 +66,7 @@ func grow1(x, sum *int) int {
func CgoCallbackGC() {
P := 100
- if os.Getenv("RUNTIME_TESTING_SHORT") != "" {
+ if os.Getenv("RUNTIME_TEST_SHORT") != "" {
P = 10
}
done := make(chan bool)
diff --git a/src/runtime/testdata/testprogcgo/catchpanic.go b/src/runtime/testdata/testprogcgo/catchpanic.go
index 55a606d1bc..c722d40a19 100644
--- a/src/runtime/testdata/testprogcgo/catchpanic.go
+++ b/src/runtime/testdata/testprogcgo/catchpanic.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/dropm.go b/src/runtime/testdata/testprogcgo/dropm.go
index 9e782f504f..700b7fa0c2 100644
--- a/src/runtime/testdata/testprogcgo/dropm.go
+++ b/src/runtime/testdata/testprogcgo/dropm.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
// Test that a sequence of callbacks from C to Go get the same m.
diff --git a/src/runtime/testdata/testprogcgo/eintr.go b/src/runtime/testdata/testprogcgo/eintr.go
index 1722a75eb9..b35b280a76 100644
--- a/src/runtime/testdata/testprogcgo/eintr.go
+++ b/src/runtime/testdata/testprogcgo/eintr.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/exec.go b/src/runtime/testdata/testprogcgo/exec.go
index 15723c7369..c268bcd9b2 100644
--- a/src/runtime/testdata/testprogcgo/exec.go
+++ b/src/runtime/testdata/testprogcgo/exec.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/gprof.go b/src/runtime/testdata/testprogcgo/gprof.go
new file mode 100644
index 0000000000..d453b4d0ce
--- /dev/null
+++ b/src/runtime/testdata/testprogcgo/gprof.go
@@ -0,0 +1,46 @@
+// Copyright 2021 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
+
+// Test taking a goroutine profile with C traceback.
+
+/*
+// Defined in gprof_c.c.
+void CallGoSleep(void);
+void gprofCgoTraceback(void* parg);
+void gprofCgoContext(void* parg);
+*/
+import "C"
+
+import (
+ "fmt"
+ "io"
+ "runtime"
+ "runtime/pprof"
+ "time"
+ "unsafe"
+)
+
+func init() {
+ register("GoroutineProfile", GoroutineProfile)
+}
+
+func GoroutineProfile() {
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.gprofCgoTraceback), unsafe.Pointer(C.gprofCgoContext), nil)
+
+ go C.CallGoSleep()
+ go C.CallGoSleep()
+ go C.CallGoSleep()
+ time.Sleep(1 * time.Second)
+
+ prof := pprof.Lookup("goroutine")
+ prof.WriteTo(io.Discard, 1)
+ fmt.Println("OK")
+}
+
+//export GoSleep
+func GoSleep() {
+ time.Sleep(time.Hour)
+}
diff --git a/src/runtime/testdata/testprogcgo/gprof_c.c b/src/runtime/testdata/testprogcgo/gprof_c.c
new file mode 100644
index 0000000000..5c7cd77022
--- /dev/null
+++ b/src/runtime/testdata/testprogcgo/gprof_c.c
@@ -0,0 +1,30 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The C definitions for gprof.go. That file uses //export so
+// it can't put function definitions in the "C" import comment.
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// Functions exported from Go.
+extern void GoSleep();
+
+struct cgoContextArg {
+ uintptr_t context;
+};
+
+void gprofCgoContext(void *arg) {
+ ((struct cgoContextArg*)arg)->context = 1;
+}
+
+void gprofCgoTraceback(void *arg) {
+ // spend some time here so the P is more likely to be retaken.
+ volatile int i;
+ for (i = 0; i < 123456789; i++);
+}
+
+void CallGoSleep() {
+ GoSleep();
+}
diff --git a/src/runtime/testdata/testprogcgo/lockosthread.go b/src/runtime/testdata/testprogcgo/lockosthread.go
index 36423d9eb0..8fcea35f52 100644
--- a/src/runtime/testdata/testprogcgo/lockosthread.go
+++ b/src/runtime/testdata/testprogcgo/lockosthread.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/needmdeadlock.go b/src/runtime/testdata/testprogcgo/needmdeadlock.go
index 5a9c359006..b95ec77468 100644
--- a/src/runtime/testdata/testprogcgo/needmdeadlock.go
+++ b/src/runtime/testdata/testprogcgo/needmdeadlock.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/numgoroutine.go b/src/runtime/testdata/testprogcgo/numgoroutine.go
index 5bdfe52ed4..1b9f202f46 100644
--- a/src/runtime/testdata/testprogcgo/numgoroutine.go
+++ b/src/runtime/testdata/testprogcgo/numgoroutine.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/pprof.go b/src/runtime/testdata/testprogcgo/pprof.go
index 3b73fa0bdd..8870d0c415 100644
--- a/src/runtime/testdata/testprogcgo/pprof.go
+++ b/src/runtime/testdata/testprogcgo/pprof.go
@@ -29,8 +29,6 @@ void cpuHog() {
void cpuHog2() {
}
-static int cpuHogCount;
-
struct cgoTracebackArg {
uintptr_t context;
uintptr_t sigContext;
@@ -47,13 +45,6 @@ void pprofCgoTraceback(void* parg) {
arg->buf[0] = (uintptr_t)(cpuHog) + 0x10;
arg->buf[1] = (uintptr_t)(cpuHog2) + 0x4;
arg->buf[2] = 0;
- ++cpuHogCount;
-}
-
-// getCpuHogCount fetches the number of times we've seen cpuHog in the
-// traceback.
-int getCpuHogCount() {
- return cpuHogCount;
}
*/
import "C"
@@ -86,7 +77,7 @@ func CgoPprof() {
}
t0 := time.Now()
- for C.getCpuHogCount() < 2 && time.Since(t0) < time.Second {
+ for time.Since(t0) < time.Second {
C.cpuHog()
}
diff --git a/src/runtime/testdata/testprogcgo/raceprof.go b/src/runtime/testdata/testprogcgo/raceprof.go
index f7ca629789..c098e16196 100644
--- a/src/runtime/testdata/testprogcgo/raceprof.go
+++ b/src/runtime/testdata/testprogcgo/raceprof.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build (linux && amd64) || (freebsd && amd64)
// +build linux,amd64 freebsd,amd64
package main
diff --git a/src/runtime/testdata/testprogcgo/racesig.go b/src/runtime/testdata/testprogcgo/racesig.go
index a079b3fd1a..9352679714 100644
--- a/src/runtime/testdata/testprogcgo/racesig.go
+++ b/src/runtime/testdata/testprogcgo/racesig.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build (linux && amd64) || (freebsd && amd64)
// +build linux,amd64 freebsd,amd64
package main
diff --git a/src/runtime/testdata/testprogcgo/segv.go b/src/runtime/testdata/testprogcgo/segv.go
index 3237a8c69c..0632475228 100644
--- a/src/runtime/testdata/testprogcgo/segv.go
+++ b/src/runtime/testdata/testprogcgo/segv.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
diff --git a/src/runtime/testdata/testprogcgo/sigstack.go b/src/runtime/testdata/testprogcgo/sigstack.go
index 21b668d6c0..12ca661033 100644
--- a/src/runtime/testdata/testprogcgo/sigstack.go
+++ b/src/runtime/testdata/testprogcgo/sigstack.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
// Test handling of Go-allocated signal stacks when calling from
diff --git a/src/runtime/testdata/testprogcgo/threadpanic.go b/src/runtime/testdata/testprogcgo/threadpanic.go
index f9b48a9026..2d24fe68ea 100644
--- a/src/runtime/testdata/testprogcgo/threadpanic.go
+++ b/src/runtime/testdata/testprogcgo/threadpanic.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9
// +build !plan9
package main
diff --git a/src/runtime/testdata/testprogcgo/threadpprof.go b/src/runtime/testdata/testprogcgo/threadpprof.go
index feb774ba59..ec5e750da9 100644
--- a/src/runtime/testdata/testprogcgo/threadpprof.go
+++ b/src/runtime/testdata/testprogcgo/threadpprof.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !plan9 && !windows
// +build !plan9,!windows
package main
@@ -33,8 +34,6 @@ void cpuHogThread() {
void cpuHogThread2() {
}
-static int cpuHogThreadCount;
-
struct cgoTracebackArg {
uintptr_t context;
uintptr_t sigContext;
@@ -49,13 +48,6 @@ void pprofCgoThreadTraceback(void* parg) {
arg->buf[0] = (uintptr_t)(cpuHogThread) + 0x10;
arg->buf[1] = (uintptr_t)(cpuHogThread2) + 0x4;
arg->buf[2] = 0;
- __sync_add_and_fetch(&cpuHogThreadCount, 1);
-}
-
-// getCPUHogThreadCount fetches the number of times we've seen cpuHogThread
-// in the traceback.
-int getCPUHogThreadCount() {
- return __sync_add_and_fetch(&cpuHogThreadCount, 0);
}
static void* cpuHogDriver(void* arg __attribute__ ((unused))) {
@@ -73,6 +65,7 @@ void runCPUHogThread(void) {
import "C"
import (
+ "context"
"fmt"
"os"
"runtime"
@@ -107,12 +100,16 @@ func pprofThread() {
os.Exit(2)
}
- C.runCPUHogThread()
+ // This goroutine may receive a profiling signal while creating the C-owned
+ // thread. If it does, the SetCgoTraceback handler will make the leaf end of
+ // the stack look almost (but not exactly) like the stacks the test case is
+ // trying to find. Attach a profiler label so the test can filter out those
+ // confusing samples.
+ pprof.Do(context.Background(), pprof.Labels("ignore", "ignore"), func(ctx context.Context) {
+ C.runCPUHogThread()
+ })
- t0 := time.Now()
- for C.getCPUHogThreadCount() < 2 && time.Since(t0) < time.Second {
- time.Sleep(100 * time.Millisecond)
- }
+ time.Sleep(1 * time.Second)
pprof.StopCPUProfile()
diff --git a/src/runtime/testdata/testprogcgo/threadprof.go b/src/runtime/testdata/testprogcgo/threadprof.go
index 2d4c1039fb..d62d4b4be8 100644
--- a/src/runtime/testdata/testprogcgo/threadprof.go
+++ b/src/runtime/testdata/testprogcgo/threadprof.go
@@ -2,21 +2,22 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// We only build this file with the tag "threadprof", since it starts
-// a thread running a busy loop at constructor time.
-
+//go:build !plan9 && !windows
// +build !plan9,!windows
-// +build threadprof
package main
/*
#include <stdint.h>
+#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
volatile int32_t spinlock;
+// Note that this thread is only started if GO_START_SIGPROF_THREAD
+// is set in the environment, which is only done when running the
+// CgoExternalThreadSIGPROF test.
static void *thread1(void *p) {
(void)p;
while (spinlock == 0)
@@ -26,9 +27,13 @@ static void *thread1(void *p) {
return NULL;
}
+// This constructor function is run when the program starts.
+// It is used for the CgoExternalThreadSIGPROF test.
__attribute__((constructor)) void issue9456() {
- pthread_t tid;
- pthread_create(&tid, 0, thread1, NULL);
+ if (getenv("GO_START_SIGPROF_THREAD") != NULL) {
+ pthread_t tid;
+ pthread_create(&tid, 0, thread1, NULL);
+ }
}
void **nullptr;
diff --git a/src/runtime/testdata/testprognet/signal.go b/src/runtime/testdata/testprognet/signal.go
index 4d2de79d97..dfa2e10e7a 100644
--- a/src/runtime/testdata/testprognet/signal.go
+++ b/src/runtime/testdata/testprognet/signal.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows && !plan9
// +build !windows,!plan9
// This is in testprognet instead of testprog because testprog
diff --git a/src/runtime/testdata/testprognet/signalexec.go b/src/runtime/testdata/testprognet/signalexec.go
index 4a988ef6c1..62ebce7176 100644
--- a/src/runtime/testdata/testprognet/signalexec.go
+++ b/src/runtime/testdata/testprognet/signalexec.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build darwin dragonfly freebsd linux netbsd openbsd
// This is in testprognet instead of testprog because testprog
diff --git a/src/runtime/testdata/testwinlib/main.c b/src/runtime/testdata/testwinlib/main.c
index e84a32f753..c3fe3cb071 100644
--- a/src/runtime/testdata/testwinlib/main.c
+++ b/src/runtime/testdata/testwinlib/main.c
@@ -41,17 +41,20 @@ int main()
if (NULL == exceptionHandlerHandle)
{
printf("cannot add vectored exception handler\n");
+ fflush(stdout);
return 2;
}
void *continueHandlerHandle = AddVectoredContinueHandler(0, customContinueHandlder);
if (NULL == continueHandlerHandle)
{
printf("cannot add vectored continue handler\n");
+ fflush(stdout);
return 2;
}
CallMeBack(throwFromC);
RemoveVectoredContinueHandler(continueHandlerHandle);
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
printf("exceptionCount: %d\ncontinueCount: %d\n", exceptionCount, continueCount);
+ fflush(stdout);
return 0;
-} \ No newline at end of file
+}
diff --git a/src/runtime/testdata/testwinlib/main.go b/src/runtime/testdata/testwinlib/main.go
index 400eaa1c82..025ef913e5 100644
--- a/src/runtime/testdata/testwinlib/main.go
+++ b/src/runtime/testdata/testwinlib/main.go
@@ -1,3 +1,4 @@
+//go:build windows && cgo
// +build windows,cgo
package main
diff --git a/src/runtime/time.go b/src/runtime/time.go
index 46e9a8c2ab..a9ad620776 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -28,8 +28,8 @@ type timer struct {
// when must be positive on an active timer.
when int64
period int64
- f func(interface{}, uintptr)
- arg interface{}
+ f func(any, uintptr)
+ arg any
seq uintptr
// What to set the when field to in timerModifiedXX status.
@@ -232,14 +232,14 @@ func resetTimer(t *timer, when int64) bool {
// modTimer modifies an existing timer.
//go:linkname modTimer time.modTimer
-func modTimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {
+func modTimer(t *timer, when, period int64, f func(any, uintptr), arg any, seq uintptr) {
modtimer(t, when, period, f, arg, seq)
}
// Go runtime.
// Ready the goroutine arg.
-func goroutineReady(arg interface{}, seq uintptr) {
+func goroutineReady(arg any, seq uintptr) {
goready(arg.(*g), 0)
}
@@ -421,7 +421,7 @@ func dodeltimer0(pp *p) {
// modtimer modifies an existing timer.
// This is called by the netpoll code or time.Ticker.Reset or time.Timer.Reset.
// Reports whether the timer was modified before it was run.
-func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) bool {
+func modtimer(t *timer, when, period int64, f func(any, uintptr), arg any, seq uintptr) bool {
if when <= 0 {
throw("timer when must be positive")
}
diff --git a/src/runtime/time_fake.go b/src/runtime/time_fake.go
index 107f6be335..b5e0463588 100644
--- a/src/runtime/time_fake.go
+++ b/src/runtime/time_fake.go
@@ -41,6 +41,10 @@ func time_now() (sec int64, nsec int32, mono int64) {
return faketime / 1e9, int32(faketime % 1e9), faketime
}
+// write is like the Unix write system call.
+// We have to avoid write barriers to avoid potential deadlock
+// on write calls.
+//go:nowritebarrierrec
func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
if !(fd == 1 || fd == 2) {
// Do an ordinary write.
diff --git a/src/runtime/time_linux_amd64.s b/src/runtime/time_linux_amd64.s
index 67cfdd8fdf..1416d23230 100644
--- a/src/runtime/time_linux_amd64.s
+++ b/src/runtime/time_linux_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !faketime
-// +build !faketime
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/time_windows_386.s b/src/runtime/time_windows_386.s
index 19ce6910d7..b8b636ef30 100644
--- a/src/runtime/time_windows_386.s
+++ b/src/runtime/time_windows_386.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !faketime
-// +build !faketime
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/time_windows_amd64.s b/src/runtime/time_windows_amd64.s
index 70f6a008cd..226f2b5136 100644
--- a/src/runtime/time_windows_amd64.s
+++ b/src/runtime/time_windows_amd64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !faketime
-// +build !faketime
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/time_windows_arm.s b/src/runtime/time_windows_arm.s
index 6552d75ff1..711af88307 100644
--- a/src/runtime/time_windows_arm.s
+++ b/src/runtime/time_windows_arm.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !faketime
-// +build !faketime
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/time_windows_arm64.s b/src/runtime/time_windows_arm64.s
index ef5b848473..e0c7d28e15 100644
--- a/src/runtime/time_windows_arm64.s
+++ b/src/runtime/time_windows_arm64.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !faketime
-// +build !faketime
#include "go_asm.h"
#include "textflag.h"
diff --git a/src/runtime/tls_arm.s b/src/runtime/tls_arm.s
index 879caac9e1..83fd37e6ec 100644
--- a/src/runtime/tls_arm.s
+++ b/src/runtime/tls_arm.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !windows
-// +build !windows
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/tls_mips64x.s b/src/runtime/tls_mips64x.s
index 779d64ba31..ec2748e5b2 100644
--- a/src/runtime/tls_mips64x.s
+++ b/src/runtime/tls_mips64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips64 || mips64le
-// +build mips64 mips64le
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/tls_mipsx.s b/src/runtime/tls_mipsx.s
index ada8d06a9e..acc3eb5a17 100644
--- a/src/runtime/tls_mipsx.s
+++ b/src/runtime/tls_mipsx.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build mips || mipsle
-// +build mips mipsle
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/tls_ppc64x.s b/src/runtime/tls_ppc64x.s
index 7e935d0eb2..17aec9fc1e 100644
--- a/src/runtime/tls_ppc64x.s
+++ b/src/runtime/tls_ppc64x.s
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ppc64 || ppc64le
-// +build ppc64 ppc64le
#include "go_asm.h"
#include "go_tls.h"
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 5b14a5f553..71a29d4316 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -426,6 +426,9 @@ func ReadTrace() []byte {
trace.footerWritten = true
// Use float64 because (trace.ticksEnd - trace.ticksStart) * 1e9 can overflow int64.
freq := float64(trace.ticksEnd-trace.ticksStart) * 1e9 / float64(trace.timeEnd-trace.timeStart) / traceTickDiv
+ if freq <= 0 {
+ throw("trace: ReadTrace got invalid frequency")
+ }
trace.lockOwner = nil
unlock(&trace.lock)
var data []byte
diff --git a/src/runtime/trace/annotation.go b/src/runtime/trace/annotation.go
index 6e18bfb755..d05b5e2261 100644
--- a/src/runtime/trace/annotation.go
+++ b/src/runtime/trace/annotation.go
@@ -98,7 +98,7 @@ func Log(ctx context.Context, category, message string) {
}
// Logf is like Log, but the value is formatted using the specified format spec.
-func Logf(ctx context.Context, category, format string, args ...interface{}) {
+func Logf(ctx context.Context, category, format string, args ...any) {
if IsEnabled() {
// Ideally this should be just Log, but that will
// add one more frame in the stack trace.
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 36627a6735..73bd0e11a9 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -96,6 +96,20 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
}
+ // runtime/internal/atomic functions call into kernel helpers on
+ // arm < 7. See runtime/internal/atomic/sys_linux_arm.s.
+ //
+ // Start in the caller's frame.
+ if GOARCH == "arm" && goarm < 7 && GOOS == "linux" && frame.pc&0xffff0000 == 0xffff0000 {
+ // Note that the calls are simple BL without pushing the return
+ // address, so we use LR directly.
+ //
+ // The kernel helpers are frameless leaf functions, so SP and
+ // LR are not touched.
+ frame.pc = frame.lr
+ frame.lr = 0
+ }
+
f := findfunc(frame.pc)
if !f.valid() {
if callback != nil || printing {
diff --git a/src/runtime/zcallback_windows.s b/src/runtime/zcallback_windows.s
index 561527c90d..bd23d71333 100644
--- a/src/runtime/zcallback_windows.s
+++ b/src/runtime/zcallback_windows.s
@@ -1,7 +1,6 @@
// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
//go:build 386 || amd64
-// +build 386 amd64
// runtime·callbackasm is called by external code to
// execute Go implemented callback function. It is not