diff options
author | Michael Stapelberg <michael@stapelberg.de> | 2013-03-23 11:28:53 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-23 11:28:53 +0100 |
commit | b39e15dde5ec7b96c15da9faf4ab5892501c1aae (patch) | |
tree | 718cede1f6ca97d082c6c40b7dc3f4f6148253c0 /src/pkg/runtime | |
parent | 04b08da9af0c450d645ab7389d1467308cfc2db8 (diff) | |
download | golang-upstream/1.1_hg20130323.tar.gz |
Imported Upstream version 1.1~hg20130323upstream/1.1_hg20130323
Diffstat (limited to 'src/pkg/runtime')
157 files changed, 5814 insertions, 3595 deletions
diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c index ad85b43ae..2dc821256 100644 --- a/src/pkg/runtime/alg.c +++ b/src/pkg/runtime/alg.c @@ -8,6 +8,8 @@ #define M0 (sizeof(uintptr)==4 ? 2860486313UL : 33054211828000289ULL) #define M1 (sizeof(uintptr)==4 ? 3267000013UL : 23344194077549503ULL) +static bool use_aeshash; + /* * map and chan helpers for * dealing with unknown types @@ -17,6 +19,10 @@ runtime·memhash(uintptr *h, uintptr s, void *a) { byte *b; uintptr hash; + if(use_aeshash) { + runtime·aeshash(h, s, a); + return; + } b = a; hash = M0 ^ *h; @@ -467,6 +473,42 @@ runtime·algarray[] = // Runtime helpers. +// used in asm_{386,amd64}.s +byte runtime·aeskeysched[HashRandomBytes]; + +void +runtime·hashinit(void) +{ + // Install aes hash algorithm if we have the instructions we need + if((runtime·cpuid_ecx & (1 << 25)) != 0 && // aes (aesenc) + (runtime·cpuid_ecx & (1 << 9)) != 0 && // sse3 (pshufb) + (runtime·cpuid_ecx & (1 << 19)) != 0) { // sse4.1 (pinsr{d,q}) + byte *rnd; + int32 n; + use_aeshash = true; + runtime·algarray[AMEM].hash = runtime·aeshash; + runtime·algarray[AMEM8].hash = runtime·aeshash; + runtime·algarray[AMEM16].hash = runtime·aeshash; + runtime·algarray[AMEM32].hash = runtime·aeshash32; + runtime·algarray[AMEM64].hash = runtime·aeshash64; + runtime·algarray[AMEM128].hash = runtime·aeshash; + runtime·algarray[ASTRING].hash = runtime·aeshashstr; + + // Initialize with random data so hash collisions will be hard to engineer. + runtime·get_random_data(&rnd, &n); + if(n > HashRandomBytes) + n = HashRandomBytes; + runtime·memmove(runtime·aeskeysched, rnd, n); + if(n < HashRandomBytes) { + // Not very random, but better than nothing. + int64 t = runtime·nanotime(); + while (n < HashRandomBytes) { + runtime·aeskeysched[n++] = (int8)(t >> (8 * (n % 8))); + } + } + } +} + // func equal(t *Type, x T, y T) (ret bool) #pragma textflag 7 void diff --git a/src/pkg/runtime/arch_386.h b/src/pkg/runtime/arch_386.h index 4df795f71..62ed11b40 100644 --- a/src/pkg/runtime/arch_386.h +++ b/src/pkg/runtime/arch_386.h @@ -1,3 +1,7 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + enum { thechar = '8', BigEndian = 0, diff --git a/src/pkg/runtime/arch_amd64.h b/src/pkg/runtime/arch_amd64.h index e83dc9105..a5e43ca8d 100644 --- a/src/pkg/runtime/arch_amd64.h +++ b/src/pkg/runtime/arch_amd64.h @@ -1,3 +1,7 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + enum { thechar = '6', BigEndian = 0, diff --git a/src/pkg/runtime/arch_arm.h b/src/pkg/runtime/arch_arm.h index f6af58514..bb65d3faf 100644 --- a/src/pkg/runtime/arch_arm.h +++ b/src/pkg/runtime/arch_arm.h @@ -1,3 +1,7 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + enum { thechar = '5', BigEndian = 0, diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s index 96f04e0ae..6bcacf4cc 100644 --- a/src/pkg/runtime/asm_386.s +++ b/src/pkg/runtime/asm_386.s @@ -6,8 +6,8 @@ TEXT _rt0_386(SB),7,$0 // copy arguments forward on an even stack - MOVL 0(SP), AX // argc - LEAL 4(SP), BX // argv + MOVL argc+0(FP), AX + MOVL argv+4(FP), BX SUBL $128, SP // plenty of scratch ANDL $~15, SP MOVL AX, 120(SP) // save argc, argv away @@ -20,15 +20,25 @@ TEXT _rt0_386(SB),7,$0 MOVL BX, g_stackguard(BP) MOVL SP, g_stackbase(BP) + // find out information about the processor we're on + MOVL $0, AX + CPUID + CMPL AX, $0 + JE nocpuinfo + MOVL $1, AX + CPUID + MOVL CX, runtime·cpuid_ecx(SB) + MOVL DX, runtime·cpuid_edx(SB) +nocpuinfo: + // if there is an _cgo_init, call it to let it // initialize and to set up GS. if not, // we set up GS ourselves. MOVL _cgo_init(SB), AX TESTL AX, AX JZ needtls - PUSHL BP + MOVL BP, 0(SP) CALL AX - POPL BP // skip runtime·ldt0setup(SB) and tls test after _cgo_init for non-windows CMPL runtime·iswindows(SB), $0 JEQ ok @@ -72,6 +82,7 @@ ok: MOVL AX, 4(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) + CALL runtime·hashinit(SB) CALL runtime·schedinit(SB) // create a new goroutine to start program @@ -706,7 +717,261 @@ TEXT runtime·stackguard(SB),7,$0 get_tls(CX) MOVL g(CX), BX MOVL g_stackguard(BX), DX - MOVL DX, guard+4(FP) + MOVL DX, limit+4(FP) RET GLOBL runtime·tls0(SB), $32 + +// hash function using AES hardware instructions +TEXT runtime·aeshash(SB),7,$0 + MOVL 4(SP), DX // ptr to hash value + MOVL 8(SP), CX // size + MOVL 12(SP), AX // ptr to data + JMP runtime·aeshashbody(SB) + +TEXT runtime·aeshashstr(SB),7,$0 + MOVL 4(SP), DX // ptr to hash value + MOVL 12(SP), AX // ptr to string struct + MOVL 4(AX), CX // length of string + MOVL (AX), AX // string data + JMP runtime·aeshashbody(SB) + +// AX: data +// CX: length +// DX: ptr to seed input / hash output +TEXT runtime·aeshashbody(SB),7,$0 + MOVL (DX), X0 // seed to low 32 bits of xmm0 + PINSRD $1, CX, X0 // size to next 32 bits of xmm0 + MOVO runtime·aeskeysched+0(SB), X2 + MOVO runtime·aeskeysched+16(SB), X3 +aesloop: + CMPL CX, $16 + JB aesloopend + MOVOU (AX), X1 + AESENC X2, X0 + AESENC X1, X0 + SUBL $16, CX + ADDL $16, AX + JMP aesloop +aesloopend: + TESTL CX, CX + JE finalize // no partial block + + TESTL $16, AX + JNE highpartial + + // address ends in 0xxxx. 16 bytes loaded + // at this address won't cross a page boundary, so + // we can load it directly. + MOVOU (AX), X1 + ADDL CX, CX + PAND masks(SB)(CX*8), X1 + JMP partial +highpartial: + // address ends in 1xxxx. Might be up against + // a page boundary, so load ending at last byte. + // Then shift bytes down using pshufb. + MOVOU -16(AX)(CX*1), X1 + ADDL CX, CX + PSHUFB shifts(SB)(CX*8), X1 +partial: + // incorporate partial block into hash + AESENC X3, X0 + AESENC X1, X0 +finalize: + // finalize hash + AESENC X2, X0 + AESENC X3, X0 + AESENC X2, X0 + MOVL X0, (DX) + RET + +TEXT runtime·aeshash32(SB),7,$0 + MOVL 4(SP), DX // ptr to hash value + MOVL 12(SP), AX // ptr to data + MOVL (DX), X0 // seed + PINSRD $1, (AX), X0 // data + AESENC runtime·aeskeysched+0(SB), X0 + AESENC runtime·aeskeysched+16(SB), X0 + AESENC runtime·aeskeysched+0(SB), X0 + MOVL X0, (DX) + RET + +TEXT runtime·aeshash64(SB),7,$0 + MOVL 4(SP), DX // ptr to hash value + MOVL 12(SP), AX // ptr to data + MOVQ (AX), X0 // data + PINSRD $2, (DX), X0 // seed + AESENC runtime·aeskeysched+0(SB), X0 + AESENC runtime·aeskeysched+16(SB), X0 + AESENC runtime·aeskeysched+0(SB), X0 + MOVL X0, (DX) + RET + + +// simple mask to get rid of data in the high part of the register. +TEXT masks(SB),7,$0 + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + + LONG $0x000000ff + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + + LONG $0x0000ffff + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + + LONG $0x00ffffff + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffffff + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffffff + LONG $0x000000ff + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffffff + LONG $0x0000ffff + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffffff + LONG $0x00ffffff + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffffff + LONG $0xffffffff + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffffff + LONG $0xffffffff + LONG $0x000000ff + LONG $0x00000000 + + LONG $0xffffffff + LONG $0xffffffff + LONG $0x0000ffff + LONG $0x00000000 + + LONG $0xffffffff + LONG $0xffffffff + LONG $0x00ffffff + LONG $0x00000000 + + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + LONG $0x00000000 + + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + LONG $0x000000ff + + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + LONG $0x0000ffff + + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + LONG $0x00ffffff + + // these are arguments to pshufb. They move data down from + // the high bytes of the register to the low bytes of the register. + // index is how many bytes to move. +TEXT shifts(SB),7,$0 + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + LONG $0x00000000 + + LONG $0xffffff0f + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + + LONG $0xffff0f0e + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + + LONG $0xff0f0e0d + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + + LONG $0x0f0e0d0c + LONG $0xffffffff + LONG $0xffffffff + LONG $0xffffffff + + LONG $0x0e0d0c0b + LONG $0xffffff0f + LONG $0xffffffff + LONG $0xffffffff + + LONG $0x0d0c0b0a + LONG $0xffff0f0e + LONG $0xffffffff + LONG $0xffffffff + + LONG $0x0c0b0a09 + LONG $0xff0f0e0d + LONG $0xffffffff + LONG $0xffffffff + + LONG $0x0b0a0908 + LONG $0x0f0e0d0c + LONG $0xffffffff + LONG $0xffffffff + + LONG $0x0a090807 + LONG $0x0e0d0c0b + LONG $0xffffff0f + LONG $0xffffffff + + LONG $0x09080706 + LONG $0x0d0c0b0a + LONG $0xffff0f0e + LONG $0xffffffff + + LONG $0x08070605 + LONG $0x0c0b0a09 + LONG $0xff0f0e0d + LONG $0xffffffff + + LONG $0x07060504 + LONG $0x0b0a0908 + LONG $0x0f0e0d0c + LONG $0xffffffff + + LONG $0x06050403 + LONG $0x0a090807 + LONG $0x0e0d0c0b + LONG $0xffffff0f + + LONG $0x05040302 + LONG $0x09080706 + LONG $0x0d0c0b0a + LONG $0xffff0f0e + + LONG $0x04030201 + LONG $0x08070605 + LONG $0x0c0b0a09 + LONG $0xff0f0e0d + diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s index 987958498..f4cfa576e 100644 --- a/src/pkg/runtime/asm_amd64.s +++ b/src/pkg/runtime/asm_amd64.s @@ -6,8 +6,8 @@ TEXT _rt0_amd64(SB),7,$-8 // copy arguments forward on an even stack - MOVQ 0(DI), AX // argc - LEAQ 8(DI), BX // argv + MOVQ DI, AX // argc + MOVQ SI, BX // argv SUBQ $(4*8+7), SP // 2args 2auto ANDQ $~15, SP MOVQ AX, 16(SP) @@ -20,6 +20,17 @@ TEXT _rt0_amd64(SB),7,$-8 MOVQ BX, g_stackguard(DI) MOVQ SP, g_stackbase(DI) + // find out information about the processor we're on + MOVQ $0, AX + CPUID + CMPQ AX, $0 + JE nocpuinfo + MOVQ $1, AX + CPUID + MOVL CX, runtime·cpuid_ecx(SB) + MOVL DX, runtime·cpuid_edx(SB) +nocpuinfo: + // if there is an _cgo_init, call it. MOVQ _cgo_init(SB), AX TESTQ AX, AX @@ -65,6 +76,7 @@ ok: MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) + CALL runtime·hashinit(SB) CALL runtime·schedinit(SB) // create a new goroutine to start program @@ -442,6 +454,12 @@ TEXT runtime·xchg(SB), 7, $0 XCHGL AX, 0(BX) RET +TEXT runtime·xchg64(SB), 7, $0 + MOVQ 8(SP), BX + MOVQ 16(SP), AX + XCHGQ AX, 0(BX) + RET + TEXT runtime·procyield(SB),7,$0 MOVL 8(SP), AX again: @@ -719,7 +737,165 @@ TEXT runtime·stackguard(SB),7,$0 get_tls(CX) MOVQ g(CX), BX MOVQ g_stackguard(BX), DX - MOVQ DX, guard+8(FP) + MOVQ DX, limit+8(FP) RET GLOBL runtime·tls0(SB), $64 + +// hash function using AES hardware instructions +TEXT runtime·aeshash(SB),7,$0 + MOVQ 8(SP), DX // ptr to hash value + MOVQ 16(SP), CX // size + MOVQ 24(SP), AX // ptr to data + JMP runtime·aeshashbody(SB) + +TEXT runtime·aeshashstr(SB),7,$0 + MOVQ 8(SP), DX // ptr to hash value + MOVQ 24(SP), AX // ptr to string struct + MOVQ 8(AX), CX // length of string + MOVQ (AX), AX // string data + JMP runtime·aeshashbody(SB) + +// AX: data +// CX: length +// DX: ptr to seed input / hash output +TEXT runtime·aeshashbody(SB),7,$0 + MOVQ (DX), X0 // seed to low 64 bits of xmm0 + PINSRQ $1, CX, X0 // size to high 64 bits of xmm0 + MOVO runtime·aeskeysched+0(SB), X2 + MOVO runtime·aeskeysched+16(SB), X3 +aesloop: + CMPQ CX, $16 + JB aesloopend + MOVOU (AX), X1 + AESENC X2, X0 + AESENC X1, X0 + SUBQ $16, CX + ADDQ $16, AX + JMP aesloop +aesloopend: + TESTQ CX, CX + JE finalize // no partial block + + TESTQ $16, AX + JNE highpartial + + // address ends in 0xxxx. 16 bytes loaded + // at this address won't cross a page boundary, so + // we can load it directly. + MOVOU (AX), X1 + ADDQ CX, CX + PAND masks(SB)(CX*8), X1 + JMP partial +highpartial: + // address ends in 1xxxx. Might be up against + // a page boundary, so load ending at last byte. + // Then shift bytes down using pshufb. + MOVOU -16(AX)(CX*1), X1 + ADDQ CX, CX + PSHUFB shifts(SB)(CX*8), X1 +partial: + // incorporate partial block into hash + AESENC X3, X0 + AESENC X1, X0 +finalize: + // finalize hash + AESENC X2, X0 + AESENC X3, X0 + AESENC X2, X0 + MOVQ X0, (DX) + RET + +TEXT runtime·aeshash32(SB),7,$0 + MOVQ 8(SP), DX // ptr to hash value + MOVQ 24(SP), AX // ptr to data + MOVQ (DX), X0 // seed + PINSRD $2, (AX), X0 // data + AESENC runtime·aeskeysched+0(SB), X0 + AESENC runtime·aeskeysched+16(SB), X0 + AESENC runtime·aeskeysched+0(SB), X0 + MOVQ X0, (DX) + RET + +TEXT runtime·aeshash64(SB),7,$0 + MOVQ 8(SP), DX // ptr to hash value + MOVQ 24(SP), AX // ptr to data + MOVQ (DX), X0 // seed + PINSRQ $1, (AX), X0 // data + AESENC runtime·aeskeysched+0(SB), X0 + AESENC runtime·aeskeysched+16(SB), X0 + AESENC runtime·aeskeysched+0(SB), X0 + MOVQ X0, (DX) + RET + +// simple mask to get rid of data in the high part of the register. +TEXT masks(SB),7,$0 + QUAD $0x0000000000000000 + QUAD $0x0000000000000000 + QUAD $0x00000000000000ff + QUAD $0x0000000000000000 + QUAD $0x000000000000ffff + QUAD $0x0000000000000000 + QUAD $0x0000000000ffffff + QUAD $0x0000000000000000 + QUAD $0x00000000ffffffff + QUAD $0x0000000000000000 + QUAD $0x000000ffffffffff + QUAD $0x0000000000000000 + QUAD $0x0000ffffffffffff + QUAD $0x0000000000000000 + QUAD $0x00ffffffffffffff + QUAD $0x0000000000000000 + QUAD $0xffffffffffffffff + QUAD $0x0000000000000000 + QUAD $0xffffffffffffffff + QUAD $0x00000000000000ff + QUAD $0xffffffffffffffff + QUAD $0x000000000000ffff + QUAD $0xffffffffffffffff + QUAD $0x0000000000ffffff + QUAD $0xffffffffffffffff + QUAD $0x00000000ffffffff + QUAD $0xffffffffffffffff + QUAD $0x000000ffffffffff + QUAD $0xffffffffffffffff + QUAD $0x0000ffffffffffff + QUAD $0xffffffffffffffff + QUAD $0x00ffffffffffffff + + // these are arguments to pshufb. They move data down from + // the high bytes of the register to the low bytes of the register. + // index is how many bytes to move. +TEXT shifts(SB),7,$0 + QUAD $0x0000000000000000 + QUAD $0x0000000000000000 + QUAD $0xffffffffffffff0f + QUAD $0xffffffffffffffff + QUAD $0xffffffffffff0f0e + QUAD $0xffffffffffffffff + QUAD $0xffffffffff0f0e0d + QUAD $0xffffffffffffffff + QUAD $0xffffffff0f0e0d0c + QUAD $0xffffffffffffffff + QUAD $0xffffff0f0e0d0c0b + QUAD $0xffffffffffffffff + QUAD $0xffff0f0e0d0c0b0a + QUAD $0xffffffffffffffff + QUAD $0xff0f0e0d0c0b0a09 + QUAD $0xffffffffffffffff + QUAD $0x0f0e0d0c0b0a0908 + QUAD $0xffffffffffffffff + QUAD $0x0e0d0c0b0a090807 + QUAD $0xffffffffffffff0f + QUAD $0x0d0c0b0a09080706 + QUAD $0xffffffffffff0f0e + QUAD $0x0c0b0a0908070605 + QUAD $0xffffffffff0f0e0d + QUAD $0x0b0a090807060504 + QUAD $0xffffffff0f0e0d0c + QUAD $0x0a09080706050403 + QUAD $0xffffff0f0e0d0c0b + QUAD $0x0908070605040302 + QUAD $0xffff0f0e0d0c0b0a + QUAD $0x0807060504030201 + QUAD $0xff0f0e0d0c0b0a09 diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s index 45b53541b..6b2d6afda 100644 --- a/src/pkg/runtime/asm_arm.s +++ b/src/pkg/runtime/asm_arm.s @@ -47,6 +47,7 @@ TEXT _rt0_arm(SB),7,$-4 MOVW R1, 8(R13) BL runtime·args(SB) BL runtime·osinit(SB) + BL runtime·hashinit(SB) BL runtime·schedinit(SB) // create a new goroutine to start program @@ -489,3 +490,17 @@ TEXT runtime·stackguard(SB),7,$0 MOVW R1, sp+0(FP) MOVW R2, limit+4(FP) RET + +// not implemented for ARM +TEXT runtime·aeshash(SB),7,$-4 + MOVW $0, R0 + MOVW (R0), R1 +TEXT runtime·aeshash32(SB),7,$-4 + MOVW $0, R0 + MOVW (R0), R1 +TEXT runtime·aeshash64(SB),7,$-4 + MOVW $0, R0 + MOVW (R0), R1 +TEXT runtime·aeshashstr(SB),7,$-4 + MOVW $0, R0 + MOVW (R0), R1 diff --git a/src/pkg/runtime/atomic_386.c b/src/pkg/runtime/atomic_386.c index 79b7cbf96..1046eb81e 100644 --- a/src/pkg/runtime/atomic_386.c +++ b/src/pkg/runtime/atomic_386.c @@ -30,3 +30,16 @@ runtime·xadd64(uint64 volatile* addr, int64 v) } return old+v; } + +#pragma textflag 7 +uint64 +runtime·xchg64(uint64 volatile* addr, uint64 v) +{ + uint64 old; + + old = *addr; + while(!runtime·cas64(addr, &old, v)) { + // nothing + } + return old; +} diff --git a/src/pkg/runtime/atomic_arm.c b/src/pkg/runtime/atomic_arm.c index 0b54840cc..9193d599d 100644 --- a/src/pkg/runtime/atomic_arm.c +++ b/src/pkg/runtime/atomic_arm.c @@ -123,6 +123,19 @@ runtime·xadd64(uint64 volatile *addr, int64 delta) #pragma textflag 7 uint64 +runtime·xchg64(uint64 volatile *addr, uint64 v) +{ + uint64 res; + + runtime·lock(LOCK(addr)); + res = *addr; + *addr = v; + runtime·unlock(LOCK(addr)); + return res; +} + +#pragma textflag 7 +uint64 runtime·atomicload64(uint64 volatile *addr) { uint64 res; diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c index 51bd529ec..19f6115a6 100644 --- a/src/pkg/runtime/cgo/callbacks.c +++ b/src/pkg/runtime/cgo/callbacks.c @@ -12,8 +12,10 @@ // void crosscall2(void (*fn)(void *, int), void *, int); // // We need to export the symbol crosscall2 in order to support -// callbacks from shared libraries. -#pragma dynexport crosscall2 crosscall2 +// callbacks from shared libraries. This applies regardless of +// linking mode. +#pragma cgo_export_static crosscall2 +#pragma cgo_export_dynamic crosscall2 // Allocate memory. This allocates the requested number of bytes in // memory controlled by the Go runtime. The allocated memory will be diff --git a/src/pkg/runtime/cgo/gcc_freebsd_arm.c b/src/pkg/runtime/cgo/gcc_freebsd_arm.c index 3bcb0b270..73c990c28 100644 --- a/src/pkg/runtime/cgo/gcc_freebsd_arm.c +++ b/src/pkg/runtime/cgo/gcc_freebsd_arm.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include <sys/types.h> +#include <machine/sysarch.h> #include <pthread.h> #include <string.h> #include "libcgo.h" @@ -22,10 +24,20 @@ void x_cgo_load_gm(void) __attribute__((naked)); void __aeabi_read_tp(void) { - // read @ 0xffff1000 __asm__ __volatile__ ( +#ifdef ARM_TP_ADDRESS + // ARM_TP_ADDRESS is (ARM_VECTORS_HIGH + 0x1000) or 0xffff1000 + // GCC inline asm doesn't provide a way to provide a constant + // to "ldr r0, =??" pseudo instruction, so we hardcode the value + // and check it with cpp. +#if ARM_TP_ADDRESS != 0xffff1000 +#error Wrong ARM_TP_ADDRESS! +#endif "ldr r0, =0xffff1000\n\t" "ldr r0, [r0]\n\t" +#else + "mrc p15, 0, r0, c13, c0, 3\n\t" +#endif "mov pc, lr\n\t" ); } diff --git a/src/pkg/runtime/cgo/gcc_openbsd_386.c b/src/pkg/runtime/cgo/gcc_openbsd_386.c index 86c1365ad..80be31b9c 100644 --- a/src/pkg/runtime/cgo/gcc_openbsd_386.c +++ b/src/pkg/runtime/cgo/gcc_openbsd_386.c @@ -48,9 +48,9 @@ tcb_fixup(int mainthread) bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE); __set_tcb(newtcb + TLS_SIZE); - // The main thread TCB is a static allocation - do not try to free it. - if(!mainthread) - free(oldtcb); + // NOTE(jsing, minux): we can't free oldtcb without causing double-free + // problem. so newtcb will be memory leaks. Get rid of this when OpenBSD + // has proper support for PT_TLS. } static void * diff --git a/src/pkg/runtime/cgo/gcc_openbsd_amd64.c b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c index d3a5e36b0..e9cc8184b 100644 --- a/src/pkg/runtime/cgo/gcc_openbsd_amd64.c +++ b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c @@ -48,9 +48,9 @@ tcb_fixup(int mainthread) bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE); __set_tcb(newtcb + TLS_SIZE); - // The main thread TCB is a static allocation - do not try to free it. - if(!mainthread) - free(oldtcb); + // NOTE(jsing, minux): we can't free oldtcb without causing double-free + // problem. so newtcb will be memory leaks. Get rid of this when OpenBSD + // has proper support for PT_TLS. } static void * diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index 590bf9b67..0c9618749 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -95,7 +95,8 @@ static void unwindm(void); // Call from Go to C. -static FuncVal unlockOSThread = { runtime·unlockOSThread }; +static void endcgo(void); +static FuncVal endcgoV = { endcgo }; void runtime·cgocall(void (*fn)(void*), void *arg) @@ -123,7 +124,7 @@ runtime·cgocall(void (*fn)(void*), void *arg) * cgo callback. Add entry to defer stack in case of panic. */ runtime·lockOSThread(); - d.fn = &unlockOSThread; + d.fn = &endcgoV; d.siz = 0; d.link = g->defer; d.argp = (void*)-1; // unused because unlockm never recovers @@ -148,6 +149,16 @@ runtime·cgocall(void (*fn)(void*), void *arg) runtime·asmcgocall(fn, arg); runtime·exitsyscall(); + if(g->defer != &d || d.fn != &endcgoV) + runtime·throw("runtime: bad defer entry in cgocallback"); + g->defer = d.link; + endcgo(); +} + +static void +endcgo(void) +{ + runtime·unlockOSThread(); m->ncgo--; if(m->ncgo == 0) { // We are going back to Go and are not in a recursive @@ -156,11 +167,6 @@ runtime·cgocall(void (*fn)(void*), void *arg) m->cgomal = nil; } - if(g->defer != &d || d.fn != &unlockOSThread) - runtime·throw("runtime: bad defer entry in cgocallback"); - g->defer = d.link; - runtime·unlockOSThread(); - if(raceenabled) runtime·raceacquire(&cgosync); } @@ -280,3 +286,9 @@ runtime·cgounimpl(void) // called from (incomplete) assembly { runtime·throw("runtime: cgo not implemented"); } + +// For cgo-using programs with external linking, +// export "main" (defined in assembly) so that libc can handle basic +// C runtime startup and call the Go program as if it were +// the C main function. +#pragma cgo_export_static main diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go index 5f84cb5a2..80549a505 100644 --- a/src/pkg/runtime/crash_test.go +++ b/src/pkg/runtime/crash_test.go @@ -91,6 +91,14 @@ func TestLockedDeadlock2(t *testing.T) { testDeadlock(t, lockedDeadlockSource2) } +func TestGoexitDeadlock(t *testing.T) { + got := executeTest(t, goexitDeadlockSource, nil) + want := "" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} + const crashSource = ` package main @@ -175,3 +183,21 @@ func main() { select {} } ` + +const goexitDeadlockSource = ` +package main +import ( + "runtime" +) + +func F() { + for i := 0; i < 10; i++ { + } +} + +func main() { + go F() + go F() + runtime.Goexit() +} +` diff --git a/src/pkg/runtime/defs2_linux.go b/src/pkg/runtime/defs2_linux.go index 9b0702955..60ecc69bb 100644 --- a/src/pkg/runtime/defs2_linux.go +++ b/src/pkg/runtime/defs2_linux.go @@ -7,7 +7,7 @@ /* * Input to cgo -cdefs -GOARCH=386 cgo -cdefs defs2.go >386/defs.h +GOARCH=386 go tool cgo -cdefs defs2_linux.go >defs_linux_386.h The asm header tricks we have to use for Linux on amd64 (see defs.c and defs1.c) don't work here, so this is yet another @@ -17,15 +17,19 @@ file. Sigh. package runtime /* -#cgo CFLAGS: -I/home/rsc/pub/linux-2.6/arch/x86/include -I/home/rsc/pub/linux-2.6/include -D_LOOSE_KERNEL_NAMES -D__ARCH_SI_UID_T=__kernel_uid32_t +#cgo CFLAGS: -I/tmp/linux/arch/x86/include -I/tmp/linux/include -D_LOOSE_KERNEL_NAMES -D__ARCH_SI_UID_T=__kernel_uid32_t #define size_t __kernel_size_t +#define pid_t int #include <asm/signal.h> #include <asm/mman.h> #include <asm/sigcontext.h> #include <asm/ucontext.h> #include <asm/siginfo.h> +#include <asm-generic/errno.h> #include <asm-generic/fcntl.h> +#include <asm-generic/poll.h> +#include <linux/eventpoll.h> // This is the sigaction structure from the Linux 2.1.68 kernel which // is used with the rt_sigaction system call. For 386 this is not @@ -35,12 +39,16 @@ struct kernel_sigaction { __sighandler_t k_sa_handler; unsigned long sa_flags; void (*sa_restorer) (void); - sigset_t sa_mask; + unsigned long long sa_mask; }; */ import "C" const ( + EINTR = C.EINTR + EAGAIN = C.EAGAIN + ENOMEM = C.ENOMEM + PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ PROT_WRITE = C.PROT_WRITE @@ -110,6 +118,17 @@ const ( O_RDONLY = C.O_RDONLY O_CLOEXEC = C.O_CLOEXEC + + EPOLLIN = C.POLLIN + EPOLLOUT = C.POLLOUT + EPOLLERR = C.POLLERR + EPOLLHUP = C.POLLHUP + EPOLLRDHUP = C.POLLRDHUP + EPOLLET = C.EPOLLET + EPOLL_CLOEXEC = C.EPOLL_CLOEXEC + EPOLL_CTL_ADD = C.EPOLL_CTL_ADD + EPOLL_CTL_DEL = C.EPOLL_CTL_DEL + EPOLL_CTL_MOD = C.EPOLL_CTL_MOD ) type Fpreg C.struct__fpreg @@ -124,3 +143,4 @@ type Sigaltstack C.struct_sigaltstack type Sigcontext C.struct_sigcontext type Ucontext C.struct_ucontext type Itimerval C.struct_itimerval +type EpollEvent C.struct_epoll_event diff --git a/src/pkg/runtime/defs_darwin.go b/src/pkg/runtime/defs_darwin.go index 7f22b0b8e..722013ba9 100644 --- a/src/pkg/runtime/defs_darwin.go +++ b/src/pkg/runtime/defs_darwin.go @@ -7,8 +7,8 @@ /* Input to cgo. -GOARCH=amd64 cgo -cdefs defs_darwin.go >defs_darwin_amd64.h -GOARCH=386 cgo -cdefs defs_darwin.go >defs_darwin_386.h +GOARCH=amd64 go tool cgo -cdefs defs_darwin.go >defs_darwin_amd64.h +GOARCH=386 go tool cgo -cdefs defs_darwin.go >defs_darwin_386.h */ package runtime @@ -19,12 +19,17 @@ package runtime #include <mach/message.h> #include <sys/types.h> #include <sys/time.h> +#include <errno.h> #include <signal.h> +#include <sys/event.h> #include <sys/mman.h> */ import "C" const ( + EINTR = C.EINTR + EFAULT = C.EFAULT + PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ PROT_WRITE = C.PROT_WRITE @@ -128,6 +133,14 @@ const ( ITIMER_REAL = C.ITIMER_REAL ITIMER_VIRTUAL = C.ITIMER_VIRTUAL ITIMER_PROF = C.ITIMER_PROF + + EV_ADD = C.EV_ADD + EV_DELETE = C.EV_DELETE + EV_CLEAR = C.EV_CLEAR + EV_RECEIPT = C.EV_RECEIPT + EV_ERROR = C.EV_ERROR + EVFILT_READ = C.EVFILT_READ + EVFILT_WRITE = C.EVFILT_WRITE ) type MachBody C.mach_msg_body_t @@ -144,6 +157,7 @@ type Sigval C.union_sigval type Siginfo C.siginfo_t type Timeval C.struct_timeval type Itimerval C.struct_itimerval +type Timespec C.struct_timespec type FPControl C.struct_fp_control type FPStatus C.struct_fp_status @@ -161,3 +175,5 @@ type ExceptionState32 C.struct_i386_exception_state type Mcontext32 C.struct_mcontext32 type Ucontext C.struct_ucontext + +type Kevent C.struct_kevent diff --git a/src/pkg/runtime/defs_darwin_386.h b/src/pkg/runtime/defs_darwin_386.h index 92732f460..7b210eebf 100644 --- a/src/pkg/runtime/defs_darwin_386.h +++ b/src/pkg/runtime/defs_darwin_386.h @@ -3,6 +3,9 @@ enum { + EINTR = 0x4, + EFAULT = 0xe, + PROT_NONE = 0x0, PROT_READ = 0x1, PROT_WRITE = 0x2, @@ -106,6 +109,14 @@ enum { ITIMER_REAL = 0x0, ITIMER_VIRTUAL = 0x1, ITIMER_PROF = 0x2, + + EV_ADD = 0x1, + EV_DELETE = 0x2, + EV_CLEAR = 0x20, + EV_RECEIPT = 0x40, + EV_ERROR = 0x4000, + EVFILT_READ = -0x1, + EVFILT_WRITE = -0x2, }; typedef struct MachBody MachBody; @@ -117,6 +128,7 @@ typedef struct Sigaction Sigaction; typedef struct Siginfo Siginfo; typedef struct Timeval Timeval; typedef struct Itimerval Itimerval; +typedef struct Timespec Timespec; typedef struct FPControl FPControl; typedef struct FPStatus FPStatus; typedef struct RegMMST RegMMST; @@ -130,6 +142,7 @@ typedef struct FloatState32 FloatState32; typedef struct ExceptionState32 ExceptionState32; typedef struct Mcontext32 Mcontext32; typedef struct Ucontext Ucontext; +typedef struct Kevent Kevent; #pragma pack on @@ -170,7 +183,7 @@ struct StackT { typedef byte Sighandler[4]; struct Sigaction { - Sighandler __sigaction_u; + byte __sigaction_u[4]; void *sa_tramp; uint32 sa_mask; int32 sa_flags; @@ -185,7 +198,7 @@ struct Siginfo { uint32 si_uid; int32 si_status; byte *si_addr; - Sigval si_value; + byte si_value[4]; int32 si_band; uint32 __pad[7]; }; @@ -197,6 +210,10 @@ struct Itimerval { Timeval it_interval; Timeval it_value; }; +struct Timespec { + int32 tv_sec; + int32 tv_nsec; +}; struct FPControl { byte Pad_cgo_0[2]; @@ -362,5 +379,14 @@ struct Ucontext { Mcontext32 *uc_mcontext; }; +struct Kevent { + uint32 ident; + int16 filter; + uint16 flags; + uint32 fflags; + int32 data; + byte *udata; +}; + #pragma pack off diff --git a/src/pkg/runtime/defs_darwin_amd64.h b/src/pkg/runtime/defs_darwin_amd64.h index d4fbfef49..2d464a9e5 100644 --- a/src/pkg/runtime/defs_darwin_amd64.h +++ b/src/pkg/runtime/defs_darwin_amd64.h @@ -3,6 +3,9 @@ enum { + EINTR = 0x4, + EFAULT = 0xe, + PROT_NONE = 0x0, PROT_READ = 0x1, PROT_WRITE = 0x2, @@ -106,6 +109,14 @@ enum { ITIMER_REAL = 0x0, ITIMER_VIRTUAL = 0x1, ITIMER_PROF = 0x2, + + EV_ADD = 0x1, + EV_DELETE = 0x2, + EV_CLEAR = 0x20, + EV_RECEIPT = 0x40, + EV_ERROR = 0x4000, + EVFILT_READ = -0x1, + EVFILT_WRITE = -0x2, }; typedef struct MachBody MachBody; @@ -117,6 +128,7 @@ typedef struct Sigaction Sigaction; typedef struct Siginfo Siginfo; typedef struct Timeval Timeval; typedef struct Itimerval Itimerval; +typedef struct Timespec Timespec; typedef struct FPControl FPControl; typedef struct FPStatus FPStatus; typedef struct RegMMST RegMMST; @@ -130,6 +142,7 @@ typedef struct FloatState32 FloatState32; typedef struct ExceptionState32 ExceptionState32; typedef struct Mcontext32 Mcontext32; typedef struct Ucontext Ucontext; +typedef struct Kevent Kevent; #pragma pack on @@ -171,7 +184,7 @@ struct StackT { typedef byte Sighandler[8]; struct Sigaction { - Sighandler __sigaction_u; + byte __sigaction_u[8]; void *sa_tramp; uint32 sa_mask; int32 sa_flags; @@ -186,7 +199,7 @@ struct Siginfo { uint32 si_uid; int32 si_status; byte *si_addr; - Sigval si_value; + byte si_value[8]; int64 si_band; uint64 __pad[7]; }; @@ -199,6 +212,10 @@ struct Itimerval { Timeval it_interval; Timeval it_value; }; +struct Timespec { + int64 tv_sec; + int64 tv_nsec; +}; struct FPControl { byte Pad_cgo_0[2]; @@ -365,5 +382,14 @@ struct Ucontext { Mcontext64 *uc_mcontext; }; +struct Kevent { + uint64 ident; + int16 filter; + uint16 flags; + uint32 fflags; + int64 data; + byte *udata; +}; + #pragma pack off diff --git a/src/pkg/runtime/defs_linux.go b/src/pkg/runtime/defs_linux.go index c0275e111..2f4e03a01 100644 --- a/src/pkg/runtime/defs_linux.go +++ b/src/pkg/runtime/defs_linux.go @@ -7,7 +7,7 @@ /* Input to cgo -cdefs -GOARCH=amd64 cgo -cdefs defs.go defs1.go >amd64/defs.h +GOARCH=amd64 go tool cgo -cdefs defs_linux.go defs1_linux.go >defs_linux_amd64.h */ package runtime @@ -25,10 +25,17 @@ package runtime #include <asm/signal.h> #include <asm/siginfo.h> #include <asm/mman.h> +#include <asm-generic/errno.h> +#include <asm-generic/poll.h> +#include <linux/eventpoll.h> */ import "C" const ( + EINTR = C.EINTR + EAGAIN = C.EAGAIN + ENOMEM = C.ENOMEM + PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ PROT_WRITE = C.PROT_WRITE @@ -95,6 +102,17 @@ const ( ITIMER_REAL = C.ITIMER_REAL ITIMER_VIRTUAL = C.ITIMER_VIRTUAL ITIMER_PROF = C.ITIMER_PROF + + EPOLLIN = C.POLLIN + EPOLLOUT = C.POLLOUT + EPOLLERR = C.POLLERR + EPOLLHUP = C.POLLHUP + EPOLLRDHUP = C.POLLRDHUP + EPOLLET = C.EPOLLET + EPOLL_CLOEXEC = C.EPOLL_CLOEXEC + EPOLL_CTL_ADD = C.EPOLL_CTL_ADD + EPOLL_CTL_DEL = C.EPOLL_CTL_DEL + EPOLL_CTL_MOD = C.EPOLL_CTL_MOD ) type Timespec C.struct_timespec @@ -102,3 +120,4 @@ type Timeval C.struct_timeval type Sigaction C.struct_sigaction type Siginfo C.siginfo_t type Itimerval C.struct_itimerval +type EpollEvent C.struct_epoll_event diff --git a/src/pkg/runtime/defs_linux_386.h b/src/pkg/runtime/defs_linux_386.h index e257a6f85..27dae9e82 100644 --- a/src/pkg/runtime/defs_linux_386.h +++ b/src/pkg/runtime/defs_linux_386.h @@ -1,8 +1,12 @@ // Created by cgo -cdefs - DO NOT EDIT -// cgo -cdefs defs2.go +// cgo -cdefs defs2_linux.go enum { + EINTR = 0x4, + EAGAIN = 0xb, + ENOMEM = 0xc, + PROT_NONE = 0x0, PROT_READ = 0x1, PROT_WRITE = 0x2, @@ -72,6 +76,17 @@ enum { O_RDONLY = 0x0, O_CLOEXEC = 0x80000, + + EPOLLIN = 0x1, + EPOLLOUT = 0x4, + EPOLLERR = 0x8, + EPOLLHUP = 0x10, + EPOLLRDHUP = 0x2000, + EPOLLET = -0x80000000, + EPOLL_CLOEXEC = 0x80000, + EPOLL_CTL_ADD = 0x1, + EPOLL_CTL_DEL = 0x2, + EPOLL_CTL_MOD = 0x3, }; typedef struct Fpreg Fpreg; @@ -86,6 +101,7 @@ typedef struct Sigaltstack Sigaltstack; typedef struct Sigcontext Sigcontext; typedef struct Ucontext Ucontext; typedef struct Itimerval Itimerval; +typedef struct EpollEvent EpollEvent; #pragma pack on @@ -186,6 +202,10 @@ struct Itimerval { Timeval it_interval; Timeval it_value; }; +struct EpollEvent { + uint32 events; + uint64 data; +}; #pragma pack off diff --git a/src/pkg/runtime/defs_linux_amd64.h b/src/pkg/runtime/defs_linux_amd64.h index bf5f79b0e..3e87df68a 100644 --- a/src/pkg/runtime/defs_linux_amd64.h +++ b/src/pkg/runtime/defs_linux_amd64.h @@ -1,8 +1,12 @@ // Created by cgo -cdefs - DO NOT EDIT -// cgo -cdefs defs.go defs1.go +// cgo -cdefs defs_linux.go defs1_linux.go enum { + EINTR = 0x4, + EAGAIN = 0xb, + ENOMEM = 0xc, + PROT_NONE = 0x0, PROT_READ = 0x1, PROT_WRITE = 0x2, @@ -69,6 +73,17 @@ enum { ITIMER_REAL = 0x0, ITIMER_VIRTUAL = 0x1, ITIMER_PROF = 0x2, + + EPOLLIN = 0x1, + EPOLLOUT = 0x4, + EPOLLERR = 0x8, + EPOLLHUP = 0x10, + EPOLLRDHUP = 0x2000, + EPOLLET = -0x80000000, + EPOLL_CLOEXEC = 0x80000, + EPOLL_CTL_ADD = 0x1, + EPOLL_CTL_DEL = 0x2, + EPOLL_CTL_MOD = 0x3, }; typedef struct Timespec Timespec; @@ -76,6 +91,7 @@ typedef struct Timeval Timeval; typedef struct Sigaction Sigaction; typedef struct Siginfo Siginfo; typedef struct Itimerval Itimerval; +typedef struct EpollEvent EpollEvent; #pragma pack on @@ -104,11 +120,15 @@ struct Itimerval { Timeval it_interval; Timeval it_value; }; +struct EpollEvent { + uint32 events; + uint64 data; +}; #pragma pack off // Created by cgo -cdefs - DO NOT EDIT -// cgo -cdefs defs.go defs1.go +// cgo -cdefs defs_linux.go defs1_linux.go enum { diff --git a/src/pkg/runtime/defs_linux_arm.h b/src/pkg/runtime/defs_linux_arm.h index f72ec3d1b..92160966e 100644 --- a/src/pkg/runtime/defs_linux_arm.h +++ b/src/pkg/runtime/defs_linux_arm.h @@ -1,9 +1,11 @@ -// godefs -f-I/usr/src/linux-headers-2.6.26-2-versatile/include defs_arm.c - -// MACHINE GENERATED - DO NOT EDIT. +// TODO: Generate using cgo like defs_linux_{386,amd64}.h // Constants enum { + EINTR = 0x4, + ENOMEM = 0xc, + EAGAIN = 0xb, + PROT_NONE = 0, PROT_READ = 0x1, PROT_WRITE = 0x2, @@ -64,6 +66,17 @@ enum { ITIMER_VIRTUAL = 0x1, O_RDONLY = 0, O_CLOEXEC = 02000000, + + EPOLLIN = 0x1, + EPOLLOUT = 0x4, + EPOLLERR = 0x8, + EPOLLHUP = 0x10, + EPOLLRDHUP = 0x2000, + EPOLLET = -0x80000000, + EPOLL_CLOEXEC = 0x80000, + EPOLL_CTL_ADD = 0x1, + EPOLL_CTL_DEL = 0x2, + EPOLL_CTL_MOD = 0x3, }; // Types @@ -145,4 +158,11 @@ struct Sigaction { void *sa_restorer; uint64 sa_mask; }; + +typedef struct EpollEvent EpollEvent; +struct EpollEvent { + uint32 events; + uint32 _pad; + uint64 data; +}; #pragma pack off diff --git a/src/pkg/runtime/defs_netbsd.go b/src/pkg/runtime/defs_netbsd.go index 53e061041..c543593fa 100644 --- a/src/pkg/runtime/defs_netbsd.go +++ b/src/pkg/runtime/defs_netbsd.go @@ -9,6 +9,7 @@ Input to cgo. GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h +GOARCH=arm go tool cgo -cdefs defs_netbsd.go defs_netbsd_arm.go >defs_netbsd_arm.h */ // +godefs map __fpregset_t [644]byte diff --git a/src/pkg/runtime/defs_netbsd_386.go b/src/pkg/runtime/defs_netbsd_386.go index e9e36608e..c26f24607 100644 --- a/src/pkg/runtime/defs_netbsd_386.go +++ b/src/pkg/runtime/defs_netbsd_386.go @@ -7,7 +7,6 @@ /* Input to cgo. -GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h */ diff --git a/src/pkg/runtime/defs_netbsd_amd64.go b/src/pkg/runtime/defs_netbsd_amd64.go index 68f586b2f..f18a7b1fe 100644 --- a/src/pkg/runtime/defs_netbsd_amd64.go +++ b/src/pkg/runtime/defs_netbsd_amd64.go @@ -8,7 +8,6 @@ Input to cgo. GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h -GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h */ package runtime diff --git a/src/pkg/runtime/defs_netbsd_arm.go b/src/pkg/runtime/defs_netbsd_arm.go new file mode 100644 index 000000000..cb0dce66b --- /dev/null +++ b/src/pkg/runtime/defs_netbsd_arm.go @@ -0,0 +1,39 @@ +// 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. + +// +build ignore + +/* +Input to cgo. + +GOARCH=arm go tool cgo -cdefs defs_netbsd.go defs_netbsd_arm.go >defs_netbsd_arm.h +*/ + +package runtime + +/* +#include <sys/types.h> +#include <machine/mcontext.h> +*/ +import "C" + +const ( + REG_R0 = C._REG_R0 + REG_R1 = C._REG_R1 + REG_R2 = C._REG_R2 + REG_R3 = C._REG_R3 + REG_R4 = C._REG_R4 + REG_R5 = C._REG_R5 + REG_R6 = C._REG_R6 + REG_R7 = C._REG_R7 + REG_R8 = C._REG_R8 + REG_R9 = C._REG_R9 + REG_R10 = C._REG_R10 + REG_R11 = C._REG_R11 + REG_R12 = C._REG_R12 + REG_R13 = C._REG_R13 + REG_R14 = C._REG_R14 + REG_R15 = C._REG_R15 + REG_CPSR = C._REG_CPSR +) diff --git a/src/pkg/runtime/defs_netbsd_arm.h b/src/pkg/runtime/defs_netbsd_arm.h index f67475c76..26b55222e 100644 --- a/src/pkg/runtime/defs_netbsd_arm.h +++ b/src/pkg/runtime/defs_netbsd_arm.h @@ -1,5 +1,5 @@ // Created by cgo -cdefs - DO NOT EDIT -// cgo -cdefs defs_netbsd.go +// cgo -cdefs defs_netbsd.go defs_netbsd_arm.go enum { @@ -138,3 +138,27 @@ struct UcontextT { }; #pragma pack off +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_netbsd.go defs_netbsd_arm.go + + +enum { + REG_R0 = 0x0, + REG_R1 = 0x1, + REG_R2 = 0x2, + REG_R3 = 0x3, + REG_R4 = 0x4, + REG_R5 = 0x5, + REG_R6 = 0x6, + REG_R7 = 0x7, + REG_R8 = 0x8, + REG_R9 = 0x9, + REG_R10 = 0xa, + REG_R11 = 0xb, + REG_R12 = 0xc, + REG_R13 = 0xd, + REG_R14 = 0xe, + REG_R15 = 0xf, + REG_CPSR = 0x10, +}; + diff --git a/src/pkg/runtime/env_plan9.c b/src/pkg/runtime/env_plan9.c index 848d73303..0483d7eef 100644 --- a/src/pkg/runtime/env_plan9.c +++ b/src/pkg/runtime/env_plan9.c @@ -20,7 +20,7 @@ runtime·getenv(int8 *s) runtime·memmove((void*)file, (void*)"/env/", 5); runtime·memmove((void*)(file+5), (void*)s, len); - fd = runtime·open(file, OREAD); + fd = runtime·open((int8*)file, OREAD, 0); if(fd < 0) return nil; n = runtime·seek(fd, 0, 2); diff --git a/src/pkg/runtime/export_futex_test.go b/src/pkg/runtime/export_futex_test.go new file mode 100644 index 000000000..bcab60fbe --- /dev/null +++ b/src/pkg/runtime/export_futex_test.go @@ -0,0 +1,13 @@ +// 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. + +// +build linux freebsd + +package runtime + +func futexsleep(addr *uint32, val uint32, ns int64) +func futexwakeup(addr *uint32, val uint32) + +var Futexsleep = futexsleep +var Futexwakeup = futexwakeup diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go index fbaffd1d5..20f234253 100644 --- a/src/pkg/runtime/extern.go +++ b/src/pkg/runtime/extern.go @@ -3,10 +3,54 @@ // license that can be found in the LICENSE file. /* - Package runtime contains operations that interact with Go's runtime system, - such as functions to control goroutines. It also includes the low-level type information - used by the reflect package; see reflect's documentation for the programmable - interface to the run-time type system. +Package runtime contains operations that interact with Go's runtime system, +such as functions to control goroutines. It also includes the low-level type information +used by the reflect package; see reflect's documentation for the programmable +interface to the run-time type system. + +Environment Variables + +The following environment variables ($name or %name%, depending on the host +operating system) control the run-time behavior of Go programs. The meanings +and use may change from release to release. + +The GOGC variable sets the initial garbage collection target percentage. +A collection is triggered when the ratio of freshly allocated data to live data +remaining after the previous collection reaches this percentage. The default +is GOGC=100. Setting GOGC=off disables the garbage collector entirely. +The runtime/debug package's SetGCPercent function allows changing this +percentage at run time. See http://golang.org/pkg/runtime/debug/#SetGCPercent. + +The GOGCTRACE variable controls debug output from the garbage collector. +Setting GOGCTRACE=1 causes the garbage collector to emit a single line to standard +error at each collection, summarizing the amount of memory collected and the +length of the pause. Setting GOGCTRACE=2 emits the same summary but also +repeats each collection. + +The GOMAXPROCS variable limits the number of operating system threads that +can execute user-level Go code simultaneously. There is no limit to the number of threads +that can be blocked in system calls on behalf of Go code; those do not count against +the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes +the limit. + +The GOTRACEBACK variable controls the amount of output generated when a Go +program fails due to an unrecovered panic or an unexpected runtime condition. +By default, a failure prints a stack trace for every extant goroutine, eliding functions +internal to the run-time system, and then exits with exit code 2. +If GOTRACEBACK=0, the per-goroutine stack traces are omitted entirely. +If GOTRACEBACK=1, the default behavior is used. +If GOTRACEBACK=2, the per-goroutine stack traces include run-time functions. +If GOTRACEBACK=crash, the per-goroutine stack traces include run-time functions, +and if possible the program crashes in an operating-specific manner instead of +exiting. For example, on Unix systems, the program raises SIGABRT to trigger a +core dump. + +The GOARCH, GOOS, GOPATH, and GOROOT environment variables complete +the set of Go environment variables. They influence the building of Go programs +(see http://golang.org/cmd/go and http://golang.org/pkg/go/build). +GOARCH, GOOS, and GOROOT are recorded at compile time and made available by +constants or functions in this package, but they do not influence the execution +of the run-time system. */ package runtime diff --git a/src/pkg/runtime/futex_test.go b/src/pkg/runtime/futex_test.go new file mode 100644 index 000000000..51f4d0f12 --- /dev/null +++ b/src/pkg/runtime/futex_test.go @@ -0,0 +1,31 @@ +// 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. + +// +build linux freebsd + +package runtime_test + +import ( + . "runtime" + "testing" + "time" +) + +func TestFutexsleep(t *testing.T) { + ch := make(chan bool, 1) + var dummy uint32 + start := time.Now() + go func() { + Entersyscall() + Futexsleep(&dummy, 0, (1<<31+100)*1e9) + Exitsyscall() + ch <- true + }() + select { + case <-ch: + t.Errorf("futexsleep finished early after %s!", time.Since(start)) + case <-time.After(time.Second): + Futexwakeup(&dummy, 1) + } +} diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go index e1e1b1d01..3475339bf 100644 --- a/src/pkg/runtime/gc_test.go +++ b/src/pkg/runtime/gc_test.go @@ -7,6 +7,7 @@ package runtime_test import ( "os" "runtime" + "runtime/debug" "testing" ) @@ -82,3 +83,17 @@ func TestGcDeepNesting(t *testing.T) { t.Fail() } } + +func TestGcHashmapIndirection(t *testing.T) { + defer debug.SetGCPercent(debug.SetGCPercent(1)) + runtime.GC() + type T struct { + a [256]int + } + m := make(map[T]T) + for i := 0; i < 2000; i++ { + var a T + a.a[0] = i + m[a] = T{} + } +} diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 37111daa9..0f92becab 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -9,703 +9,831 @@ #include "type.h" #include "race.h" -/* Hmap flag values */ -#define IndirectVal (1<<0) /* storing pointers to values */ -#define IndirectKey (1<<1) /* storing pointers to keys */ -#define CanFreeTable (1<<2) /* okay to free subtables */ -#define CanFreeKey (1<<3) /* okay to free pointers to keys */ - -struct Hmap { /* a hash table; initialize with hash_init() */ - uintgo count; /* elements in table - must be first */ - uint8 datasize; /* amount of data to store in entry */ - uint8 flag; - uint8 valoff; /* offset of value in key+value data block */ - int32 changes; /* inc'ed whenever a subtable is created/grown */ - uintptr hash0; /* hash seed */ - struct hash_subtable *st; /* first-level table */ +// This file contains the implementation of Go's map type. +// +// The map is just a hash table. The data is arranged +// into an array of buckets. Each bucket contains up to +// 8 key/value pairs. The low-order bits of the hash are +// used to select a bucket. Each bucket contains a few +// high-order bits of each hash to distinguish the entries +// within a single bucket. +// +// If more than 8 keys hash to a bucket, we chain on +// extra buckets. +// +// When the hashtable grows, we allocate a new array +// of buckets twice as big. Buckets are incrementally +// copied from the old bucket array to the new bucket array. +// +// Map iterators walk through the array of buckets and +// return the keys in walk order (bucket #, then overflow +// chain order, then bucket index). To maintain iteration +// semantics, we never move keys within their bucket (if +// we did, keys might be returned 0 or 2 times). When +// growing the table, iterators remain iterating through the +// old table and must check the new table if the bucket +// they are iterating through has been moved ("evacuated") +// to the new table. + +// Maximum number of key/value pairs a bucket can hold. +#define BUCKETSIZE 8 + +// Maximum average load of a bucket that triggers growth. +#define LOAD 6.5 + +// Picking LOAD: too large and we have lots of overflow +// buckets, too small and we waste a lot of space. I wrote +// a simple program to check some stats for different loads: +// (64-bit, 8 byte keys and values) +// LOAD %overflow bytes/entry hitprobe missprobe +// 4.00 2.13 20.77 3.00 4.00 +// 4.50 4.05 17.30 3.25 4.50 +// 5.00 6.85 14.77 3.50 5.00 +// 5.50 10.55 12.94 3.75 5.50 +// 6.00 15.27 11.67 4.00 6.00 +// 6.50 20.90 10.79 4.25 6.50 +// 7.00 27.14 10.15 4.50 7.00 +// 7.50 34.03 9.73 4.75 7.50 +// 8.00 41.10 9.40 5.00 8.00 +// +// %overflow = percentage of buckets which have an overflow bucket +// bytes/entry = overhead bytes used per key/value pair +// hitprobe = # of entries to check when looking up a present key +// missprobe = # of entries to check when looking up an absent key +// +// Keep in mind this data is for maximally loaded tables, i.e. just +// before the table grows. Typical tables will be somewhat less loaded. + +// Maximum key or value size to keep inline (instead of mallocing per element). +// Must fit in a uint8. +// Fast versions cannot handle big values - the cutoff size for +// fast versions in ../../cmd/gc/walk.c must be at most this value. +#define MAXKEYSIZE 128 +#define MAXVALUESIZE 128 + +typedef struct Bucket Bucket; +struct Bucket +{ + uint8 tophash[BUCKETSIZE]; // top 8 bits of hash of each entry (0 = empty) + Bucket *overflow; // overflow bucket, if any + byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values }; +// NOTE: packing all the keys together and then all the values together makes the +// code a bit more complicated than alternating key/value/key/value/... but it allows +// us to eliminate padding which would be needed for, e.g., map[int64]int8. -#define MaxData 255 +// Low-order bit of overflow field is used to mark a bucket as already evacuated +// without destroying the overflow pointer. +// Only buckets in oldbuckets will be marked as evacuated. +// Evacuated bit will be set identically on the base bucket and any overflow buckets. +#define evacuated(b) (((uintptr)(b)->overflow & 1) != 0) +#define overflowptr(b) ((Bucket*)((uintptr)(b)->overflow & ~(uintptr)1)) -struct hash_entry { - hash_hash_t hash; /* hash value of data */ - byte data[1]; /* user data has "datasize" bytes */ -}; +// Initialize bucket to the empty state. This only works if BUCKETSIZE==8! +#define clearbucket(b) { *(uint64*)((b)->tophash) = 0; (b)->overflow = nil; } -struct hash_subtable { - uint8 power; /* bits used to index this table */ - uint8 used; /* bits in hash used before reaching this table */ - uint8 datasize; /* bytes of client data in an entry */ - uint8 max_probes; /* max number of probes when searching */ - int16 limit_bytes; /* max_probes * (datasize+sizeof (hash_hash_t)) */ - struct hash_entry *last; /* points to last element of entry[] */ - struct hash_entry entry[1]; /* 2**power+max_probes-1 elements of elemsize bytes */ +struct Hmap +{ + uintgo count; // # live cells == size of map. Must be first (used by len() builtin) + uint8 B; // log_2 of # of buckets (can hold up to LOAD * 2^B items) + uint8 flags; + uint8 keysize; // key size in bytes + uint8 valuesize; // value size in bytes + uint16 bucketsize; // bucket size in bytes + + uintptr hash0; // hash seed + byte *buckets; // array of 2^B Buckets + byte *oldbuckets; // previous bucket array of half the size, non-nil only when growing + uintptr nevacuate; // progress counter for evacuation (buckets less than this have been evacuated) }; -#define HASH_DATA_EQ(eq, t, h,x,y) ((eq)=0, (*t->key->alg->equal) (&(eq), t->key->size, (x), (y)), (eq)) - -#define HASH_REHASH 0x2 /* an internal flag */ -/* the number of bits used is stored in the flags word too */ -#define HASH_USED(x) ((x) >> 2) -#define HASH_MAKE_USED(x) ((x) << 2) +// possible flags +enum +{ + IndirectKey = 1, // storing pointers to keys + IndirectValue = 2, // storing pointers to values + Iterator = 4, // there may be an iterator using buckets + OldIterator = 8, // there may be an iterator using oldbuckets + CanFreeBucket = 16, // ok to free buckets + CanFreeKey = 32, // keys are indirect and ok to free keys +}; -#define HASH_LOW 6 -#define HASH_ONE (((hash_hash_t)1) << HASH_LOW) -#define HASH_MASK (HASH_ONE - 1) -#define HASH_ADJUST(x) (((x) < HASH_ONE) << HASH_LOW) +// Macros for dereferencing indirect keys +#define IK(h, p) (((h)->flags & IndirectKey) != 0 ? *(byte**)(p) : (p)) +#define IV(h, p) (((h)->flags & IndirectValue) != 0 ? *(byte**)(p) : (p)) -#define HASH_BITS (sizeof (hash_hash_t) * 8) +enum +{ + docheck = 0, // check invariants before and after every op. Slow!!! + debug = 0, // print every operation +}; +static void +check(MapType *t, Hmap *h) +{ + uintptr bucket, oldbucket; + Bucket *b; + uintptr i; + uintptr hash; + uintgo cnt; + uint8 top; + bool eq; + byte *k, *v; -#define HASH_SUBHASH HASH_MASK -#define HASH_NIL 0 -#define HASH_NIL_MEMSET 0 + cnt = 0; -#define HASH_OFFSET(base, byte_offset) \ - ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) + // check buckets + for(bucket = 0; bucket < (uintptr)1 << h->B; bucket++) { + if(h->oldbuckets != nil) { + oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1); + b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize); + if(!evacuated(b)) + continue; // b is still uninitialized + } + for(b = (Bucket*)(h->buckets + bucket * h->bucketsize); b != nil; b = b->overflow) { + for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] == 0) + continue; + cnt++; + t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k)); + if(!eq) + continue; // NaN! + hash = h->hash0; + t->key->alg->hash(&hash, t->key->size, IK(h, k)); + top = hash >> (8*sizeof(uintptr) - 8); + if(top == 0) + top = 1; + if(top != b->tophash[i]) + runtime·throw("bad hash"); + } + } + } -#define HASH_MAX_PROBES 15 /* max entries to probe before rehashing */ -#define HASH_MAX_POWER 12 /* max power of 2 to create sub-tables */ + // check oldbuckets + if(h->oldbuckets != nil) { + for(oldbucket = 0; oldbucket < (uintptr)1 << (h->B - 1); oldbucket++) { + b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize); + if(evacuated(b)) + continue; + if(oldbucket < h->nevacuate) + runtime·throw("bucket became unevacuated"); + for(; b != nil; b = overflowptr(b)) { + for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] == 0) + continue; + cnt++; + t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k)); + if(!eq) + continue; // NaN! + hash = h->hash0; + t->key->alg->hash(&hash, t->key->size, IK(h, k)); + top = hash >> (8*sizeof(uintptr) - 8); + if(top == 0) + top = 1; + if(top != b->tophash[i]) + runtime·throw("bad hash (old)"); + } + } + } + } -/* return a hash layer with 2**power empty entries */ -static struct hash_subtable * -hash_subtable_new (Hmap *h, int32 power, int32 used) -{ - int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - int32 bytes = elemsize << power; - struct hash_subtable *st; - int32 limit_bytes = HASH_MAX_PROBES * elemsize; - int32 max_probes = HASH_MAX_PROBES; - - if (bytes < limit_bytes) { - limit_bytes = bytes; - max_probes = 1 << power; + if(cnt != h->count) { + runtime·printf("%D %D\n", (uint64)cnt, (uint64)h->count); + runtime·throw("entries missing"); } - bytes += limit_bytes - elemsize; - st = runtime·mallocgc(offsetof (struct hash_subtable, entry[0]) + bytes, UseSpanType ? FlagNoPointers : 0, 1, 1); - st->power = power; - st->used = used; - st->datasize = h->datasize; - st->max_probes = max_probes; - st->limit_bytes = limit_bytes; - st->last = HASH_OFFSET (st->entry, bytes) - 1; - memset (st->entry, HASH_NIL_MEMSET, bytes); - return (st); } static void -init_sizes (int64 hint, int32 *init_power) +hash_init(MapType *t, Hmap *h, uint32 hint) { - int32 log = 0; - int32 i; - - for (i = 32; i != 0; i >>= 1) { - if ((hint >> (log + i)) != 0) { - log += i; - } + uint8 B; + byte *buckets; + uintptr i; + uintptr keysize, valuesize, bucketsize; + uint8 flags; + Bucket *b; + + flags = CanFreeBucket; + + // figure out how big we have to make everything + keysize = t->key->size; + if(keysize > MAXKEYSIZE) { + flags |= IndirectKey | CanFreeKey; + keysize = sizeof(byte*); } - log += 1 + (((hint << 3) >> log) >= 11); /* round up for utilization */ - if (log <= 14) { - *init_power = log; - } else { - *init_power = 12; + valuesize = t->elem->size; + if(valuesize >= MAXVALUESIZE) { + flags |= IndirectValue; + valuesize = sizeof(byte*); + } + bucketsize = offsetof(Bucket, data[0]) + (keysize + valuesize) * BUCKETSIZE; + + // invariants we depend on. We should probably check these at compile time + // somewhere, but for now we'll do it here. + if(t->key->align > BUCKETSIZE) + runtime·throw("key align too big"); + if(t->elem->align > BUCKETSIZE) + runtime·throw("value align too big"); + if(t->key->size % t->key->align != 0) + runtime·throw("key size not a multiple of key align"); + if(t->elem->size % t->elem->align != 0) + runtime·throw("value size not a multiple of value align"); + if(BUCKETSIZE < 8) + runtime·throw("bucketsize too small for proper alignment"); + if(BUCKETSIZE != 8) + runtime·throw("must redo clearbucket"); + if(sizeof(void*) == 4 && t->key->align > 4) + runtime·throw("need padding in bucket (key)"); + if(sizeof(void*) == 4 && t->elem->align > 4) + runtime·throw("need padding in bucket (value)"); + + // find size parameter which will hold the requested # of elements + B = 0; + while(hint > BUCKETSIZE && hint > LOAD * ((uintptr)1 << B)) + B++; + + // allocate initial hash table + // If hint is large zeroing this memory could take a while. + buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0); + for(i = 0; i < (uintptr)1 << B; i++) { + b = (Bucket*)(buckets + i * bucketsize); + clearbucket(b); } -} -static void -hash_init (Hmap *h, int32 datasize, int64 hint) -{ - int32 init_power; - - if(datasize < sizeof (void *)) - datasize = sizeof (void *); - datasize = ROUND(datasize, sizeof (void *)); - init_sizes (hint, &init_power); - h->datasize = datasize; - assert (h->datasize == datasize); - assert (sizeof (void *) <= h->datasize); + // initialize Hmap + // Note: we save all these stores to the end so gciter doesn't see + // a partially initialized map. h->count = 0; - h->changes = 0; - h->st = hash_subtable_new (h, init_power, 0); + h->B = B; + h->flags = flags; + h->keysize = keysize; + h->valuesize = valuesize; + h->bucketsize = bucketsize; h->hash0 = runtime·fastrand1(); + h->buckets = buckets; + h->oldbuckets = nil; + h->nevacuate = 0; + if(docheck) + check(t, h); } +// Moves entries in oldbuckets[i] to buckets[i] and buckets[i+2^k]. +// We leave the original bucket intact, except for the evacuated marks, so that +// iterators can still iterate through the old buckets. static void -hash_remove_n (struct hash_subtable *st, struct hash_entry *dst_e, int32 n) +evacuate(MapType *t, Hmap *h, uintptr oldbucket) { - int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); - struct hash_entry *src_e = HASH_OFFSET (dst_e, n * elemsize); - struct hash_entry *last_e = st->last; - int32 shift = HASH_BITS - (st->power + st->used); - int32 index_mask = (((hash_hash_t)1) << st->power) - 1; - int32 dst_i = (((byte *) dst_e) - ((byte *) st->entry)) / elemsize; - int32 src_i = dst_i + n; - hash_hash_t hash; - int32 skip; - int32 bytes; - - while (dst_e != src_e) { - if (src_e <= last_e) { - struct hash_entry *cp_e = src_e; - int32 save_dst_i = dst_i; - while (cp_e <= last_e && (hash = cp_e->hash) != HASH_NIL && - ((hash >> shift) & index_mask) <= dst_i) { - cp_e = HASH_OFFSET (cp_e, elemsize); - dst_i++; + Bucket *b; + Bucket *nextb; + Bucket *x, *y; + Bucket *newx, *newy; + uintptr xi, yi; + uintptr newbit; + uintptr hash; + uintptr i; + byte *k, *v; + byte *xk, *yk, *xv, *yv; + byte *ob; + + b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize); + newbit = (uintptr)1 << (h->B - 1); + + if(!evacuated(b)) { + // TODO: reuse overflow buckets instead of using new ones, if there + // is no iterator using the old buckets. (If CanFreeBuckets and !OldIterator.) + + x = (Bucket*)(h->buckets + oldbucket * h->bucketsize); + y = (Bucket*)(h->buckets + (oldbucket + newbit) * h->bucketsize); + clearbucket(x); + clearbucket(y); + xi = 0; + yi = 0; + xk = x->data; + yk = y->data; + xv = xk + h->keysize * BUCKETSIZE; + yv = yk + h->keysize * BUCKETSIZE; + do { + for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] == 0) + continue; + hash = h->hash0; + t->key->alg->hash(&hash, t->key->size, IK(h, k)); + // NOTE: if key != key, then this hash could be (and probably will be) + // entirely different from the old hash. We effectively only update + // the B'th bit of the hash in this case. + if((hash & newbit) == 0) { + if(xi == BUCKETSIZE) { + newx = runtime·mallocgc(h->bucketsize, 0, 1, 0); + clearbucket(newx); + x->overflow = newx; + x = newx; + xi = 0; + xk = x->data; + xv = xk + h->keysize * BUCKETSIZE; + } + x->tophash[xi] = b->tophash[i]; + if((h->flags & IndirectKey) != 0) { + *(byte**)xk = *(byte**)k; // copy pointer + } else { + t->key->alg->copy(t->key->size, xk, k); // copy value + } + if((h->flags & IndirectValue) != 0) { + *(byte**)xv = *(byte**)v; + } else { + t->elem->alg->copy(t->elem->size, xv, v); + } + xi++; + xk += h->keysize; + xv += h->valuesize; + } else { + if(yi == BUCKETSIZE) { + newy = runtime·mallocgc(h->bucketsize, 0, 1, 0); + clearbucket(newy); + y->overflow = newy; + y = newy; + yi = 0; + yk = y->data; + yv = yk + h->keysize * BUCKETSIZE; + } + y->tophash[yi] = b->tophash[i]; + if((h->flags & IndirectKey) != 0) { + *(byte**)yk = *(byte**)k; + } else { + t->key->alg->copy(t->key->size, yk, k); + } + if((h->flags & IndirectValue) != 0) { + *(byte**)yv = *(byte**)v; + } else { + t->elem->alg->copy(t->elem->size, yv, v); + } + yi++; + yk += h->keysize; + yv += h->valuesize; + } } - bytes = ((byte *) cp_e) - (byte *) src_e; - memmove (dst_e, src_e, bytes); - dst_e = HASH_OFFSET (dst_e, bytes); - src_e = cp_e; - src_i += dst_i - save_dst_i; - if (src_e <= last_e && (hash = src_e->hash) != HASH_NIL) { - skip = ((hash >> shift) & index_mask) - dst_i; + + // mark as evacuated so we don't do it again. + // this also tells any iterators that this data isn't golden anymore. + nextb = b->overflow; + b->overflow = (Bucket*)((uintptr)nextb + 1); + + b = nextb; + } while(b != nil); + + // Free old overflow buckets as much as we can. + if((h->flags & OldIterator) == 0) { + b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize); + if((h->flags & CanFreeBucket) != 0) { + while((nextb = overflowptr(b)) != nil) { + b->overflow = nextb->overflow; + runtime·free(nextb); + } } else { - skip = src_i - dst_i; + // can't explicitly free overflow buckets, but at least + // we can unlink them. + b->overflow = (Bucket*)1; } - } else { - skip = src_i - dst_i; } - bytes = skip * elemsize; - memset (dst_e, HASH_NIL_MEMSET, bytes); - dst_e = HASH_OFFSET (dst_e, bytes); - dst_i += skip; } -} -static int32 -hash_insert_internal (MapType*, struct hash_subtable **pst, int32 flags, hash_hash_t hash, - Hmap *h, void *data, void **pres); + // advance evacuation mark + if(oldbucket == h->nevacuate) { + h->nevacuate = oldbucket + 1; + if(oldbucket + 1 == newbit) { // newbit == # of oldbuckets + // free main bucket array + if((h->flags & (OldIterator | CanFreeBucket)) == CanFreeBucket) { + ob = h->oldbuckets; + h->oldbuckets = nil; + runtime·free(ob); + } else { + h->oldbuckets = nil; + } + } + } + if(docheck) + check(t, h); +} static void -hash_conv (MapType *t, Hmap *h, - struct hash_subtable *st, int32 flags, - hash_hash_t hash, - struct hash_entry *e) +grow_work(MapType *t, Hmap *h, uintptr bucket) { - int32 new_flags = (flags + HASH_MAKE_USED (st->power)) | HASH_REHASH; - int32 shift = HASH_BITS - HASH_USED (new_flags); - hash_hash_t prefix_mask = (-(hash_hash_t)1) << shift; - int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - void *dummy_result; - struct hash_entry *de; - int32 index_mask = (1 << st->power) - 1; - hash_hash_t e_hash; - struct hash_entry *pe = HASH_OFFSET (e, -elemsize); - - while (e != st->entry && (e_hash = pe->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { - e = pe; - pe = HASH_OFFSET (pe, -elemsize); - } + uintptr noldbuckets; - de = e; - while (e <= st->last && - (e_hash = e->hash) != HASH_NIL && - (e_hash & HASH_MASK) != HASH_SUBHASH) { - struct hash_entry *target_e = HASH_OFFSET (st->entry, ((e_hash >> shift) & index_mask) * elemsize); - struct hash_entry *ne = HASH_OFFSET (e, elemsize); - hash_hash_t current = e_hash & prefix_mask; - if (de < target_e) { - memset (de, HASH_NIL_MEMSET, ((byte *) target_e) - (byte *) de); - de = target_e; - } - if ((hash & prefix_mask) == current || - (ne <= st->last && (e_hash = ne->hash) != HASH_NIL && - (e_hash & prefix_mask) == current)) { - struct hash_subtable *new_st = hash_subtable_new (h, 1, HASH_USED (new_flags)); - int32 rc = hash_insert_internal (t, &new_st, new_flags, e->hash, h, e->data, &dummy_result); - assert (rc == 0); - memcpy(dummy_result, e->data, h->datasize); - e = ne; - while (e <= st->last && (e_hash = e->hash) != HASH_NIL && (e_hash & prefix_mask) == current) { - assert ((e_hash & HASH_MASK) != HASH_SUBHASH); - rc = hash_insert_internal (t, &new_st, new_flags, e_hash, h, e->data, &dummy_result); - assert (rc == 0); - memcpy(dummy_result, e->data, h->datasize); - e = HASH_OFFSET (e, elemsize); - } - memset (de->data, HASH_NIL_MEMSET, h->datasize); - *(struct hash_subtable **)de->data = new_st; - de->hash = current | HASH_SUBHASH; - } else { - if (e != de) { - memcpy (de, e, elemsize); - } - e = HASH_OFFSET (e, elemsize); - } - de = HASH_OFFSET (de, elemsize); - } - if (e != de) { - hash_remove_n (st, de, (((byte *) e) - (byte *) de) / elemsize); - } + noldbuckets = (uintptr)1 << (h->B - 1); + + // make sure we evacuate the oldbucket corresponding + // to the bucket we're about to use + evacuate(t, h, bucket & (noldbuckets - 1)); + + // evacuate one more oldbucket to make progress on growing + if(h->oldbuckets != nil) + evacuate(t, h, h->nevacuate); } static void -hash_grow (MapType *t, Hmap *h, struct hash_subtable **pst, int32 flags) +hash_grow(MapType *t, Hmap *h) { - struct hash_subtable *old_st = *pst; - int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - *pst = hash_subtable_new (h, old_st->power + 1, HASH_USED (flags)); - struct hash_entry *last_e = old_st->last; - struct hash_entry *e; - void *dummy_result; - int32 used = 0; - - flags |= HASH_REHASH; - for (e = old_st->entry; e <= last_e; e = HASH_OFFSET (e, elemsize)) { - hash_hash_t hash = e->hash; - if (hash != HASH_NIL) { - int32 rc = hash_insert_internal (t, pst, flags, e->hash, h, e->data, &dummy_result); - assert (rc == 0); - memcpy(dummy_result, e->data, h->datasize); - used++; - } + byte *old_buckets; + byte *new_buckets; + uint8 flags; + + // allocate a bigger hash table + if(h->oldbuckets != nil) + runtime·throw("evacuation not done in time"); + old_buckets = h->buckets; + // NOTE: this could be a big malloc, but since we don't need zeroing it is probably fast. + new_buckets = runtime·mallocgc(h->bucketsize << (h->B + 1), 0, 1, 0); + flags = (h->flags & ~(Iterator | OldIterator)); + if((h->flags & Iterator) != 0) { + flags |= OldIterator; + // We can't free indirect keys any more, as + // they are potentially aliased across buckets. + flags &= ~CanFreeKey; } - if (h->flag & CanFreeTable) - free (old_st); + + // commit the grow (atomic wrt gc) + h->B++; + h->flags = flags; + h->oldbuckets = old_buckets; + h->buckets = new_buckets; + h->nevacuate = 0; + + // the actual copying of the hash table data is done incrementally + // by grow_work() and evacuate(). + if(docheck) + check(t, h); } -static int32 -hash_lookup (MapType *t, Hmap *h, void *data, void **pres) +// returns ptr to value associated with key *keyp, or nil if none. +// if it returns non-nil, updates *keyp to point to the currently stored key. +static byte* +hash_lookup(MapType *t, Hmap *h, byte **keyp) { - int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - hash_hash_t hash; - struct hash_subtable *st = h->st; - int32 used = 0; - hash_hash_t e_hash; - struct hash_entry *e; - struct hash_entry *end_e; void *key; + uintptr hash; + uintptr bucket; + Bucket *b; + uint8 top; + uintptr i; bool eq; + byte *k, *k2, *v; + key = *keyp; + if(docheck) + check(t, h); hash = h->hash0; - (*t->key->alg->hash) (&hash, t->key->size, data); - hash &= ~HASH_MASK; - hash += HASH_ADJUST (hash); - for (;;) { - int32 shift = HASH_BITS - (st->power + used); - int32 index_mask = (1 << st->power) - 1; - int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ - - e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ - e_hash = e->hash; - if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ - break; - } - used += st->power; - st = *(struct hash_subtable **)e->data; - } - end_e = HASH_OFFSET (e, st->limit_bytes); - while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { - e = HASH_OFFSET (e, elemsize); - } - while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { - key = e->data; - if (h->flag & IndirectKey) - key = *(void**)e->data; - if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */ - *pres = e->data; - return (1); + t->key->alg->hash(&hash, t->key->size, key); + bucket = hash & (((uintptr)1 << h->B) - 1); + if(h->oldbuckets != nil) + grow_work(t, h, bucket); + b = (Bucket*)(h->buckets + bucket * h->bucketsize); + top = hash >> (sizeof(uintptr)*8 - 8); + if(top == 0) + top = 1; + do { + for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] == top) { + k2 = IK(h, k); + t->key->alg->equal(&eq, t->key->size, key, k2); + if(eq) { + *keyp = k2; + return IV(h, v); + } + } } - e = HASH_OFFSET (e, elemsize); - } - USED(e_hash); - *pres = 0; - return (0); + b = b->overflow; + } while(b != nil); + return nil; } -static int32 -hash_remove (MapType *t, Hmap *h, void *data) +// When an item is not found, fast versions return a pointer to this zeroed memory. +static uint8 empty_value[MAXVALUESIZE]; + +// Specialized versions of mapaccess1 for specific types. +// See ./hashmap_fast and ../../cmd/gc/walk.c. +#define HASH_LOOKUP1 runtime·mapaccess1_fast32 +#define HASH_LOOKUP2 runtime·mapaccess2_fast32 +#define KEYTYPE uint32 +#define HASHFUNC runtime·algarray[AMEM32].hash +#define EQFUNC(x,y) ((x) == (y)) +#define QUICKEQ(x) true +#include "hashmap_fast.c" + +#undef HASH_LOOKUP1 +#undef HASH_LOOKUP2 +#undef KEYTYPE +#undef HASHFUNC +#undef EQFUNC +#undef QUICKEQ + +#define HASH_LOOKUP1 runtime·mapaccess1_fast64 +#define HASH_LOOKUP2 runtime·mapaccess2_fast64 +#define KEYTYPE uint64 +#define HASHFUNC runtime·algarray[AMEM64].hash +#define EQFUNC(x,y) ((x) == (y)) +#define QUICKEQ(x) true +#include "hashmap_fast.c" + +#undef HASH_LOOKUP1 +#undef HASH_LOOKUP2 +#undef KEYTYPE +#undef HASHFUNC +#undef EQFUNC +#undef QUICKEQ + +#define HASH_LOOKUP1 runtime·mapaccess1_faststr +#define HASH_LOOKUP2 runtime·mapaccess2_faststr +#define KEYTYPE String +#define HASHFUNC runtime·algarray[ASTRING].hash +#define EQFUNC(x,y) ((x).len == (y).len && ((x).str == (y).str || runtime·mcmp((x).str, (y).str, (x).len) == 0)) +#define QUICKEQ(x) ((x).len < 32) +#include "hashmap_fast.c" + +static void +hash_insert(MapType *t, Hmap *h, void *key, void *value) { - int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - hash_hash_t hash; - struct hash_subtable *st = h->st; - int32 used = 0; - hash_hash_t e_hash; - struct hash_entry *e; - struct hash_entry *end_e; + uintptr hash; + uintptr bucket; + uintptr i; bool eq; - void *key; - + Bucket *b; + Bucket *newb; + uint8 *inserti; + byte *insertk, *insertv; + uint8 top; + byte *k, *v; + byte *kmem, *vmem; + + if(docheck) + check(t, h); hash = h->hash0; - (*t->key->alg->hash) (&hash, t->key->size, data); - hash &= ~HASH_MASK; - hash += HASH_ADJUST (hash); - for (;;) { - int32 shift = HASH_BITS - (st->power + used); - int32 index_mask = (1 << st->power) - 1; - int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ - - e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ - e_hash = e->hash; - if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ - break; + t->key->alg->hash(&hash, t->key->size, key); + again: + bucket = hash & (((uintptr)1 << h->B) - 1); + if(h->oldbuckets != nil) + grow_work(t, h, bucket); + b = (Bucket*)(h->buckets + bucket * h->bucketsize); + top = hash >> (sizeof(uintptr)*8 - 8); + if(top == 0) + top = 1; + inserti = 0; + insertk = nil; + insertv = nil; + while(true) { + for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] != top) { + if(b->tophash[i] == 0 && inserti == nil) { + inserti = &b->tophash[i]; + insertk = k; + insertv = v; + } + continue; + } + t->key->alg->equal(&eq, t->key->size, key, IK(h, k)); + if(!eq) + continue; + // already have a mapping for key. Update it. + t->key->alg->copy(t->key->size, IK(h, k), key); // Need to update key for keys which are distinct but equal (e.g. +0.0 and -0.0) + t->elem->alg->copy(t->elem->size, IV(h, v), value); + if(docheck) + check(t, h); + return; } - used += st->power; - st = *(struct hash_subtable **)e->data; - } - end_e = HASH_OFFSET (e, st->limit_bytes); - while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { - e = HASH_OFFSET (e, elemsize); + if(b->overflow == nil) + break; + b = b->overflow; } - while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { - key = e->data; - if (h->flag & IndirectKey) - key = *(void**)e->data; - if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */ - // Free key if indirect, but only if reflect can't be - // holding a pointer to it. Deletions are rare, - // indirect (large) keys are rare, reflect on maps - // is rare. So in the rare, rare, rare case of deleting - // an indirect key from a map that has been reflected on, - // we leave the key for garbage collection instead of - // freeing it here. - if (h->flag & CanFreeKey) - free (key); - if (h->flag & IndirectVal) - free (*(void**)((byte*)e->data + h->valoff)); - hash_remove_n (st, e, 1); - h->count--; - return (1); - } - e = HASH_OFFSET (e, elemsize); + + // did not find mapping for key. Allocate new cell & add entry. + if(h->count >= LOAD * ((uintptr)1 << h->B) && h->count >= BUCKETSIZE) { + hash_grow(t, h); + goto again; // Growing the table invalidates everything, so try again } - USED(e_hash); - return (0); -} -static int32 -hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_hash_t hash, - Hmap *h, void *data, void **pres) -{ - int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - bool eq; + if(inserti == nil) { + // all current buckets are full, allocate a new one. + newb = runtime·mallocgc(h->bucketsize, 0, 1, 0); + clearbucket(newb); + b->overflow = newb; + inserti = newb->tophash; + insertk = newb->data; + insertv = insertk + h->keysize * BUCKETSIZE; + } - if ((flags & HASH_REHASH) == 0) { - hash += HASH_ADJUST (hash); - hash &= ~HASH_MASK; + // store new key/value at insert position + if((h->flags & IndirectKey) != 0) { + kmem = runtime·mallocgc(t->key->size, 0, 1, 0); + *(byte**)insertk = kmem; + insertk = kmem; } - for (;;) { - struct hash_subtable *st = *pst; - int32 shift = HASH_BITS - (st->power + HASH_USED (flags)); - int32 index_mask = (1 << st->power) - 1; - int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ - struct hash_entry *start_e = - HASH_OFFSET (st->entry, i * elemsize); /* start_e is the pointer to element i */ - struct hash_entry *e = start_e; /* e is going to range over [start_e, end_e) */ - struct hash_entry *end_e; - hash_hash_t e_hash = e->hash; - - if ((e_hash & HASH_MASK) == HASH_SUBHASH) { /* a subtable */ - pst = (struct hash_subtable **) e->data; - flags += HASH_MAKE_USED (st->power); - continue; - } - end_e = HASH_OFFSET (start_e, st->limit_bytes); - while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { - e = HASH_OFFSET (e, elemsize); - i++; - } - if (e != end_e && e_hash != HASH_NIL) { - /* ins_e ranges over the elements that may match */ - struct hash_entry *ins_e = e; - int32 ins_i = i; - hash_hash_t ins_e_hash; - void *key; - while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { - key = ins_e->data; - if (h->flag & IndirectKey) - key = *(void**)key; - if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */ - *pres = ins_e->data; - return (1); - } - if (e_hash == hash) { /* adjust hash if it collides */ - assert ((flags & HASH_REHASH) == 0); - hash++; - if ((hash & HASH_MASK) == HASH_SUBHASH) - runtime·throw("runtime: map hash collision overflow"); - } - ins_e = HASH_OFFSET (ins_e, elemsize); - ins_i++; - if (e_hash <= hash) { /* set e to insertion point */ - e = ins_e; - i = ins_i; - } - } - /* set ins_e to the insertion point for the new element */ - ins_e = e; - ins_i = i; - ins_e_hash = 0; - /* move ins_e to point at the end of the contiguous block, but - stop if any element can't be moved by one up */ - while (ins_e <= st->last && (ins_e_hash = ins_e->hash) != HASH_NIL && - ins_i + 1 - ((ins_e_hash >> shift) & index_mask) < st->max_probes && - (ins_e_hash & HASH_MASK) != HASH_SUBHASH) { - ins_e = HASH_OFFSET (ins_e, elemsize); - ins_i++; - } - if (e == end_e || ins_e > st->last || ins_e_hash != HASH_NIL) { - e = end_e; /* can't insert; must grow or convert to subtable */ - } else { /* make space for element */ - memmove (HASH_OFFSET (e, elemsize), e, ((byte *) ins_e) - (byte *) e); - } - } - if (e != end_e) { - e->hash = hash; - *pres = e->data; - return (0); - } - h->changes++; - if (st->power < HASH_MAX_POWER) { - hash_grow (t, h, pst, flags); - } else { - hash_conv (t, h, st, flags, hash, start_e); - } + if((h->flags & IndirectValue) != 0) { + vmem = runtime·mallocgc(t->elem->size, 0, 1, 0); + *(byte**)insertv = vmem; + insertv = vmem; } + t->key->alg->copy(t->key->size, insertk, key); + t->elem->alg->copy(t->elem->size, insertv, value); + *inserti = top; + h->count++; + if(docheck) + check(t, h); } -static int32 -hash_insert (MapType *t, Hmap *h, void *data, void **pres) +static void +hash_remove(MapType *t, Hmap *h, void *key) { uintptr hash; - int32 rc; - + uintptr bucket; + Bucket *b; + uint8 top; + uintptr i; + byte *k, *v; + bool eq; + + if(docheck) + check(t, h); hash = h->hash0; - (*t->key->alg->hash) (&hash, t->key->size, data); - rc = hash_insert_internal (t, &h->st, 0, hash, h, data, pres); + t->key->alg->hash(&hash, t->key->size, key); + bucket = hash & (((uintptr)1 << h->B) - 1); + if(h->oldbuckets != nil) + grow_work(t, h, bucket); + b = (Bucket*)(h->buckets + bucket * h->bucketsize); + top = hash >> (sizeof(uintptr)*8 - 8); + if(top == 0) + top = 1; + do { + for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] != top) + continue; + t->key->alg->equal(&eq, t->key->size, key, IK(h, k)); + if(!eq) + continue; + + if((h->flags & CanFreeKey) != 0) { + k = *(byte**)k; + } + if((h->flags & IndirectValue) != 0) { + v = *(byte**)v; + } - h->count += (rc == 0); /* increment count if element didn't previously exist */ - return (rc); + b->tophash[i] = 0; + h->count--; + + if((h->flags & CanFreeKey) != 0) { + runtime·free(k); + } + if((h->flags & IndirectValue) != 0) { + runtime·free(v); + } + // TODO: consolidate buckets if they are mostly empty + // can only consolidate if there are no live iterators at this size. + if(docheck) + check(t, h); + return; + } + b = b->overflow; + } while(b != nil); } -static uint32 -hash_count (Hmap *h) -{ - return (h->count); -} +// TODO: shrink the map, the same way we grow it. -static void -iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) +// If you modify hash_iter, also change cmd/gc/range.c to indicate +// the size of this structure. +struct hash_iter { - int32 elemsize = it->elemsize; - hash_hash_t last_hash = it->last_hash; - struct hash_entry *e; - hash_hash_t e_hash; - struct hash_iter_sub *sub = &it->subtable_state[it->i]; - struct hash_entry *last; - - for (;;) { - int32 shift = HASH_BITS - (st->power + used); - int32 index_mask = (1 << st->power) - 1; - int32 i = (last_hash >> shift) & index_mask; - - last = st->last; - e = HASH_OFFSET (st->entry, i * elemsize); - sub->start = st->entry; - sub->last = last; - - if ((e->hash & HASH_MASK) != HASH_SUBHASH) { - break; - } - sub->e = HASH_OFFSET (e, elemsize); - sub = &it->subtable_state[++(it->i)]; - used += st->power; - st = *(struct hash_subtable **)e->data; - } - while (e <= last && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { - e = HASH_OFFSET (e, elemsize); - } - sub->e = e; -} + uint8* key; // Must be in first position. Write nil to indicate iteration end (see cmd/gc/range.c). + uint8* value; -static void * -hash_next (struct hash_iter *it) -{ - int32 elemsize; - struct hash_iter_sub *sub; - struct hash_entry *e; - struct hash_entry *last; - hash_hash_t e_hash; - - if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ - if (~it->last_hash == 0) - return (0); - it->changes = it->h->changes; - it->i = 0; - iter_restart (it, it->h->st, 0); - } - elemsize = it->elemsize; - -Again: - e_hash = 0; - sub = &it->subtable_state[it->i]; - e = sub->e; - last = sub->last; - - if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { - struct hash_entry *start = HASH_OFFSET (e, -(elemsize * HASH_MAX_PROBES)); - struct hash_entry *pe = HASH_OFFSET (e, -elemsize); - hash_hash_t last_hash = it->last_hash; - if (start < sub->start) { - start = sub->start; - } - while (e != start && ((e_hash = pe->hash) == HASH_NIL || last_hash < e_hash)) { - e = pe; - pe = HASH_OFFSET (pe, -elemsize); - } - while (e <= last && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { - e = HASH_OFFSET (e, elemsize); - } - } + MapType *t; + Hmap *h; - for (;;) { - while (e <= last && (e_hash = e->hash) == HASH_NIL) { - e = HASH_OFFSET (e, elemsize); - } - if (e > last) { - if (it->i == 0) { - if(!it->cycled) { - // Wrap to zero and iterate up until it->cycle. - it->cycled = true; - it->last_hash = 0; - it->subtable_state[0].e = it->h->st->entry; - it->subtable_state[0].start = it->h->st->entry; - it->subtable_state[0].last = it->h->st->last; - goto Again; - } - // Set last_hash to impossible value and - // break it->changes, so that check at top of - // hash_next will be used if we get called again. - it->last_hash = ~(uintptr_t)0; - it->changes--; - return (0); - } else { - it->i--; - sub = &it->subtable_state[it->i]; - e = sub->e; - last = sub->last; - } - } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { - if(it->cycled && e->hash > it->cycle) { - // Already returned this. - // Set last_hash to impossible value and - // break it->changes, so that check at top of - // hash_next will be used if we get called again. - it->last_hash = ~(uintptr_t)0; - it->changes--; - return (0); - } - it->last_hash = e->hash; - sub->e = HASH_OFFSET (e, elemsize); - return (e->data); - } else { - struct hash_subtable *st = - *(struct hash_subtable **)e->data; - sub->e = HASH_OFFSET (e, elemsize); - it->i++; - assert (it->i < sizeof (it->subtable_state) / - sizeof (it->subtable_state[0])); - sub = &it->subtable_state[it->i]; - sub->e = e = st->entry; - sub->start = st->entry; - sub->last = last = st->last; - } - } -} + // end point for iteration + uintptr endbucket; + bool wrapped; + // state of table at time iterator is initialized + uint8 B; + byte *buckets; + + // iter state + uintptr bucket; + struct Bucket *bptr; + uintptr i; +}; + +// iterator state: +// bucket: the current bucket ID +// b: the current Bucket in the chain +// i: the next offset to check in the current bucket static void -hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it) +hash_iter_init(MapType *t, Hmap *h, struct hash_iter *it) { - it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - it->changes = h->changes; - it->i = 0; - it->h = h; it->t = t; - it->last_hash = 0; - it->subtable_state[0].e = h->st->entry; - it->subtable_state[0].start = h->st->entry; - it->subtable_state[0].last = h->st->last; - - // fastrand1 returns 31 useful bits. - // We don't care about not having a bottom bit but we - // do want top bits. - if(sizeof(void*) == 8) - it->cycle = (uint64)runtime·fastrand1()<<33 | (uint64)runtime·fastrand1()<<2; - else - it->cycle = runtime·fastrand1()<<1; - it->cycled = false; - it->last_hash = it->cycle; - iter_restart(it, it->h->st, 0); -} + it->h = h; -static void -clean_st (Hmap *h, struct hash_subtable *st, int32 *slots, int32 *used) -{ - int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); - struct hash_entry *e = st->entry; - struct hash_entry *last = st->last; - int32 lslots = (((byte *) (last+1)) - (byte *) e) / elemsize; - int32 lused = 0; - - while (e <= last) { - hash_hash_t hash = e->hash; - if ((hash & HASH_MASK) == HASH_SUBHASH) { - clean_st (h, *(struct hash_subtable **)e->data, slots, used); - } else { - lused += (hash != HASH_NIL); - } - e = HASH_OFFSET (e, elemsize); - } - if (h->flag & CanFreeTable) - free (st); - *slots += lslots; - *used += lused; -} + // grab snapshot of bucket state + it->B = h->B; + it->buckets = h->buckets; -static void -hash_destroy (Hmap *h) -{ - int32 slots = 0; - int32 used = 0; + // iterator state + it->bucket = it->endbucket = runtime·fastrand1() & (((uintptr)1 << h->B) - 1); + it->wrapped = false; + it->bptr = nil; - clean_st (h, h->st, &slots, &used); - free (h); + // Remember we have an iterator at this level. + h->flags |= Iterator; } +// initializes it->key and it->value to the next key/value pair +// in the iteration, or nil if we've reached the end. static void -hash_visit_internal (struct hash_subtable *st, - int32 used, int32 level, - void (*data_visit) (void *arg, int32 level, void *data), - void *arg) +hash_next(struct hash_iter *it) { - int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); - struct hash_entry *e = st->entry; - int32 shift = HASH_BITS - (used + st->power); - int32 i = 0; - - while (e <= st->last) { - int32 index = ((e->hash >> (shift - 1)) >> 1) & ((1 << st->power) - 1); - if ((e->hash & HASH_MASK) == HASH_SUBHASH) { - (*data_visit) (arg, level, e->data); - hash_visit_internal (*(struct hash_subtable **)e->data, - used + st->power, level + 1, data_visit, arg); - } else { - (*data_visit) (arg, level, e->data); + Hmap *h; + MapType *t; + uintptr bucket; + Bucket *b; + uintptr i; + bool eq; + byte *k, *v; + byte *rk, *rv; + + h = it->h; + t = it->t; + bucket = it->bucket; + b = it->bptr; + i = it->i; + +next: + if(b == nil) { + if(bucket == it->endbucket && it->wrapped) { + // end of iteration + it->key = nil; + it->value = nil; + return; + } + if(h->oldbuckets != nil && it->B == h->B) { + // Iterator was started in the middle of a grow, and the grow isn't done yet. + // Make sure the bucket we're about to read is valid. + grow_work(t, h, bucket); } - if (e->hash != HASH_NIL) { - assert (i < index + st->max_probes); - assert (index <= i); + b = (Bucket*)(it->buckets + bucket * h->bucketsize); + bucket++; + if(bucket == ((uintptr)1 << it->B)) { + bucket = 0; + it->wrapped = true; + } + i = 0; + } + k = b->data + h->keysize * i; + v = b->data + h->keysize * BUCKETSIZE + h->valuesize * i; + for(; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] != 0) { + if(!evacuated(b)) { + // this is the golden data, we can return it. + it->key = IK(h, k); + it->value = IV(h, v); + } else { + // The hash table has grown since the iterator was started. + // The golden data for this key is now somewhere else. + t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k)); + if(eq) { + // Check the current hash table for the data. + // This code handles the case where the key + // has been deleted, updated, or deleted and reinserted. + // NOTE: we need to regrab the key as it has potentially been + // updated to an equal() but not identical key (e.g. +0.0 vs -0.0). + rk = IK(h, k); + rv = hash_lookup(t, it->h, &rk); + if(rv == nil) + continue; // key has been deleted + it->key = rk; + it->value = rv; + } else { + // if key!=key then the entry can't be deleted or + // updated, so we can just return it. That's lucky for + // us because when key!=key we can't look it up + // successfully in the current table. + it->key = IK(h, k); + it->value = IV(h, v); + } + } + it->bucket = bucket; + it->bptr = b; + it->i = i + 1; + return; } - e = HASH_OFFSET (e, elemsize); - i++; } + b = overflowptr(b); + i = 0; + goto next; } -static void -hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg) -{ - hash_visit_internal (h->st, 0, 0, data_visit, arg); -} + +#define PHASE_BUCKETS 0 +#define PHASE_OLD_BUCKETS 1 +#define PHASE_TABLE 2 +#define PHASE_OLD_TABLE 3 +#define PHASE_DONE 4 // Initialize the iterator. // Returns false if Hmap contains no pointers (in which case the iterator is not initialized). @@ -713,73 +841,141 @@ bool hash_gciter_init (Hmap *h, struct hash_gciter *it) { // GC during map initialization - if(h->st == nil) + if(h->buckets == nil) return false; - it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); - it->flag = h->flag; - it->valoff = h->valoff; - it->i = 0; - it->st = h->st; - it->subtable_state[it->i].e = h->st->entry; - it->subtable_state[it->i].last = h->st->last; + it->h = h; + it->phase = PHASE_BUCKETS; + it->bucket = 0; + it->b = nil; + + // TODO: finish evacuating oldbuckets so that we can collect + // oldbuckets? We don't want to keep a partially evacuated + // table around forever, so each gc could make at least some + // evacuation progress. Need to be careful about concurrent + // access if we do concurrent gc. Even if not, we don't want + // to make the gc pause any longer than it has to be. + return true; } -// Returns true and fills *data with subtable/key/value data, +// Returns true and fills *data with internal structure/key/value data, // or returns false if the iterator has terminated. +// Ugh, this interface is really annoying. I want a callback fn! bool -hash_gciter_next (struct hash_gciter *it, struct hash_gciter_data *data) +hash_gciter_next(struct hash_gciter *it, struct hash_gciter_data *data) { - struct hash_entry *e; - struct hash_gciter_sub *sub; + Hmap *h; + uintptr bucket, oldbucket; + Bucket *b, *oldb; + uintptr i; + byte *k, *v; + + h = it->h; + bucket = it->bucket; + b = it->b; + i = it->i; data->st = nil; data->key_data = nil; data->val_data = nil; - - // pointer to the first-level table - if(it->st != nil) { - data->st = it->st; - it->st = nil; - return true; - } - -popped: - sub = &it->subtable_state[it->i]; - e = sub->e; - while (e <= sub->last) { - if ((e->hash & HASH_MASK) == HASH_SUBHASH) { - struct hash_subtable *st = *(struct hash_subtable **)e->data; - data->st = st; - sub->e = HASH_OFFSET (e, it->elemsize); - - // push - it->i++; - assert (it->i < nelem(it->subtable_state)); - sub++; - sub->e = st->entry; - sub->last = st->last; - - return true; + data->indirectkey = (h->flags & IndirectKey) != 0; + data->indirectval = (h->flags & IndirectValue) != 0; + +next: + switch (it->phase) { + case PHASE_BUCKETS: + if(b != nil) { + k = b->data + h->keysize * i; + v = b->data + h->keysize * BUCKETSIZE + h->valuesize * i; + for(; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] != 0) { + data->key_data = k; + data->val_data = v; + it->bucket = bucket; + it->b = b; + it->i = i + 1; + return true; + } + } + b = b->overflow; + if(b != nil) { + data->st = (byte*)b; + it->bucket = bucket; + it->b = b; + it->i = 0; + return true; + } + } + while(bucket < ((uintptr)1 << h->B)) { + if(h->oldbuckets != nil) { + oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1); + oldb = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize); + if(!evacuated(oldb)) { + // new bucket isn't valid yet + bucket++; + continue; + } + } + b = (Bucket*)(h->buckets + bucket * h->bucketsize); + i = 0; + bucket++; + goto next; + } + it->phase = PHASE_OLD_BUCKETS; + bucket = 0; + b = nil; + goto next; + case PHASE_OLD_BUCKETS: + if(h->oldbuckets == nil) { + it->phase = PHASE_TABLE; + goto next; + } + if(b != nil) { + k = b->data + h->keysize * i; + v = b->data + h->keysize * BUCKETSIZE + h->valuesize * i; + for(; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { + if(b->tophash[i] != 0) { + data->key_data = k; + data->val_data = v; + it->bucket = bucket; + it->b = b; + it->i = i + 1; + return true; + } + } + b = overflowptr(b); + if(b != nil) { + data->st = (byte*)b; + it->bucket = bucket; + it->b = b; + it->i = 0; + return true; + } + } + if(bucket < ((uintptr)1 << (h->B - 1))) { + b = (Bucket*)(h->oldbuckets + bucket * h->bucketsize); + bucket++; + i = 0; + goto next; } - if(e->hash != HASH_NIL) { - void *key_data = e->data; - void *val_data = (byte*)e->data + it->valoff; - data->key_data = key_data; - data->val_data = val_data; - data->indirectkey = (it->flag & IndirectKey) != 0; - data->indirectval = (it->flag & IndirectVal) != 0; - sub->e = HASH_OFFSET (e, it->elemsize); + it->phase = PHASE_TABLE; + goto next; + case PHASE_TABLE: + it->phase = PHASE_OLD_TABLE; + data->st = h->buckets; + return true; + case PHASE_OLD_TABLE: + it->phase = PHASE_DONE; + if(h->oldbuckets != nil) { + data->st = h->oldbuckets; return true; + } else { + goto next; } - e = HASH_OFFSET (e, it->elemsize); - } - if(it->i != 0) { - // pop - it->i--; - goto popped; } + if(it->phase != PHASE_DONE) + runtime·throw("bad phase at done"); return false; } @@ -787,35 +983,13 @@ popped: /// interfaces to go runtime // -static void** -hash_valptr(Hmap *h, void *p) -{ - p = (byte*)p + h->valoff; - if(h->flag & IndirectVal) - p = *(void**)p; - return p; -} - - -static void** -hash_keyptr(Hmap *h, void *p) -{ - if(h->flag & IndirectKey) - p = *(void**)p; - return p; -} - -static int32 debug = 0; - Hmap* runtime·makemap_c(MapType *typ, int64 hint) { Hmap *h; - Type *key, *val; - uintptr ksize, vsize; + Type *key; key = typ->key; - val = typ->elem; if(hint < 0 || (int32)hint != hint) runtime·panicstring("makemap: size out of range"); @@ -824,7 +998,6 @@ runtime·makemap_c(MapType *typ, int64 hint) runtime·throw("runtime.makemap: unsupported map key type"); h = runtime·mal(sizeof(*h)); - h->flag |= CanFreeTable; /* until reflect gets involved, free is okay */ if(UseSpanType) { if(false) { @@ -833,34 +1006,14 @@ runtime·makemap_c(MapType *typ, int64 hint) runtime·settype(h, (uintptr)typ | TypeInfo_Map); } - ksize = ROUND(key->size, sizeof(void*)); - vsize = ROUND(val->size, sizeof(void*)); - if(ksize > MaxData || vsize > MaxData || ksize+vsize > MaxData) { - // Either key is too big, or value is, or combined they are. - // Prefer to keep the key if possible, because we look at - // keys more often than values. - if(ksize > MaxData - sizeof(void*)) { - // No choice but to indirect the key. - h->flag |= IndirectKey; - h->flag |= CanFreeKey; /* until reflect gets involved, free is okay */ - ksize = sizeof(void*); - } - if(vsize > MaxData - ksize) { - // Have to indirect the value. - h->flag |= IndirectVal; - vsize = sizeof(void*); - } - } - - h->valoff = ksize; - hash_init(h, ksize+vsize, hint); + hash_init(typ, h, hint); // these calculations are compiler dependent. // figure out offsets of map call arguments. if(debug) { runtime·printf("makemap: map=%p; keysize=%p; valsize=%p; keyalg=%p; valalg=%p\n", - h, key->size, val->size, key->alg, val->alg); + h, key->size, typ->elem->size, key->alg, typ->elem->alg); } return h; @@ -890,7 +1043,7 @@ runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres) Type *elem; elem = t->elem; - if(h == nil) { + if(h == nil || h->count == 0) { elem->alg->copy(elem->size, av, nil); *pres = false; return; @@ -899,10 +1052,11 @@ runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres) if(runtime·gcwaiting) runtime·gosched(); - res = nil; - if(hash_lookup(t, h, ak, (void**)&res)) { + res = hash_lookup(t, h, &ak); + + if(res != nil) { *pres = true; - elem->alg->copy(elem->size, av, hash_valptr(h, res)); + elem->alg->copy(elem->size, av, res); } else { *pres = false; elem->alg->copy(elem->size, av, nil); @@ -915,7 +1069,7 @@ void runtime·mapaccess1(MapType *t, Hmap *h, ...) { byte *ak, *av; - bool pres; + byte *res; if(raceenabled && h != nil) runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess1); @@ -923,7 +1077,12 @@ runtime·mapaccess1(MapType *t, Hmap *h, ...) ak = (byte*)(&h + 1); av = ak + ROUND(t->key->size, Structrnd); - runtime·mapaccess(t, h, ak, av, &pres); + if(h == nil || h->count == 0) { + t->elem->alg->copy(t->elem->size, av, nil); + } else { + res = hash_lookup(t, h, &ak); + t->elem->alg->copy(t->elem->size, av, res); + } if(debug) { runtime·prints("runtime.mapaccess1: map="); @@ -932,8 +1091,6 @@ runtime·mapaccess1(MapType *t, Hmap *h, ...) t->key->alg->print(t->key->size, ak); runtime·prints("; val="); t->elem->alg->print(t->elem->size, av); - runtime·prints("; pres="); - runtime·printbool(pres); runtime·prints("\n"); } } @@ -960,7 +1117,7 @@ runtime·mapaccess2(MapType *t, Hmap *h, ...) runtime·prints("; key="); t->key->alg->print(t->key->size, ak); runtime·prints("; val="); - t->elem->alg->print(t->key->size, av); + t->elem->alg->print(t->elem->size, av); runtime·prints("; pres="); runtime·printbool(*ap); runtime·prints("\n"); @@ -999,9 +1156,6 @@ reflect·mapaccess(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres) void runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av) { - byte *res; - int32 hit; - if(h == nil) runtime·panicstring("assignment to entry in nil map"); @@ -1010,19 +1164,9 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av) if(av == nil) { hash_remove(t, h, ak); - return; - } - - res = nil; - hit = hash_insert(t, h, ak, (void**)&res); - if(!hit) { - if(h->flag & IndirectKey) - *(void**)res = runtime·mal(t->key->size); - if(h->flag & IndirectVal) - *(void**)(res+h->valoff) = runtime·mal(t->elem->size); + } else { + hash_insert(t, h, ak, av); } - t->key->alg->copy(t->key->size, hash_keyptr(h, res), ak); - t->elem->alg->copy(t->elem->size, hash_valptr(h, res), av); if(debug) { runtime·prints("mapassign: map="); @@ -1031,10 +1175,6 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av) t->key->alg->print(t->key->size, ak); runtime·prints("; val="); t->elem->alg->print(t->elem->size, av); - runtime·prints("; hit="); - runtime·printint(hit); - runtime·prints("; res="); - runtime·printpointer(res); runtime·prints("\n"); } } @@ -1112,20 +1252,20 @@ void runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) { if(h == nil) { - it->data = nil; + it->key = nil; return; } if(raceenabled) runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapiterinit); hash_iter_init(t, h, it); - it->data = hash_next(it); + hash_next(it); if(debug) { runtime·prints("runtime.mapiterinit: map="); runtime·printpointer(h); runtime·prints("; iter="); runtime·printpointer(it); - runtime·prints("; data="); - runtime·printpointer(it->data); + runtime·prints("; key="); + runtime·printpointer(it->key); runtime·prints("\n"); } } @@ -1135,20 +1275,18 @@ runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) void reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) { - uint8 flag; + uint8 flags; if(h != nil && t->key->size > sizeof(void*)) { // reflect·mapiterkey returns pointers to key data, // and reflect holds them, so we cannot free key data - // eagerly anymore. Updating h->flag now is racy, - // but it's okay because this is the only possible store - // after creation. - flag = h->flag; - if(flag & IndirectKey) - flag &= ~CanFreeKey; + // eagerly anymore. + flags = h->flags; + if(flags & IndirectKey) + flags &= ~CanFreeKey; else - flag &= ~CanFreeTable; - h->flag = flag; + flags &= ~CanFreeBucket; + h->flags = flags; } it = runtime·mal(sizeof *it); @@ -1165,12 +1303,12 @@ runtime·mapiternext(struct hash_iter *it) if(runtime·gcwaiting) runtime·gosched(); - it->data = hash_next(it); + hash_next(it); if(debug) { runtime·prints("runtime.mapiternext: iter="); runtime·printpointer(it); - runtime·prints("; data="); - runtime·printpointer(it->data); + runtime·prints("; key="); + runtime·printpointer(it->key); runtime·prints("\n"); } } @@ -1188,25 +1326,23 @@ reflect·mapiternext(struct hash_iter *it) void runtime·mapiter1(struct hash_iter *it, ...) { - Hmap *h; byte *ak, *res; Type *key; - h = it->h; ak = (byte*)(&it + 1); - res = it->data; + res = it->key; if(res == nil) runtime·throw("runtime.mapiter1: key:val nil pointer"); key = it->t->key; - key->alg->copy(key->size, ak, hash_keyptr(h, res)); + key->alg->copy(key->size, ak, res); if(debug) { - runtime·prints("mapiter2: iter="); + runtime·prints("mapiter1: iter="); runtime·printpointer(it); runtime·prints("; map="); - runtime·printpointer(h); + runtime·printpointer(it->h); runtime·prints("\n"); } } @@ -1217,11 +1353,11 @@ runtime·mapiterkey(struct hash_iter *it, void *ak) byte *res; Type *key; - res = it->data; + res = it->key; if(res == nil) return false; key = it->t->key; - key->alg->copy(key->size, ak, hash_keyptr(it->h, res)); + key->alg->copy(key->size, ak, res); return true; } @@ -1237,14 +1373,13 @@ reflect·mapiterkey(struct hash_iter *it, uintptr key, bool ok) key = 0; ok = false; - res = it->data; + res = it->key; if(res == nil) { key = 0; ok = false; } else { tkey = it->t->key; key = 0; - res = (byte*)hash_keyptr(it->h, res); if(tkey->size <= sizeof(key)) tkey->alg->copy(tkey->size, (byte*)&key, res); else @@ -1276,7 +1411,6 @@ reflect·maplen(Hmap *h, intgo len) void runtime·mapiter2(struct hash_iter *it, ...) { - Hmap *h; byte *ak, *av, *res; MapType *t; @@ -1284,19 +1418,18 @@ runtime·mapiter2(struct hash_iter *it, ...) ak = (byte*)(&it + 1); av = ak + ROUND(t->key->size, t->elem->align); - res = it->data; + res = it->key; if(res == nil) runtime·throw("runtime.mapiter2: key:val nil pointer"); - h = it->h; - t->key->alg->copy(t->key->size, ak, hash_keyptr(h, res)); - t->elem->alg->copy(t->elem->size, av, hash_valptr(h, res)); + t->key->alg->copy(t->key->size, ak, res); + t->elem->alg->copy(t->elem->size, av, it->value); if(debug) { runtime·prints("mapiter2: iter="); runtime·printpointer(it); runtime·prints("; map="); - runtime·printpointer(h); + runtime·printpointer(it->h); runtime·prints("\n"); } } diff --git a/src/pkg/runtime/hashmap.h b/src/pkg/runtime/hashmap.h index 9b82f299e..2988417f6 100644 --- a/src/pkg/runtime/hashmap.h +++ b/src/pkg/runtime/hashmap.h @@ -2,180 +2,28 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* A hash table. - Example, hashing nul-terminated char*s: - hash_hash_t str_hash (void *v) { - char *s; - hash_hash_t hash = 0; - for (s = *(char **)v; *s != 0; s++) { - hash = (hash ^ *s) * 2654435769U; - } - return (hash); - } - int str_eq (void *a, void *b) { - return (strcmp (*(char **)a, *(char **)b) == 0); - } - void str_del (void *arg, void *data) { - *(char **)arg = *(char **)data; - } - - struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); - ... 3=> 2**3 entries initial size - ... 12=> 2**12 entries before sprouting sub-tables - ... 15=> number of adjacent probes to attempt before growing - - Example lookup: - char *key = "foobar"; - char **result_ptr; - if (hash_lookup (h, &key, (void **) &result_ptr)) { - printf ("found in table: %s\n", *result_ptr); - } else { - printf ("not found in table\n"); - } - - Example insertion: - char *key = strdup ("foobar"); - char **result_ptr; - if (hash_lookup (h, &key, (void **) &result_ptr)) { - printf ("found in table: %s\n", *result_ptr); - printf ("to overwrite, do *result_ptr = key\n"); - } else { - printf ("not found in table; inserted as %s\n", *result_ptr); - assert (*result_ptr == key); - } - - Example deletion: - char *key = "foobar"; - char *result; - if (hash_remove (h, &key, &result)) { - printf ("key found and deleted from table\n"); - printf ("called str_del (&result, data) to copy data to result: %s\n", result); - } else { - printf ("not found in table\n"); - } - - Example iteration over the elements of *h: - char **data; - struct hash_iter it; - hash_iter_init (h, &it); - for (data = hash_next (&it); data != 0; data = hash_next (&it)) { - printf ("%s\n", *data); - } - */ - -#define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c)) -#define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c)) -#define assert(a) if(!(a)) runtime·throw("hashmap assert") -#define free(x) runtime·free(x) -#define memmove(a,b,c) runtime·memmove(a, b, c) - struct Hmap; /* opaque */ -struct hash_subtable; /* opaque */ -struct hash_entry; /* opaque */ - -typedef uintptr uintptr_t; -typedef uintptr_t hash_hash_t; - -struct hash_iter { - uint8* data; /* returned from next */ - int32 elemsize; /* size of elements in table */ - int32 changes; /* number of changes observed last time */ - int32 i; /* stack pointer in subtable_state */ - bool cycled; /* have reached the end and wrapped to 0 */ - hash_hash_t last_hash; /* last hash value returned */ - hash_hash_t cycle; /* hash value where we started */ - struct Hmap *h; /* the hash table */ - MapType *t; /* the map type */ - struct hash_iter_sub { - struct hash_entry *e; /* pointer into subtable */ - struct hash_entry *start; /* start of subtable */ - struct hash_entry *last; /* last entry in subtable */ - } subtable_state[4]; /* Should be large enough unless the hashing is - so bad that many distinct data values hash - to the same hash value. */ -}; - -/* Return a hashtable h 2**init_power empty entries, each with - "datasize" data bytes. - (*data_hash)(a) should return the hash value of data element *a. - (*data_eq)(a,b) should return whether the data at "a" and the data at "b" - are equal. - (*data_del)(arg, a) will be invoked when data element *a is about to be removed - from the table. "arg" is the argument passed to "hash_remove()". - - Growing is accomplished by resizing if the current tables size is less than - a threshold, and by adding subtables otherwise. hint should be set - the expected maximum size of the table. - "datasize" should be in [sizeof (void*), ..., 255]. If you need a - bigger "datasize", store a pointer to another piece of memory. */ - -//struct hash *hash_new (int32 datasize, -// hash_hash_t (*data_hash) (void *), -// int32 (*data_eq) (void *, void *), -// void (*data_del) (void *, void *), -// int64 hint); - -/* Lookup *data in *h. If the data is found, return 1 and place a pointer to - the found element in *pres. Otherwise return 0 and place 0 in *pres. */ -// int32 hash_lookup (struct hash *h, void *data, void **pres); - -/* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) - where p points to the data in the table, then remove it from *h and return - 1. Otherwise return 0. */ -// int32 hash_remove (struct hash *h, void *data, void *arg); - -/* Lookup *data in *h. If the data is found, return 1, and place a pointer - to the found element in *pres. Otherwise, return 0, allocate a region - for the data to be inserted, and place a pointer to the inserted element - in *pres; it is the caller's responsibility to copy the data to be - inserted to the pointer returned in *pres in this case. - - If using garbage collection, it is the caller's responsibility to - add references for **pres if HASH_ADDED is returned. */ -// int32 hash_insert (struct hash *h, void *data, void **pres); - -/* Return the number of elements in the table. */ -// uint32 hash_count (struct hash *h); - -/* The following call is useful only if not using garbage collection on the - table. - Remove all sub-tables associated with *h. - This undoes the effects of hash_init(). - If other memory pointed to by user data must be freed, the caller is - responsible for doing so by iterating over *h first; see - hash_iter_init()/hash_next(). */ -// void hash_destroy (struct hash *h); - -/*----- iteration -----*/ - -/* Initialize *it from *h. */ -// void hash_iter_init (struct hash *h, struct hash_iter *it); - -/* Return the next used entry in the table with which *it was initialized. */ -// void *hash_next (struct hash_iter *it); - -/*---- test interface ----*/ -/* Call (*data_visit) (arg, level, data) for every data entry in the table, - whether used or not. "level" is the subtable level, 0 means first level. */ -/* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ -// void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); /* Used by the garbage collector */ struct hash_gciter { - int32 elemsize; - uint8 flag; - uint8 valoff; - uint32 i; /* stack pointer in subtable_state */ - struct hash_subtable *st; - struct hash_gciter_sub { - struct hash_entry *e; /* pointer into subtable */ - struct hash_entry *last; /* last entry in subtable */ - } subtable_state[4]; + Hmap *h; + int32 phase; + uintptr bucket; + struct Bucket *b; + uintptr i; }; + +// this data is used by the garbage collector to keep the map's +// internal structures from being reclaimed. The iterator must +// return in st every live object (ones returned by mallocgc) so +// that those objects won't be collected, and it must return +// every key & value in key_data/val_data so they can get scanned +// for pointers they point to. Note that if you malloc storage +// for keys and values, you need to do both. struct hash_gciter_data { - struct hash_subtable *st; /* subtable pointer, or nil */ + uint8 *st; /* internal structure, or nil */ uint8 *key_data; /* key data, or nil */ uint8 *val_data; /* value data, or nil */ bool indirectkey; /* storing pointers to keys */ diff --git a/src/pkg/runtime/hashmap_fast.c b/src/pkg/runtime/hashmap_fast.c new file mode 100644 index 000000000..2169f4c30 --- /dev/null +++ b/src/pkg/runtime/hashmap_fast.c @@ -0,0 +1,149 @@ +// 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. + +// Fast hashmap lookup specialized to a specific key type. +// Included by hashmap.c once for each specialized type. + +// Note that this code differs from hash_lookup in that +// it returns a pointer to the result, not the result itself. +// The returned pointer is only valid until the next GC +// point, so the caller must dereference it before then. + +// +build ignore + +#pragma textflag 7 +void +HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value) +{ + uintptr hash; + uintptr bucket; + Bucket *b; + uint8 top; + uintptr i; + KEYTYPE *k; + byte *v; + + if(debug) { + runtime·prints("runtime.mapaccess1_fastXXX: map="); + runtime·printpointer(h); + runtime·prints("; key="); + t->key->alg->print(t->key->size, &key); + runtime·prints("\n"); + } + if(h == nil || h->count == 0) { + value = empty_value; + FLUSH(&value); + return; + } + if(raceenabled) + runtime·racereadpc(h, runtime·getcallerpc(&t), HASH_LOOKUP1); + if(docheck) + check(t, h); + + if(h->B == 0 && (h->count == 1 || QUICKEQ(key))) { + // One-bucket table. Don't hash, just check each bucket entry. + b = (Bucket*)h->buckets; + for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { + if(b->tophash[i] != 0 && EQFUNC(key, *k)) { + value = v; + FLUSH(&value); + return; + } + } + } else { + hash = h->hash0; + HASHFUNC(&hash, sizeof(KEYTYPE), &key); + bucket = hash & (((uintptr)1 << h->B) - 1); + if(h->oldbuckets != nil) + grow_work(t, h, bucket); + b = (Bucket*)(h->buckets + bucket * (offsetof(Bucket, data[0]) + BUCKETSIZE * sizeof(KEYTYPE) + BUCKETSIZE * h->valuesize)); + top = hash >> (sizeof(uintptr)*8 - 8); + if(top == 0) + top = 1; + do { + for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { + if(b->tophash[i] == top && EQFUNC(key, *k)) { + value = v; + FLUSH(&value); + return; + } + } + b = b->overflow; + } while(b != nil); + } + value = empty_value; + FLUSH(&value); +} + +#pragma textflag 7 +void +HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res) +{ + uintptr hash; + uintptr bucket; + Bucket *b; + uint8 top; + uintptr i; + KEYTYPE *k; + byte *v; + + if(debug) { + runtime·prints("runtime.mapaccess2_fastXXX: map="); + runtime·printpointer(h); + runtime·prints("; key="); + t->key->alg->print(t->key->size, &key); + runtime·prints("\n"); + } + if(h == nil || h->count == 0) { + value = empty_value; + res = false; + FLUSH(&value); + FLUSH(&res); + return; + } + if(raceenabled) + runtime·racereadpc(h, runtime·getcallerpc(&t), HASH_LOOKUP2); + if(docheck) + check(t, h); + + if(h->B == 0 && (h->count == 1 || QUICKEQ(key))) { + // One-bucket table. Don't hash, just check each bucket entry. + b = (Bucket*)h->buckets; + for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { + if(b->tophash[i] != 0 && EQFUNC(key, *k)) { + value = v; + res = true; + FLUSH(&value); + FLUSH(&res); + return; + } + } + } else { + hash = h->hash0; + HASHFUNC(&hash, sizeof(KEYTYPE), &key); + bucket = hash & (((uintptr)1 << h->B) - 1); + if(h->oldbuckets != nil) + grow_work(t, h, bucket); + b = (Bucket*)(h->buckets + bucket * (offsetof(Bucket, data[0]) + BUCKETSIZE * sizeof(KEYTYPE) + BUCKETSIZE * h->valuesize)); + top = hash >> (sizeof(uintptr)*8 - 8); + if(top == 0) + top = 1; + do { + for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) { + if(b->tophash[i] == top && EQFUNC(key, *k)) { + value = v; + res = true; + FLUSH(&value); + FLUSH(&res); + return; + } + } + b = b->overflow; + } while(b != nil); + } + value = empty_value; + res = false; + FLUSH(&value); + FLUSH(&res); +} diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc index ac131b3af..fa28e2b73 100644 --- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -35,7 +35,7 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) MSpan *s; void *v; - if(runtime·gcwaiting && g != m->g0 && m->locks == 0) + if(runtime·gcwaiting && g != m->g0 && m->locks == 0 && dogc) runtime·gosched(); if(m->mallocing) runtime·throw("malloc/free - deadlock"); @@ -516,7 +516,7 @@ runtime·settype_flush(M *mp, bool sysalloc) nbytes3 = 8*sizeof(uintptr) + 1*ntypes; if(!sysalloc) { - data3 = runtime·mallocgc(nbytes3, FlagNoPointers, 0, 1); + data3 = runtime·mallocgc(nbytes3, FlagNoProfiling|FlagNoPointers, 0, 1); } else { data3 = runtime·SysAlloc(nbytes3); if(data3 == nil) @@ -554,7 +554,7 @@ runtime·settype_flush(M *mp, bool sysalloc) nbytes2 = ntypes * sizeof(uintptr); if(!sysalloc) { - data2 = runtime·mallocgc(nbytes2, FlagNoPointers, 0, 1); + data2 = runtime·mallocgc(nbytes2, FlagNoProfiling|FlagNoPointers, 0, 1); } else { data2 = runtime·SysAlloc(nbytes2); if(data2 == nil) diff --git a/src/pkg/runtime/map_test.go b/src/pkg/runtime/map_test.go new file mode 100644 index 000000000..29e19db2c --- /dev/null +++ b/src/pkg/runtime/map_test.go @@ -0,0 +1,282 @@ +// 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. + +package runtime_test + +import ( + "fmt" + "math" + "runtime" + "sort" + "testing" +) + +// negative zero is a good test because: +// 1) 0 and -0 are equal, yet have distinct representations. +// 2) 0 is represented as all zeros, -0 isn't. +// I'm not sure the language spec actually requires this behavior, +// but it's what the current map implementation does. +func TestNegativeZero(t *testing.T) { + m := make(map[float64]bool, 0) + + m[+0.0] = true + m[math.Copysign(0.0, -1.0)] = true // should overwrite +0 entry + + if len(m) != 1 { + t.Error("length wrong") + } + + for k, _ := range m { + if math.Copysign(1.0, k) > 0 { + t.Error("wrong sign") + } + } + + m = make(map[float64]bool, 0) + m[math.Copysign(0.0, -1.0)] = true + m[+0.0] = true // should overwrite -0.0 entry + + if len(m) != 1 { + t.Error("length wrong") + } + + for k, _ := range m { + if math.Copysign(1.0, k) < 0 { + t.Error("wrong sign") + } + } +} + +// nan is a good test because nan != nan, and nan has +// a randomized hash value. +func TestNan(t *testing.T) { + m := make(map[float64]int, 0) + nan := math.NaN() + m[nan] = 1 + m[nan] = 2 + m[nan] = 4 + if len(m) != 3 { + t.Error("length wrong") + } + s := 0 + for k, v := range m { + if k == k { + t.Error("nan disappeared") + } + if (v & (v - 1)) != 0 { + t.Error("value wrong") + } + s |= v + } + if s != 7 { + t.Error("values wrong") + } +} + +// Maps aren't actually copied on assignment. +func TestAlias(t *testing.T) { + m := make(map[int]int, 0) + m[0] = 5 + n := m + n[0] = 6 + if m[0] != 6 { + t.Error("alias didn't work") + } +} + +func TestGrowWithNaN(t *testing.T) { + m := make(map[float64]int, 4) + nan := math.NaN() + m[nan] = 1 + m[nan] = 2 + m[nan] = 4 + cnt := 0 + s := 0 + growflag := true + for k, v := range m { + if growflag { + // force a hashtable resize + for i := 0; i < 100; i++ { + m[float64(i)] = i + } + growflag = false + } + if k != k { + cnt++ + s |= v + } + } + if cnt != 3 { + t.Error("NaN keys lost during grow") + } + if s != 7 { + t.Error("NaN values lost during grow") + } +} + +type FloatInt struct { + x float64 + y int +} + +func TestGrowWithNegativeZero(t *testing.T) { + negzero := math.Copysign(0.0, -1.0) + m := make(map[FloatInt]int, 4) + m[FloatInt{0.0, 0}] = 1 + m[FloatInt{0.0, 1}] = 2 + m[FloatInt{0.0, 2}] = 4 + m[FloatInt{0.0, 3}] = 8 + growflag := true + s := 0 + cnt := 0 + negcnt := 0 + // The first iteration should return the +0 key. + // The subsequent iterations should return the -0 key. + // I'm not really sure this is required by the spec, + // but it makes sense. + // TODO: are we allowed to get the first entry returned again??? + for k, v := range m { + if v == 0 { + continue + } // ignore entries added to grow table + cnt++ + if math.Copysign(1.0, k.x) < 0 { + if v&16 == 0 { + t.Error("key/value not updated together 1") + } + negcnt++ + s |= v & 15 + } else { + if v&16 == 16 { + t.Error("key/value not updated together 2", k, v) + } + s |= v + } + if growflag { + // force a hashtable resize + for i := 0; i < 100; i++ { + m[FloatInt{3.0, i}] = 0 + } + // then change all the entries + // to negative zero + m[FloatInt{negzero, 0}] = 1 | 16 + m[FloatInt{negzero, 1}] = 2 | 16 + m[FloatInt{negzero, 2}] = 4 | 16 + m[FloatInt{negzero, 3}] = 8 | 16 + growflag = false + } + } + if s != 15 { + t.Error("entry missing", s) + } + if cnt != 4 { + t.Error("wrong number of entries returned by iterator", cnt) + } + if negcnt != 3 { + t.Error("update to negzero missed by iteration", negcnt) + } +} + +func TestIterGrowAndDelete(t *testing.T) { + m := make(map[int]int, 4) + for i := 0; i < 100; i++ { + m[i] = i + } + growflag := true + for k := range m { + if growflag { + // grow the table + for i := 100; i < 1000; i++ { + m[i] = i + } + // delete all odd keys + for i := 1; i < 1000; i += 2 { + delete(m, i) + } + growflag = false + } else { + if k&1 == 1 { + t.Error("odd value returned") + } + } + } +} + +// make sure old bucket arrays don't get GCd while +// an iterator is still using them. +func TestIterGrowWithGC(t *testing.T) { + m := make(map[int]int, 4) + for i := 0; i < 16; i++ { + m[i] = i + } + growflag := true + bitmask := 0 + for k := range m { + if k < 16 { + bitmask |= 1 << uint(k) + } + if growflag { + // grow the table + for i := 100; i < 1000; i++ { + m[i] = i + } + // trigger a gc + runtime.GC() + growflag = false + } + } + if bitmask != 1<<16-1 { + t.Error("missing key", bitmask) + } +} + +func TestBigItems(t *testing.T) { + var key [256]string + for i := 0; i < 256; i++ { + key[i] = "foo" + } + m := make(map[[256]string][256]string, 4) + for i := 0; i < 100; i++ { + key[37] = fmt.Sprintf("string%02d", i) + m[key] = key + } + var keys [100]string + var values [100]string + i := 0 + for k, v := range m { + keys[i] = k[37] + values[i] = v[37] + i++ + } + sort.Strings(keys[:]) + sort.Strings(values[:]) + for i := 0; i < 100; i++ { + if keys[i] != fmt.Sprintf("string%02d", i) { + t.Errorf("#%d: missing key: %v", i, keys[i]) + } + if values[i] != fmt.Sprintf("string%02d", i) { + t.Errorf("#%d: missing value: %v", i, values[i]) + } + } +} + +type empty struct { +} + +func TestEmptyKeyAndValue(t *testing.T) { + a := make(map[int]empty, 4) + b := make(map[empty]int, 4) + c := make(map[empty]empty, 4) + a[0] = empty{} + b[empty{}] = 0 + b[empty{}] = 1 + c[empty{}] = empty{} + + if len(a) != 1 { + t.Errorf("empty value insert problem") + } + if b[empty{}] != 1 { + t.Errorf("empty key returned wrong value") + } +} diff --git a/src/pkg/runtime/mapspeed_test.go b/src/pkg/runtime/mapspeed_test.go new file mode 100644 index 000000000..a37974060 --- /dev/null +++ b/src/pkg/runtime/mapspeed_test.go @@ -0,0 +1,150 @@ +// 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. +package runtime_test + +import ( + "fmt" + "strings" + "testing" +) + +const size = 10 + +func BenchmarkHashStringSpeed(b *testing.B) { + strings := make([]string, size) + for i := 0; i < size; i++ { + strings[i] = fmt.Sprintf("string#%d", i) + } + sum := 0 + m := make(map[string]int, size) + for i := 0; i < size; i++ { + m[strings[i]] = 0 + } + idx := 0 + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum += m[strings[idx]] + idx++ + if idx == size { + idx = 0 + } + } +} + +func BenchmarkHashInt32Speed(b *testing.B) { + ints := make([]int32, size) + for i := 0; i < size; i++ { + ints[i] = int32(i) + } + sum := 0 + m := make(map[int32]int, size) + for i := 0; i < size; i++ { + m[ints[i]] = 0 + } + idx := 0 + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum += m[ints[idx]] + idx++ + if idx == size { + idx = 0 + } + } +} + +func BenchmarkHashInt64Speed(b *testing.B) { + ints := make([]int64, size) + for i := 0; i < size; i++ { + ints[i] = int64(i) + } + sum := 0 + m := make(map[int64]int, size) + for i := 0; i < size; i++ { + m[ints[i]] = 0 + } + idx := 0 + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum += m[ints[idx]] + idx++ + if idx == size { + idx = 0 + } + } +} +func BenchmarkHashStringArraySpeed(b *testing.B) { + stringpairs := make([][2]string, size) + for i := 0; i < size; i++ { + for j := 0; j < 2; j++ { + stringpairs[i][j] = fmt.Sprintf("string#%d/%d", i, j) + } + } + sum := 0 + m := make(map[[2]string]int, size) + for i := 0; i < size; i++ { + m[stringpairs[i]] = 0 + } + idx := 0 + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum += m[stringpairs[idx]] + idx++ + if idx == size { + idx = 0 + } + } +} + +func BenchmarkMegMap(b *testing.B) { + m := make(map[string]bool) + for suffix := 'A'; suffix <= 'G'; suffix++ { + m[strings.Repeat("X", 1<<20-1)+fmt.Sprint(suffix)] = true + } + key := strings.Repeat("X", 1<<20-1) + "k" + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = m[key] + } +} + +func BenchmarkMegOneMap(b *testing.B) { + m := make(map[string]bool) + m[strings.Repeat("X", 1<<20)] = true + key := strings.Repeat("Y", 1<<20) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = m[key] + } +} + +func BenchmarkMegEmptyMap(b *testing.B) { + m := make(map[string]bool) + key := strings.Repeat("X", 1<<20) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = m[key] + } +} + +func BenchmarkSmallStrMap(b *testing.B) { + m := make(map[string]bool) + for suffix := 'A'; suffix <= 'G'; suffix++ { + m[fmt.Sprint(suffix)] = true + } + key := "k" + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = m[key] + } +} +func BenchmarkIntMap(b *testing.B) { + m := make(map[int]bool) + for i := 0; i < 8; i++ { + m[i] = true + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = m[7] + } +} diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c index ac8b5aa0d..ec2a91ad5 100644 --- a/src/pkg/runtime/mcentral.c +++ b/src/pkg/runtime/mcentral.c @@ -19,7 +19,6 @@ #include "malloc.h" static bool MCentral_Grow(MCentral *c); -static void* MCentral_Alloc(MCentral *c); static void MCentral_Free(MCentral *c, void *v); // Initialize a single central free list. diff --git a/src/pkg/runtime/mem_darwin.c b/src/pkg/runtime/mem_darwin.c index 04e719394..7aa607f8e 100644 --- a/src/pkg/runtime/mem_darwin.c +++ b/src/pkg/runtime/mem_darwin.c @@ -37,7 +37,12 @@ runtime·SysFree(void *v, uintptr n) void* runtime·SysReserve(void *v, uintptr n) { - return runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + void *p; + + p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + if(p < (void*)4096) + return nil; + return p; } enum @@ -52,7 +57,7 @@ runtime·SysMap(void *v, uintptr n) mstats.sys += n; p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); diff --git a/src/pkg/runtime/mem_freebsd.c b/src/pkg/runtime/mem_freebsd.c index f217e9db1..805e74cff 100644 --- a/src/pkg/runtime/mem_freebsd.c +++ b/src/pkg/runtime/mem_freebsd.c @@ -8,6 +8,11 @@ #include "os_GOOS.h" #include "malloc.h" +enum +{ + ENOMEM = 12, +}; + void* runtime·SysAlloc(uintptr n) { @@ -36,20 +41,20 @@ runtime·SysFree(void *v, uintptr n) void* runtime·SysReserve(void *v, uintptr n) { + void *p; + // On 64-bit, people with ulimit -v set complain if we reserve too // much address space. Instead, assume that the reservation is okay // and check the assumption in SysMap. if(sizeof(void*) == 8) return v; - return runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); + if(p < (void*)4096) + return nil; + return p; } -enum -{ - ENOMEM = 12, -}; - void runtime·SysMap(void *v, uintptr n) { @@ -60,7 +65,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { runtime·printf("runtime: address space conflict: map(%p) = %p\n", v, p); @@ -70,7 +75,7 @@ runtime·SysMap(void *v, uintptr n) } p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); diff --git a/src/pkg/runtime/mem_linux.c b/src/pkg/runtime/mem_linux.c index ebcec1e86..1bae755fa 100644 --- a/src/pkg/runtime/mem_linux.c +++ b/src/pkg/runtime/mem_linux.c @@ -10,8 +10,6 @@ enum { - EAGAIN = 11, - ENOMEM = 12, _PAGE_SIZE = 4096, }; diff --git a/src/pkg/runtime/mem_netbsd.c b/src/pkg/runtime/mem_netbsd.c index 77ce04c4e..e5bdac0ef 100644 --- a/src/pkg/runtime/mem_netbsd.c +++ b/src/pkg/runtime/mem_netbsd.c @@ -50,10 +50,9 @@ runtime·SysReserve(void *v, uintptr n) return v; p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); - if (p == ((void *)-ENOMEM)) + if(p < (void*)4096) return nil; - else - return p; + return p; } void @@ -66,7 +65,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { runtime·printf("runtime: address space conflict: map(%p) = %p\n", v, p); @@ -76,7 +75,7 @@ runtime·SysMap(void *v, uintptr n) } p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); diff --git a/src/pkg/runtime/mem_openbsd.c b/src/pkg/runtime/mem_openbsd.c index 77ce04c4e..e5bdac0ef 100644 --- a/src/pkg/runtime/mem_openbsd.c +++ b/src/pkg/runtime/mem_openbsd.c @@ -50,10 +50,9 @@ runtime·SysReserve(void *v, uintptr n) return v; p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); - if (p == ((void *)-ENOMEM)) + if(p < (void*)4096) return nil; - else - return p; + return p; } void @@ -66,7 +65,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { runtime·printf("runtime: address space conflict: map(%p) = %p\n", v, p); @@ -76,7 +75,7 @@ runtime·SysMap(void *v, uintptr n) } p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); - if(p == (void*)-ENOMEM) + if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); diff --git a/src/pkg/runtime/memmove_amd64.s b/src/pkg/runtime/memmove_amd64.s index e78be8145..6174407e3 100644 --- a/src/pkg/runtime/memmove_amd64.s +++ b/src/pkg/runtime/memmove_amd64.s @@ -23,11 +23,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// void runtime·memmove(void*, void*, uintptr) TEXT runtime·memmove(SB), 7, $0 MOVQ to+0(FP), DI MOVQ fr+8(FP), SI - MOVLQSX n+16(FP), BX + MOVQ n+16(FP), BX /* * check and set for backwards @@ -38,7 +39,7 @@ TEXT runtime·memmove(SB), 7, $0 /* * forward copy loop */ -forward: +forward: MOVQ BX, CX SHRQ $3, CX ANDQ $7, BX diff --git a/src/pkg/runtime/memmove_linux_amd64_test.go b/src/pkg/runtime/memmove_linux_amd64_test.go new file mode 100644 index 000000000..f7221f4f5 --- /dev/null +++ b/src/pkg/runtime/memmove_linux_amd64_test.go @@ -0,0 +1,61 @@ +// 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. + +package runtime_test + +import ( + "io/ioutil" + "os" + "reflect" + "syscall" + "testing" + "unsafe" +) + +// TestMemmoveOverflow maps 3GB of memory and calls memmove on +// the corresponding slice. +func TestMemmoveOverflow(t *testing.T) { + // Create a temporary file. + tmp, err := ioutil.TempFile("", "go-memmovetest") + if err != nil { + t.Fatal(err) + } + _, err = tmp.Write(make([]byte, 65536)) + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmp.Name()) + defer tmp.Close() + + // Set up mappings. + base, _, errno := syscall.Syscall6(syscall.SYS_MMAP, + 0xa0<<32, 3<<30, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_PRIVATE|syscall.MAP_ANONYMOUS, ^uintptr(0), 0) + if errno != 0 { + t.Skipf("could not create memory mapping: %s", errno) + } + syscall.Syscall(syscall.SYS_MUNMAP, base, 3<<30, 0) + + for off := uintptr(0); off < 3<<30; off += 65536 { + _, _, errno := syscall.Syscall6(syscall.SYS_MMAP, + base+off, 65536, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FIXED, tmp.Fd(), 0) + if errno != 0 { + t.Fatalf("could not map a page at requested 0x%x: %s", base+off, errno) + } + defer syscall.Syscall(syscall.SYS_MUNMAP, base+off, 65536, 0) + } + + var s []byte + sp := (*reflect.SliceHeader)(unsafe.Pointer(&s)) + sp.Data = base + sp.Len, sp.Cap = 3<<30, 3<<30 + + n := copy(s[1:], s) + if n != 3<<30-1 { + t.Fatalf("copied %d bytes, expected %d", n, 3<<30-1) + } + n = copy(s, s[1:]) + if n != 3<<30-1 { + t.Fatalf("copied %d bytes, expected %d", n, 3<<30-1) + } +} diff --git a/src/pkg/runtime/mfixalloc.c b/src/pkg/runtime/mfixalloc.c index c916d588f..c7dab8aea 100644 --- a/src/pkg/runtime/mfixalloc.c +++ b/src/pkg/runtime/mfixalloc.c @@ -30,6 +30,11 @@ void* runtime·FixAlloc_Alloc(FixAlloc *f) { void *v; + + if(f->size == 0) { + runtime·printf("runtime: use of FixAlloc_Alloc before FixAlloc_Init\n"); + runtime·throw("runtime: internal error"); + } if(f->list) { v = f->list; diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index 010f9cd96..aa499f476 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -140,6 +140,7 @@ static Workbuf* getempty(Workbuf*); static Workbuf* getfull(Workbuf*); static void putempty(Workbuf*); static Workbuf* handoff(Workbuf*); +static void gchelperstart(void); static struct { uint64 full; // lock-free list of full blocks @@ -191,7 +192,7 @@ static struct { // markonly marks an object. It returns true if the object // has been marked by this function, false otherwise. -// This function isn't thread-safe and doesn't append the object to any buffer. +// This function doesn't append the object to any buffer. static bool markonly(void *obj) { @@ -254,13 +255,23 @@ found: // Only care about allocated and not marked. if((bits & (bitAllocated|bitMarked)) != bitAllocated) return false; - *bitp |= bitMarked<<shift; + if(work.nproc == 1) + *bitp |= bitMarked<<shift; + else { + for(;;) { + x = *bitp; + if(x & (bitMarked<<shift)) + return false; + if(runtime·casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift)))) + break; + } + } // The object is now marked return true; } -// PtrTarget and BitTarget are structures used by intermediate buffers. +// PtrTarget is a structure used by intermediate buffers. // The intermediate buffers hold GC data before it // is moved/flushed to the work buffer (Workbuf). // The size of an intermediate buffer is very small, @@ -272,25 +283,17 @@ struct PtrTarget uintptr ti; }; -typedef struct BitTarget BitTarget; -struct BitTarget -{ - void *p; - uintptr ti; - uintptr *bitp, shift; -}; - typedef struct BufferList BufferList; struct BufferList { PtrTarget ptrtarget[IntermediateBufferCapacity]; - BitTarget bittarget[IntermediateBufferCapacity]; Obj obj[IntermediateBufferCapacity]; - BufferList *next; + uint32 busy; + byte pad[CacheLineSize]; }; -static BufferList *bufferList; +#pragma dataflag 16 // no pointers +static BufferList bufferList[MaxGcproc]; -static Lock lock; static Type *itabtype; static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); @@ -301,7 +304,6 @@ static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); // and are prepared to be scanned by the garbage collector. // // _wp, _wbuf, _nobj are input/output parameters and are specifying the work buffer. -// bitbuf holds temporary data generated by this function. // // A simplified drawing explaining how the todo-list moves from a structure to another: // @@ -309,14 +311,12 @@ static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); // (find pointers) // Obj ------> PtrTarget (pointer targets) // ↑ | -// | | flushptrbuf (1st part, -// | | find block start) -// | ↓ -// `--------- BitTarget (pointer targets and the corresponding locations in bitmap) -// flushptrbuf -// (2nd part, mark and enqueue) +// | | +// `----------' +// flushptrbuf +// (find block start, mark and enqueue) static void -flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, BitTarget *bitbuf) +flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj) { byte *p, *arena_start, *obj; uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti, n; @@ -325,7 +325,6 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf Obj *wp; Workbuf *wbuf; PtrTarget *ptrbuf_end; - BitTarget *bitbufpos, *bt; arena_start = runtime·mheap->arena_start; @@ -359,8 +358,6 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf { // Multi-threaded version. - bitbufpos = bitbuf; - while(ptrbuf < ptrbuf_end) { obj = ptrbuf->p; ti = ptrbuf->ti; @@ -438,26 +435,22 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf // Only care about allocated and not marked. if((bits & (bitAllocated|bitMarked)) != bitAllocated) continue; - - *bitbufpos++ = (BitTarget){obj, ti, bitp, shift}; - } - - runtime·lock(&lock); - for(bt=bitbuf; bt<bitbufpos; bt++){ - xbits = *bt->bitp; - bits = xbits >> bt->shift; - if((bits & bitMarked) != 0) - continue; - - // Mark the block - *bt->bitp = xbits | (bitMarked << bt->shift); + if(work.nproc == 1) + *bitp |= bitMarked<<shift; + else { + for(;;) { + x = *bitp; + if(x & (bitMarked<<shift)) + goto continue_obj; + if(runtime·casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift)))) + break; + } + } // If object has no pointers, don't need to scan further. if((bits & bitNoPointers) != 0) continue; - obj = bt->p; - // Ask span about size class. // (Manually inlined copy of MHeap_Lookup.) x = (uintptr)obj >> PageShift; @@ -467,11 +460,11 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf PREFETCH(obj); - *wp = (Obj){obj, s->elemsize, bt->ti}; + *wp = (Obj){obj, s->elemsize, ti}; wp++; nobj++; + continue_obj:; } - runtime·unlock(&lock); // If another proc wants a pointer, give it some. if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) { @@ -575,20 +568,19 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) byte *b, *arena_start, *arena_used; uintptr n, i, end_b, elemsize, size, ti, objti, count, type; uintptr *pc, precise_type, nominal_size; - uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti; + uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret; void *obj; Type *t; Slice *sliceptr; Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4]; BufferList *scanbuffers; PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos; - BitTarget *bitbuf; Obj *objbuf, *objbuf_end, *objbufpos; Eface *eface; Iface *iface; Hmap *hmap; MapType *maptype; - bool didmark, mapkey_kind, mapval_kind; + bool mapkey_kind, mapval_kind; struct hash_gciter map_iter; struct hash_gciter_data d; Hchan *chan; @@ -606,26 +598,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) precise_type = false; nominal_size = 0; - // Allocate ptrbuf, bitbuf + // Allocate ptrbuf { - runtime·lock(&lock); - - if(bufferList == nil) { - bufferList = runtime·SysAlloc(sizeof(*bufferList)); - if(bufferList == nil) - runtime·throw("runtime: cannot allocate memory"); - bufferList->next = nil; - } - scanbuffers = bufferList; - bufferList = bufferList->next; - + scanbuffers = &bufferList[m->helpgc]; ptrbuf = &scanbuffers->ptrtarget[0]; ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget); - bitbuf = &scanbuffers->bittarget[0]; objbuf = &scanbuffers->obj[0]; objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj); - - runtime·unlock(&lock); } ptrbufpos = ptrbuf; @@ -638,6 +617,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) mapkey_ti = mapval_ti = 0; chan = nil; chantype = nil; + chan_ret = nil; goto next_block; @@ -703,7 +683,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) mapval_kind = maptype->elem->kind; mapval_ti = (uintptr)maptype->elem->gc | PRECISE; - map_ret = 0; + map_ret = nil; pc = mapProg; } else { goto next_block; @@ -712,6 +692,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) case TypeInfo_Chan: chan = (Hchan*)b; chantype = (ChanType*)t; + chan_ret = nil; pc = chanProg; break; default: @@ -759,17 +740,29 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) case GC_STRING: obj = *(void**)(stack_top.b + pc[1]); + markonly(obj); pc += 2; - break; + continue; case GC_EFACE: eface = (Eface*)(stack_top.b + pc[1]); pc += 2; - if(eface->type != nil && (eface->data >= arena_start && eface->data < arena_used)) { - t = eface->type; + if(eface->type == nil) + continue; + + // eface->type + t = eface->type; + if((void*)t >= arena_start && (void*)t < arena_used) { + *ptrbufpos++ = (PtrTarget){t, 0}; + if(ptrbufpos == ptrbuf_end) + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); + } + + // eface->data + if(eface->data >= arena_start && eface->data < arena_used) { if(t->size <= sizeof(void*)) { if((t->kind & KindNoPointers)) - break; + continue; obj = eface->data; if((t->kind & ~KindNoPointers) == KindPtr) @@ -785,13 +778,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) iface = (Iface*)(stack_top.b + pc[1]); pc += 2; if(iface->tab == nil) - break; + continue; // iface->tab if((void*)iface->tab >= arena_start && (void*)iface->tab < arena_used) { *ptrbufpos++ = (PtrTarget){iface->tab, (uintptr)itabtype->gc}; if(ptrbufpos == ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); } // iface->data @@ -799,7 +792,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) t = iface->tab->type; if(t->size <= sizeof(void*)) { if((t->kind & KindNoPointers)) - break; + continue; obj = iface->data; if((t->kind & ~KindNoPointers) == KindPtr) @@ -812,13 +805,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) break; case GC_DEFAULT_PTR: - while((i = stack_top.b) <= end_b) { + while(stack_top.b <= end_b) { + obj = *(byte**)stack_top.b; stack_top.b += PtrSize; - obj = *(byte**)i; if(obj >= arena_start && obj < arena_used) { *ptrbufpos++ = (PtrTarget){obj, 0}; if(ptrbufpos == ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); } } goto next_block; @@ -826,9 +819,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) case GC_END: if(--stack_top.count != 0) { // Next iteration of a loop if possible. - elemsize = stack_top.elemsize; - stack_top.b += elemsize; - if(stack_top.b + elemsize <= end_b+PtrSize) { + stack_top.b += stack_top.elemsize; + if(stack_top.b + stack_top.elemsize <= end_b+PtrSize) { pc = stack_top.loop_or_ret; continue; } @@ -894,10 +886,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) pc += 3; continue; } - runtime·lock(&lock); - didmark = markonly(hmap); - runtime·unlock(&lock); - if(didmark) { + if(markonly(hmap)) { maptype = (MapType*)pc[2]; if(hash_gciter_init(hmap, &map_iter)) { mapkey_size = maptype->key->size; @@ -923,31 +912,41 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) while(hash_gciter_next(&map_iter, &d)) { // buffers: reserve space for 2 objects. if(ptrbufpos+2 >= ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); if(objbufpos+2 >= objbuf_end) flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); - if(d.st != nil) { - runtime·lock(&lock); + if(d.st != nil) markonly(d.st); - runtime·unlock(&lock); - } + if(d.key_data != nil) { if(!(mapkey_kind & KindNoPointers) || d.indirectkey) { if(!d.indirectkey) *objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti}; - else + else { + if(Debug) { + obj = *(void**)d.key_data; + if(!(arena_start <= obj && obj < arena_used)) + runtime·throw("scanblock: inconsistent hashmap"); + } *ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti}; + } } if(!(mapval_kind & KindNoPointers) || d.indirectval) { if(!d.indirectval) *objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti}; - else + else { + if(Debug) { + obj = *(void**)d.val_data; + if(!(arena_start <= obj && obj < arena_used)) + runtime·throw("scanblock: inconsistent hashmap"); + } *ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti}; + } } } } - if(map_ret == 0) + if(map_ret == nil) goto next_block; pc = map_ret; continue; @@ -961,7 +960,26 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) *objbufpos++ = (Obj){obj, size, objti}; if(objbufpos == objbuf_end) flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); - break; + continue; + + case GC_CHAN_PTR: + // Similar to GC_MAP_PTR + chan = *(Hchan**)(stack_top.b + pc[1]); + if(chan == nil) { + pc += 3; + continue; + } + if(markonly(chan)) { + chantype = (ChanType*)pc[2]; + if(!(chantype->elem->kind & KindNoPointers)) { + // Start chanProg. + chan_ret = pc+3; + pc = chanProg+1; + continue; + } + } + pc += 3; + continue; case GC_CHAN: // There are no heap pointers in struct Hchan, @@ -981,7 +999,10 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); } } - goto next_block; + if(chan_ret == nil) + goto next_block; + pc = chan_ret; + continue; default: runtime·throw("scanblock: invalid GC instruction"); @@ -991,7 +1012,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) if(obj >= arena_start && obj < arena_used) { *ptrbufpos++ = (PtrTarget){obj, objti}; if(ptrbufpos == ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); } } @@ -1000,7 +1021,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) // the loop by setting b, n, ti to the parameters for the next block. if(nobj == 0) { - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); if(nobj == 0) { @@ -1026,11 +1047,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) nobj--; } -endscan: - runtime·lock(&lock); - scanbuffers->next = bufferList; - bufferList = scanbuffers; - runtime·unlock(&lock); +endscan:; } // debug_scanblock is the debug copy of scanblock. @@ -1339,7 +1356,7 @@ addstackroots(G *gp) runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk); runtime·throw("scanstack"); } - addroot((Obj){sp, (byte*)stk - sp, 0}); + addroot((Obj){sp, (byte*)stk - sp, (uintptr)defaultProg | PRECISE | LOOP}); sp = (byte*)stk->gobuf.sp; guard = stk->stackguard; stk = (Stktop*)stk->stackbase; @@ -1381,14 +1398,17 @@ addroots(void) for(spanidx=0; spanidx<runtime·mheap->nspan; spanidx++) { s = allspans[spanidx]; if(s->state == MSpanInUse) { + // The garbage collector ignores type pointers stored in MSpan.types: + // - Compiler-generated types are stored outside of heap. + // - The reflect package has runtime-generated types cached in its data structures. + // The garbage collector relies on finding the references via that cache. switch(s->types.compression) { case MTypes_Empty: case MTypes_Single: break; case MTypes_Words: case MTypes_Bytes: - // TODO(atom): consider using defaultProg instead of 0 - addroot((Obj){(byte*)&s->types.data, sizeof(void*), 0}); + markonly((byte*)s->types.data); break; } } @@ -1655,6 +1675,8 @@ runtime·memorydump(void) void runtime·gchelper(void) { + gchelperstart(); + // parallel mark for over gc roots runtime·parfordo(work.markfor); @@ -1668,6 +1690,7 @@ runtime·gchelper(void) } runtime·parfordo(work.sweepfor); + bufferList[m->helpgc].busy = 0; if(runtime·xadd(&work.ndone, +1) == work.nproc-1) runtime·notewakeup(&work.alldone); } @@ -1757,6 +1780,8 @@ runtime·gc(int32 force) // a problem in the past. if((((uintptr)&work.empty) & 7) != 0) runtime·throw("runtime: gc work buffer is misaligned"); + if((((uintptr)&work.full) & 7) != 0) + runtime·throw("runtime: gc work buffer is misaligned"); // The gc is turned off (via enablegc) until // the bootstrap has completed. @@ -1857,6 +1882,7 @@ gc(struct gc_args *args) t1 = runtime·nanotime(); + gchelperstart(); runtime·parfordo(work.markfor); scanblock(nil, nil, 0, true); @@ -1868,6 +1894,7 @@ gc(struct gc_args *args) t2 = runtime·nanotime(); runtime·parfordo(work.sweepfor); + bufferList[m->helpgc].busy = 0; t3 = runtime·nanotime(); if(work.nproc > 1) @@ -2009,6 +2036,15 @@ runtime∕debug·setGCPercent(intgo in, intgo out) } static void +gchelperstart(void) +{ + if(m->helpgc < 0 || m->helpgc >= MaxGcproc) + runtime·throw("gchelperstart: bad m->helpgc"); + if(runtime·xchg(&bufferList[m->helpgc].busy, 1)) + runtime·throw("gchelperstart: already busy"); +} + +static void runfinq(void) { Finalizer *f; diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h index 87b604a36..18f3654b4 100644 --- a/src/pkg/runtime/mgc0.h +++ b/src/pkg/runtime/mgc0.h @@ -24,6 +24,7 @@ enum { GC_ARRAY_NEXT, // The next element of an array. Args: none GC_CALL, // Call a subroutine. Args: (off, objgcrel) GC_MAP_PTR, // Go map. Args: (off, MapType*) + GC_CHAN_PTR, // Go channel. Args: (off, ChanType*) GC_STRING, // Go string. Args: (off) GC_EFACE, // interface{}. Args: (off) GC_IFACE, // interface{...}. Args: (off) diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c index f45149d63..177f40659 100644 --- a/src/pkg/runtime/mheap.c +++ b/src/pkg/runtime/mheap.c @@ -409,6 +409,9 @@ runtime·MHeap_Scavenger(void) bool trace; Note note, *notep; + g->issystem = true; + g->isbackground = true; + // If we go two minutes without a garbage collection, force one to run. forcegc = 2*60*1e9; // If a span goes unused for 5 minutes after a garbage collection, diff --git a/src/pkg/runtime/netpoll.goc b/src/pkg/runtime/netpoll.goc new file mode 100644 index 000000000..06b6d6172 --- /dev/null +++ b/src/pkg/runtime/netpoll.goc @@ -0,0 +1,351 @@ +// 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. + +// +build darwin linux + +package net + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "arch_GOARCH.h" +#include "malloc.h" + +// Integrated network poller (platform-independent part). +// A particular implementation (epoll/kqueue) must define the following functions: +// void runtime·netpollinit(void); // to initialize the poller +// int32 runtime·netpollopen(int32 fd, PollDesc *pd); // to arm edge-triggered notifications + // and associate fd with pd. +// An implementation must call the following function to denote that the pd is ready. +// void runtime·netpollready(G **gpp, PollDesc *pd, int32 mode); + +#define READY ((G*)1) + +struct PollDesc +{ + PollDesc* link; // in pollcache, protected by pollcache.Lock + Lock; // protectes the following fields + int32 fd; + bool closing; + uintptr seq; // protects from stale timers and ready notifications + G* rg; // G waiting for read or READY (binary semaphore) + Timer rt; // read deadline timer (set if rt.fv != nil) + int64 rd; // read deadline + G* wg; // the same for writes + Timer wt; + int64 wd; +}; + +static struct +{ + Lock; + PollDesc* first; + // PollDesc objects must be type-stable, + // because we can get ready notification from epoll/kqueue + // after the descriptor is closed/reused. + // Stale notifications are detected using seq variable, + // seq is incremented when deadlines are changed or descriptor is reused. +} pollcache; + +static void netpollblock(PollDesc*, int32); +static G* netpollunblock(PollDesc*, int32); +static void deadline(int64, Eface); +static void readDeadline(int64, Eface); +static void writeDeadline(int64, Eface); +static PollDesc* allocPollDesc(void); +static intgo checkerr(PollDesc *pd, int32 mode); + +static FuncVal deadlineFn = {(void(*)(void))deadline}; +static FuncVal readDeadlineFn = {(void(*)(void))readDeadline}; +static FuncVal writeDeadlineFn = {(void(*)(void))writeDeadline}; + +func runtime_pollServerInit() { + runtime·netpollinit(); +} + +func runtime_pollOpen(fd int) (pd *PollDesc, errno int) { + pd = allocPollDesc(); + runtime·lock(pd); + if(pd->wg != nil && pd->wg != READY) + runtime·throw("runtime_pollOpen: blocked write on free descriptor"); + if(pd->rg != nil && pd->rg != READY) + runtime·throw("runtime_pollOpen: blocked read on free descriptor"); + pd->fd = fd; + pd->closing = false; + pd->seq++; + pd->rg = nil; + pd->rd = 0; + pd->wg = nil; + pd->wd = 0; + runtime·unlock(pd); + + errno = runtime·netpollopen(fd, pd); +} + +func runtime_pollClose(pd *PollDesc) { + if(!pd->closing) + runtime·throw("runtime_pollClose: close w/o unblock"); + if(pd->wg != nil && pd->wg != READY) + runtime·throw("runtime_pollClose: blocked write on closing descriptor"); + if(pd->rg != nil && pd->rg != READY) + runtime·throw("runtime_pollClose: blocked read on closing descriptor"); + runtime·netpollclose(pd->fd); + runtime·lock(&pollcache); + pd->link = pollcache.first; + pollcache.first = pd; + runtime·unlock(&pollcache); +} + +func runtime_pollReset(pd *PollDesc, mode int) (err int) { + runtime·lock(pd); + err = checkerr(pd, mode); + if(err) + goto ret; + if(mode == 'r') + pd->rg = nil; + else if(mode == 'w') + pd->wg = nil; +ret: + runtime·unlock(pd); +} + +func runtime_pollWait(pd *PollDesc, mode int) (err int) { + runtime·lock(pd); + err = checkerr(pd, mode); + if(err) + goto ret; + netpollblock(pd, mode); + err = checkerr(pd, mode); +ret: + runtime·unlock(pd); +} + +func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) { + runtime·lock(pd); + if(pd->closing) + goto ret; + pd->seq++; // invalidate current timers + // Reset current timers. + if(pd->rt.fv) { + runtime·deltimer(&pd->rt); + pd->rt.fv = nil; + } + if(pd->wt.fv) { + runtime·deltimer(&pd->wt); + pd->wt.fv = nil; + } + // Setup new timers. + if(d != 0 && d <= runtime·nanotime()) { + d = -1; + } + if(mode == 'r' || mode == 'r'+'w') + pd->rd = d; + if(mode == 'w' || mode == 'r'+'w') + pd->wd = d; + if(pd->rd > 0 && pd->rd == pd->wd) { + pd->rt.fv = &deadlineFn; + pd->rt.when = pd->rd; + // Copy current seq into the timer arg. + // Timer func will check the seq against current descriptor seq, + // if they differ the descriptor was reused or timers were reset. + pd->rt.arg.type = (Type*)pd->seq; + pd->rt.arg.data = pd; + runtime·addtimer(&pd->rt); + } else { + if(pd->rd > 0) { + pd->rt.fv = &readDeadlineFn; + pd->rt.when = pd->rd; + pd->rt.arg.type = (Type*)pd->seq; + pd->rt.arg.data = pd; + runtime·addtimer(&pd->rt); + } + if(pd->wd > 0) { + pd->wt.fv = &writeDeadlineFn; + pd->wt.when = pd->wd; + pd->wt.arg.type = (Type*)pd->seq; + pd->wt.arg.data = pd; + runtime·addtimer(&pd->wt); + } + } +ret: + runtime·unlock(pd); +} + +func runtime_pollUnblock(pd *PollDesc) { + G *rg, *wg; + + runtime·lock(pd); + if(pd->closing) + runtime·throw("runtime_pollUnblock: already closing"); + pd->closing = true; + pd->seq++; + rg = netpollunblock(pd, 'r'); + wg = netpollunblock(pd, 'w'); + if(pd->rt.fv) { + runtime·deltimer(&pd->rt); + pd->rt.fv = nil; + } + if(pd->wt.fv) { + runtime·deltimer(&pd->wt); + pd->wt.fv = nil; + } + runtime·unlock(pd); + if(rg) + runtime·ready(rg); + if(wg) + runtime·ready(wg); +} + +// make pd ready, newly runnable goroutines (if any) are enqueued info gpp list +void +runtime·netpollready(G **gpp, PollDesc *pd, int32 mode) +{ + G *rg, *wg; + + rg = wg = nil; + runtime·lock(pd); + if(mode == 'r' || mode == 'r'+'w') + rg = netpollunblock(pd, 'r'); + if(mode == 'w' || mode == 'r'+'w') + wg = netpollunblock(pd, 'w'); + runtime·unlock(pd); + if(rg) { + rg->schedlink = *gpp; + *gpp = rg; + } + if(wg) { + wg->schedlink = *gpp; + *gpp = wg; + } +} + +static intgo +checkerr(PollDesc *pd, int32 mode) +{ + if(pd->closing) + return 1; // errClosing + if((mode == 'r' && pd->rd < 0) || (mode == 'w' && pd->wd < 0)) + return 2; // errTimeout + return 0; +} + +static void +netpollblock(PollDesc *pd, int32 mode) +{ + G **gpp; + + gpp = &pd->rg; + if(mode == 'w') + gpp = &pd->wg; + if(*gpp == READY) { + *gpp = nil; + return; + } + if(*gpp != nil) + runtime·throw("epoll: double wait"); + *gpp = g; + runtime·park(runtime·unlock, &pd->Lock, "IO wait"); + runtime·lock(pd); +} + +static G* +netpollunblock(PollDesc *pd, int32 mode) +{ + G **gpp, *old; + + gpp = &pd->rg; + if(mode == 'w') + gpp = &pd->wg; + if(*gpp == READY) + return nil; + if(*gpp == nil) { + *gpp = READY; + return nil; + } + old = *gpp; + *gpp = nil; + return old; +} + +static void +deadlineimpl(int64 now, Eface arg, bool read, bool write) +{ + PollDesc *pd; + uint32 seq; + G *rg, *wg; + + USED(now); + pd = (PollDesc*)arg.data; + // This is the seq when the timer was set. + // If it's stale, ignore the timer event. + seq = (uintptr)arg.type; + rg = wg = nil; + runtime·lock(pd); + if(seq != pd->seq) { + // The descriptor was reused or timers were reset. + runtime·unlock(pd); + return; + } + if(read) { + if(pd->rd <= 0 || pd->rt.fv == nil) + runtime·throw("deadlineimpl: inconsistent read deadline"); + pd->rd = -1; + pd->rt.fv = nil; + rg = netpollunblock(pd, 'r'); + } + if(write) { + if(pd->wd <= 0 || (pd->wt.fv == nil && !read)) + runtime·throw("deadlineimpl: inconsistent write deadline"); + pd->wd = -1; + pd->wt.fv = nil; + wg = netpollunblock(pd, 'w'); + } + runtime·unlock(pd); + if(rg) + runtime·ready(rg); + if(wg) + runtime·ready(wg); +} + +static void +deadline(int64 now, Eface arg) +{ + deadlineimpl(now, arg, true, true); +} + +static void +readDeadline(int64 now, Eface arg) +{ + deadlineimpl(now, arg, true, false); +} + +static void +writeDeadline(int64 now, Eface arg) +{ + deadlineimpl(now, arg, false, true); +} + +static PollDesc* +allocPollDesc(void) +{ + PollDesc *pd; + uint32 i, n; + + runtime·lock(&pollcache); + if(pollcache.first == nil) { + n = PageSize/sizeof(*pd); + if(n == 0) + n = 1; + // Must be in non-GC memory because can be referenced + // only from epoll/kqueue internals. + pd = runtime·SysAlloc(n*sizeof(*pd)); + for(i = 0; i < n; i++) { + pd[i].link = pollcache.first; + pollcache.first = &pd[i]; + } + } + pd = pollcache.first; + pollcache.first = pd->link; + runtime·unlock(&pollcache); + return pd; +} diff --git a/src/pkg/runtime/netpoll_epoll.c b/src/pkg/runtime/netpoll_epoll.c new file mode 100644 index 000000000..d6ef0d144 --- /dev/null +++ b/src/pkg/runtime/netpoll_epoll.c @@ -0,0 +1,92 @@ +// 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. + +// +build linux + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" + +int32 runtime·epollcreate(int32 size); +int32 runtime·epollcreate1(int32 flags); +int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev); +int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout); +void runtime·closeonexec(int32 fd); + +static int32 epfd = -1; // epoll descriptor + +void +runtime·netpollinit(void) +{ + epfd = runtime·epollcreate1(EPOLL_CLOEXEC); + if(epfd >= 0) + return; + epfd = runtime·epollcreate(1024); + if(epfd >= 0) { + runtime·closeonexec(epfd); + return; + } + runtime·printf("netpollinit: failed to create descriptor (%d)\n", -epfd); + runtime·throw("netpollinit: failed to create descriptor"); +} + +int32 +runtime·netpollopen(int32 fd, PollDesc *pd) +{ + EpollEvent ev; + int32 res; + + ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET; + ev.data = (uint64)pd; + res = runtime·epollctl(epfd, EPOLL_CTL_ADD, fd, &ev); + return -res; +} + +int32 +runtime·netpollclose(int32 fd) +{ + EpollEvent ev; + int32 res; + + res = runtime·epollctl(epfd, EPOLL_CTL_DEL, fd, &ev); + return -res; +} + +// polls for ready network connections +// returns list of goroutines that become runnable +G* +runtime·netpoll(bool block) +{ + EpollEvent events[128], *ev; + int32 n, i, waitms, mode; + G *gp; + + if(epfd == -1) + return nil; + waitms = -1; + if(!block) + waitms = 0; +retry: + n = runtime·epollwait(epfd, events, nelem(events), waitms); + if(n < 0) { + if(n != -EINTR) + runtime·printf("epollwait failed with %d\n", -n); + goto retry; + } + gp = nil; + for(i = 0; i < n; i++) { + ev = &events[i]; + if(ev->events == 0) + continue; + mode = 0; + if(ev->events & (EPOLLIN|EPOLLRDHUP|EPOLLHUP|EPOLLERR)) + mode += 'r'; + if(ev->events & (EPOLLOUT|EPOLLHUP|EPOLLERR)) + mode += 'w'; + if(mode) + runtime·netpollready(&gp, (void*)ev->data, mode); + } + if(block && gp == nil) + goto retry; + return gp; +} diff --git a/src/pkg/runtime/netpoll_kqueue.c b/src/pkg/runtime/netpoll_kqueue.c new file mode 100644 index 000000000..ad721e293 --- /dev/null +++ b/src/pkg/runtime/netpoll_kqueue.c @@ -0,0 +1,105 @@ +// 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. + +// +build darwin + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" + +// Integrated network poller (kqueue-based implementation). + +int32 runtime·kqueue(void); +int32 runtime·kevent(int32, Kevent*, int32, Kevent*, int32, Timespec*); +void runtime·closeonexec(int32); + +static int32 kq = -1; + +void +runtime·netpollinit(void) +{ + kq = runtime·kqueue(); + if(kq < 0) { + runtime·printf("netpollinit: kqueue failed with %d\n", -kq); + runtime·throw("netpollinit: kqueue failed"); + } + runtime·closeonexec(kq); +} + +int32 +runtime·netpollopen(int32 fd, PollDesc *pd) +{ + Kevent ev[2]; + int32 n; + + // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR) + // for the whole fd lifetime. The notifications are automatically unregistered + // when fd is closed. + ev[0].ident = fd; + ev[0].filter = EVFILT_READ; + ev[0].flags = EV_ADD|EV_RECEIPT|EV_CLEAR; + ev[0].fflags = 0; + ev[0].data = 0; + ev[0].udata = (byte*)pd; + ev[1] = ev[0]; + ev[1].filter = EVFILT_WRITE; + n = runtime·kevent(kq, ev, 2, ev, 2, nil); + if(n < 0) + return -n; + if(n != 2 || + (ev[0].flags&EV_ERROR) == 0 || ev[0].ident != fd || ev[0].filter != EVFILT_READ || + (ev[1].flags&EV_ERROR) == 0 || ev[1].ident != fd || ev[1].filter != EVFILT_WRITE) + return EFAULT; // just to mark out from other errors + if(ev[0].data != 0) + return ev[0].data; + if(ev[1].data != 0) + return ev[1].data; + return 0; +} + +int32 +runtime·netpollclose(int32 fd) +{ + // Don't need to unregister because calling close() + // on fd will remove any kevents that reference the descriptor. + USED(fd); + return 0; +} + +// Polls for ready network connections. +// Returns list of goroutines that become runnable. +G* +runtime·netpoll(bool block) +{ + Kevent events[64], *ev; + Timespec ts, *tp; + int32 n, i; + G *gp; + + if(kq == -1) + return nil; + tp = nil; + if(!block) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + tp = &ts; + } + gp = nil; +retry: + n = runtime·kevent(kq, nil, 0, events, nelem(events), tp); + if(n < 0) { + if(n != -EINTR) + runtime·printf("kqueue failed with %d\n", -n); + goto retry; + } + for(i = 0; i < n; i++) { + ev = &events[i]; + if(ev->filter == EVFILT_READ) + runtime·netpollready(&gp, (PollDesc*)ev->udata, 'r'); + if(ev->filter == EVFILT_WRITE) + runtime·netpollready(&gp, (PollDesc*)ev->udata, 'w'); + } + if(block && gp == nil) + goto retry; + return gp; +} diff --git a/src/pkg/runtime/netpoll_stub.c b/src/pkg/runtime/netpoll_stub.c new file mode 100644 index 000000000..39d19a4ce --- /dev/null +++ b/src/pkg/runtime/netpoll_stub.c @@ -0,0 +1,18 @@ +// 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. + +// +build freebsd netbsd openbsd plan9 windows + +#include "runtime.h" + +// Polls for ready network connections. +// Returns list of goroutines that become runnable. +G* +runtime·netpoll(bool block) +{ + // Implementation for platforms that do not support + // integrated network poller. + USED(block); + return nil; +} diff --git a/src/pkg/runtime/thread_darwin.c b/src/pkg/runtime/os_darwin.c index adb1ffe6a..6216e3a3c 100644 --- a/src/pkg/runtime/thread_darwin.c +++ b/src/pkg/runtime/os_darwin.c @@ -5,6 +5,7 @@ #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" +#include "signal_unix.h" #include "stack.h" extern SigTab runtime·sigtab[]; @@ -69,6 +70,22 @@ runtime·osinit(void) } void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + static byte urandom_data[HashRandomBytes]; + int32 fd; + fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0); + if(runtime·read(fd, urandom_data, HashRandomBytes) == HashRandomBytes) { + *rnd = urandom_data; + *rnd_len = HashRandomBytes; + } else { + *rnd = nil; + *rnd_len = 0; + } + runtime·close(fd); +} + +void runtime·goenvs(void) { runtime·goenvs_unix(); @@ -392,9 +409,14 @@ int32 runtime·mach_semacquire(uint32 sem, int64 ns) { int32 r; + int64 secs; if(ns >= 0) { - r = runtime·mach_semaphore_timedwait(sem, ns/1000000000LL, ns%1000000000LL); + secs = ns/1000000000LL; + // Avoid overflow + if(secs > 1LL<<30) + secs = 1LL<<30; + r = runtime·mach_semaphore_timedwait(sem, secs, ns%1000000000LL); if(r == KERN_ABORTED || r == KERN_OPERATION_TIMED_OUT) return -1; if(r != 0) @@ -530,3 +552,41 @@ runtime·badsignal(int32 sig) runtime·write(2, "\n", 1); runtime·exit(1); } + +void +runtime·setsig(int32 i, GoSighandler *fn, bool restart) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask = ~(uintptr)0; + sa.sa_tramp = (void*)runtime·sigtramp; // runtime·sigtramp's job is to call into real handler + *(uintptr*)sa.__sigaction_u = (uintptr)fn; + runtime·sigaction(i, &sa, nil); +} + +GoSighandler* +runtime·getsig(int32 i) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + return *(void**)sa.__sigaction_u; +} + +void +runtime·signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = (void*)p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} diff --git a/src/pkg/runtime/os_darwin.h b/src/pkg/runtime/os_darwin.h index 5fcb717cb..802410975 100644 --- a/src/pkg/runtime/os_darwin.h +++ b/src/pkg/runtime/os_darwin.h @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define SIG_DFL ((void*)0) -#define SIG_IGN ((void*)1) -#define SIGHUP 1 #define SS_DISABLE 4 int32 runtime·bsdthread_create(void*, M*, G*, void(*)(void)); @@ -27,8 +24,6 @@ void runtime·sigprocmask(int32, Sigset*, Sigset*); struct Sigaction; void runtime·sigaction(uintptr, struct Sigaction*, struct Sigaction*); -void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); -void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); struct StackT; void runtime·sigaltstack(struct StackT*, struct StackT*); @@ -36,7 +31,6 @@ void runtime·sigtramp(void); void runtime·sigpanic(void); void runtime·setitimer(int32, Itimerval*, Itimerval*); -void runtime·raisesigpipe(void); #define NSIG 32 #define SI_USER 0 /* empirically true, but not what headers say */ diff --git a/src/pkg/runtime/thread_freebsd.c b/src/pkg/runtime/os_freebsd.c index 3ae14ee0a..68c0f4750 100644 --- a/src/pkg/runtime/thread_freebsd.c +++ b/src/pkg/runtime/os_freebsd.c @@ -1,9 +1,11 @@ -// Use of this source file is governed by a BSD-style -// license that can be found in the LICENSE file.` +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" +#include "signal_unix.h" #include "stack.h" extern SigTab runtime·sigtab[]; @@ -44,11 +46,16 @@ runtime·futexsleep(uint32 *addr, uint32 val, int64 ns) { int32 ret; Timespec ts, *tsp; + int64 secs; if(ns < 0) tsp = nil; else { - ts.tv_sec = ns / 1000000000LL; + secs = ns / 1000000000LL; + // Avoid overflow + if(secs > 1LL<<30) + secs = 1LL<<30; + ts.tv_sec = secs; ts.tv_nsec = ns % 1000000000LL; tsp = &ts; } @@ -116,6 +123,22 @@ runtime·osinit(void) } void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + static byte urandom_data[HashRandomBytes]; + int32 fd; + fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0); + if(runtime·read(fd, urandom_data, HashRandomBytes) == HashRandomBytes) { + *rnd = urandom_data; + *rnd_len = HashRandomBytes; + } else { + *rnd = nil; + *rnd_len = 0; + } + runtime·close(fd); +} + +void runtime·goenvs(void) { runtime·goenvs_unix(); @@ -241,3 +264,58 @@ runtime·badsignal(int32 sig) runtime·write(2, "\n", 1); runtime·exit(1); } + +extern void runtime·sigtramp(void); + +typedef struct sigaction { + union { + void (*__sa_handler)(int32); + void (*__sa_sigaction)(int32, Siginfo*, void *); + } __sigaction_u; /* signal handler */ + int32 sa_flags; /* see signal options below */ + Sigset sa_mask; /* signal mask to apply */ +} Sigaction; + +void +runtime·setsig(int32 i, GoSighandler *fn, bool restart) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask.__bits[0] = ~(uint32)0; + sa.sa_mask.__bits[1] = ~(uint32)0; + sa.sa_mask.__bits[2] = ~(uint32)0; + sa.sa_mask.__bits[3] = ~(uint32)0; + if(fn == runtime·sighandler) + fn = (void*)runtime·sigtramp; + sa.__sigaction_u.__sa_sigaction = (void*)fn; + runtime·sigaction(i, &sa, nil); +} + +GoSighandler* +runtime·getsig(int32 i) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if((void*)sa.__sigaction_u.__sa_sigaction == runtime·sigtramp) + return runtime·sighandler; + return (void*)sa.__sigaction_u.__sa_sigaction; +} + +void +runtime·signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = (void*)p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} diff --git a/src/pkg/runtime/os_freebsd.h b/src/pkg/runtime/os_freebsd.h index a37ad7cd8..e9be1362c 100644 --- a/src/pkg/runtime/os_freebsd.h +++ b/src/pkg/runtime/os_freebsd.h @@ -1,20 +1,18 @@ -#define SIG_DFL ((void*)0) -#define SIG_IGN ((void*)1) -#define SIGHUP 1 +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #define SS_DISABLE 4 int32 runtime·thr_new(ThrParam*, int32); -void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); void runtime·sigpanic(void); void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); struct sigaction; void runtime·sigaction(int32, struct sigaction*, struct sigaction*); void runtime·sigprocmask(Sigset *, Sigset *); -void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); void runtime·setitimer(int32, Itimerval*, Itimerval*); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); -void runtime·raisesigpipe(void); #define NSIG 33 #define SI_USER 0x10001 diff --git a/src/pkg/runtime/os_freebsd_arm.c b/src/pkg/runtime/os_freebsd_arm.c new file mode 100644 index 000000000..7eaa45c44 --- /dev/null +++ b/src/pkg/runtime/os_freebsd_arm.c @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" + +void +runtime·checkgoarm(void) +{ + // TODO(minux) +} + +#pragma textflag 7 +int64 +runtime·cputicks(void) +{ + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // TODO: need more entropy to better seed fastrand1. + return runtime·nanotime(); +} diff --git a/src/pkg/runtime/thread_linux.c b/src/pkg/runtime/os_linux.c index 78ddef878..e4ae1a5d8 100644 --- a/src/pkg/runtime/thread_linux.c +++ b/src/pkg/runtime/os_linux.c @@ -5,14 +5,11 @@ #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" +#include "signal_unix.h" #include "stack.h" extern SigTab runtime·sigtab[]; -int32 runtime·open(uint8*, int32, int32); -int32 runtime·close(int32); -int32 runtime·read(int32, void*, int32); - static Sigset sigset_none; static Sigset sigset_all = { ~(uint32)0, ~(uint32)0 }; @@ -29,9 +26,6 @@ enum { FUTEX_WAIT = 0, FUTEX_WAKE = 1, - - EINTR = 4, - EAGAIN = 11, }; // Atomically, @@ -42,15 +36,17 @@ void runtime·futexsleep(uint32 *addr, uint32 val, int64 ns) { Timespec ts, *tsp; + int64 secs; if(ns < 0) tsp = nil; else { - ts.tv_sec = ns/1000000000LL; - ts.tv_nsec = ns%1000000000LL; + secs = ns/1000000000LL; // Avoid overflow - if(ts.tv_sec > 1<<30) - ts.tv_sec = 1<<30; + if(secs > 1LL<<30) + secs = 1LL<<30; + ts.tv_sec = secs; + ts.tv_nsec = ns%1000000000LL; tsp = &ts; } @@ -164,6 +160,32 @@ runtime·osinit(void) runtime·ncpu = getproccount(); } +// Random bytes initialized at startup. These come +// from the ELF AT_RANDOM auxiliary vector (vdso_linux_amd64.c). +byte* runtime·startup_random_data; +uint32 runtime·startup_random_data_len; + +void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + if(runtime·startup_random_data != nil) { + *rnd = runtime·startup_random_data; + *rnd_len = runtime·startup_random_data_len; + } else { + static byte urandom_data[HashRandomBytes]; + int32 fd; + fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0); + if(runtime·read(fd, urandom_data, HashRandomBytes) == HashRandomBytes) { + *rnd = urandom_data; + *rnd_len = HashRandomBytes; + } else { + *rnd = nil; + *rnd_len = 0; + } + runtime·close(fd); + } +} + void runtime·goenvs(void) { @@ -203,8 +225,8 @@ runtime·sigpanic(void) if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) { if(g->sigpc == 0) runtime·panicstring("call of nil func value"); - } runtime·panicstring("invalid memory address or nil pointer dereference"); + } runtime·printf("unexpected fault address %p\n", g->sigcode1); runtime·throw("fault"); case SIGSEGV: @@ -290,3 +312,60 @@ runtime·badsignal(int32 sig) runtime·write(2, "\n", 1); runtime·exit(1); } + +#ifdef GOARCH_386 +#define sa_handler k_sa_handler +#endif + +/* + * This assembler routine takes the args from registers, puts them on the stack, + * and calls sighandler(). + */ +extern void runtime·sigtramp(void); +extern void runtime·sigreturn(void); // calls runtime·sigreturn + +void +runtime·setsig(int32 i, GoSighandler *fn, bool restart) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask = ~0ULL; + // TODO(adonovan): Linux manpage says "sa_restorer element is + // obsolete and should not be used". Avoid it here, and test. + sa.sa_restorer = (void*)runtime·sigreturn; + if(fn == runtime·sighandler) + fn = (void*)runtime·sigtramp; + sa.sa_handler = fn; + if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction failure"); +} + +GoSighandler* +runtime·getsig(int32 i) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction read failure"); + if((void*)sa.sa_handler == runtime·sigtramp) + return runtime·sighandler; + return (void*)sa.sa_handler; +} + +void +runtime·signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} diff --git a/src/pkg/runtime/os_linux.h b/src/pkg/runtime/os_linux.h index a23fe0f73..b2d3f6f2a 100644 --- a/src/pkg/runtime/os_linux.h +++ b/src/pkg/runtime/os_linux.h @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define SIG_DFL ((void*)0) -#define SIG_IGN ((void*)1) -#define SIGHUP 1 #define SS_DISABLE 2 // Linux-specific system calls @@ -13,14 +10,11 @@ int32 runtime·clone(int32, void*, M*, G*, void(*)(void)); struct Sigaction; int32 runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr); -void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); -void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); void runtime·sigpanic(void); void runtime·setitimer(int32, Itimerval*, Itimerval*); -void runtime·raisesigpipe(void); #define NSIG 65 #define SI_USER 0 diff --git a/src/pkg/runtime/os_linux_386.c b/src/pkg/runtime/os_linux_386.c new file mode 100644 index 000000000..18becb6e6 --- /dev/null +++ b/src/pkg/runtime/os_linux_386.c @@ -0,0 +1,37 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" + +#define AT_NULL 0 +#define AT_RANDOM 25 +#define AT_SYSINFO 32 +extern uint32 runtime·_vdso; + +#pragma textflag 7 +void +runtime·linux_setup_vdso(int32 argc, byte **argv) +{ + byte **envp; + uint32 *auxv; + + // skip envp to get to ELF auxiliary vector. + for(envp = &argv[argc+1]; *envp != nil; envp++) + ; + envp++; + + for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) { + if(auxv[0] == AT_SYSINFO) { + runtime·_vdso = auxv[1]; + continue; + } + if(auxv[0] == AT_RANDOM) { + runtime·startup_random_data = (byte*)auxv[1]; + runtime·startup_random_data_len = 16; + continue; + } + } +} diff --git a/src/pkg/runtime/os_linux_arm.c b/src/pkg/runtime/os_linux_arm.c new file mode 100644 index 000000000..dd0fa9415 --- /dev/null +++ b/src/pkg/runtime/os_linux_arm.c @@ -0,0 +1,82 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" + +#define AT_NULL 0 +#define AT_PLATFORM 15 // introduced in at least 2.6.11 +#define AT_HWCAP 16 // introduced in at least 2.6.11 +#define AT_RANDOM 25 // introduced in 2.6.29 +#define HWCAP_VFP (1 << 6) // introduced in at least 2.6.11 +#define HWCAP_VFPv3 (1 << 13) // introduced in 2.6.30 +static uint32 runtime·randomNumber; +uint8 runtime·armArch = 6; // we default to ARMv6 +uint32 runtime·hwcap; // set by setup_auxv +uint8 runtime·goarm; // set by 5l + +void +runtime·checkgoarm(void) +{ + if(runtime·goarm > 5 && !(runtime·hwcap & HWCAP_VFP)) { + runtime·printf("runtime: this CPU has no floating point hardware, so it cannot run\n"); + runtime·printf("this GOARM=%d binary. Recompile using GOARM=5.\n", runtime·goarm); + runtime·exit(1); + } + if(runtime·goarm > 6 && !(runtime·hwcap & HWCAP_VFPv3)) { + runtime·printf("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n"); + runtime·printf("this GOARM=%d binary. Recompile using GOARM=6.\n", runtime·goarm); + runtime·exit(1); + } +} + +#pragma textflag 7 +void +runtime·setup_auxv(int32 argc, void *argv_list) +{ + byte **argv; + byte **envp; + byte *rnd; + uint32 *auxv; + uint32 t; + + argv = &argv_list; + + // skip envp to get to ELF auxiliary vector. + for(envp = &argv[argc+1]; *envp != nil; envp++) + ; + envp++; + + for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) { + switch(auxv[0]) { + case AT_RANDOM: // kernel provided 16-byte worth of random data + if(auxv[1]) { + rnd = (byte*)auxv[1]; + runtime·randomNumber = rnd[4] | rnd[5]<<8 | rnd[6]<<16 | rnd[7]<<24; + } + break; + case AT_PLATFORM: // v5l, v6l, v7l + if(auxv[1]) { + t = *(uint8*)(auxv[1]+1); + if(t >= '5' && t <= '7') + runtime·armArch = t - '0'; + } + break; + case AT_HWCAP: // CPU capability bit flags + runtime·hwcap = auxv[1]; + break; + } + } +} + +#pragma textflag 7 +int64 +runtime·cputicks(void) +{ + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // runtime·randomNumber provides better seeding of fastrand1. + return runtime·nanotime() + runtime·randomNumber; +} diff --git a/src/pkg/runtime/thread_netbsd.c b/src/pkg/runtime/os_netbsd.c index f333c6dd8..936334cac 100644 --- a/src/pkg/runtime/thread_netbsd.c +++ b/src/pkg/runtime/os_netbsd.c @@ -1,9 +1,11 @@ -// Use of this source file is governed by a BSD-style -// license that can be found in the LICENSE file.` +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" +#include "signal_unix.h" #include "stack.h" enum @@ -181,6 +183,22 @@ runtime·osinit(void) } void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + static byte urandom_data[HashRandomBytes]; + int32 fd; + fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0); + if(runtime·read(fd, urandom_data, HashRandomBytes) == HashRandomBytes) { + *rnd = urandom_data; + *rnd_len = HashRandomBytes; + } else { + *rnd = nil; + *rnd_len = 0; + } + runtime·close(fd); +} + +void runtime·goenvs(void) { runtime·goenvs_unix(); @@ -286,3 +304,58 @@ runtime·badsignal(int32 sig) runtime·write(2, "\n", 1); runtime·exit(1); } + +extern void runtime·sigtramp(void); + +typedef struct sigaction { + union { + void (*_sa_handler)(int32); + void (*_sa_sigaction)(int32, Siginfo*, void *); + } _sa_u; /* signal handler */ + uint32 sa_mask[4]; /* signal mask to apply */ + int32 sa_flags; /* see signal options below */ +} Sigaction; + +void +runtime·setsig(int32 i, GoSighandler *fn, bool restart) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask[0] = ~0U; + sa.sa_mask[1] = ~0U; + sa.sa_mask[2] = ~0U; + sa.sa_mask[3] = ~0U; + if (fn == runtime·sighandler) + fn = (void*)runtime·sigtramp; + sa._sa_u._sa_sigaction = (void*)fn; + runtime·sigaction(i, &sa, nil); +} + +GoSighandler* +runtime·getsig(int32 i) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if((void*)sa._sa_u._sa_sigaction == runtime·sigtramp) + return runtime·sighandler; + return (void*)sa._sa_u._sa_sigaction; +} + +void +runtime·signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = (void*)p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} diff --git a/src/pkg/runtime/os_netbsd.h b/src/pkg/runtime/os_netbsd.h index 19d72fd25..c193ae0b4 100644 --- a/src/pkg/runtime/os_netbsd.h +++ b/src/pkg/runtime/os_netbsd.h @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define SIG_DFL ((void*)0) -#define SIG_IGN ((void*)1) -#define SIGHUP 1 #define SS_DISABLE 4 #define SIG_BLOCK 1 @@ -13,9 +10,6 @@ struct sigaction; -void runtime·raisesigpipe(void); -void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); -void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); void runtime·sigpanic(void); void runtime·setitimer(int32, Itimerval*, Itimerval*); @@ -23,6 +17,7 @@ void runtime·sigaction(int32, struct sigaction*, struct sigaction*); void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); void runtime·sigprocmask(int32, Sigset*, Sigset*); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); +extern void runtime·lwp_tramp(void); #define NSIG 33 #define SI_USER 0 diff --git a/src/pkg/runtime/os_netbsd_386.c b/src/pkg/runtime/os_netbsd_386.c new file mode 100644 index 000000000..23e9db3c1 --- /dev/null +++ b/src/pkg/runtime/os_netbsd_386.c @@ -0,0 +1,17 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" + +void +runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) +{ + mc->__gregs[REG_EIP] = (uint32)runtime·lwp_tramp; + mc->__gregs[REG_UESP] = (uint32)stack; + mc->__gregs[REG_EBX] = (uint32)mp; + mc->__gregs[REG_EDX] = (uint32)gp; + mc->__gregs[REG_ESI] = (uint32)fn; +} diff --git a/src/pkg/runtime/os_netbsd_amd64.c b/src/pkg/runtime/os_netbsd_amd64.c new file mode 100644 index 000000000..226846cbb --- /dev/null +++ b/src/pkg/runtime/os_netbsd_amd64.c @@ -0,0 +1,18 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" + +void +runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) +{ + // Machine dependent mcontext initialisation for LWP. + mc->__gregs[REG_RIP] = (uint64)runtime·lwp_tramp; + mc->__gregs[REG_RSP] = (uint64)stack; + mc->__gregs[REG_R8] = (uint64)mp; + mc->__gregs[REG_R9] = (uint64)gp; + mc->__gregs[REG_R12] = (uint64)fn; +} diff --git a/src/pkg/runtime/os_netbsd_arm.c b/src/pkg/runtime/os_netbsd_arm.c new file mode 100644 index 000000000..385e6406d --- /dev/null +++ b/src/pkg/runtime/os_netbsd_arm.c @@ -0,0 +1,33 @@ +// 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. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" +#include "signal_GOOS_GOARCH.h" + +void +runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) +{ + mc->__gregs[REG_R15] = (uint32)runtime·lwp_tramp; + mc->__gregs[REG_R13] = (uint32)stack; + mc->__gregs[REG_R0] = (uint32)mp; + mc->__gregs[REG_R1] = (uint32)gp; + mc->__gregs[REG_R2] = (uint32)fn; +} + +void +runtime·checkgoarm(void) +{ + // TODO(minux) +} + +#pragma textflag 7 +int64 +runtime·cputicks() { + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // TODO: need more entropy to better seed fastrand1. + return runtime·nanotime(); +} diff --git a/src/pkg/runtime/thread_openbsd.c b/src/pkg/runtime/os_openbsd.c index 700c48147..4ce64f9f2 100644 --- a/src/pkg/runtime/thread_openbsd.c +++ b/src/pkg/runtime/os_openbsd.c @@ -1,9 +1,11 @@ -// Use of this source file is governed by a BSD-style -// license that can be found in the LICENSE file.` +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" +#include "signal_unix.h" #include "stack.h" enum @@ -61,6 +63,7 @@ int32 runtime·semasleep(int64 ns) { Timespec ts; + int64 secs; // spin-mutex lock while(runtime·xchg(&m->waitsemalock, 1)) @@ -75,7 +78,11 @@ runtime·semasleep(int64 ns) runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock, nil); else { ns += runtime·nanotime(); - ts.tv_sec = ns/1000000000LL; + secs = ns/1000000000LL; + // Avoid overflow + if(secs >= 1LL<<31) + secs = (1LL<<31) - 1; + ts.tv_sec = secs; ts.tv_nsec = ns%1000000000LL; runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock, nil); } @@ -160,6 +167,22 @@ runtime·osinit(void) } void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + static byte urandom_data[HashRandomBytes]; + int32 fd; + fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0); + if(runtime·read(fd, urandom_data, HashRandomBytes) == HashRandomBytes) { + *rnd = urandom_data; + *rnd_len = HashRandomBytes; + } else { + *rnd = nil; + *rnd_len = 0; + } + runtime·close(fd); +} + +void runtime·goenvs(void) { runtime·goenvs_unix(); @@ -263,3 +286,55 @@ runtime·badsignal(int32 sig) runtime·write(2, "\n", 1); runtime·exit(1); } + +extern void runtime·sigtramp(void); + +typedef struct sigaction { + union { + void (*__sa_handler)(int32); + void (*__sa_sigaction)(int32, Siginfo*, void *); + } __sigaction_u; /* signal handler */ + uint32 sa_mask; /* signal mask to apply */ + int32 sa_flags; /* see signal options below */ +} Sigaction; + +void +runtime·setsig(int32 i, GoSighandler *fn, bool restart) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask = ~0U; + if(fn == runtime·sighandler) + fn = (void*)runtime·sigtramp; + sa.__sigaction_u.__sa_sigaction = (void*)fn; + runtime·sigaction(i, &sa, nil); +} + +GoSighandler* +runtime·getsig(int32 i) +{ + Sigaction sa; + + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if((void*)sa.__sigaction_u.__sa_sigaction == runtime·sigtramp) + return runtime·sighandler; + return (void*)sa.__sigaction_u.__sa_sigaction; +} + +void +runtime·signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = (void*)p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} diff --git a/src/pkg/runtime/os_openbsd.h b/src/pkg/runtime/os_openbsd.h index a599aad05..dbfa4b69f 100644 --- a/src/pkg/runtime/os_openbsd.h +++ b/src/pkg/runtime/os_openbsd.h @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define SIG_DFL ((void*)0) -#define SIG_IGN ((void*)1) -#define SIGHUP 1 #define SS_DISABLE 4 #define SIG_BLOCK 1 @@ -13,14 +10,11 @@ struct sigaction; -void runtime·raisesigpipe(void); -void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); void runtime·sigpanic(void); void runtime·setitimer(int32, Itimerval*, Itimerval*); void runtime·sigaction(int32, struct sigaction*, struct sigaction*); void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); -void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); Sigset runtime·sigprocmask(int32, Sigset); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); diff --git a/src/pkg/runtime/thread_plan9.c b/src/pkg/runtime/os_plan9.c index 7f94623e7..c7ed59fc9 100644 --- a/src/pkg/runtime/thread_plan9.c +++ b/src/pkg/runtime/os_plan9.c @@ -19,6 +19,10 @@ runtime·mpreinit(M *mp) // Initialize stack and goroutine for note handling. mp->gsignal = runtime·malg(32*1024); mp->notesig = (int8*)runtime·malloc(ERRMAX*sizeof(int8)); + + // Initialize stack for handling strings from the + // errstr system call, as used in package syscall. + mp->errstr = (byte*)runtime·malloc(ERRMAX*sizeof(byte)); } // Called to initialize a new m (including the bootstrap m). @@ -44,7 +48,7 @@ getproccount(void) int32 fd, i, n, ncpu; byte buf[2048]; - fd = runtime·open((byte*)"/dev/sysstat", OREAD); + fd = runtime·open("/dev/sysstat", OREAD, 0); if(fd < 0) return 1; ncpu = 0; @@ -68,7 +72,7 @@ getpid(void) int32 fd; runtime·memclr(b, sizeof(b)); - fd = runtime·open((byte*)"#c/pid", 0); + fd = runtime·open("#c/pid", 0, 0); if(fd >= 0) { runtime·read(fd, b, sizeof(b)); runtime·close(fd); @@ -88,6 +92,20 @@ runtime·osinit(void) } void +runtime·crash(void) +{ + runtime·notify(nil); + *(int32*)0 = 0; +} + +void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + *rnd = nil; + *rnd_len = 0; +} + +void runtime·goenvs(void) { } @@ -191,7 +209,7 @@ runtime·postnote(int32 pid, int8* msg) p--; runtime·memmove((void*)p, (void*)"/note", 5); - fd = runtime·open(buf, OWRITE); + fd = runtime·open((int8*)buf, OWRITE, 0); if(fd < 0) return -1; diff --git a/src/pkg/runtime/os_plan9.h b/src/pkg/runtime/os_plan9.h index c2cdf5b44..f0474cda5 100644 --- a/src/pkg/runtime/os_plan9.h +++ b/src/pkg/runtime/os_plan9.h @@ -3,12 +3,9 @@ // license that can be found in the LICENSE file. // Plan 9-specific system calls -int32 runtime·open(uint8 *file, int32 mode); int32 runtime·pread(int32 fd, void *buf, int32 nbytes, int64 offset); int32 runtime·pwrite(int32 fd, void *buf, int32 nbytes, int64 offset); -int32 runtime·read(int32 fd, void *buf, int32 nbytes); int64 runtime·seek(int32 fd, int64 offset, int32 whence); -int32 runtime·close(int32 fd); void runtime·exits(int8* msg); intptr runtime·brk_(void*); int32 runtime·sleep(int32 ms); @@ -19,7 +16,6 @@ int32 runtime·plan9_semrelease(uint32 *addr, int32 count); int32 runtime·notify(void (*fn)(void*, int8*)); int32 runtime·noted(int32); void runtime·sigtramp(void*, int8*); -int32 runtime·sighandler(void*, int8*, G*); void runtime·sigpanic(void); void runtime·goexitsall(int8*); void runtime·setfpmasks(void); diff --git a/src/pkg/runtime/signal_plan9_386.c b/src/pkg/runtime/os_plan9_386.c index 17bc11749..3396e44e7 100644 --- a/src/pkg/runtime/signal_plan9_386.c +++ b/src/pkg/runtime/os_plan9_386.c @@ -28,6 +28,7 @@ runtime·dumpregs(Ureg *u) int32 runtime·sighandler(void *v, int8 *s, G *gp) { + bool crash; Ureg *ureg; uintptr *sp; SigTab *sig, *nsig; @@ -93,11 +94,15 @@ Throw: runtime·printf("PC=%X\n", ureg->pc); runtime·printf("\n"); - if(runtime·gotraceback()) { + if(runtime·gotraceback(&crash)) { runtime·traceback((void*)ureg->pc, (void*)ureg->sp, 0, gp); runtime·tracebackothers(gp); runtime·dumpregs(ureg); } + + if(crash) + runtime·crash(); + runtime·goexitsall(""); runtime·exits(s); @@ -112,6 +117,12 @@ runtime·sigenable(uint32 sig) } void +runtime·sigdisable(uint32 sig) +{ + USED(sig); +} + +void runtime·resetcpuprofiler(int32 hz) { // TODO: Enable profiling interrupts. diff --git a/src/pkg/runtime/signal_plan9_amd64.c b/src/pkg/runtime/os_plan9_amd64.c index e4f946abc..cf0a82b6b 100644 --- a/src/pkg/runtime/signal_plan9_amd64.c +++ b/src/pkg/runtime/os_plan9_amd64.c @@ -36,6 +36,7 @@ runtime·dumpregs(Ureg *u) int32 runtime·sighandler(void *v, int8 *s, G *gp) { + bool crash; Ureg *ureg; uintptr *sp; SigTab *sig, *nsig; @@ -101,11 +102,15 @@ Throw: runtime·printf("PC=%X\n", ureg->ip); runtime·printf("\n"); - if(runtime·gotraceback()) { + if(runtime·gotraceback(&crash)) { runtime·traceback((void*)ureg->ip, (void*)ureg->sp, 0, gp); runtime·tracebackothers(gp); runtime·dumpregs(ureg); } + + if(crash) + runtime·crash(); + runtime·goexitsall(""); runtime·exits(s); @@ -119,6 +124,12 @@ runtime·sigenable(uint32 sig) } void +runtime·sigdisable(uint32 sig) +{ + USED(sig); +} + +void runtime·resetcpuprofiler(int32 hz) { // TODO: Enable profiling interrupts. diff --git a/src/pkg/runtime/thread_windows.c b/src/pkg/runtime/os_windows.c index ae4e82e50..b28affe31 100644 --- a/src/pkg/runtime/thread_windows.c +++ b/src/pkg/runtime/os_windows.c @@ -11,6 +11,9 @@ #pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll" #pragma dynimport runtime·CreateThread CreateThread "kernel32.dll" #pragma dynimport runtime·CreateWaitableTimer CreateWaitableTimerA "kernel32.dll" +#pragma dynimport runtime·CryptAcquireContextW CryptAcquireContextW "advapi32.dll" +#pragma dynimport runtime·CryptGenRandom CryptGenRandom "advapi32.dll" +#pragma dynimport runtime·CryptReleaseContext CryptReleaseContext "advapi32.dll" #pragma dynimport runtime·DuplicateHandle DuplicateHandle "kernel32.dll" #pragma dynimport runtime·ExitProcess ExitProcess "kernel32.dll" #pragma dynimport runtime·FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll" @@ -31,11 +34,17 @@ #pragma dynimport runtime·timeBeginPeriod timeBeginPeriod "winmm.dll" #pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll" #pragma dynimport runtime·WriteFile WriteFile "kernel32.dll" +#pragma dynimport runtime·NtWaitForSingleObject NtWaitForSingleObject "ntdll.dll" + +extern void *runtime·NtWaitForSingleObject; extern void *runtime·CloseHandle; extern void *runtime·CreateEvent; extern void *runtime·CreateThread; extern void *runtime·CreateWaitableTimer; +extern void *runtime·CryptAcquireContextW; +extern void *runtime·CryptGenRandom; +extern void *runtime·CryptReleaseContext; extern void *runtime·DuplicateHandle; extern void *runtime·ExitProcess; extern void *runtime·FreeEnvironmentStringsW; @@ -79,6 +88,24 @@ runtime·osinit(void) } void +runtime·get_random_data(byte **rnd, int32 *rnd_len) +{ + uintptr handle; + *rnd = nil; + *rnd_len = 0; + if(runtime·stdcall(runtime·CryptAcquireContextW, 5, &handle, nil, nil, + (uintptr)1 /* PROV_RSA_FULL */, + (uintptr)0xf0000000U /* CRYPT_VERIFYCONTEXT */) != 0) { + static byte random_data[HashRandomBytes]; + if(runtime·stdcall(runtime·CryptGenRandom, 3, handle, (uintptr)HashRandomBytes, random_data)) { + *rnd = random_data; + *rnd_len = HashRandomBytes; + } + runtime·stdcall(runtime·CryptReleaseContext, 2, handle, (uintptr)0); + } +} + +void runtime·goenvs(void) { extern Slice syscall·envs; @@ -135,22 +162,6 @@ runtime·write(int32 fd, void *buf, int32 n) return written; } -#pragma textflag 7 -void -runtime·osyield(void) -{ - runtime·stdcall(runtime·Sleep, 1, (uintptr)0); -} - -void -runtime·usleep(uint32 us) -{ - us /= 1000; - if(us == 0) - us = 1; - runtime·stdcall(runtime·Sleep, 1, (uintptr)us); -} - #define INFINITE ((uintptr)0xFFFFFFFF) int32 @@ -444,3 +455,16 @@ int32 runtime·badcallbacklen = sizeof runtime·badcallbackmsg - 1; int8 runtime·badsignalmsg[] = "runtime: signal received on thread not created by Go.\n"; int32 runtime·badsignallen = sizeof runtime·badsignalmsg - 1; + +void +runtime·crash(void) +{ + // 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. +} diff --git a/src/pkg/runtime/signal_windows_386.c b/src/pkg/runtime/os_windows_386.c index d76d5bf4b..20fbea13d 100644 --- a/src/pkg/runtime/signal_windows_386.c +++ b/src/pkg/runtime/os_windows_386.c @@ -27,6 +27,7 @@ runtime·dumpregs(Context *r) uint32 runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) { + bool crash; uintptr *sp; switch(info->ExceptionCode) { @@ -74,11 +75,15 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) } runtime·printf("\n"); - if(runtime·gotraceback()){ + if(runtime·gotraceback(&crash)){ runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, gp); runtime·tracebackothers(gp); runtime·dumpregs(r); } + + if(crash) + runtime·crash(); + runtime·exit(2); return 0; @@ -91,6 +96,12 @@ runtime·sigenable(uint32 sig) } void +runtime·sigdisable(uint32 sig) +{ + USED(sig); +} + +void runtime·dosigprof(Context *r, G *gp) { runtime·sigprof((uint8*)r->Eip, (uint8*)r->Esp, nil, gp); diff --git a/src/pkg/runtime/signal_windows_amd64.c b/src/pkg/runtime/os_windows_amd64.c index 3729aa57b..881c73c93 100644 --- a/src/pkg/runtime/signal_windows_amd64.c +++ b/src/pkg/runtime/os_windows_amd64.c @@ -35,6 +35,7 @@ runtime·dumpregs(Context *r) uint32 runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) { + bool crash; uintptr *sp; switch(info->ExceptionCode) { @@ -81,11 +82,14 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) } runtime·printf("\n"); - if(runtime·gotraceback()){ + if(runtime·gotraceback(&crash)){ runtime·traceback((void*)r->Rip, (void*)r->Rsp, 0, gp); runtime·tracebackothers(gp); runtime·dumpregs(r); } + + if(crash) + runtime·crash(); runtime·exit(2); return 0; @@ -98,6 +102,12 @@ runtime·sigenable(uint32 sig) } void +runtime·sigdisable(uint32 sig) +{ + USED(sig); +} + +void runtime·dosigprof(Context *r, G *gp) { runtime·sigprof((uint8*)r->Rip, (uint8*)r->Rsp, nil, gp); diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c index 2f553f417..d0cf3ad6f 100644 --- a/src/pkg/runtime/panic.c +++ b/src/pkg/runtime/panic.c @@ -5,6 +5,7 @@ #include "runtime.h" #include "arch_GOARCH.h" #include "stack.h" +#include "malloc.h" // Code related to defer, panic and recover. @@ -383,7 +384,10 @@ nomatch: void runtime·startpanic(void) { - if(m->mcache == nil) // can happen if called from signal handler or throw + if(runtime·mheap == 0 || runtime·mheap->cachealloc.size == 0) { // very early + runtime·printf("runtime: panic before malloc heap initialized\n"); + m->mallocing = 1; // tell rest of panic not to try to malloc + } else if(m->mcache == nil) // can happen if called from signal handler or throw m->mcache = runtime·allocmcache(); if(m->dying) { runtime·printf("panic during panic\n"); @@ -398,12 +402,13 @@ void runtime·dopanic(int32 unused) { static bool didothers; + bool crash; if(g->sig != 0) runtime·printf("[signal %x code=%p addr=%p pc=%p]\n", g->sig, g->sigcode0, g->sigcode1, g->sigpc); - if(runtime·gotraceback()){ + if(runtime·gotraceback(&crash)){ if(g != m->g0) { runtime·printf("\n"); runtime·goroutineheader(g); @@ -424,6 +429,9 @@ runtime·dopanic(int32 unused) runtime·lock(&deadlock); runtime·lock(&deadlock); } + + if(crash) + runtime·crash(); runtime·exit(2); } diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c index aa5537d02..a4468c2af 100644 --- a/src/pkg/runtime/parfor.c +++ b/src/pkg/runtime/parfor.c @@ -46,6 +46,7 @@ void runtime·parforsetup(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void (*body)(ParFor*, uint32)) { uint32 i, begin, end; + uint64 *pos; if(desc == nil || nthr == 0 || nthr > desc->nthrmax || body == nil) { runtime·printf("desc=%p nthr=%d count=%d body=%p\n", desc, nthr, n, body); @@ -67,7 +68,10 @@ runtime·parforsetup(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, for(i=0; i<nthr; i++) { begin = (uint64)n*i / nthr; end = (uint64)n*(i+1) / nthr; - desc->thr[i].pos = (uint64)begin | (((uint64)end)<<32); + pos = &desc->thr[i].pos; + if(((uintptr)pos & 7) != 0) + runtime·throw("parforsetup: pos is not aligned"); + *pos = (uint64)begin | (((uint64)end)<<32); } } diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index 4ce0a718c..8d05730e4 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -49,6 +49,7 @@ struct Sched { Note stopnote; uint32 sysmonwait; Note sysmonnote; + uint64 lastpoll; int32 profilehz; // cpu profiling rate }; @@ -71,8 +72,6 @@ M* runtime·extram; int8* runtime·goos; int32 runtime·ncpu; static int32 newprocs; -// Keep trace of scavenger's goroutine for deadlock detection. -static G *scvg; void runtime·mstart(void); static void runqput(P*, G*); @@ -109,6 +108,7 @@ static void globrunqput(G*); static G* globrunqget(P*); static P* pidleget(void); static void pidleput(P*); +static void injectglist(G*); // The bootstrap sequence is: // @@ -137,6 +137,7 @@ runtime·schedinit(void) // so that we don't need to call malloc when we crash. // runtime·findfunc(0); + runtime·sched.lastpoll = runtime·nanotime(); procs = 1; p = runtime·getenv("GOMAXPROCS"); if(p != nil && (n = runtime·atoi(p)) > 0) { @@ -174,8 +175,7 @@ runtime·main(void) runtime·lockOSThread(); if(m != &runtime·m0) runtime·throw("runtime·main not on m0"); - scvg = runtime·newproc1(&scavenger, nil, 0, 0, runtime·main); - scvg->issystem = true; + runtime·newproc1(&scavenger, nil, 0, 0, runtime·main); main·init(); runtime·unlockOSThread(); @@ -232,7 +232,7 @@ runtime·tracebackothers(G *me) G *gp; int32 traceback; - traceback = runtime·gotraceback(); + traceback = runtime·gotraceback(nil); for(gp = runtime·allg; gp != nil; gp = gp->alllink) { if(gp == me || gp->status == Gdead) continue; @@ -332,7 +332,7 @@ runtime·helpgc(int32 nproc) mp = mget(); if(mp == nil) runtime·throw("runtime·gcprocs inconsistency"); - mp->helpgc = 1; + mp->helpgc = n; mp->mcache = runtime·allp[pos]->mcache; pos++; runtime·notewakeup(&mp->park); @@ -386,16 +386,19 @@ runtime·stoptheworld(void) static void mhelpgc(void) { - m->helpgc = 1; + m->helpgc = -1; } void runtime·starttheworld(void) { - P *p; + P *p, *p1; M *mp; + G *gp; bool add; + gp = runtime·netpoll(false); // non-blocking + injectglist(gp); add = needaddgcproc(); runtime·lock(&runtime·sched); if(newprocs) { @@ -405,6 +408,7 @@ runtime·starttheworld(void) procresize(runtime·gomaxprocs); runtime·gcwaiting = 0; + p1 = nil; while(p = pidleget()) { // procresize() puts p's with work at the beginning of the list. // Once we reach a p without a run queue, the rest don't have one either. @@ -414,8 +418,9 @@ runtime·starttheworld(void) } mp = mget(); if(mp == nil) { - pidleput(p); - break; + p->link = p1; + p1 = p; + continue; } if(mp->nextp) runtime·throw("starttheworld: inconsistent mp->nextp"); @@ -428,6 +433,13 @@ runtime·starttheworld(void) } runtime·unlock(&runtime·sched); + while(p1) { + p = p1; + p1 = p1->link; + add = false; + newm(nil, p); + } + if(add) { // If GC could have used another helper proc, start one now, // in the hope that it will be available next time. @@ -473,7 +485,7 @@ runtime·mstart(void) m->mstartfn(); if(m->helpgc) { - m->helpgc = false; + m->helpgc = 0; stopm(); } else if(m != &runtime·m0) { acquirep(m->nextp); @@ -782,8 +794,8 @@ retry: runtime·notesleep(&m->park); runtime·noteclear(&m->park); if(m->helpgc) { - m->helpgc = 0; runtime·gchelper(); + m->helpgc = 0; m->mcache = nil; goto retry; } @@ -970,7 +982,7 @@ execute(G *gp) } // Finds a runnable goroutine to execute. -// Tries to steal from other P's and get g from global queue. +// Tries to steal from other P's, get g from global queue, poll network. static G* findrunnable(void) { @@ -995,6 +1007,13 @@ top: if(gp) return gp; } + // poll network + gp = runtime·netpoll(false); // non-blocking + if(gp) { + injectglist(gp->schedlink); + gp->status = Grunnable; + return gp; + } // If number of spinning M's >= number of busy P's, block. // This is necessary to prevent excessive CPU consumption // when GOMAXPROCS>>1 but the program parallelism is low. @@ -1049,10 +1068,54 @@ stop: break; } } + // poll network + if(runtime·xchg64(&runtime·sched.lastpoll, 0) != 0) { + if(m->p) + runtime·throw("findrunnable: netpoll with p"); + if(m->spinning) + runtime·throw("findrunnable: netpoll with spinning"); + gp = runtime·netpoll(true); // block until new work is available + runtime·atomicstore64(&runtime·sched.lastpoll, runtime·nanotime()); + if(gp) { + runtime·lock(&runtime·sched); + p = pidleget(); + runtime·unlock(&runtime·sched); + if(p) { + acquirep(p); + injectglist(gp->schedlink); + gp->status = Grunnable; + return gp; + } + injectglist(gp); + } + } stopm(); goto top; } +// Injects the list of runnable G's into the scheduler. +// Can run concurrently with GC. +static void +injectglist(G *glist) +{ + int32 n; + G *gp; + + if(glist == nil) + return; + runtime·lock(&runtime·sched); + for(n = 0; glist; n++) { + gp = glist; + glist = gp->schedlink; + gp->status = Grunnable; + globrunqput(gp); + } + runtime·unlock(&runtime·sched); + + for(; n && runtime·sched.npidle; n--) + startm(nil, false); +} + // One round of scheduler: find a runnable goroutine and execute it. // Never returns. static void @@ -1164,6 +1227,11 @@ goexit0(G *gp) gp->lockedm = nil; m->curg = nil; m->lockedg = nil; + if(m->locked & ~LockExternal) { + runtime·printf("invalid m->locked = %d", m->locked); + runtime·throw("internal lockOSThread error"); + } + m->locked = 0; runtime·unwindstack(gp, nil); gfput(m->p, gp); schedule(); @@ -1251,7 +1319,7 @@ void p = releasep(); handoffp(p); - if(g == scvg) // do not consider blocked scavenger for deadlock detection + if(g->isbackground) // do not consider blocked scavenger for deadlock detection inclocked(1); runtime·gosave(&g->sched); // re-save for traceback } @@ -1283,7 +1351,7 @@ runtime·exitsyscall(void) return; } - if(g == scvg) // do not consider blocked scavenger for deadlock detection + if(g->isbackground) // do not consider blocked scavenger for deadlock detection inclocked(-1); // Try to get any other idle P. m->p = nil; @@ -1885,7 +1953,7 @@ checkdead(void) } grunning = 0; for(gp = runtime·allg; gp; gp = gp->alllink) { - if(gp == scvg) + if(gp->isbackground) continue; s = gp->status; if(s == Gwaiting) @@ -1905,6 +1973,8 @@ static void sysmon(void) { uint32 idle, delay; + int64 now, lastpoll; + G *gp; uint32 ticks[MaxGomaxprocs]; idle = 0; // how many cycles in succession we had not wokeup somebody @@ -1929,6 +1999,14 @@ sysmon(void) } else runtime·unlock(&runtime·sched); } + // poll network if not polled for more than 10ms + lastpoll = runtime·atomicload64(&runtime·sched.lastpoll); + now = runtime·nanotime(); + if(lastpoll != 0 && lastpoll + 10*1000*1000 > now) { + gp = runtime·netpoll(false); // non-blocking + injectglist(gp); + } + // retake P's blocked in syscalls if(retake(ticks)) idle = 0; else diff --git a/src/pkg/runtime/race/testdata/map_test.go b/src/pkg/runtime/race/testdata/map_test.go index 6f86a50b7..35db8db69 100644 --- a/src/pkg/runtime/race/testdata/map_test.go +++ b/src/pkg/runtime/race/testdata/map_test.go @@ -94,8 +94,7 @@ func TestNoRaceMapRangeRange(t *testing.T) { <-ch } -// Map len is not instrumented. -func TestRaceFailingMapLen(t *testing.T) { +func TestRaceMapLen(t *testing.T) { m := make(map[string]bool) ch := make(chan bool, 1) go func() { @@ -117,8 +116,7 @@ func TestRaceMapDelete(t *testing.T) { <-ch } -// Map len is not instrumented. -func TestRaceFailingMapLenDelete(t *testing.T) { +func TestRaceMapLenDelete(t *testing.T) { m := make(map[string]bool) ch := make(chan bool, 1) go func() { diff --git a/src/pkg/runtime/race/testdata/mop_test.go b/src/pkg/runtime/race/testdata/mop_test.go index f2daa3730..26cd3a4e4 100644 --- a/src/pkg/runtime/race/testdata/mop_test.go +++ b/src/pkg/runtime/race/testdata/mop_test.go @@ -306,6 +306,102 @@ func TestNoRacePlus(t *testing.T) { <-ch } +func TestRaceComplement(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + x = ^y + ch <- 1 + }() + go func() { + y = ^z + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceDiv(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + x = y / (z + 1) + ch <- 1 + }() + go func() { + y = z + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceDivConst(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + x = y / 3 + ch <- 1 + }() + go func() { + y = z + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceMod(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + x = y % (z + 1) + ch <- 1 + }() + go func() { + y = z + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceModConst(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + x = y % 3 + ch <- 1 + }() + go func() { + y = z + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceRotate(t *testing.T) { + var x, y, z uint32 + ch := make(chan int, 2) + + go func() { + x = y<<12 | y>>20 + ch <- 1 + }() + go func() { + y = z + ch <- 1 + }() + <-ch + <-ch +} + // May crash if the instrumentation is reckless. func TestNoRaceEnoughRegisters(t *testing.T) { // from erf.go diff --git a/src/pkg/runtime/race/testdata/regression_test.go b/src/pkg/runtime/race/testdata/regression_test.go index afe8cc5ec..f08ee3ed3 100644 --- a/src/pkg/runtime/race/testdata/regression_test.go +++ b/src/pkg/runtime/race/testdata/regression_test.go @@ -45,6 +45,18 @@ func InstrumentMapLen3() { _ = len(*m[0]) } +func TestRaceUnaddressableMapLen(t *testing.T) { + m := make(map[int]map[int]int) + ch := make(chan int, 1) + m[0] = make(map[int]int) + go func() { + _ = len(m[0]) + ch <- 0 + }() + m[0][0] = 1 + <-ch +} + type Rect struct { x, y int } diff --git a/src/pkg/runtime/race/testdata/slice_test.go b/src/pkg/runtime/race/testdata/slice_test.go index 773463662..1fe051b12 100644 --- a/src/pkg/runtime/race/testdata/slice_test.go +++ b/src/pkg/runtime/race/testdata/slice_test.go @@ -463,3 +463,24 @@ func TestRaceSliceRuneToString(t *testing.T) { s[9] = 42 <-c } + +func TestRaceConcatString(t *testing.T) { + s := "hello" + c := make(chan string, 1) + go func() { + c <- s + " world" + }() + s = "world" + <-c +} + +func TestRaceCompareString(t *testing.T) { + s1 := "hello" + s2 := "world" + c := make(chan bool, 1) + go func() { + c <- s1 == s2 + }() + s1 = s2 + <-c +} diff --git a/src/pkg/runtime/rt0_darwin_386.s b/src/pkg/runtime/rt0_darwin_386.s index 30b497f5e..4b4c1f294 100644 --- a/src/pkg/runtime/rt0_darwin_386.s +++ b/src/pkg/runtime/rt0_darwin_386.s @@ -2,7 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Darwin and Linux use the same linkage to main +TEXT _rt0_386_darwin(SB),7,$8 + MOVL 8(SP), AX + LEAL 12(SP), BX + MOVL AX, 0(SP) + MOVL BX, 4(SP) + CALL main(SB) + INT $3 -TEXT _rt0_386_darwin(SB),7,$0 +TEXT main(SB),7,$0 JMP _rt0_386(SB) diff --git a/src/pkg/runtime/rt0_darwin_amd64.s b/src/pkg/runtime/rt0_darwin_amd64.s index 4cfab5876..45e69a015 100644 --- a/src/pkg/runtime/rt0_darwin_amd64.s +++ b/src/pkg/runtime/rt0_darwin_amd64.s @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Darwin and Linux use the same linkage to main - TEXT _rt0_amd64_darwin(SB),7,$-8 + LEAQ 8(SP), SI // argv + MOVQ 0(SP), DI // argc + MOVQ $main(SB), AX + JMP AX + +TEXT main(SB),7,$-8 MOVQ $_rt0_amd64(SB), AX - MOVQ SP, DI JMP AX diff --git a/src/pkg/runtime/rt0_freebsd_386.s b/src/pkg/runtime/rt0_freebsd_386.s index 3ca981b3a..c84482cdb 100644 --- a/src/pkg/runtime/rt0_freebsd_386.s +++ b/src/pkg/runtime/rt0_freebsd_386.s @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Darwin and Linux use the same linkage to main +TEXT _rt0_386_freebsd(SB),7,$8 + MOVL 8(SP), AX + LEAL 12(SP), BX + MOVL AX, 0(SP) + MOVL BX, 4(SP) + CALL main(SB) + INT $3 -TEXT _rt0_386_freebsd(SB),7,$0 +TEXT main(SB),7,$0 JMP _rt0_386(SB) - diff --git a/src/pkg/runtime/rt0_freebsd_amd64.s b/src/pkg/runtime/rt0_freebsd_amd64.s index 5d2eeeeff..e6c6fb9ca 100644 --- a/src/pkg/runtime/rt0_freebsd_amd64.s +++ b/src/pkg/runtime/rt0_freebsd_amd64.s @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Darwin and Linux use the same linkage to main - TEXT _rt0_amd64_freebsd(SB),7,$-8 - MOVQ $_rt0_amd64(SB), DX - JMP DX + LEAQ 8(DI), SI // argv + MOVQ 0(DI), DI // argc + MOVQ $main(SB), AX + JMP AX + +TEXT main(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/pkg/runtime/rt0_linux_386.s b/src/pkg/runtime/rt0_linux_386.s index 83149540e..73cca5d98 100644 --- a/src/pkg/runtime/rt0_linux_386.s +++ b/src/pkg/runtime/rt0_linux_386.s @@ -2,11 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Darwin and Linux use the same linkage to main - -TEXT _rt0_386_linux(SB),7,$0 +TEXT _rt0_386_linux(SB),7,$8 + MOVL 8(SP), AX + LEAL 12(SP), BX + MOVL AX, 0(SP) + MOVL BX, 4(SP) CALL runtime·linux_setup_vdso(SB) - JMP _rt0_386(SB) + CALL main(SB) + INT $3 + +TEXT main(SB),7,$0 + JMP _rt0_386(SB) TEXT _fallback_vdso(SB),7,$0 INT $0x80 diff --git a/src/pkg/runtime/rt0_linux_amd64.s b/src/pkg/runtime/rt0_linux_amd64.s index dac9ae181..dfc9c0421 100644 --- a/src/pkg/runtime/rt0_linux_amd64.s +++ b/src/pkg/runtime/rt0_linux_amd64.s @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Darwin and Linux use the same linkage to main - TEXT _rt0_amd64_linux(SB),7,$-8 + LEAQ 8(SP), SI // argv + MOVQ 0(SP), DI // argc + MOVQ $main(SB), AX + JMP AX + +TEXT main(SB),7,$-8 MOVQ $_rt0_amd64(SB), AX - MOVQ SP, DI JMP AX diff --git a/src/pkg/runtime/rt0_netbsd_386.s b/src/pkg/runtime/rt0_netbsd_386.s index 829e4133b..b4c029c53 100644 --- a/src/pkg/runtime/rt0_netbsd_386.s +++ b/src/pkg/runtime/rt0_netbsd_386.s @@ -2,5 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -TEXT _rt0_386_netbsd(SB),7,$0 - JMP _rt0_386(SB) +TEXT _rt0_386_netbsd(SB),7,$8 + MOVL 8(SP), AX + LEAL 12(SP), BX + MOVL AX, 0(SP) + MOVL BX, 4(SP) + CALL main(SB) + INT $3 + +TEXT main(SB),7,$0 + JMP _rt0_386(SB) diff --git a/src/pkg/runtime/rt0_netbsd_amd64.s b/src/pkg/runtime/rt0_netbsd_amd64.s index 85482b98d..9e7b78edc 100644 --- a/src/pkg/runtime/rt0_netbsd_amd64.s +++ b/src/pkg/runtime/rt0_netbsd_amd64.s @@ -3,6 +3,11 @@ // license that can be found in the LICENSE file. TEXT _rt0_amd64_netbsd(SB),7,$-8 - MOVQ $_rt0_amd64(SB), DX - MOVQ SP, DI - JMP DX + LEAQ 8(SP), SI // argv + MOVQ 0(SP), DI // argc + MOVQ $main(SB), AX + JMP AX + +TEXT main(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/pkg/runtime/rt0_openbsd_386.s b/src/pkg/runtime/rt0_openbsd_386.s index e7e0da78f..9c00a7334 100644 --- a/src/pkg/runtime/rt0_openbsd_386.s +++ b/src/pkg/runtime/rt0_openbsd_386.s @@ -2,5 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -TEXT _rt0_386_openbsd(SB),7,$0 - JMP _rt0_386(SB) +TEXT _rt0_386_openbsd(SB),7,$8 + MOVL 8(SP), AX + LEAL 12(SP), BX + MOVL AX, 0(SP) + MOVL BX, 4(SP) + CALL main(SB) + INT $3 + +TEXT main(SB),7,$0 + JMP _rt0_386(SB) diff --git a/src/pkg/runtime/rt0_openbsd_amd64.s b/src/pkg/runtime/rt0_openbsd_amd64.s index e7fce5969..245a4c0f9 100644 --- a/src/pkg/runtime/rt0_openbsd_amd64.s +++ b/src/pkg/runtime/rt0_openbsd_amd64.s @@ -3,6 +3,11 @@ // license that can be found in the LICENSE file. TEXT _rt0_amd64_openbsd(SB),7,$-8 - MOVQ $_rt0_amd64(SB), DX - MOVQ SP, DI - JMP DX + LEAQ 8(SP), SI // argv + MOVQ 0(SP), DI // argc + MOVQ $main(SB), AX + JMP AX + +TEXT main(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/pkg/runtime/rt0_plan9_386.s b/src/pkg/runtime/rt0_plan9_386.s index 56f3a0f6c..7af1eae7c 100644 --- a/src/pkg/runtime/rt0_plan9_386.s +++ b/src/pkg/runtime/rt0_plan9_386.s @@ -26,6 +26,13 @@ argv_fix: LOOP argv_fix CALL runtime·asminit(SB) + + MOVL 0(SP), AX + LEAL 4(SP), BX + PUSHL BX + PUSHL AX + PUSHL $-1 + JMP _rt0_386(SB) DATA runtime·isplan9(SB)/4, $1 diff --git a/src/pkg/runtime/rt0_plan9_amd64.s b/src/pkg/runtime/rt0_plan9_amd64.s index 2b1fa2ae1..16e5e82b7 100644 --- a/src/pkg/runtime/rt0_plan9_amd64.s +++ b/src/pkg/runtime/rt0_plan9_amd64.s @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -TEXT _rt0_amd64_plan9(SB),7, $0 +TEXT _rt0_amd64_plan9(SB),7,$-8 + LEAQ 8(SP), SI // argv + MOVQ 0(SP), DI // argc MOVQ $_rt0_amd64(SB), AX - MOVQ SP, DI JMP AX DATA runtime·isplan9(SB)/4, $1 diff --git a/src/pkg/runtime/rt0_windows_386.s b/src/pkg/runtime/rt0_windows_386.s index a06aa787e..6e34c6c17 100644 --- a/src/pkg/runtime/rt0_windows_386.s +++ b/src/pkg/runtime/rt0_windows_386.s @@ -2,8 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -TEXT _rt0_386_windows(SB),7,$0 +TEXT _rt0_386_windows(SB),7,$12 + MOVL 12(SP), AX + LEAL 16(SP), BX + MOVL AX, 4(SP) + MOVL BX, 8(SP) + MOVL $-1, 0(SP) // return PC for main + JMP main(SB) + +TEXT main(SB),7,$0 JMP _rt0_386(SB) + DATA runtime·iswindows(SB)/4, $1 GLOBL runtime·iswindows(SB), $4 diff --git a/src/pkg/runtime/rt0_windows_amd64.s b/src/pkg/runtime/rt0_windows_amd64.s index dc1408adc..b48c05570 100644 --- a/src/pkg/runtime/rt0_windows_amd64.s +++ b/src/pkg/runtime/rt0_windows_amd64.s @@ -4,9 +4,14 @@ #include "zasm_GOOS_GOARCH.h" -TEXT _rt0_amd64_windows(SB),7,$-8 +TEXT _rt0_amd64_windows(SB),7,$-8 + LEAQ 8(SP), SI // argv + MOVQ 0(SP), DI // argc + MOVQ $main(SB), AX + JMP AX + +TEXT main(SB),7,$-8 MOVQ $_rt0_amd64(SB), AX - MOVQ SP, DI JMP AX DATA runtime·iswindows(SB)/4, $1 diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c index 4d57cbafd..d62408118 100644 --- a/src/pkg/runtime/runtime.c +++ b/src/pkg/runtime/runtime.c @@ -17,21 +17,34 @@ enum { */ void runtime·sigpanic(void); +// The GOTRACEBACK environment variable controls the +// behavior of a Go program that is crashing and exiting. +// GOTRACEBACK=0 suppress all tracebacks +// GOTRACEBACK=1 default behavior - show tracebacks but exclude runtime frames +// GOTRACEBACK=2 show tracebacks including runtime frames +// GOTRACEBACK=crash show tracebacks including runtime frames, then crash (core dump etc) int32 -runtime·gotraceback(void) +runtime·gotraceback(bool *crash) { byte *p; + if(crash != nil) + *crash = false; p = runtime·getenv("GOTRACEBACK"); if(p == nil || p[0] == '\0') return 1; // default is on + if(runtime·strcmp(p, (byte*)"crash") == 0) { + if(crash != nil) + *crash = true; + return 2; // extra information + } return runtime·atoi(p); } int32 -runtime·mcmp(byte *s1, byte *s2, uint32 n) +runtime·mcmp(byte *s1, byte *s2, uintptr n) { - uint32 i; + uintptr i; byte c1, c2; for(i=0; i<n; i++) { @@ -75,6 +88,11 @@ runtime·args(int32 c, uint8 **v) int32 runtime·isplan9; int32 runtime·iswindows; +// Information about what cpu features are available. +// Set on startup in asm_{x86/amd64}.s. +uint32 runtime·cpuid_ecx; +uint32 runtime·cpuid_edx; + void runtime·goargs(void) { @@ -156,6 +174,10 @@ TestAtomic64(void) runtime·throw("xadd64 failed"); if(runtime·atomicload64(&z64) != (2ull<<40)+2) runtime·throw("xadd64 failed"); + if(runtime·xchg64(&z64, (3ull<<40)+3) != (2ull<<40)+2) + runtime·throw("xchg64 failed"); + if(runtime·atomicload64(&z64) != (3ull<<40)+3) + runtime·throw("xchg64 failed"); } void diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 08f43a69b..46c77e3fd 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -85,6 +85,7 @@ typedef struct LFNode LFNode; typedef struct ParFor ParFor; typedef struct ParForThread ParForThread; typedef struct CgoMal CgoMal; +typedef struct PollDesc PollDesc; /* * Per-CPU declaration. @@ -235,8 +236,9 @@ struct G int8* waitreason; // if status==Gwaiting G* schedlink; bool ispanic; - bool issystem; - int8 raceignore; // ignore race detection events + bool issystem; // do not output in stack dump + bool isbackground; // ignore in deadlock detector + int8 raceignore; // ignore race detection events M* m; // for debuggers, but offset not hard-coded M* lockedm; int32 sig; @@ -320,6 +322,7 @@ struct M #endif #ifdef GOOS_plan9 int8* notesig; + byte* errstr; #endif SEH* seh; uintptr end[]; @@ -381,6 +384,8 @@ enum SigThrow = 1<<2, // if signal.Notify doesn't take it, exit loudly SigPanic = 1<<3, // if the signal is from the kernel, panic SigDefault = 1<<4, // if the signal isn't explicitly requested, don't monitor it + SigHandling = 1<<5, // our signal handler is registered + SigIgnored = 1<<6, // the signal was ignored before we registered for it }; // NOTE(rsc): keep in sync with extern.go:/type.Func. @@ -482,6 +487,7 @@ struct ParFor bool wait; // if true, wait while all threads finish processing, // otherwise parfor may return while other threads are still working ParForThread *thr; // array of thread descriptors + uint32 pad; // to align ParForThread.pos for 64-bit atomic operations // stats uint64 nsteal; uint64 nstealcnt; @@ -555,11 +561,25 @@ struct Alg extern Alg runtime·algarray[Amax]; +byte* runtime·startup_random_data; +uint32 runtime·startup_random_data_len; +void runtime·get_random_data(byte**, int32*); + +enum { + // hashinit wants this many random bytes + HashRandomBytes = 32 +}; +void runtime·hashinit(void); + void runtime·memhash(uintptr*, uintptr, void*); void runtime·nohash(uintptr*, uintptr, void*); void runtime·strhash(uintptr*, uintptr, void*); void runtime·interhash(uintptr*, uintptr, void*); void runtime·nilinterhash(uintptr*, uintptr, void*); +void runtime·aeshash(uintptr*, uintptr, void*); +void runtime·aeshash32(uintptr*, uintptr, void*); +void runtime·aeshash64(uintptr*, uintptr, void*); +void runtime·aeshashstr(uintptr*, uintptr, void*); void runtime·memequal(bool*, uintptr, void*, void*); void runtime·noequal(bool*, uintptr, void*, void*); @@ -578,7 +598,6 @@ void runtime·memcopy16(uintptr, void*, void*); void runtime·memcopy32(uintptr, void*, void*); void runtime·memcopy64(uintptr, void*, void*); void runtime·memcopy128(uintptr, void*, void*); -void runtime·memcopy(uintptr, void*, void*); void runtime·strcopy(uintptr, void*, void*); void runtime·algslicecopy(uintptr, void*, void*); void runtime·intercopy(uintptr, void*, void*); @@ -635,6 +654,8 @@ extern bool runtime·iscgo; extern void (*runtime·sysargs)(int32, uint8**); extern uint32 runtime·maxstring; extern uint32 runtime·Hchansize; +extern uint32 runtime·cpuid_ecx; +extern uint32 runtime·cpuid_edx; /* * common functions and data @@ -666,8 +687,8 @@ void runtime·panicstring(int8*); void runtime·prints(int8*); void runtime·printf(int8*, ...); byte* runtime·mchr(byte*, byte, byte*); -int32 runtime·mcmp(byte*, byte*, uint32); -void runtime·memmove(void*, void*, uint32); +int32 runtime·mcmp(byte*, byte*, uintptr); +void runtime·memmove(void*, void*, uintptr); void* runtime·mal(uintptr); String runtime·catstring(String, String); String runtime·gostring(byte*); @@ -677,11 +698,15 @@ String runtime·gostringnocopy(byte*); String runtime·gostringw(uint16*); void runtime·initsig(void); void runtime·sigenable(uint32 sig); -int32 runtime·gotraceback(void); +void runtime·sigdisable(uint32 sig); +int32 runtime·gotraceback(bool *crash); void runtime·goroutineheader(G*); void runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp); void runtime·tracebackothers(G*); +int32 runtime·open(int8*, int32, int32); +int32 runtime·read(int32, void*, int32); int32 runtime·write(int32, void*, int32); +int32 runtime·close(int32); int32 runtime·mincore(void*, uintptr, byte*); bool runtime·cas(uint32*, uint32, uint32); bool runtime·cas64(uint64*, uint64*, uint64); @@ -691,6 +716,7 @@ bool runtime·casp(void**, void*, void*); uint32 runtime·xadd(uint32 volatile*, int32); uint64 runtime·xadd64(uint64 volatile*, int64); uint32 runtime·xchg(uint32 volatile*, uint32); +uint64 runtime·xchg64(uint64 volatile*, uint64); uint32 runtime·atomicload(uint32 volatile*); void runtime·atomicstore(uint32 volatile*, uint32); void runtime·atomicstore64(uint64 volatile*, uint64); @@ -761,6 +787,14 @@ int64 runtime·cputicks(void); int64 runtime·tickspersecond(void); void runtime·blockevent(int64, int32); extern int64 runtime·blockprofilerate; +void runtime·addtimer(Timer*); +bool runtime·deltimer(Timer*); +G* runtime·netpoll(bool); +void runtime·netpollinit(void); +int32 runtime·netpollopen(int32, PollDesc*); +int32 runtime·netpollclose(int32); +void runtime·netpollready(G**, PollDesc*, int32); +void runtime·crash(void); #pragma varargck argpos runtime·printf 1 #pragma varargck type "d" int32 @@ -929,7 +963,6 @@ void runtime·mapassign(MapType*, Hmap*, byte*, byte*); void runtime·mapaccess(MapType*, Hmap*, byte*, byte*, bool*); void runtime·mapiternext(struct hash_iter*); bool runtime·mapiterkey(struct hash_iter*, void*); -void runtime·mapiterkeyvalue(struct hash_iter*, void*, void*); Hmap* runtime·makemap_c(MapType*, int64); Hchan* runtime·makechan_c(ChanType*, int64); diff --git a/src/pkg/runtime/signal_386.c b/src/pkg/runtime/signal_386.c new file mode 100644 index 000000000..72b4a66f8 --- /dev/null +++ b/src/pkg/runtime/signal_386.c @@ -0,0 +1,123 @@ +// 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. + +// +build darwin freebsd linux netbsd openbsd + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" +#include "signal_GOOS_GOARCH.h" +#include "signals_GOOS.h" + +void +runtime·dumpregs(Siginfo *info, void *ctxt) +{ + USED(info); + USED(ctxt); + + runtime·printf("eax %x\n", SIG_EAX(info, ctxt)); + runtime·printf("ebx %x\n", SIG_EBX(info, ctxt)); + runtime·printf("ecx %x\n", SIG_ECX(info, ctxt)); + runtime·printf("edx %x\n", SIG_EDX(info, ctxt)); + runtime·printf("edi %x\n", SIG_EDI(info, ctxt)); + runtime·printf("esi %x\n", SIG_ESI(info, ctxt)); + runtime·printf("ebp %x\n", SIG_EBP(info, ctxt)); + runtime·printf("esp %x\n", SIG_ESP(info, ctxt)); + runtime·printf("eip %x\n", SIG_EIP(info, ctxt)); + runtime·printf("eflags %x\n", SIG_EFLAGS(info, ctxt)); + runtime·printf("cs %x\n", SIG_CS(info, ctxt)); + runtime·printf("fs %x\n", SIG_FS(info, ctxt)); + runtime·printf("gs %x\n", SIG_GS(info, ctxt)); +} + +void +runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp) +{ + uintptr *sp; + SigTab *t; + bool crash; + + if(sig == SIGPROF) { + if(gp != m->g0 && gp != m->gsignal) + runtime·sigprof((byte*)SIG_EIP(info, ctxt), (byte*)SIG_ESP(info, ctxt), nil, gp); + return; + } + + t = &runtime·sigtab[sig]; + if(SIG_CODE0(info, ctxt) != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) + goto Throw; + + // Make it look like a call to the signal func. + // Have to pass arguments out of band since + // augmenting the stack frame would break + // the unwinding code. + gp->sig = sig; + gp->sigcode0 = SIG_CODE0(info, ctxt); + gp->sigcode1 = SIG_CODE1(info, ctxt); + gp->sigpc = SIG_EIP(info, ctxt); + +#ifdef GOOS_darwin + // Work around Leopard bug that doesn't set FPE_INTDIV. + // Look at instruction to see if it is a divide. + // Not necessary in Snow Leopard (si_code will be != 0). + if(sig == SIGFPE && gp->sigcode0 == 0) { + byte *pc; + pc = (byte*)gp->sigpc; + if(pc[0] == 0x66) // 16-bit instruction prefix + pc++; + if(pc[0] == 0xF6 || pc[0] == 0xF7) + gp->sigcode0 = FPE_INTDIV; + } +#endif + + // Only push runtime·sigpanic if eip != 0. + // If eip == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will + // make the trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic and we + // won't get to see who faulted.) + if(SIG_EIP(info, ctxt) != 0) { + sp = (uintptr*)SIG_ESP(info, ctxt); + *--sp = SIG_EIP(info, ctxt); + SIG_ESP(info, ctxt) = (uintptr)sp; + } + SIG_EIP(info, ctxt) = (uintptr)runtime·sigpanic; + return; + } + + if(SIG_CODE0(info, ctxt) == SI_USER || (t->flags & SigNotify)) + if(runtime·sigsend(sig)) + return; + if(t->flags & SigKill) + runtime·exit(2); + if(!(t->flags & SigThrow)) + return; + +Throw: + runtime·startpanic(); + + if(sig < 0 || sig >= NSIG) + runtime·printf("Signal %d\n", sig); + else + runtime·printf("%s\n", runtime·sigtab[sig].name); + + runtime·printf("PC=%x\n", SIG_EIP(info, ctxt)); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } + runtime·printf("\n"); + + if(runtime·gotraceback(&crash)){ + runtime·traceback((void*)SIG_EIP(info, ctxt), (void*)SIG_ESP(info, ctxt), 0, gp); + runtime·tracebackothers(gp); + runtime·dumpregs(info, ctxt); + } + + if(crash) + runtime·crash(); + + runtime·exit(2); +} diff --git a/src/pkg/runtime/signal_amd64.c b/src/pkg/runtime/signal_amd64.c new file mode 100644 index 000000000..ce17bf36d --- /dev/null +++ b/src/pkg/runtime/signal_amd64.c @@ -0,0 +1,133 @@ +// 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. + +// +build darwin freebsd linux netbsd openbsd + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" +#include "signal_GOOS_GOARCH.h" +#include "signals_GOOS.h" + +void +runtime·dumpregs(Siginfo *info, void *ctxt) +{ + USED(info); + USED(ctxt); + + runtime·printf("rax %X\n", SIG_RAX(info, ctxt)); + runtime·printf("rbx %X\n", SIG_RBX(info, ctxt)); + runtime·printf("rcx %X\n", SIG_RCX(info, ctxt)); + runtime·printf("rdx %X\n", SIG_RDX(info, ctxt)); + runtime·printf("rdi %X\n", SIG_RDI(info, ctxt)); + runtime·printf("rsi %X\n", SIG_RSI(info, ctxt)); + runtime·printf("rbp %X\n", SIG_RBP(info, ctxt)); + runtime·printf("rsp %X\n", SIG_RSP(info, ctxt)); + runtime·printf("r8 %X\n", SIG_R8(info, ctxt) ); + runtime·printf("r9 %X\n", SIG_R9(info, ctxt) ); + runtime·printf("r10 %X\n", SIG_R10(info, ctxt)); + runtime·printf("r11 %X\n", SIG_R11(info, ctxt)); + runtime·printf("r12 %X\n", SIG_R12(info, ctxt)); + runtime·printf("r13 %X\n", SIG_R13(info, ctxt)); + runtime·printf("r14 %X\n", SIG_R14(info, ctxt)); + runtime·printf("r15 %X\n", SIG_R15(info, ctxt)); + runtime·printf("rip %X\n", SIG_RIP(info, ctxt)); + runtime·printf("rflags %X\n", SIG_RFLAGS(info, ctxt)); + runtime·printf("cs %X\n", SIG_CS(info, ctxt)); + runtime·printf("fs %X\n", SIG_FS(info, ctxt)); + runtime·printf("gs %X\n", SIG_GS(info, ctxt)); +} + +void +runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp) +{ + uintptr *sp; + SigTab *t; + bool crash; + + if(sig == SIGPROF) { + if(gp != m->g0 && gp != m->gsignal) + runtime·sigprof((byte*)SIG_RIP(info, ctxt), (byte*)SIG_RSP(info, ctxt), nil, gp); + return; + } + + t = &runtime·sigtab[sig]; + if(SIG_CODE0(info, ctxt) != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) + goto Throw; + + // Make it look like a call to the signal func. + // Have to pass arguments out of band since + // augmenting the stack frame would break + // the unwinding code. + gp->sig = sig; + gp->sigcode0 = SIG_CODE0(info, ctxt); + gp->sigcode1 = SIG_CODE1(info, ctxt); + gp->sigpc = SIG_RIP(info, ctxt); + +#ifdef GOOS_darwin + // Work around Leopard bug that doesn't set FPE_INTDIV. + // Look at instruction to see if it is a divide. + // Not necessary in Snow Leopard (si_code will be != 0). + if(sig == SIGFPE && gp->sigcode0 == 0) { + byte *pc; + pc = (byte*)gp->sigpc; + if((pc[0]&0xF0) == 0x40) // 64-bit REX prefix + pc++; + else if(pc[0] == 0x66) // 16-bit instruction prefix + pc++; + if(pc[0] == 0xF6 || pc[0] == 0xF7) + gp->sigcode0 = FPE_INTDIV; + } +#endif + + // Only push runtime·sigpanic if rip != 0. + // If rip == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will + // make the trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic and we + // won't get to see who faulted.) + if(SIG_RIP(info, ctxt) != 0) { + sp = (uintptr*)SIG_RSP(info, ctxt); + *--sp = SIG_RIP(info, ctxt); + SIG_RSP(info, ctxt) = (uintptr)sp; + } + SIG_RIP(info, ctxt) = (uintptr)runtime·sigpanic; + return; + } + + if(SIG_CODE0(info, ctxt) == SI_USER || (t->flags & SigNotify)) + if(runtime·sigsend(sig)) + return; + if(t->flags & SigKill) + runtime·exit(2); + if(!(t->flags & SigThrow)) + return; + +Throw: + runtime·startpanic(); + + if(sig < 0 || sig >= NSIG) + runtime·printf("Signal %d\n", sig); + else + runtime·printf("%s\n", runtime·sigtab[sig].name); + + runtime·printf("PC=%X\n", SIG_RIP(info, ctxt)); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } + runtime·printf("\n"); + + if(runtime·gotraceback(&crash)){ + runtime·traceback((void*)SIG_RIP(info, ctxt), (void*)SIG_RSP(info, ctxt), 0, gp); + runtime·tracebackothers(gp); + runtime·dumpregs(info, ctxt); + } + + if(crash) + runtime·crash(); + + runtime·exit(2); +} diff --git a/src/pkg/runtime/signal_arm.c b/src/pkg/runtime/signal_arm.c new file mode 100644 index 000000000..adf61de6b --- /dev/null +++ b/src/pkg/runtime/signal_arm.c @@ -0,0 +1,124 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" +#include "signal_GOOS_GOARCH.h" +#include "signals_GOOS.h" + +void +runtime·dumpregs(Siginfo *info, void *ctxt) +{ + USED(info); + USED(ctxt); + + runtime·printf("trap %x\n", SIG_TRAP(info, ctxt)); + runtime·printf("error %x\n", SIG_ERROR(info, ctxt)); + runtime·printf("oldmask %x\n", SIG_OLDMASK(info, ctxt)); + runtime·printf("r0 %x\n", SIG_R0(info, ctxt)); + runtime·printf("r1 %x\n", SIG_R1(info, ctxt)); + runtime·printf("r2 %x\n", SIG_R2(info, ctxt)); + runtime·printf("r3 %x\n", SIG_R3(info, ctxt)); + runtime·printf("r4 %x\n", SIG_R4(info, ctxt)); + runtime·printf("r5 %x\n", SIG_R5(info, ctxt)); + runtime·printf("r6 %x\n", SIG_R6(info, ctxt)); + runtime·printf("r7 %x\n", SIG_R7(info, ctxt)); + runtime·printf("r8 %x\n", SIG_R8(info, ctxt)); + runtime·printf("r9 %x\n", SIG_R9(info, ctxt)); + runtime·printf("r10 %x\n", SIG_R10(info, ctxt)); + runtime·printf("fp %x\n", SIG_FP(info, ctxt)); + runtime·printf("ip %x\n", SIG_IP(info, ctxt)); + runtime·printf("sp %x\n", SIG_SP(info, ctxt)); + runtime·printf("lr %x\n", SIG_LR(info, ctxt)); + runtime·printf("pc %x\n", SIG_PC(info, ctxt)); + runtime·printf("cpsr %x\n", SIG_CPSR(info, ctxt)); + runtime·printf("fault %x\n", SIG_FAULT(info, ctxt)); +} + +void +runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp) +{ + SigTab *t; + bool crash; + + if(sig == SIGPROF) { + if(gp != m->g0 && gp != m->gsignal) + runtime·sigprof((uint8*)SIG_PC(info, ctxt), (uint8*)SIG_SP(info, ctxt), (uint8*)SIG_LR(info, ctxt), gp); + return; + } + + t = &runtime·sigtab[sig]; + if(SIG_CODE0(info, ctxt) != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) + goto Throw; + + // Make it look like a call to the signal func. + // Have to pass arguments out of band since + // augmenting the stack frame would break + // the unwinding code. + gp->sig = sig; + gp->sigcode0 = SIG_CODE0(info, ctxt); + gp->sigcode1 = SIG_FAULT(info, ctxt); + gp->sigpc = SIG_PC(info, ctxt); + + // We arrange lr, and pc to pretend the panicking + // function calls sigpanic directly. + // Always save LR to stack so that panics in leaf + // functions are correctly handled. This smashes + // the stack frame but we're not going back there + // anyway. + SIG_SP(info, ctxt) -= 4; + *(uint32*)SIG_SP(info, ctxt) = SIG_LR(info, ctxt); + // Don't bother saving PC if it's zero, which is + // probably a call to a nil func: the old link register + // is more useful in the stack trace. + if(gp->sigpc != 0) + SIG_LR(info, ctxt) = gp->sigpc; + // In case we are panicking from external C code + SIG_R10(info, ctxt) = (uintptr)gp; + SIG_R9(info, ctxt) = (uintptr)m; + SIG_PC(info, ctxt) = (uintptr)runtime·sigpanic; + return; + } + + if(SIG_CODE0(info, ctxt) == SI_USER || (t->flags & SigNotify)) + if(runtime·sigsend(sig)) + return; + if(t->flags & SigKill) + runtime·exit(2); + if(!(t->flags & SigThrow)) + return; + +Throw: + if(runtime·panicking) // traceback already printed + runtime·exit(2); + runtime·panicking = 1; + + if(sig < 0 || sig >= NSIG) + runtime·printf("Signal %d\n", sig); + else + runtime·printf("%s\n", runtime·sigtab[sig].name); + + runtime·printf("PC=%x\n", SIG_PC(info, ctxt)); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } + runtime·printf("\n"); + + if(runtime·gotraceback(&crash)){ + runtime·traceback((void*)SIG_PC(info, ctxt), (void*)SIG_SP(info, ctxt), (void*)SIG_LR(info, ctxt), gp); + runtime·tracebackothers(gp); + runtime·printf("\n"); + runtime·dumpregs(info, ctxt); + } + + if(crash) + runtime·crash(); + + runtime·exit(2); +} diff --git a/src/pkg/runtime/signal_darwin_386.c b/src/pkg/runtime/signal_darwin_386.c deleted file mode 100644 index 132ca931b..000000000 --- a/src/pkg/runtime/signal_darwin_386.c +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "os_GOOS.h" -#include "signals_GOOS.h" - -void -runtime·dumpregs(Regs32 *r) -{ - runtime·printf("eax %x\n", r->eax); - runtime·printf("ebx %x\n", r->ebx); - runtime·printf("ecx %x\n", r->ecx); - runtime·printf("edx %x\n", r->edx); - runtime·printf("edi %x\n", r->edi); - runtime·printf("esi %x\n", r->esi); - runtime·printf("ebp %x\n", r->ebp); - runtime·printf("esp %x\n", r->esp); - runtime·printf("eip %x\n", r->eip); - runtime·printf("eflags %x\n", r->eflags); - runtime·printf("cs %x\n", r->cs); - runtime·printf("fs %x\n", r->fs); - runtime·printf("gs %x\n", r->gs); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Mcontext32 *mc; - Regs32 *r; - uintptr *sp; - byte *pc; - SigTab *t; - - uc = context; - mc = uc->uc_mcontext; - r = &mc->ss; - - if(sig == SIGPROF) { - if(gp != m->g0 && gp != m->gsignal) - runtime·sigprof((uint8*)r->eip, (uint8*)r->esp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Work around Leopard bug that doesn't set FPE_INTDIV. - // Look at instruction to see if it is a divide. - // Not necessary in Snow Leopard (si_code will be != 0). - if(sig == SIGFPE && info->si_code == 0) { - pc = (byte*)r->eip; - if(pc[0] == 0x66) // 16-bit instruction prefix - pc++; - if(pc[0] == 0xF6 || pc[0] == 0xF7) - info->si_code = FPE_INTDIV; - } - - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = (uintptr)info->si_addr; - gp->sigpc = r->eip; - - // Only push runtime·sigpanic if r->eip != 0. - // If r->eip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->eip != 0) { - sp = (uintptr*)r->esp; - *--sp = r->eip; - r->esp = (uintptr)sp; - } - r->eip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG){ - runtime·printf("Signal %d\n", sig); - }else{ - runtime·printf("%s\n", runtime·sigtab[sig].name); - } - - runtime·printf("PC=%x\n", r->eip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->eip, (void*)r->esp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - StackT st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(*(void**)sa.__sigaction_u == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0U; - sa.sa_tramp = (void*)runtime·sigtramp; // runtime·sigtramp's job is to call into real handler - *(uintptr*)sa.__sigaction_u = (uintptr)fn; - runtime·sigaction(i, &sa, nil); -} diff --git a/src/pkg/runtime/signal_darwin_386.h b/src/pkg/runtime/signal_darwin_386.h new file mode 100644 index 000000000..5459e10a1 --- /dev/null +++ b/src/pkg/runtime/signal_darwin_386.h @@ -0,0 +1,23 @@ +// 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. + +#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext->ss) + +#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).eax) +#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).ebx) +#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).ecx) +#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).edx) +#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).edi) +#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).esi) +#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).ebp) +#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).esp) +#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).eip) +#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).eflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr) diff --git a/src/pkg/runtime/signal_darwin_amd64.c b/src/pkg/runtime/signal_darwin_amd64.c deleted file mode 100644 index 4b7256bf4..000000000 --- a/src/pkg/runtime/signal_darwin_amd64.c +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "os_GOOS.h" -#include "signals_GOOS.h" - -void -runtime·dumpregs(Regs64 *r) -{ - runtime·printf("rax %X\n", r->rax); - runtime·printf("rbx %X\n", r->rbx); - runtime·printf("rcx %X\n", r->rcx); - runtime·printf("rdx %X\n", r->rdx); - runtime·printf("rdi %X\n", r->rdi); - runtime·printf("rsi %X\n", r->rsi); - runtime·printf("rbp %X\n", r->rbp); - runtime·printf("rsp %X\n", r->rsp); - runtime·printf("r8 %X\n", r->r8 ); - runtime·printf("r9 %X\n", r->r9 ); - runtime·printf("r10 %X\n", r->r10); - runtime·printf("r11 %X\n", r->r11); - runtime·printf("r12 %X\n", r->r12); - runtime·printf("r13 %X\n", r->r13); - runtime·printf("r14 %X\n", r->r14); - runtime·printf("r15 %X\n", r->r15); - runtime·printf("rip %X\n", r->rip); - runtime·printf("rflags %X\n", r->rflags); - runtime·printf("cs %X\n", r->cs); - runtime·printf("fs %X\n", r->fs); - runtime·printf("gs %X\n", r->gs); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Mcontext64 *mc; - Regs64 *r; - uintptr *sp; - byte *pc; - SigTab *t; - - uc = context; - mc = uc->uc_mcontext; - r = &mc->ss; - - if(sig == SIGPROF) { - if(gp != m->g0 && gp != m->gsignal) - runtime·sigprof((uint8*)r->rip, (uint8*)r->rsp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Work around Leopard bug that doesn't set FPE_INTDIV. - // Look at instruction to see if it is a divide. - // Not necessary in Snow Leopard (si_code will be != 0). - if(sig == SIGFPE && info->si_code == 0) { - pc = (byte*)r->rip; - if((pc[0]&0xF0) == 0x40) // 64-bit REX prefix - pc++; - else if(pc[0] == 0x66) // 16-bit instruction prefix - pc++; - if(pc[0] == 0xF6 || pc[0] == 0xF7) - info->si_code = FPE_INTDIV; - } - - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = (uintptr)info->si_addr; - gp->sigpc = r->rip; - - // Only push runtime·sigpanic if r->rip != 0. - // If r->rip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->rip != 0) { - sp = (uintptr*)r->rsp; - *--sp = r->rip; - r->rsp = (uintptr)sp; - } - r->rip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG){ - runtime·printf("Signal %d\n", sig); - }else{ - runtime·printf("%s\n", runtime·sigtab[sig].name); - } - - runtime·printf("PC=%X\n", r->rip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->rip, (void*)r->rsp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - StackT st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(*(void**)sa.__sigaction_u == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; - sa.sa_tramp = runtime·sigtramp; // runtime·sigtramp's job is to call into real handler - *(uintptr*)sa.__sigaction_u = (uintptr)fn; - runtime·sigaction(i, &sa, nil); -} diff --git a/src/pkg/runtime/signal_darwin_amd64.h b/src/pkg/runtime/signal_darwin_amd64.h new file mode 100644 index 000000000..e3da6de3a --- /dev/null +++ b/src/pkg/runtime/signal_darwin_amd64.h @@ -0,0 +1,31 @@ +// 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. + +#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext->ss) + +#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).rax) +#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).rbx) +#define SIG_RCX(info, ctxt) (SIG_REGS(ctxt).rcx) +#define SIG_RDX(info, ctxt) (SIG_REGS(ctxt).rdx) +#define SIG_RDI(info, ctxt) (SIG_REGS(ctxt).rdi) +#define SIG_RSI(info, ctxt) (SIG_REGS(ctxt).rsi) +#define SIG_RBP(info, ctxt) (SIG_REGS(ctxt).rbp) +#define SIG_RSP(info, ctxt) (SIG_REGS(ctxt).rsp) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).r8) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).r9) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).r10) +#define SIG_R11(info, ctxt) (SIG_REGS(ctxt).r11) +#define SIG_R12(info, ctxt) (SIG_REGS(ctxt).r12) +#define SIG_R13(info, ctxt) (SIG_REGS(ctxt).r13) +#define SIG_R14(info, ctxt) (SIG_REGS(ctxt).r14) +#define SIG_R15(info, ctxt) (SIG_REGS(ctxt).r15) +#define SIG_RIP(info, ctxt) (SIG_REGS(ctxt).rip) +#define SIG_RFLAGS(info, ctxt) (SIG_REGS(ctxt).rflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr) diff --git a/src/pkg/runtime/signal_freebsd_386.c b/src/pkg/runtime/signal_freebsd_386.c deleted file mode 100644 index 254e5e277..000000000 --- a/src/pkg/runtime/signal_freebsd_386.c +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - int32 sa_flags; /* see signal options below */ - Sigset sa_mask; /* signal mask to apply */ -} Sigaction; - -void -runtime·dumpregs(Mcontext *r) -{ - runtime·printf("eax %x\n", r->mc_eax); - runtime·printf("ebx %x\n", r->mc_ebx); - runtime·printf("ecx %x\n", r->mc_ecx); - runtime·printf("edx %x\n", r->mc_edx); - runtime·printf("edi %x\n", r->mc_edi); - runtime·printf("esi %x\n", r->mc_esi); - runtime·printf("ebp %x\n", r->mc_ebp); - runtime·printf("esp %x\n", r->mc_esp); - runtime·printf("eip %x\n", r->mc_eip); - runtime·printf("eflags %x\n", r->mc_eflags); - runtime·printf("cs %x\n", r->mc_cs); - runtime·printf("fs %x\n", r->mc_fs); - runtime·printf("gs %x\n", r->mc_gs); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Mcontext *r; - uintptr *sp; - SigTab *t; - - uc = context; - r = &uc->uc_mcontext; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->mc_eip, (uint8*)r->mc_esp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = (uintptr)info->si_addr; - gp->sigpc = r->mc_eip; - - // Only push runtime·sigpanic if r->mc_eip != 0. - // If r->mc_eip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->mc_eip != 0) { - sp = (uintptr*)r->mc_esp; - *--sp = r->mc_eip; - r->mc_esp = (uintptr)sp; - } - r->mc_eip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", r->mc_eip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->mc_eip, (void*)r->mc_esp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = (int8*)p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask.__bits[0] = ~(uint32)0; - sa.sa_mask.__bits[1] = ~(uint32)0; - sa.sa_mask.__bits[2] = ~(uint32)0; - sa.sa_mask.__bits[3] = ~(uint32)0; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} diff --git a/src/pkg/runtime/signal_freebsd_386.h b/src/pkg/runtime/signal_freebsd_386.h new file mode 100644 index 000000000..a24f1ee96 --- /dev/null +++ b/src/pkg/runtime/signal_freebsd_386.h @@ -0,0 +1,23 @@ +// 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. + +#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext) + +#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).mc_eax) +#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).mc_ebx) +#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).mc_ecx) +#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).mc_edx) +#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).mc_edi) +#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).mc_esi) +#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).mc_ebp) +#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).mc_esp) +#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).mc_eip) +#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).mc_eflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).mc_cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).mc_fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).mc_gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr) diff --git a/src/pkg/runtime/signal_freebsd_amd64.c b/src/pkg/runtime/signal_freebsd_amd64.c deleted file mode 100644 index 7dbf36075..000000000 --- a/src/pkg/runtime/signal_freebsd_amd64.c +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - int32 sa_flags; /* see signal options below */ - Sigset sa_mask; /* signal mask to apply */ -} Sigaction; - -void -runtime·dumpregs(Mcontext *r) -{ - runtime·printf("rax %X\n", r->mc_rax); - runtime·printf("rbx %X\n", r->mc_rbx); - runtime·printf("rcx %X\n", r->mc_rcx); - runtime·printf("rdx %X\n", r->mc_rdx); - runtime·printf("rdi %X\n", r->mc_rdi); - runtime·printf("rsi %X\n", r->mc_rsi); - runtime·printf("rbp %X\n", r->mc_rbp); - runtime·printf("rsp %X\n", r->mc_rsp); - runtime·printf("r8 %X\n", r->mc_r8 ); - runtime·printf("r9 %X\n", r->mc_r9 ); - runtime·printf("r10 %X\n", r->mc_r10); - runtime·printf("r11 %X\n", r->mc_r11); - runtime·printf("r12 %X\n", r->mc_r12); - runtime·printf("r13 %X\n", r->mc_r13); - runtime·printf("r14 %X\n", r->mc_r14); - runtime·printf("r15 %X\n", r->mc_r15); - runtime·printf("rip %X\n", r->mc_rip); - runtime·printf("rflags %X\n", r->mc_flags); - runtime·printf("cs %X\n", r->mc_cs); - runtime·printf("fs %X\n", r->mc_fs); - runtime·printf("gs %X\n", r->mc_gs); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Mcontext *r; - uintptr *sp; - SigTab *t; - - uc = context; - r = &uc->uc_mcontext; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->mc_rip, (uint8*)r->mc_rsp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = (uintptr)info->si_addr; - gp->sigpc = r->mc_rip; - - // Only push runtime·sigpanic if r->mc_rip != 0. - // If r->mc_rip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->mc_rip != 0) { - sp = (uintptr*)r->mc_rsp; - *--sp = r->mc_rip; - r->mc_rsp = (uintptr)sp; - } - r->mc_rip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", r->mc_rip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->mc_rip, (void*)r->mc_rsp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = (int8*)p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask.__bits[0] = ~(uint32)0; - sa.sa_mask.__bits[1] = ~(uint32)0; - sa.sa_mask.__bits[2] = ~(uint32)0; - sa.sa_mask.__bits[3] = ~(uint32)0; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} diff --git a/src/pkg/runtime/signal_freebsd_amd64.h b/src/pkg/runtime/signal_freebsd_amd64.h new file mode 100644 index 000000000..7d35b7f85 --- /dev/null +++ b/src/pkg/runtime/signal_freebsd_amd64.h @@ -0,0 +1,31 @@ +// 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. + +#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext) + +#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).mc_rax) +#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).mc_rbx) +#define SIG_RCX(info, ctxt) (SIG_REGS(ctxt).mc_rcx) +#define SIG_RDX(info, ctxt) (SIG_REGS(ctxt).mc_rdx) +#define SIG_RDI(info, ctxt) (SIG_REGS(ctxt).mc_rdi) +#define SIG_RSI(info, ctxt) (SIG_REGS(ctxt).mc_rsi) +#define SIG_RBP(info, ctxt) (SIG_REGS(ctxt).mc_rbp) +#define SIG_RSP(info, ctxt) (SIG_REGS(ctxt).mc_rsp) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).mc_r8) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).mc_r9) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).mc_r10) +#define SIG_R11(info, ctxt) (SIG_REGS(ctxt).mc_r11) +#define SIG_R12(info, ctxt) (SIG_REGS(ctxt).mc_r12) +#define SIG_R13(info, ctxt) (SIG_REGS(ctxt).mc_r13) +#define SIG_R14(info, ctxt) (SIG_REGS(ctxt).mc_r14) +#define SIG_R15(info, ctxt) (SIG_REGS(ctxt).mc_r15) +#define SIG_RIP(info, ctxt) (SIG_REGS(ctxt).mc_rip) +#define SIG_RFLAGS(info, ctxt) (SIG_REGS(ctxt).mc_rflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).mc_cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).mc_fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).mc_gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr) diff --git a/src/pkg/runtime/signal_freebsd_arm.c b/src/pkg/runtime/signal_freebsd_arm.c deleted file mode 100644 index 50c3221bb..000000000 --- a/src/pkg/runtime/signal_freebsd_arm.c +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -#define r0 __gregs[0] -#define r1 __gregs[1] -#define r2 __gregs[2] -#define r3 __gregs[3] -#define r4 __gregs[4] -#define r5 __gregs[5] -#define r6 __gregs[6] -#define r7 __gregs[7] -#define r8 __gregs[8] -#define r9 __gregs[9] -#define r10 __gregs[10] -#define r11 __gregs[11] -#define r12 __gregs[12] -#define r13 __gregs[13] -#define r14 __gregs[14] -#define r15 __gregs[15] -#define cpsr __gregs[16] - -void -runtime·dumpregs(Mcontext *r) -{ - runtime·printf("r0 %x\n", r->r0); - runtime·printf("r1 %x\n", r->r1); - runtime·printf("r2 %x\n", r->r2); - runtime·printf("r3 %x\n", r->r3); - runtime·printf("r4 %x\n", r->r4); - runtime·printf("r5 %x\n", r->r5); - runtime·printf("r6 %x\n", r->r6); - runtime·printf("r7 %x\n", r->r7); - runtime·printf("r8 %x\n", r->r8); - runtime·printf("r9 %x\n", r->r9); - runtime·printf("r10 %x\n", r->r10); - runtime·printf("fp %x\n", r->r11); - runtime·printf("ip %x\n", r->r12); - runtime·printf("sp %x\n", r->r13); - runtime·printf("lr %x\n", r->r14); - runtime·printf("pc %x\n", r->r15); - runtime·printf("cpsr %x\n", r->cpsr); -} - -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - int32 sa_flags; /* see signal options below */ - Sigset sa_mask; /* signal mask to apply */ -} Sigaction; - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Mcontext *r; - SigTab *t; - - uc = context; - r = &uc->uc_mcontext; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = (uintptr)info->si_addr; - gp->sigpc = r->r15; - - // Only push runtime·sigpanic if r->mc_rip != 0. - // If r->mc_rip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->r15 != 0) - r->r14 = r->r15; - // In case we are panicking from external C code - r->r10 = (uintptr)gp; - r->r9 = (uintptr)m; - r->r15 = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%x\n", r->r15); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->r15, (void*)r->r13, (void*)r->r14, gp); - runtime·tracebackothers(gp); - runtime·printf("\n"); - runtime·dumpregs(r); - } - -// breakpoint(); - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = (uint8*)p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask.__bits[0] = ~(uint32)0; - sa.sa_mask.__bits[1] = ~(uint32)0; - sa.sa_mask.__bits[2] = ~(uint32)0; - sa.sa_mask.__bits[3] = ~(uint32)0; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} - -void -runtime·checkgoarm(void) -{ - // TODO(minux) -} - -#pragma textflag 7 -int64 -runtime·cputicks(void) -{ - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). - // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. - // TODO: need more entropy to better seed fastrand1. - return runtime·nanotime(); -} diff --git a/src/pkg/runtime/signal_freebsd_arm.h b/src/pkg/runtime/signal_freebsd_arm.h new file mode 100644 index 000000000..87a45aa27 --- /dev/null +++ b/src/pkg/runtime/signal_freebsd_arm.h @@ -0,0 +1,28 @@ +// 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. + +#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext) + +#define SIG_R0(info, ctxt) (SIG_REGS(ctxt).__gregs[0]) +#define SIG_R1(info, ctxt) (SIG_REGS(ctxt).__gregs[1]) +#define SIG_R2(info, ctxt) (SIG_REGS(ctxt).__gregs[2]) +#define SIG_R3(info, ctxt) (SIG_REGS(ctxt).__gregs[3]) +#define SIG_R4(info, ctxt) (SIG_REGS(ctxt).__gregs[4]) +#define SIG_R5(info, ctxt) (SIG_REGS(ctxt).__gregs[5]) +#define SIG_R6(info, ctxt) (SIG_REGS(ctxt).__gregs[6]) +#define SIG_R7(info, ctxt) (SIG_REGS(ctxt).__gregs[7]) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).__gregs[8]) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).__gregs[9]) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).__gregs[10]) +#define SIG_FP(info, ctxt) (SIG_REGS(ctxt).__gregs[11]) +#define SIG_IP(info, ctxt) (SIG_REGS(ctxt).__gregs[12]) +#define SIG_SP(info, ctxt) (SIG_REGS(ctxt).__gregs[13]) +#define SIG_LR(info, ctxt) (SIG_REGS(ctxt).__gregs[14]) +#define SIG_PC(info, ctxt) (SIG_REGS(ctxt).__gregs[15]) +#define SIG_CPSR(info, ctxt) (SIG_REGS(ctxt).__gregs[16]) +#define SIG_FAULT(info, ctxt) ((uintptr)(info)->si_addr) +#define SIG_TRAP(info, ctxt) (0) +#define SIG_ERROR(info, ctxt) (0) +#define SIG_OLDMASK(info, ctxt) (0) +#define SIG_CODE0(info, ctxt) ((uintptr)(info)->si_code) diff --git a/src/pkg/runtime/signal_linux_386.c b/src/pkg/runtime/signal_linux_386.c deleted file mode 100644 index 9b45ec3bd..000000000 --- a/src/pkg/runtime/signal_linux_386.c +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -void -runtime·dumpregs(Sigcontext *r) -{ - runtime·printf("eax %x\n", r->eax); - runtime·printf("ebx %x\n", r->ebx); - runtime·printf("ecx %x\n", r->ecx); - runtime·printf("edx %x\n", r->edx); - runtime·printf("edi %x\n", r->edi); - runtime·printf("esi %x\n", r->esi); - runtime·printf("ebp %x\n", r->ebp); - runtime·printf("esp %x\n", r->esp); - runtime·printf("eip %x\n", r->eip); - runtime·printf("eflags %x\n", r->eflags); - runtime·printf("cs %x\n", r->cs); - runtime·printf("fs %x\n", r->fs); - runtime·printf("gs %x\n", r->gs); -} - -/* - * This assembler routine takes the args from registers, puts them on the stack, - * and calls sighandler(). - */ -extern void runtime·sigtramp(void); -extern void runtime·sigreturn(void); // calls runtime·sigreturn - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Sigcontext *r; - uintptr *sp; - SigTab *t; - - uc = context; - r = &uc->uc_mcontext; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->eip, (uint8*)r->esp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = ((uintptr*)info)[3]; - gp->sigpc = r->eip; - - // Only push runtime·sigpanic if r->eip != 0. - // If r->eip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->eip != 0) { - sp = (uintptr*)r->esp; - *--sp = r->eip; - r->esp = (uintptr)sp; - } - r->eip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", r->eip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->eip, (void*)r->esp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) - runtime·throw("rt_sigaction read failure"); - if(sa.k_sa_handler == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; - sa.sa_restorer = (void*)runtime·sigreturn; - if(fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.k_sa_handler = fn; - if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) - runtime·throw("rt_sigaction failure"); -} - -#define AT_NULL 0 -#define AT_SYSINFO 32 -extern uint32 runtime·_vdso; - -#pragma textflag 7 -void -runtime·linux_setup_vdso(int32 argc, void *argv_list) -{ - byte **argv = &argv_list; - byte **envp; - uint32 *auxv; - - // skip envp to get to ELF auxiliary vector. - for(envp = &argv[argc+1]; *envp != nil; envp++) - ; - envp++; - - for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) { - if(auxv[0] == AT_SYSINFO) { - runtime·_vdso = auxv[1]; - break; - } - } -} diff --git a/src/pkg/runtime/signal_linux_386.h b/src/pkg/runtime/signal_linux_386.h new file mode 100644 index 000000000..f77f1c9d5 --- /dev/null +++ b/src/pkg/runtime/signal_linux_386.h @@ -0,0 +1,24 @@ +// 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. + +#define SIG_REGS(ctxt) (*((Sigcontext*)&((Ucontext*)(ctxt))->uc_mcontext)) + +#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).eax) +#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).ebx) +#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).ecx) +#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).edx) +#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).edi) +#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).esi) +#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).ebp) +#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).esp) +#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).eip) +#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).eflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) (((uintptr*)(info))[2]) + diff --git a/src/pkg/runtime/signal_linux_amd64.c b/src/pkg/runtime/signal_linux_amd64.c deleted file mode 100644 index c4e39a6ab..000000000 --- a/src/pkg/runtime/signal_linux_amd64.c +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -void -runtime·dumpregs(Sigcontext *r) -{ - runtime·printf("rax %X\n", r->rax); - runtime·printf("rbx %X\n", r->rbx); - runtime·printf("rcx %X\n", r->rcx); - runtime·printf("rdx %X\n", r->rdx); - runtime·printf("rdi %X\n", r->rdi); - runtime·printf("rsi %X\n", r->rsi); - runtime·printf("rbp %X\n", r->rbp); - runtime·printf("rsp %X\n", r->rsp); - runtime·printf("r8 %X\n", r->r8 ); - runtime·printf("r9 %X\n", r->r9 ); - runtime·printf("r10 %X\n", r->r10); - runtime·printf("r11 %X\n", r->r11); - runtime·printf("r12 %X\n", r->r12); - runtime·printf("r13 %X\n", r->r13); - runtime·printf("r14 %X\n", r->r14); - runtime·printf("r15 %X\n", r->r15); - runtime·printf("rip %X\n", r->rip); - runtime·printf("rflags %X\n", r->eflags); - runtime·printf("cs %X\n", (uint64)r->cs); - runtime·printf("fs %X\n", (uint64)r->fs); - runtime·printf("gs %X\n", (uint64)r->gs); -} - -/* - * This assembler routine takes the args from registers, puts them on the stack, - * and calls sighandler(). - */ -extern void runtime·sigtramp(void); -extern void runtime·sigreturn(void); // calls runtime·sigreturn - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Mcontext *mc; - Sigcontext *r; - uintptr *sp; - SigTab *t; - - uc = context; - mc = &uc->uc_mcontext; - r = (Sigcontext*)mc; // same layout, more conveient names - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->rip, (uint8*)r->rsp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = ((uintptr*)info)[2]; - gp->sigpc = r->rip; - - // Only push runtime·sigpanic if r->rip != 0. - // If r->rip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->rip != 0) { - sp = (uintptr*)r->rsp; - *--sp = r->rip; - r->rsp = (uintptr)sp; - } - r->rip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", r->rip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->rip, (void*)r->rsp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) - runtime·throw("rt_sigaction read failure"); - if(sa.sa_handler == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; - // TODO(adonovan): Linux manpage says "sa_restorer element is - // obsolete and should not be used". Avoid it here, and test. - sa.sa_restorer = (void*)runtime·sigreturn; - if(fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.sa_handler = fn; - if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) - runtime·throw("rt_sigaction failure"); -} diff --git a/src/pkg/runtime/signal_linux_amd64.h b/src/pkg/runtime/signal_linux_amd64.h new file mode 100644 index 000000000..5a9a3e5da --- /dev/null +++ b/src/pkg/runtime/signal_linux_amd64.h @@ -0,0 +1,32 @@ +// 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. + +#define SIG_REGS(ctxt) (*((Sigcontext*)&((Ucontext*)(ctxt))->uc_mcontext)) + +#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).rax) +#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).rbx) +#define SIG_RCX(info, ctxt) (SIG_REGS(ctxt).rcx) +#define SIG_RDX(info, ctxt) (SIG_REGS(ctxt).rdx) +#define SIG_RDI(info, ctxt) (SIG_REGS(ctxt).rdi) +#define SIG_RSI(info, ctxt) (SIG_REGS(ctxt).rsi) +#define SIG_RBP(info, ctxt) (SIG_REGS(ctxt).rbp) +#define SIG_RSP(info, ctxt) (SIG_REGS(ctxt).rsp) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).r8) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).r9) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).r10) +#define SIG_R11(info, ctxt) (SIG_REGS(ctxt).r11) +#define SIG_R12(info, ctxt) (SIG_REGS(ctxt).r12) +#define SIG_R13(info, ctxt) (SIG_REGS(ctxt).r13) +#define SIG_R14(info, ctxt) (SIG_REGS(ctxt).r14) +#define SIG_R15(info, ctxt) (SIG_REGS(ctxt).r15) +#define SIG_RIP(info, ctxt) (SIG_REGS(ctxt).rip) +#define SIG_RFLAGS(info, ctxt) ((uint64)SIG_REGS(ctxt).eflags) + +#define SIG_CS(info, ctxt) ((uint64)SIG_REGS(ctxt).cs) +#define SIG_FS(info, ctxt) ((uint64)SIG_REGS(ctxt).fs) +#define SIG_GS(info, ctxt) ((uint64)SIG_REGS(ctxt).gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) (((uintptr*)(info))[2]) + diff --git a/src/pkg/runtime/signal_linux_arm.c b/src/pkg/runtime/signal_linux_arm.c deleted file mode 100644 index c26caa7cd..000000000 --- a/src/pkg/runtime/signal_linux_arm.c +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -void -runtime·dumpregs(Sigcontext *r) -{ - runtime·printf("trap %x\n", r->trap_no); - runtime·printf("error %x\n", r->error_code); - runtime·printf("oldmask %x\n", r->oldmask); - runtime·printf("r0 %x\n", r->arm_r0); - runtime·printf("r1 %x\n", r->arm_r1); - runtime·printf("r2 %x\n", r->arm_r2); - runtime·printf("r3 %x\n", r->arm_r3); - runtime·printf("r4 %x\n", r->arm_r4); - runtime·printf("r5 %x\n", r->arm_r5); - runtime·printf("r6 %x\n", r->arm_r6); - runtime·printf("r7 %x\n", r->arm_r7); - runtime·printf("r8 %x\n", r->arm_r8); - runtime·printf("r9 %x\n", r->arm_r9); - runtime·printf("r10 %x\n", r->arm_r10); - runtime·printf("fp %x\n", r->arm_fp); - runtime·printf("ip %x\n", r->arm_ip); - runtime·printf("sp %x\n", r->arm_sp); - runtime·printf("lr %x\n", r->arm_lr); - runtime·printf("pc %x\n", r->arm_pc); - runtime·printf("cpsr %x\n", r->arm_cpsr); - runtime·printf("fault %x\n", r->fault_address); -} - -/* - * This assembler routine takes the args from registers, puts them on the stack, - * and calls sighandler(). - */ -extern void runtime·sigtramp(void); -extern void runtime·sigreturn(void); // calls runtime·sigreturn - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Ucontext *uc; - Sigcontext *r; - SigTab *t; - - uc = context; - r = &uc->uc_mcontext; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->arm_pc, (uint8*)r->arm_sp, (uint8*)r->arm_lr, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = r->fault_address; - gp->sigpc = r->arm_pc; - - // We arrange lr, and pc to pretend the panicking - // function calls sigpanic directly. - // Always save LR to stack so that panics in leaf - // functions are correctly handled. This smashes - // the stack frame but we're not going back there - // anyway. - r->arm_sp -= 4; - *(uint32 *)r->arm_sp = r->arm_lr; - // Don't bother saving PC if it's zero, which is - // probably a call to a nil func: the old link register - // is more useful in the stack trace. - if(r->arm_pc != 0) - r->arm_lr = r->arm_pc; - // In case we are panicking from external C code - r->arm_r10 = (uintptr)gp; - r->arm_r9 = (uintptr)m; - r->arm_pc = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - if(runtime·panicking) // traceback already printed - runtime·exit(2); - runtime·panicking = 1; - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%x\n", r->arm_pc); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->arm_pc, (void*)r->arm_sp, (void*)r->arm_lr, gp); - runtime·tracebackothers(gp); - runtime·printf("\n"); - runtime·dumpregs(r); - } - -// breakpoint(); - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) - runtime·throw("rt_sigaction read failure"); - if(sa.sa_handler == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; - sa.sa_restorer = (void*)runtime·sigreturn; - if(fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.sa_handler = fn; - if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) - runtime·throw("rt_sigaction failure"); -} - -#define AT_NULL 0 -#define AT_PLATFORM 15 // introduced in at least 2.6.11 -#define AT_HWCAP 16 // introduced in at least 2.6.11 -#define AT_RANDOM 25 // introduced in 2.6.29 -#define HWCAP_VFP (1 << 6) // introduced in at least 2.6.11 -#define HWCAP_VFPv3 (1 << 13) // introduced in 2.6.30 -static uint32 runtime·randomNumber; -uint8 runtime·armArch = 6; // we default to ARMv6 -uint32 runtime·hwcap; // set by setup_auxv -uint8 runtime·goarm; // set by 5l - -void -runtime·checkgoarm(void) -{ - if(runtime·goarm > 5 && !(runtime·hwcap & HWCAP_VFP)) { - runtime·printf("runtime: this CPU has no floating point hardware, so it cannot run\n"); - runtime·printf("this GOARM=%d binary. Recompile using GOARM=5.\n", runtime·goarm); - runtime·exit(1); - } - if(runtime·goarm > 6 && !(runtime·hwcap & HWCAP_VFPv3)) { - runtime·printf("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n"); - runtime·printf("this GOARM=%d binary. Recompile using GOARM=6.\n", runtime·goarm); - runtime·exit(1); - } -} - -#pragma textflag 7 -void -runtime·setup_auxv(int32 argc, void *argv_list) -{ - byte **argv; - byte **envp; - byte *rnd; - uint32 *auxv; - uint32 t; - - argv = &argv_list; - - // skip envp to get to ELF auxiliary vector. - for(envp = &argv[argc+1]; *envp != nil; envp++) - ; - envp++; - - for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) { - switch(auxv[0]) { - case AT_RANDOM: // kernel provided 16-byte worth of random data - if(auxv[1]) { - rnd = (byte*)auxv[1]; - runtime·randomNumber = rnd[4] | rnd[5]<<8 | rnd[6]<<16 | rnd[7]<<24; - } - break; - case AT_PLATFORM: // v5l, v6l, v7l - if(auxv[1]) { - t = *(uint8*)(auxv[1]+1); - if(t >= '5' && t <= '7') - runtime·armArch = t - '0'; - } - break; - case AT_HWCAP: // CPU capability bit flags - runtime·hwcap = auxv[1]; - break; - } - } -} - -#pragma textflag 7 -int64 -runtime·cputicks(void) -{ - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). - // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. - // runtime·randomNumber provides better seeding of fastrand1. - return runtime·nanotime() + runtime·randomNumber; -} diff --git a/src/pkg/runtime/signal_linux_arm.h b/src/pkg/runtime/signal_linux_arm.h new file mode 100644 index 000000000..a674c0d57 --- /dev/null +++ b/src/pkg/runtime/signal_linux_arm.h @@ -0,0 +1,28 @@ +// 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. + +#define SIG_REGS(ctxt) (*((Sigcontext*)&((Ucontext*)(ctxt))->uc_mcontext)) + +#define SIG_R0(info, ctxt) (SIG_REGS(ctxt).arm_r0) +#define SIG_R1(info, ctxt) (SIG_REGS(ctxt).arm_r1) +#define SIG_R2(info, ctxt) (SIG_REGS(ctxt).arm_r2) +#define SIG_R3(info, ctxt) (SIG_REGS(ctxt).arm_r3) +#define SIG_R4(info, ctxt) (SIG_REGS(ctxt).arm_r4) +#define SIG_R5(info, ctxt) (SIG_REGS(ctxt).arm_r5) +#define SIG_R6(info, ctxt) (SIG_REGS(ctxt).arm_r6) +#define SIG_R7(info, ctxt) (SIG_REGS(ctxt).arm_r7) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).arm_r8) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).arm_r9) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).arm_r10) +#define SIG_FP(info, ctxt) (SIG_REGS(ctxt).arm_fp) +#define SIG_IP(info, ctxt) (SIG_REGS(ctxt).arm_ip) +#define SIG_SP(info, ctxt) (SIG_REGS(ctxt).arm_sp) +#define SIG_LR(info, ctxt) (SIG_REGS(ctxt).arm_lr) +#define SIG_PC(info, ctxt) (SIG_REGS(ctxt).arm_pc) +#define SIG_CPSR(info, ctxt) (SIG_REGS(ctxt).arm_cpsr) +#define SIG_FAULT(info, ctxt) (SIG_REGS(ctxt).fault_address) +#define SIG_TRAP(info, ctxt) (SIG_REGS(ctxt).trap_no) +#define SIG_ERROR(info, ctxt) (SIG_REGS(ctxt).error_code) +#define SIG_OLDMASK(info, ctxt) (SIG_REGS(ctxt).oldmask) +#define SIG_CODE0(info, ctxt) ((uintptr)(info)->si_code) diff --git a/src/pkg/runtime/signal_netbsd_386.c b/src/pkg/runtime/signal_netbsd_386.c deleted file mode 100644 index 08744c425..000000000 --- a/src/pkg/runtime/signal_netbsd_386.c +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -extern void runtime·lwp_tramp(void); -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*_sa_handler)(int32); - void (*_sa_sigaction)(int32, Siginfo*, void *); - } _sa_u; /* signal handler */ - uint32 sa_mask[4]; /* signal mask to apply */ - int32 sa_flags; /* see signal options below */ -} Sigaction; - -void -runtime·dumpregs(McontextT *mc) -{ - runtime·printf("eax %x\n", mc->__gregs[REG_EAX]); - runtime·printf("ebx %x\n", mc->__gregs[REG_EBX]); - runtime·printf("ecx %x\n", mc->__gregs[REG_ECX]); - runtime·printf("edx %x\n", mc->__gregs[REG_EDX]); - runtime·printf("edi %x\n", mc->__gregs[REG_EDI]); - runtime·printf("esi %x\n", mc->__gregs[REG_ESI]); - runtime·printf("ebp %x\n", mc->__gregs[REG_EBP]); - runtime·printf("esp %x\n", mc->__gregs[REG_UESP]); - runtime·printf("eip %x\n", mc->__gregs[REG_EIP]); - runtime·printf("eflags %x\n", mc->__gregs[REG_EFL]); - runtime·printf("cs %x\n", mc->__gregs[REG_CS]); - runtime·printf("fs %x\n", mc->__gregs[REG_FS]); - runtime·printf("gs %x\n", mc->__gregs[REG_GS]); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - UcontextT *uc = context; - McontextT *mc = &uc->uc_mcontext; - uintptr *sp; - SigTab *t; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)mc->__gregs[REG_EIP], - (uint8*)mc->__gregs[REG_UESP], nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // We need to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->_code; - gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */ - gp->sigpc = mc->__gregs[REG_EIP]; - - // Only push runtime·sigpanic if __gregs[REG_EIP] != 0. - // If __gregs[REG_EIP] == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will make the - // trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic - // and we won't get to see who faulted.) - if(mc->__gregs[REG_EIP] != 0) { - sp = (uintptr*)mc->__gregs[REG_UESP]; - *--sp = mc->__gregs[REG_EIP]; - mc->__gregs[REG_UESP] = (uintptr)sp; - } - mc->__gregs[REG_EIP] = (uintptr)runtime·sigpanic; - return; - } - - if(info->_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", mc->__gregs[REG_EIP]); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)mc->__gregs[REG_EIP], - (void*)mc->__gregs[REG_UESP], 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(mc); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa._sa_u._sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask[0] = ~0U; - sa.sa_mask[1] = ~0U; - sa.sa_mask[2] = ~0U; - sa.sa_mask[3] = ~0U; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa._sa_u._sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} - -void -runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) -{ - mc->__gregs[REG_EIP] = (uint32)runtime·lwp_tramp; - mc->__gregs[REG_UESP] = (uint32)stack; - mc->__gregs[REG_EBX] = (uint32)mp; - mc->__gregs[REG_EDX] = (uint32)gp; - mc->__gregs[REG_ESI] = (uint32)fn; -} diff --git a/src/pkg/runtime/signal_netbsd_386.h b/src/pkg/runtime/signal_netbsd_386.h new file mode 100644 index 000000000..d5a8a0c4b --- /dev/null +++ b/src/pkg/runtime/signal_netbsd_386.h @@ -0,0 +1,23 @@ +// 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. + +#define SIG_REGS(ctxt) (((UcontextT*)(ctxt))->uc_mcontext) + +#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EAX]) +#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EBX]) +#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_ECX]) +#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EDX]) +#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EDI]) +#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_ESI]) +#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EBP]) +#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_UESP]) +#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EIP]) +#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EFL]) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_CS]) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_FS]) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_GS]) + +#define SIG_CODE0(info, ctxt) ((info)->_code) +#define SIG_CODE1(info, ctxt) (*(uintptr*)&(info)->_reason[0]) diff --git a/src/pkg/runtime/signal_netbsd_amd64.c b/src/pkg/runtime/signal_netbsd_amd64.c deleted file mode 100644 index 46afb682b..000000000 --- a/src/pkg/runtime/signal_netbsd_amd64.c +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -extern void runtime·lwp_tramp(void); -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*_sa_handler)(int32); - void (*_sa_sigaction)(int32, Siginfo*, void *); - } _sa_u; /* signal handler */ - uint32 sa_mask[4]; /* signal mask to apply */ - int32 sa_flags; /* see signal options below */ -} Sigaction; - -void -runtime·dumpregs(McontextT *mc) -{ - runtime·printf("rax %X\n", mc->__gregs[REG_RAX]); - runtime·printf("rbx %X\n", mc->__gregs[REG_RBX]); - runtime·printf("rcx %X\n", mc->__gregs[REG_RCX]); - runtime·printf("rdx %X\n", mc->__gregs[REG_RDX]); - runtime·printf("rdi %X\n", mc->__gregs[REG_RDI]); - runtime·printf("rsi %X\n", mc->__gregs[REG_RSI]); - runtime·printf("rbp %X\n", mc->__gregs[REG_RBP]); - runtime·printf("rsp %X\n", mc->__gregs[REG_RSP]); - runtime·printf("r8 %X\n", mc->__gregs[REG_R8]); - runtime·printf("r9 %X\n", mc->__gregs[REG_R9]); - runtime·printf("r10 %X\n", mc->__gregs[REG_R10]); - runtime·printf("r11 %X\n", mc->__gregs[REG_R11]); - runtime·printf("r12 %X\n", mc->__gregs[REG_R12]); - runtime·printf("r13 %X\n", mc->__gregs[REG_R13]); - runtime·printf("r14 %X\n", mc->__gregs[REG_R14]); - runtime·printf("r15 %X\n", mc->__gregs[REG_R15]); - runtime·printf("rip %X\n", mc->__gregs[REG_RIP]); - runtime·printf("rflags %X\n", mc->__gregs[REG_RFLAGS]); - runtime·printf("cs %X\n", mc->__gregs[REG_CS]); - runtime·printf("fs %X\n", mc->__gregs[REG_FS]); - runtime·printf("gs %X\n", mc->__gregs[REG_GS]); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - UcontextT *uc = context; - McontextT *mc = &uc->uc_mcontext; - uintptr *sp; - SigTab *t; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)mc->__gregs[REG_RIP], - (uint8*)mc->__gregs[REG_RSP], nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // We need to pass arguments out of band since augmenting the - // stack frame would break the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->_code; - gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */ - gp->sigpc = mc->__gregs[REG_RIP]; - - // Only push runtime·sigpanic if __gregs[REG_RIP] != 0. - // If __gregs[REG_RIP] == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will make the - // trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic - // and we won't get to see who faulted.) - if(mc->__gregs[REG_RIP] != 0) { - sp = (uintptr*)mc->__gregs[REG_RSP]; - *--sp = mc->__gregs[REG_RIP]; - mc->__gregs[REG_RSP] = (uintptr)sp; - } - mc->__gregs[REG_RIP] = (uintptr)runtime·sigpanic; - return; - } - - if(info->_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", mc->__gregs[REG_RIP]); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)mc->__gregs[REG_RIP], - (void*)mc->__gregs[REG_RSP], 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(mc); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa._sa_u._sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask[0] = ~0U; - sa.sa_mask[1] = ~0U; - sa.sa_mask[2] = ~0U; - sa.sa_mask[3] = ~0U; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa._sa_u._sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} - -void -runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) -{ - // Machine dependent mcontext initialisation for LWP. - mc->__gregs[REG_RIP] = (uint64)runtime·lwp_tramp; - mc->__gregs[REG_RSP] = (uint64)stack; - mc->__gregs[REG_R8] = (uint64)mp; - mc->__gregs[REG_R9] = (uint64)gp; - mc->__gregs[REG_R12] = (uint64)fn; -} diff --git a/src/pkg/runtime/signal_netbsd_amd64.h b/src/pkg/runtime/signal_netbsd_amd64.h new file mode 100644 index 000000000..7ec4cd98c --- /dev/null +++ b/src/pkg/runtime/signal_netbsd_amd64.h @@ -0,0 +1,31 @@ +// 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. + +#define SIG_REGS(ctxt) (((UcontextT*)(ctxt))->uc_mcontext) + +#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RAX]) +#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RBX]) +#define SIG_RCX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RCX]) +#define SIG_RDX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RDX]) +#define SIG_RDI(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RDI]) +#define SIG_RSI(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RSI]) +#define SIG_RBP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RBP]) +#define SIG_RSP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RSP]) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R8]) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R9]) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R10]) +#define SIG_R11(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R11]) +#define SIG_R12(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R12]) +#define SIG_R13(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R13]) +#define SIG_R14(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R14]) +#define SIG_R15(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R15]) +#define SIG_RIP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RIP]) +#define SIG_RFLAGS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_RFLAGS]) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_CS]) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_FS]) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_GS]) + +#define SIG_CODE0(info, ctxt) ((info)->_code) +#define SIG_CODE1(info, ctxt) (*(uintptr*)&(info)->_reason[0]) diff --git a/src/pkg/runtime/signal_netbsd_arm.c b/src/pkg/runtime/signal_netbsd_arm.c deleted file mode 100644 index 97f62687b..000000000 --- a/src/pkg/runtime/signal_netbsd_arm.c +++ /dev/null @@ -1,208 +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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -#define r0 __gregs[0] -#define r1 __gregs[1] -#define r2 __gregs[2] -#define r3 __gregs[3] -#define r4 __gregs[4] -#define r5 __gregs[5] -#define r6 __gregs[6] -#define r7 __gregs[7] -#define r8 __gregs[8] -#define r9 __gregs[9] -#define r10 __gregs[10] -#define r11 __gregs[11] -#define r12 __gregs[12] -#define r13 __gregs[13] -#define r14 __gregs[14] -#define r15 __gregs[15] -#define cpsr __gregs[16] - -void -runtime·dumpregs(McontextT *r) -{ - runtime·printf("r0 %x\n", r->r0); - runtime·printf("r1 %x\n", r->r1); - runtime·printf("r2 %x\n", r->r2); - runtime·printf("r3 %x\n", r->r3); - runtime·printf("r4 %x\n", r->r4); - runtime·printf("r5 %x\n", r->r5); - runtime·printf("r6 %x\n", r->r6); - runtime·printf("r7 %x\n", r->r7); - runtime·printf("r8 %x\n", r->r8); - runtime·printf("r9 %x\n", r->r9); - runtime·printf("r10 %x\n", r->r10); - runtime·printf("fp %x\n", r->r11); - runtime·printf("ip %x\n", r->r12); - runtime·printf("sp %x\n", r->r13); - runtime·printf("lr %x\n", r->r14); - runtime·printf("pc %x\n", r->r15); - runtime·printf("cpsr %x\n", r->cpsr); -} - -extern void runtime·lwp_tramp(void); -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*_sa_handler)(int32); - void (*_sa_sigaction)(int32, Siginfo*, void *); - } _sa_u; /* signal handler */ - uint32 sa_mask[4]; /* signal mask to apply */ - int32 sa_flags; /* see signal options below */ -} Sigaction; - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - UcontextT *uc; - McontextT *r; - SigTab *t; - - uc = context; - r = &uc->uc_mcontext; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // We have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->_code; - gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */ - gp->sigpc = r->r15; - - // We arrange lr, and pc to pretend the panicking - // function calls sigpanic directly. - // Always save LR to stack so that panics in leaf - // functions are correctly handled. This smashes - // the stack frame but we're not going back there - // anyway. - r->r13 -= 4; - *(uint32 *)r->r13 = r->r14; - // Don't bother saving PC if it's zero, which is - // probably a call to a nil func: the old link register - // is more useful in the stack trace. - if(r->r15 != 0) - r->r14 = r->r15; - // In case we are panicking from external C code - r->r10 = (uintptr)gp; - r->r9 = (uintptr)m; - r->r15 = (uintptr)runtime·sigpanic; - return; - } - - if(info->_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%x\n", r->r15); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->r15, (void*)r->r13, (void*)r->r14, gp); - runtime·tracebackothers(gp); - runtime·printf("\n"); - runtime·dumpregs(r); - } - -// breakpoint(); - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = (uint8*)p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa._sa_u._sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask[0] = ~0U; - sa.sa_mask[1] = ~0U; - sa.sa_mask[2] = ~0U; - sa.sa_mask[3] = ~0U; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa._sa_u._sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} - -void -runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) -{ - mc->r15 = (uint32)runtime·lwp_tramp; - mc->r13 = (uint32)stack; - mc->r0 = (uint32)mp; - mc->r1 = (uint32)gp; - mc->r2 = (uint32)fn; -} - -void -runtime·checkgoarm(void) -{ - // TODO(minux) -} - -#pragma textflag 7 -int64 -runtime·cputicks() { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). - // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. - // TODO: need more entropy to better seed fastrand1. - return runtime·nanotime(); -} diff --git a/src/pkg/runtime/signal_netbsd_arm.h b/src/pkg/runtime/signal_netbsd_arm.h new file mode 100644 index 000000000..12f5827a6 --- /dev/null +++ b/src/pkg/runtime/signal_netbsd_arm.h @@ -0,0 +1,30 @@ +// 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. + +#define SIG_REGS(ctxt) (((UcontextT*)(ctxt))->uc_mcontext) + +#define SIG_R0(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R0]) +#define SIG_R1(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R1]) +#define SIG_R2(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R2]) +#define SIG_R3(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R3]) +#define SIG_R4(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R4]) +#define SIG_R5(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R5]) +#define SIG_R6(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R6]) +#define SIG_R7(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R7]) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R8]) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R9]) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R10]) +#define SIG_FP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R11]) +#define SIG_IP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R12]) +#define SIG_SP(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R13]) +#define SIG_LR(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R14]) +#define SIG_PC(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_R15]) +#define SIG_CPSR(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_CPSR]) +#define SIG_FAULT(info, ctxt) (*(uintptr*)&(info)->_reason[0]) +#define SIG_TRAP(info, ctxt) (0) +#define SIG_ERROR(info, ctxt) (0) +#define SIG_OLDMASK(info, ctxt) (0) + +#define SIG_CODE0(info, ctxt) ((info)->_code) +#define SIG_CODE1(info, ctxt) (*(uintptr*)&(info)->_reason[0]) diff --git a/src/pkg/runtime/signal_openbsd_386.c b/src/pkg/runtime/signal_openbsd_386.c deleted file mode 100644 index 516797c8d..000000000 --- a/src/pkg/runtime/signal_openbsd_386.c +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - uint32 sa_mask; /* signal mask to apply */ - int32 sa_flags; /* see signal options below */ -} Sigaction; - -void -runtime·dumpregs(Sigcontext *r) -{ - runtime·printf("eax %x\n", r->sc_eax); - runtime·printf("ebx %x\n", r->sc_ebx); - runtime·printf("ecx %x\n", r->sc_ecx); - runtime·printf("edx %x\n", r->sc_edx); - runtime·printf("edi %x\n", r->sc_edi); - runtime·printf("esi %x\n", r->sc_esi); - runtime·printf("ebp %x\n", r->sc_ebp); - runtime·printf("esp %x\n", r->sc_esp); - runtime·printf("eip %x\n", r->sc_eip); - runtime·printf("eflags %x\n", r->sc_eflags); - runtime·printf("cs %x\n", r->sc_cs); - runtime·printf("fs %x\n", r->sc_fs); - runtime·printf("gs %x\n", r->sc_gs); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Sigcontext *r = context; - uintptr *sp; - SigTab *t; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->sc_eip, (uint8*)r->sc_esp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = *(uintptr*)((byte*)info + 12); /* si_addr */ - gp->sigpc = r->sc_eip; - - // Only push runtime·sigpanic if r->sc_eip != 0. - // If r->sc_eip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->sc_eip != 0) { - sp = (uintptr*)r->sc_esp; - *--sp = r->sc_eip; - r->sc_esp = (uintptr)sp; - } - r->sc_eip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", r->sc_eip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->sc_eip, (void*)r->sc_esp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; - if (fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} diff --git a/src/pkg/runtime/signal_openbsd_386.h b/src/pkg/runtime/signal_openbsd_386.h new file mode 100644 index 000000000..0ba66ab9f --- /dev/null +++ b/src/pkg/runtime/signal_openbsd_386.h @@ -0,0 +1,23 @@ +// 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. + +#define SIG_REGS(ctxt) (*(Sigcontext*)(ctxt)) + +#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).sc_eax) +#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).sc_ebx) +#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).sc_ecx) +#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).sc_edx) +#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).sc_edi) +#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).sc_esi) +#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).sc_ebp) +#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).sc_esp) +#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).sc_eip) +#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).sc_eflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).sc_cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).sc_fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).sc_gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr) diff --git a/src/pkg/runtime/signal_openbsd_amd64.c b/src/pkg/runtime/signal_openbsd_amd64.c deleted file mode 100644 index 0d0db770b..000000000 --- a/src/pkg/runtime/signal_openbsd_amd64.c +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2009 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. - -#include "runtime.h" -#include "defs_GOOS_GOARCH.h" -#include "signals_GOOS.h" -#include "os_GOOS.h" - -extern void runtime·sigtramp(void); - -typedef struct sigaction { - union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - uint32 sa_mask; /* signal mask to apply */ - int32 sa_flags; /* see signal options below */ -} Sigaction; - -void -runtime·dumpregs(Sigcontext *r) -{ - runtime·printf("rax %X\n", r->sc_rax); - runtime·printf("rbx %X\n", r->sc_rbx); - runtime·printf("rcx %X\n", r->sc_rcx); - runtime·printf("rdx %X\n", r->sc_rdx); - runtime·printf("rdi %X\n", r->sc_rdi); - runtime·printf("rsi %X\n", r->sc_rsi); - runtime·printf("rbp %X\n", r->sc_rbp); - runtime·printf("rsp %X\n", r->sc_rsp); - runtime·printf("r8 %X\n", r->sc_r8); - runtime·printf("r9 %X\n", r->sc_r9); - runtime·printf("r10 %X\n", r->sc_r10); - runtime·printf("r11 %X\n", r->sc_r11); - runtime·printf("r12 %X\n", r->sc_r12); - runtime·printf("r13 %X\n", r->sc_r13); - runtime·printf("r14 %X\n", r->sc_r14); - runtime·printf("r15 %X\n", r->sc_r15); - runtime·printf("rip %X\n", r->sc_rip); - runtime·printf("rflags %X\n", r->sc_rflags); - runtime·printf("cs %X\n", r->sc_cs); - runtime·printf("fs %X\n", r->sc_fs); - runtime·printf("gs %X\n", r->sc_gs); -} - -void -runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) -{ - Sigcontext *r = context; - uintptr *sp; - SigTab *t; - - if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->sc_rip, - (uint8*)r->sc_rsp, nil, gp); - return; - } - - t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil || gp == m->g0) - goto Throw; - // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. - gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = *(uintptr*)((byte*)info + 16); /* si_addr */ - gp->sigpc = r->sc_rip; - - // Only push runtime·sigpanic if r->sc_rip != 0. - // If r->sc_rip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->sc_rip != 0) { - sp = (uintptr*)r->sc_rsp; - *--sp = r->sc_rip; - r->sc_rsp = (uintptr)sp; - } - r->sc_rip = (uintptr)runtime·sigpanic; - return; - } - - if(info->si_code == SI_USER || (t->flags & SigNotify)) - if(runtime·sigsend(sig)) - return; - if(t->flags & SigKill) - runtime·exit(2); - if(!(t->flags & SigThrow)) - return; - -Throw: - runtime·startpanic(); - - if(sig < 0 || sig >= NSIG) - runtime·printf("Signal %d\n", sig); - else - runtime·printf("%s\n", runtime·sigtab[sig].name); - - runtime·printf("PC=%X\n", r->sc_rip); - if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { - runtime·printf("signal arrived during cgo execution\n"); - gp = m->lockedg; - } - runtime·printf("\n"); - - if(runtime·gotraceback()){ - runtime·traceback((void*)r->sc_rip, (void*)r->sc_rsp, 0, gp); - runtime·tracebackothers(gp); - runtime·dumpregs(r); - } - - runtime·exit(2); -} - -void -runtime·signalstack(byte *p, int32 n) -{ - Sigaltstack st; - - st.ss_sp = p; - st.ss_size = n; - st.ss_flags = 0; - if(p == nil) - st.ss_flags = SS_DISABLE; - runtime·sigaltstack(&st, nil); -} - -void -runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) -{ - Sigaction sa; - - // If SIGHUP handler is SIG_IGN, assume running - // under nohup and do not set explicit handler. - if(i == SIGHUP) { - runtime·memclr((byte*)&sa, sizeof sa); - runtime·sigaction(i, nil, &sa); - if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) - return; - } - - runtime·memclr((byte*)&sa, sizeof sa); - sa.sa_flags = SA_SIGINFO|SA_ONSTACK; - if(restart) - sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0U; - if(fn == runtime·sighandler) - fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; - runtime·sigaction(i, &sa, nil); -} diff --git a/src/pkg/runtime/signal_openbsd_amd64.h b/src/pkg/runtime/signal_openbsd_amd64.h new file mode 100644 index 000000000..b46a5dfa6 --- /dev/null +++ b/src/pkg/runtime/signal_openbsd_amd64.h @@ -0,0 +1,31 @@ +// 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. + +#define SIG_REGS(ctxt) (*(Sigcontext*)(ctxt)) + +#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).sc_rax) +#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).sc_rbx) +#define SIG_RCX(info, ctxt) (SIG_REGS(ctxt).sc_rcx) +#define SIG_RDX(info, ctxt) (SIG_REGS(ctxt).sc_rdx) +#define SIG_RDI(info, ctxt) (SIG_REGS(ctxt).sc_rdi) +#define SIG_RSI(info, ctxt) (SIG_REGS(ctxt).sc_rsi) +#define SIG_RBP(info, ctxt) (SIG_REGS(ctxt).sc_rbp) +#define SIG_RSP(info, ctxt) (SIG_REGS(ctxt).sc_rsp) +#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).sc_r8) +#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).sc_r9) +#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).sc_r10) +#define SIG_R11(info, ctxt) (SIG_REGS(ctxt).sc_r11) +#define SIG_R12(info, ctxt) (SIG_REGS(ctxt).sc_r12) +#define SIG_R13(info, ctxt) (SIG_REGS(ctxt).sc_r13) +#define SIG_R14(info, ctxt) (SIG_REGS(ctxt).sc_r14) +#define SIG_R15(info, ctxt) (SIG_REGS(ctxt).sc_r15) +#define SIG_RIP(info, ctxt) (SIG_REGS(ctxt).sc_rip) +#define SIG_RFLAGS(info, ctxt) (SIG_REGS(ctxt).sc_rflags) + +#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).sc_cs) +#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).sc_fs) +#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).sc_gs) + +#define SIG_CODE0(info, ctxt) ((info)->si_code) +#define SIG_CODE1(info, ctxt) (*(uintptr*)((byte*)(info) + 16)) diff --git a/src/pkg/runtime/signal_unix.c b/src/pkg/runtime/signal_unix.c index 9b7e8b03a..54e461f99 100644 --- a/src/pkg/runtime/signal_unix.c +++ b/src/pkg/runtime/signal_unix.c @@ -7,6 +7,7 @@ #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" +#include "signal_unix.h" extern SigTab runtime·sigtab[]; @@ -21,6 +22,20 @@ runtime·initsig(void) t = &runtime·sigtab[i]; if((t->flags == 0) || (t->flags & SigDefault)) continue; + + // For some signals, we respect an inherited SIG_IGN handler + // rather than insist on installing our own default handler. + // Even these signals can be fetched using the os/signal package. + switch(i) { + case SIGHUP: + case SIGINT: + if(runtime·getsig(i) == SIG_IGN) { + t->flags = SigNotify | SigIgnored; + continue; + } + } + + t->flags |= SigHandling; runtime·setsig(i, runtime·sighandler, true); } } @@ -28,18 +43,35 @@ runtime·initsig(void) void runtime·sigenable(uint32 sig) { - int32 i; SigTab *t; - for(i = 0; i<NSIG; i++) { - // ~0 means all signals. - if(~sig == 0 || i == sig) { - t = &runtime·sigtab[i]; - if(t->flags & SigDefault) { - runtime·setsig(i, runtime·sighandler, true); - t->flags &= ~SigDefault; // make this idempotent - } - } + if(sig >= NSIG) + return; + + t = &runtime·sigtab[sig]; + if((t->flags & SigNotify) && !(t->flags & SigHandling)) { + t->flags |= SigHandling; + if(runtime·getsig(sig) == SIG_IGN) + t->flags |= SigIgnored; + runtime·setsig(sig, runtime·sighandler, true); + } +} + +void +runtime·sigdisable(uint32 sig) +{ + SigTab *t; + + if(sig >= NSIG) + return; + + t = &runtime·sigtab[sig]; + if((t->flags & SigNotify) && (t->flags & SigHandling)) { + t->flags &= ~SigHandling; + if(t->flags & SigIgnored) + runtime·setsig(sig, SIG_IGN, true); + else + runtime·setsig(sig, SIG_DFL, true); } } @@ -66,5 +98,23 @@ void os·sigpipe(void) { runtime·setsig(SIGPIPE, SIG_DFL, false); - runtime·raisesigpipe(); + runtime·raise(SIGPIPE); +} + +void +runtime·crash(void) +{ +#ifdef GOOS_darwin + // OS X core dumps are linear dumps of the mapped memory, + // from the first virtual byte to the last, with zeros in the gaps. + // Because of the way we arrange the address space on 64-bit systems, + // this means the OS X core file will be >128 GB and even on a zippy + // workstation can take OS X well over an hour to write (uninterruptible). + // Save users from making that mistake. + if(sizeof(void*) == 8) + return; +#endif + + runtime·setsig(SIGABRT, SIG_DFL, false); + runtime·raise(SIGABRT); } diff --git a/src/pkg/runtime/signal_unix.h b/src/pkg/runtime/signal_unix.h new file mode 100644 index 000000000..2d84a0186 --- /dev/null +++ b/src/pkg/runtime/signal_unix.h @@ -0,0 +1,14 @@ +// 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. + +#define SIG_DFL ((void*)0) +#define SIG_IGN ((void*)1) + +typedef void GoSighandler(int32, Siginfo*, void*, G*); +void runtime·setsig(int32, GoSighandler*, bool); +GoSighandler* runtime·getsig(int32); + +void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); +void runtime·raise(int32); + diff --git a/src/pkg/runtime/signals_plan9.h b/src/pkg/runtime/signals_plan9.h index 0f1165e2a..f9bec65fc 100644 --- a/src/pkg/runtime/signals_plan9.h +++ b/src/pkg/runtime/signals_plan9.h @@ -1,3 +1,7 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #define N SigNotify #define T SigThrow #define P SigPanic diff --git a/src/pkg/runtime/sigqueue.goc b/src/pkg/runtime/sigqueue.goc index ab5f312e4..7e083685d 100644 --- a/src/pkg/runtime/sigqueue.goc +++ b/src/pkg/runtime/sigqueue.goc @@ -133,8 +133,6 @@ done:; // Must only be called from a single goroutine at a time. func signal_enable(s uint32) { - int32 i; - if(!sig.inuse) { // The first call to signal_enable is for us // to use for initialization. It does not pass @@ -144,16 +142,16 @@ func signal_enable(s uint32) { return; } - if(~s == 0) { - // Special case: want everything. - for(i=0; i<nelem(sig.wanted); i++) - sig.wanted[i] = ~(uint32)0; - runtime·sigenable(s); - return; - } - if(s >= nelem(sig.wanted)*32) return; sig.wanted[s/32] |= 1U<<(s&31); runtime·sigenable(s); } + +// Must only be called from a single goroutine at a time. +func signal_disable(s uint32) { + if(s >= nelem(sig.wanted)*32) + return; + sig.wanted[s/32] &= ~(1U<<(s&31)); + runtime·sigdisable(s); +} diff --git a/src/pkg/runtime/stack_test.go b/src/pkg/runtime/stack_test.go index 759f7c46e..da0181a66 100644 --- a/src/pkg/runtime/stack_test.go +++ b/src/pkg/runtime/stack_test.go @@ -1533,7 +1533,7 @@ func stack5000() (uintptr, uintptr) { var buf [5000]byte; use(buf[:]); return St func TestStackMem(t *testing.T) { const ( BatchSize = 32 - BatchCount = 512 + BatchCount = 256 ArraySize = 1024 RecursionDepth = 128 ) @@ -1562,6 +1562,11 @@ func TestStackMem(t *testing.T) { for i := 0; i < BatchSize; i++ { <-c } + + // The goroutines have signaled via c that they are ready to exit. + // Give them a chance to exit by sleeping. If we don't wait, we + // might not reuse them on the next batch. + time.Sleep(10 * time.Millisecond) } s1 := new(MemStats) ReadMemStats(s1) @@ -1571,7 +1576,9 @@ func TestStackMem(t *testing.T) { if consumed > estimate { t.Fatalf("Stack mem: want %v, got %v", estimate, consumed) } - if s1.StackInuse > 4<<20 { - t.Fatalf("Stack inuse: want %v, got %v", 4<<20, s1.StackInuse) + inuse := s1.StackInuse - s0.StackInuse + t.Logf("Inuse %vMB for stack mem", inuse>>20) + if inuse > 4<<20 { + t.Fatalf("Stack inuse: want %v, got %v", 4<<20, inuse) } } diff --git a/src/pkg/runtime/string_test.go b/src/pkg/runtime/string_test.go index 8f13f0f42..6ba3c1d29 100644 --- a/src/pkg/runtime/string_test.go +++ b/src/pkg/runtime/string_test.go @@ -1,3 +1,7 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package runtime_test import ( diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c index d7221c476..578406247 100644 --- a/src/pkg/runtime/symtab.c +++ b/src/pkg/runtime/symtab.c @@ -670,6 +670,6 @@ runtime·showframe(Func *f, bool current) if(current && m->throwing > 0) return 1; if(traceback < 0) - traceback = runtime·gotraceback(); + traceback = runtime·gotraceback(nil); return traceback > 1 || f != nil && contains(f->name, ".") && !hasprefix(f->name, "runtime."); } diff --git a/src/pkg/runtime/sys_darwin_386.s b/src/pkg/runtime/sys_darwin_386.s index 8a938f9f4..59bb9d80d 100644 --- a/src/pkg/runtime/sys_darwin_386.s +++ b/src/pkg/runtime/sys_darwin_386.s @@ -24,18 +24,34 @@ TEXT runtime·exit1(SB),7,$0 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$0 + MOVL $5, AX + INT $0x80 + RET + +TEXT runtime·close(SB),7,$0 + MOVL $6, AX + INT $0x80 + RET + +TEXT runtime·read(SB),7,$0 + MOVL $3, AX + INT $0x80 + RET + TEXT runtime·write(SB),7,$0 MOVL $4, AX INT $0x80 RET -TEXT runtime·raisesigpipe(SB),7,$8 - get_tls(CX) - MOVL m(CX), DX - MOVL m_procid(DX), DX - MOVL DX, 0(SP) // thread_port - MOVL $13, 4(SP) // signal: SIGPIPE - MOVL $328, AX // __pthread_kill +TEXT runtime·raise(SB),7,$16 + MOVL $20, AX // getpid + INT $0x80 + MOVL AX, 4(SP) // pid + MOVL sig+0(FP), AX + MOVL AX, 8(SP) // signal + MOVL $1, 12(SP) // posix + MOVL $37, AX // kill INT $0x80 RET @@ -473,3 +489,32 @@ TEXT runtime·sysctl(SB),7,$0 RET MOVL $0, AX RET + +// int32 runtime·kqueue(void); +TEXT runtime·kqueue(SB),7,$0 + MOVL $362, AX + INT $0x80 + JAE 2(PC) + NEGL AX + RET + +// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout); +TEXT runtime·kevent(SB),7,$0 + MOVL $363, AX + INT $0x80 + JAE 2(PC) + NEGL AX + RET + +// int32 runtime·closeonexec(int32 fd); +TEXT runtime·closeonexec(SB),7,$32 + MOVL $92, AX // fcntl + // 0(SP) is where the caller PC would be; kernel skips it + MOVL fd+0(FP), BX + MOVL BX, 4(SP) // fd + MOVL $2, 8(SP) // F_SETFD + MOVL $1, 12(SP) // FD_CLOEXEC + INT $0x80 + JAE 2(PC) + NEGL AX + RET diff --git a/src/pkg/runtime/sys_darwin_amd64.s b/src/pkg/runtime/sys_darwin_amd64.s index 4e43a76c3..b324a0424 100644 --- a/src/pkg/runtime/sys_darwin_amd64.s +++ b/src/pkg/runtime/sys_darwin_amd64.s @@ -30,6 +30,28 @@ TEXT runtime·exit1(SB),7,$0 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$0 + MOVQ 8(SP), DI // arg 1 pathname + MOVL 16(SP), SI // arg 2 flags + MOVL 20(SP), DX // arg 3 mode + MOVL $(0x2000000+5), AX // syscall entry + SYSCALL + RET + +TEXT runtime·close(SB),7,$0 + MOVL 8(SP), DI // arg 1 fd + MOVL $(0x2000000+6), AX // syscall entry + SYSCALL + RET + +TEXT runtime·read(SB),7,$0 + MOVL 8(SP), DI // arg 1 fd + MOVQ 16(SP), SI // arg 2 buf + MOVL 24(SP), DX // arg 3 count + MOVL $(0x2000000+3), AX // syscall entry + SYSCALL + RET + TEXT runtime·write(SB),7,$0 MOVL 8(SP), DI // arg 1 fd MOVQ 16(SP), SI // arg 2 buf @@ -38,12 +60,13 @@ TEXT runtime·write(SB),7,$0 SYSCALL RET -TEXT runtime·raisesigpipe(SB),7,$24 - get_tls(CX) - MOVQ m(CX), DX - MOVL $13, DI // arg 1 SIGPIPE - MOVQ m_procid(DX), SI // arg 2 thread_port - MOVL $(0x2000000+328), AX // syscall entry __pthread_kill +TEXT runtime·raise(SB),7,$24 + MOVL $(0x2000000+20), AX // getpid + SYSCALL + MOVQ AX, DI // arg 1 - pid + MOVL sig+0(FP), SI // arg 2 - signal + MOVL $1, DX // arg 3 - posix + MOVL $(0x2000000+37), AX // kill SYSCALL RET @@ -267,7 +290,7 @@ TEXT runtime·bsdthread_create(SB),7,$0 MOVQ $(0x2000000+360), AX // bsdthread_create SYSCALL JCC 3(PC) - NEGL AX + NEGQ AX RET MOVL $0, AX RET @@ -320,7 +343,7 @@ TEXT runtime·bsdthread_register(SB),7,$0 MOVQ $(0x2000000+366), AX // bsdthread_register SYSCALL JCC 3(PC) - NEGL AX + NEGQ AX RET MOVL $0, AX RET @@ -413,7 +436,41 @@ TEXT runtime·sysctl(SB),7,$0 MOVL $(0x2000000+202), AX // syscall entry SYSCALL JCC 3(PC) - NEGL AX + NEGQ AX RET MOVL $0, AX RET + +// int32 runtime·kqueue(void); +TEXT runtime·kqueue(SB),7,$0 + MOVQ $0, DI + MOVQ $0, SI + MOVQ $0, DX + MOVL $(0x2000000+362), AX + SYSCALL + JCC 2(PC) + NEGQ AX + RET + +// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout); +TEXT runtime·kevent(SB),7,$0 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVQ 32(SP), R10 + MOVL 40(SP), R8 + MOVQ 48(SP), R9 + MOVL $(0x2000000+363), AX + SYSCALL + JCC 2(PC) + NEGQ AX + RET + +// void runtime·closeonexec(int32 fd); +TEXT runtime·closeonexec(SB),7,$0 + MOVL 8(SP), DI // fd + MOVQ $2, SI // F_SETFD + MOVQ $1, DX // FD_CLOEXEC + MOVL $(0x2000000+92), AX // fcntl + SYSCALL + RET diff --git a/src/pkg/runtime/sys_freebsd_386.s b/src/pkg/runtime/sys_freebsd_386.s index d5370267a..d960663cb 100644 --- a/src/pkg/runtime/sys_freebsd_386.s +++ b/src/pkg/runtime/sys_freebsd_386.s @@ -56,6 +56,21 @@ TEXT runtime·exit1(SB),7,$-4 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$-4 + MOVL $5, AX + INT $0x80 + RET + +TEXT runtime·close(SB),7,$-4 + MOVL $6, AX + INT $0x80 + RET + +TEXT runtime·read(SB),7,$-4 + MOVL $3, AX + INT $0x80 + RET + TEXT runtime·write(SB),7,$-4 MOVL $4, AX INT $0x80 @@ -66,16 +81,17 @@ TEXT runtime·getrlimit(SB),7,$-4 INT $0x80 RET -TEXT runtime·raisesigpipe(SB),7,$12 +TEXT runtime·raise(SB),7,$16 // thr_self(&8(SP)) LEAL 8(SP), AX - MOVL AX, 0(SP) + MOVL AX, 4(SP) MOVL $432, AX INT $0x80 // thr_kill(self, SIGPIPE) MOVL 8(SP), AX - MOVL AX, 0(SP) - MOVL $13, 4(SP) + MOVL AX, 4(SP) + MOVL sig+0(FP), AX + MOVL AX, 8(SP) MOVL $433, AX INT $0x80 RET diff --git a/src/pkg/runtime/sys_freebsd_amd64.s b/src/pkg/runtime/sys_freebsd_amd64.s index 40c6237e2..cfa33d4fb 100644 --- a/src/pkg/runtime/sys_freebsd_amd64.s +++ b/src/pkg/runtime/sys_freebsd_amd64.s @@ -58,6 +58,28 @@ TEXT runtime·exit1(SB),7,$-8 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 pathname + MOVL 16(SP), SI // arg 2 flags + MOVL 20(SP), DX // arg 3 mode + MOVL $5, AX + SYSCALL + RET + +TEXT runtime·close(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fd + MOVL $6, AX + SYSCALL + RET + +TEXT runtime·read(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fd + MOVQ 16(SP), SI // arg 2 buf + MOVL 24(SP), DX // arg 3 count + MOVL $3, AX + SYSCALL + RET + TEXT runtime·write(SB),7,$-8 MOVL 8(SP), DI // arg 1 fd MOVQ 16(SP), SI // arg 2 buf @@ -73,14 +95,14 @@ TEXT runtime·getrlimit(SB),7,$-8 SYSCALL RET -TEXT runtime·raisesigpipe(SB),7,$16 +TEXT runtime·raise(SB),7,$16 // thr_self(&8(SP)) LEAQ 8(SP), DI // arg 1 &8(SP) MOVL $432, AX SYSCALL // thr_kill(self, SIGPIPE) MOVQ 8(SP), DI // arg 1 id - MOVQ $13, SI // arg 2 SIGPIPE + MOVL sig+0(FP), SI // arg 2 MOVL $433, AX SYSCALL RET @@ -239,7 +261,7 @@ TEXT runtime·sysctl(SB),7,$0 MOVQ $202, AX // sys___sysctl SYSCALL JCC 3(PC) - NEGL AX + NEGQ AX RET MOVL $0, AX RET diff --git a/src/pkg/runtime/sys_freebsd_arm.s b/src/pkg/runtime/sys_freebsd_arm.s index 77050e8d0..5531936ff 100644 --- a/src/pkg/runtime/sys_freebsd_arm.s +++ b/src/pkg/runtime/sys_freebsd_arm.s @@ -54,6 +54,20 @@ TEXT runtime·exit1(SB),7,$-8 MOVW.CS R9, (R9) RET +TEXT runtime·open(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 name + MOVW 4(FP), R1 // arg 2 mode + MOVW 8(FP), R2 // arg 3 perm + SWI $5 + RET + +TEXT runtime·read(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 fd + MOVW 4(FP), R1 // arg 2 buf + MOVW 8(FP), R2 // arg 3 count + SWI $3 + RET + TEXT runtime·write(SB),7,$-8 MOVW 0(FP), R0 // arg 1 fd MOVW 4(FP), R1 // arg 2 buf @@ -61,6 +75,11 @@ TEXT runtime·write(SB),7,$-8 SWI $4 RET +TEXT runtime·close(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 fd + SWI $6 + RET + TEXT runtime·getrlimit(SB),7,$-8 MOVW 0(FP), R0 MOVW 4(FP), R1 @@ -68,13 +87,13 @@ TEXT runtime·getrlimit(SB),7,$-8 SWI $194 RET -TEXT runtime·raisesigpipe(SB),7,$8 +TEXT runtime·raise(SB),7,$8 // thr_self(&4(R13)) MOVW $4(R13), R0 // arg 1 &4(R13) SWI $432 // thr_kill(self, SIGPIPE) MOVW 4(R13), R0 // arg 1 id - MOVW $13, R1 // arg 2 SIGPIPE + MOVW sig+0(FP), R1 // arg 2 - signal SWI $433 RET diff --git a/src/pkg/runtime/sys_linux_386.s b/src/pkg/runtime/sys_linux_386.s index f27fd4713..76ebe3dcf 100644 --- a/src/pkg/runtime/sys_linux_386.s +++ b/src/pkg/runtime/sys_linux_386.s @@ -77,11 +77,11 @@ TEXT runtime·usleep(SB),7,$8 CALL *runtime·_vdso(SB) RET -TEXT runtime·raisesigpipe(SB),7,$12 +TEXT runtime·raise(SB),7,$12 MOVL $224, AX // syscall - gettid CALL *runtime·_vdso(SB) - MOVL AX, 0(SP) // arg 1 tid - MOVL $13, 4(SP) // arg 2 SIGPIPE + MOVL AX, BX // arg 1 tid + MOVL sig+0(FP), CX // arg 2 signal MOVL $238, AX // syscall - tkill CALL *runtime·_vdso(SB) RET @@ -430,3 +430,46 @@ TEXT runtime·sched_getaffinity(SB),7,$0 MOVL 12(SP), DX CALL *runtime·_vdso(SB) RET + +// int32 runtime·epollcreate(int32 size); +TEXT runtime·epollcreate(SB),7,$0 + MOVL $254, AX + MOVL 4(SP), BX + CALL *runtime·_vdso(SB) + RET + +// int32 runtime·epollcreate1(int32 flags); +TEXT runtime·epollcreate1(SB),7,$0 + MOVL $329, AX + MOVL 4(SP), BX + CALL *runtime·_vdso(SB) + RET + +// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev); +TEXT runtime·epollctl(SB),7,$0 + MOVL $255, AX + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + CALL *runtime·_vdso(SB) + RET + +// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout); +TEXT runtime·epollwait(SB),7,$0 + MOVL $256, AX + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + CALL *runtime·_vdso(SB) + RET + +// void runtime·closeonexec(int32 fd); +TEXT runtime·closeonexec(SB),7,$0 + MOVL $55, AX // fcntl + MOVL 4(SP), BX // fd + MOVL $2, CX // F_SETFD + MOVL $1, DX // FD_CLOEXEC + CALL *runtime·_vdso(SB) + RET diff --git a/src/pkg/runtime/sys_linux_amd64.s b/src/pkg/runtime/sys_linux_amd64.s index e45943758..2d802abb6 100644 --- a/src/pkg/runtime/sys_linux_amd64.s +++ b/src/pkg/runtime/sys_linux_amd64.s @@ -75,11 +75,11 @@ TEXT runtime·usleep(SB),7,$16 SYSCALL RET -TEXT runtime·raisesigpipe(SB),7,$12 +TEXT runtime·raise(SB),7,$12 MOVL $186, AX // syscall - gettid SYSCALL MOVL AX, DI // arg 1 tid - MOVL $13, SI // arg 2 SIGPIPE + MOVL sig+0(FP), SI // arg 2 MOVL $200, AX // syscall - tkill SYSCALL RET @@ -347,3 +347,46 @@ TEXT runtime·sched_getaffinity(SB),7,$0 MOVL $204, AX // syscall entry SYSCALL RET + +// int32 runtime·epollcreate(int32 size); +TEXT runtime·epollcreate(SB),7,$0 + MOVL 8(SP), DI + MOVL $213, AX // syscall entry + SYSCALL + RET + +// int32 runtime·epollcreate1(int32 flags); +TEXT runtime·epollcreate1(SB),7,$0 + MOVL 8(SP), DI + MOVL $291, AX // syscall entry + SYSCALL + RET + +// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev); +TEXT runtime·epollctl(SB),7,$0 + MOVL 8(SP), DI + MOVL 12(SP), SI + MOVL 16(SP), DX + MOVQ 24(SP), R10 + MOVL $233, AX // syscall entry + SYSCALL + RET + +// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout); +TEXT runtime·epollwait(SB),7,$0 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL 28(SP), R10 + MOVL $232, AX // syscall entry + SYSCALL + RET + +// void runtime·closeonexec(int32 fd); +TEXT runtime·closeonexec(SB),7,$0 + MOVL 8(SP), DI // fd + MOVQ $2, SI // F_SETFD + MOVQ $1, DX // FD_CLOEXEC + MOVL $72, AX // fcntl + SYSCALL + RET diff --git a/src/pkg/runtime/sys_linux_arm.s b/src/pkg/runtime/sys_linux_arm.s index 8bae2933f..7f813482d 100644 --- a/src/pkg/runtime/sys_linux_arm.s +++ b/src/pkg/runtime/sys_linux_arm.s @@ -36,6 +36,11 @@ #define SYS_ugetrlimit (SYS_BASE + 191) #define SYS_sched_getaffinity (SYS_BASE + 242) #define SYS_clock_gettime (SYS_BASE + 263) +#define SYS_epoll_create (SYS_BASE + 250) +#define SYS_epoll_ctl (SYS_BASE + 251) +#define SYS_epoll_wait (SYS_BASE + 252) +#define SYS_epoll_create1 (SYS_BASE + 357) +#define SYS_fcntl (SYS_BASE + 55) #define ARM_BASE (SYS_BASE + 0x0f0000) @@ -92,11 +97,11 @@ TEXT runtime·exit1(SB),7,$-4 MOVW $1003, R1 MOVW R0, (R1) // fail hard -TEXT runtime·raisesigpipe(SB),7,$-4 +TEXT runtime·raise(SB),7,$-4 MOVW $SYS_gettid, R7 SWI $0 // arg 1 tid already in R0 from gettid - MOVW $13, R1 // arg 2 SIGPIPE + MOVW sig+0(FP), R1 // arg 2 - signal MOVW $SYS_tkill, R7 SWI $0 RET @@ -371,7 +376,6 @@ cascheck: MOVW $0, R0 RET - TEXT runtime·casp(SB),7,$0 B runtime·cas(SB) @@ -387,3 +391,46 @@ TEXT runtime·sched_getaffinity(SB),7,$0 MOVW $SYS_sched_getaffinity, R7 SWI $0 RET + +// int32 runtime·epollcreate(int32 size) +TEXT runtime·epollcreate(SB),7,$0 + MOVW 0(FP), R0 + MOVW $SYS_epoll_create, R7 + SWI $0 + RET + +// int32 runtime·epollcreate1(int32 flags) +TEXT runtime·epollcreate1(SB),7,$0 + MOVW 0(FP), R0 + MOVW $SYS_epoll_create1, R7 + SWI $0 + RET + +// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev) +TEXT runtime·epollctl(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW 12(FP), R3 + MOVW $SYS_epoll_ctl, R7 + SWI $0 + RET + +// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout) +TEXT runtime·epollwait(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW 12(FP), R3 + MOVW $SYS_epoll_wait, R7 + SWI $0 + RET + +// void runtime·closeonexec(int32 fd) +TEXT runtime·closeonexec(SB),7,$0 + MOVW 0(FP), R0 // fd + MOVW $2, R1 // F_SETFD + MOVW $1, R2 // FD_CLOEXEC + MOVW $SYS_fcntl, R7 + SWI $0 + RET diff --git a/src/pkg/runtime/sys_netbsd_386.s b/src/pkg/runtime/sys_netbsd_386.s index 3d3d31273..992eba77d 100644 --- a/src/pkg/runtime/sys_netbsd_386.s +++ b/src/pkg/runtime/sys_netbsd_386.s @@ -22,6 +22,21 @@ TEXT runtime·exit1(SB),7,$-4 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$-4 + MOVL $5, AX + INT $0x80 + RET + +TEXT runtime·close(SB),7,$-4 + MOVL $6, AX + INT $0x80 + RET + +TEXT runtime·read(SB),7,$-4 + MOVL $3, AX + INT $0x80 + RET + TEXT runtime·write(SB),7,$-4 MOVL $4, AX // sys_write INT $0x80 @@ -46,12 +61,13 @@ TEXT runtime·usleep(SB),7,$24 INT $0x80 RET -TEXT runtime·raisesigpipe(SB),7,$12 +TEXT runtime·raise(SB),7,$12 MOVL $311, AX // sys__lwp_self INT $0x80 MOVL $0, 0(SP) MOVL AX, 4(SP) // arg 1 - target - MOVL $13, 8(SP) // arg 2 - signo == SIGPIPE + MOVL sig+0(FP), AX + MOVL AX, 8(SP) // arg 2 - signo MOVL $318, AX // sys__lwp_kill INT $0x80 RET @@ -72,8 +88,6 @@ TEXT runtime·mmap(SB),7,$36 STOSL MOVL $197, AX // sys_mmap INT $0x80 - JCC 2(PC) - NEGL AX RET TEXT runtime·munmap(SB),7,$-4 diff --git a/src/pkg/runtime/sys_netbsd_amd64.s b/src/pkg/runtime/sys_netbsd_amd64.s index e73e83ded..574d8a91b 100644 --- a/src/pkg/runtime/sys_netbsd_amd64.s +++ b/src/pkg/runtime/sys_netbsd_amd64.s @@ -16,7 +16,7 @@ TEXT runtime·lwp_create(SB),7,$0 MOVL $309, AX // sys__lwp_create SYSCALL JCC 2(PC) - NEGL AX + NEGQ AX RET TEXT runtime·lwp_tramp(SB),7,$0 @@ -79,6 +79,28 @@ TEXT runtime·exit1(SB),7,$-8 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 pathname + MOVL 16(SP), SI // arg 2 flags + MOVL 20(SP), DX // arg 3 mode + MOVL $5, AX + SYSCALL + RET + +TEXT runtime·close(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fd + MOVL $6, AX + SYSCALL + RET + +TEXT runtime·read(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fd + MOVQ 16(SP), SI // arg 2 buf + MOVL 24(SP), DX // arg 3 count + MOVL $3, AX + SYSCALL + RET + TEXT runtime·write(SB),7,$-8 MOVL 8(SP), DI // arg 1 - fd MOVQ 16(SP), SI // arg 2 - buf @@ -103,11 +125,11 @@ TEXT runtime·usleep(SB),7,$16 SYSCALL RET -TEXT runtime·raisesigpipe(SB),7,$16 +TEXT runtime·raise(SB),7,$16 MOVL $311, AX // sys__lwp_self SYSCALL MOVQ AX, DI // arg 1 - target - MOVQ $13, SI // arg 2 - signo == SIGPIPE + MOVL sig+0(FP), SI // arg 2 - signo MOVL $318, AX // sys__lwp_kill SYSCALL RET @@ -231,8 +253,6 @@ TEXT runtime·mmap(SB),7,$0 MOVQ $0, R9 // arg 6 - pad MOVL $197, AX // sys_mmap SYSCALL - JCC 2(PC) - NEGL AX ADDQ $16, SP RET @@ -284,7 +304,7 @@ TEXT runtime·sysctl(SB),7,$0 MOVQ $202, AX // sys___sysctl SYSCALL JCC 3(PC) - NEGL AX + NEGQ AX RET MOVL $0, AX RET diff --git a/src/pkg/runtime/sys_netbsd_arm.s b/src/pkg/runtime/sys_netbsd_arm.s index 4a119c5de..3ff335f4d 100644 --- a/src/pkg/runtime/sys_netbsd_arm.s +++ b/src/pkg/runtime/sys_netbsd_arm.s @@ -21,6 +21,25 @@ TEXT runtime·exit1(SB),7,$-4 MOVW $1, R9 // crash MOVW R9, (R9) RET + +TEXT runtime·open(SB),7,$-8 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + SWI $0xa00005 + RET + +TEXT runtime·close(SB),7,$-8 + MOVW 0(FP), R0 + SWI $0xa00006 + RET + +TEXT runtime·read(SB),7,$-8 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + SWI $0xa00003 + RET TEXT runtime·write(SB),7,$-4 MOVW 0(FP), R0 // arg 1 - fd @@ -88,9 +107,9 @@ TEXT runtime·usleep(SB),7,$16 SWI $0xa001ae // sys_nanosleep RET -TEXT runtime·raisesigpipe(SB),7,$16 +TEXT runtime·raise(SB),7,$16 SWI $0xa00137 // sys__lwp_self, the returned R0 is arg 1 - MOVW $13, R1 // arg 2 - signo == SIGPIPE + MOVW sig+0(FP), R1 // arg 2 - signal SWI $0xa0013e // sys__lwp_kill RET diff --git a/src/pkg/runtime/sys_openbsd_386.s b/src/pkg/runtime/sys_openbsd_386.s index c62e0f949..37b6ff215 100644 --- a/src/pkg/runtime/sys_openbsd_386.s +++ b/src/pkg/runtime/sys_openbsd_386.s @@ -24,6 +24,21 @@ TEXT runtime·exit1(SB),7,$8 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$-4 + MOVL $5, AX + INT $0x80 + RET + +TEXT runtime·close(SB),7,$-4 + MOVL $6, AX + INT $0x80 + RET + +TEXT runtime·read(SB),7,$-4 + MOVL $3, AX + INT $0x80 + RET + TEXT runtime·write(SB),7,$-4 MOVL $4, AX // sys_write INT $0x80 @@ -47,12 +62,13 @@ TEXT runtime·usleep(SB),7,$20 INT $0x80 RET -TEXT runtime·raisesigpipe(SB),7,$12 +TEXT runtime·raise(SB),7,$12 MOVL $299, AX // sys_getthrid INT $0x80 MOVL $0, 0(SP) MOVL AX, 4(SP) // arg 1 - pid - MOVL $13, 8(SP) // arg 2 - signum == SIGPIPE + MOVL sig+0(FP), AX + MOVL AX, 8(SP) // arg 2 - signum MOVL $37, AX // sys_kill INT $0x80 RET @@ -73,8 +89,6 @@ TEXT runtime·mmap(SB),7,$36 STOSL MOVL $197, AX // sys_mmap INT $0x80 - JCC 2(PC) - NEGL AX RET TEXT runtime·munmap(SB),7,$-4 diff --git a/src/pkg/runtime/sys_openbsd_amd64.s b/src/pkg/runtime/sys_openbsd_amd64.s index 8a736507f..cbd2c2f76 100644 --- a/src/pkg/runtime/sys_openbsd_amd64.s +++ b/src/pkg/runtime/sys_openbsd_amd64.s @@ -23,7 +23,7 @@ TEXT runtime·tfork(SB),7,$32 // Return if tfork syscall failed. JCC 3(PC) - NEGL AX + NEGQ AX RET // In parent, return. @@ -87,6 +87,28 @@ TEXT runtime·exit1(SB),7,$-8 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·open(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 pathname + MOVL 16(SP), SI // arg 2 flags + MOVL 20(SP), DX // arg 3 mode + MOVL $5, AX + SYSCALL + RET + +TEXT runtime·close(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fd + MOVL $6, AX + SYSCALL + RET + +TEXT runtime·read(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fd + MOVQ 16(SP), SI // arg 2 buf + MOVL 24(SP), DX // arg 3 count + MOVL $3, AX + SYSCALL + RET + TEXT runtime·write(SB),7,$-8 MOVL 8(SP), DI // arg 1 - fd MOVQ 16(SP), SI // arg 2 - buf @@ -111,11 +133,11 @@ TEXT runtime·usleep(SB),7,$16 SYSCALL RET -TEXT runtime·raisesigpipe(SB),7,$16 +TEXT runtime·raise(SB),7,$16 MOVL $299, AX // sys_getthrid SYSCALL MOVQ AX, DI // arg 1 - pid - MOVQ $13, SI // arg 2 - signum == SIGPIPE + MOVL sig+0(FP), SI // arg 2 - signum MOVL $37, AX // sys_kill SYSCALL RET @@ -220,8 +242,6 @@ TEXT runtime·mmap(SB),7,$0 MOVQ $0, R9 // arg 6 - pad MOVL $197, AX SYSCALL - JCC 2(PC) - NEGL AX ADDQ $16, SP RET @@ -272,7 +292,7 @@ TEXT runtime·sysctl(SB),7,$0 MOVQ $202, AX // sys___sysctl SYSCALL JCC 3(PC) - NEGL AX + NEGQ AX RET MOVL $0, AX RET diff --git a/src/pkg/runtime/sys_plan9_386.s b/src/pkg/runtime/sys_plan9_386.s index 3385b083a..1f860a961 100644 --- a/src/pkg/runtime/sys_plan9_386.s +++ b/src/pkg/runtime/sys_plan9_386.s @@ -170,3 +170,30 @@ TEXT runtime·sigtramp(SB),7,$0 // Only used by the 64-bit runtime. TEXT runtime·setfpmasks(SB),7,$0 RET + +#define ERRMAX 128 /* from os_plan9.h */ + +// func errstr() String +// Only used by package syscall. +// Grab error string due to a syscall made +// in entersyscall mode, without going +// through the allocator (issue 4994). +// See ../syscall/asm_plan9_386.s:/·Syscall/ +TEXT runtime·errstr(SB),7,$0 + get_tls(AX) + MOVL m(AX), BX + MOVL m_errstr(BX), CX + MOVL CX, 4(SP) + MOVL $ERRMAX, 8(SP) + MOVL $41, AX + INT $64 + + // syscall requires caller-save + MOVL 4(SP), CX + + // push the argument + PUSHL CX + CALL runtime·findnull(SB) + POPL CX + MOVL AX, 8(SP) + RET diff --git a/src/pkg/runtime/sys_plan9_amd64.s b/src/pkg/runtime/sys_plan9_amd64.s index b34f98a68..c0c896ebc 100644 --- a/src/pkg/runtime/sys_plan9_amd64.s +++ b/src/pkg/runtime/sys_plan9_amd64.s @@ -206,3 +206,31 @@ TEXT runtime·setfpmasks(SB),7,$8 MOVL AX, 0(SP) LDMXCSR 0(SP) RET + +#define ERRMAX 128 /* from os_plan9.h */ + +// func errstr() String +// Only used by package syscall. +// Grab error string due to a syscall made +// in entersyscall mode, without going +// through the allocator (issue 4994). +// See ../syscall/asm_plan9_386.s:/·Syscall/ +TEXT runtime·errstr(SB),7,$0 + get_tls(AX) + MOVQ m(AX), BX + MOVQ m_errstr(BX), CX + MOVQ CX, 8(SP) + MOVQ $ERRMAX, 16(SP) + MOVQ $0x8000, AX + MOVQ $41, BP + SYSCALL + + // syscall requires caller-save + MOVQ 8(SP), CX + + // push the argument + PUSHQ CX + CALL runtime·findnull(SB) + POPQ CX + MOVQ AX, 16(SP) + RET diff --git a/src/pkg/runtime/sys_windows_386.s b/src/pkg/runtime/sys_windows_386.s index ca59f0a1d..206cdccc4 100644 --- a/src/pkg/runtime/sys_windows_386.s +++ b/src/pkg/runtime/sys_windows_386.s @@ -314,3 +314,46 @@ TEXT runtime·remove_exception_handler(SB),7,$0 MOVL AX, 0(FS) RET + +TEXT runtime·osyield(SB),7,$20 + // Tried NtYieldExecution but it doesn't yield hard enough. + // NtWaitForSingleObject being used here as Sleep(0). + MOVL runtime·NtWaitForSingleObject(SB), AX + MOVL $-1, hi-4(SP) + MOVL $-1, lo-8(SP) + LEAL lo-8(SP), BX + MOVL BX, ptime-12(SP) + MOVL $0, alertable-16(SP) + MOVL $-1, handle-20(SP) + MOVL SP, BP + CALL checkstack4<>(SB) + CALL AX + MOVL BP, SP + RET + +TEXT runtime·usleep(SB),7,$20 + MOVL runtime·NtWaitForSingleObject(SB), AX + // Have 1us units; need negative 100ns units. + // Assume multiply by 10 will not overflow 32-bit word. + MOVL usec+0(FP), BX + IMULL $10, BX + NEGL BX + MOVL $-1, hi-4(SP) + MOVL BX, lo-8(SP) + LEAL lo-8(SP), BX + MOVL BX, ptime-12(SP) + MOVL $0, alertable-16(SP) + MOVL $-1, handle-20(SP) + MOVL SP, BP + CALL checkstack4<>(SB) + CALL AX + MOVL BP, SP + RET + +// This function requires 4 bytes of stack, +// to simulate what calling NtWaitForSingleObject will use. +// (It is just a CALL to the system call dispatch.) +// If the linker okays the call to checkstack4 (a NOSPLIT function) +// then the call to NtWaitForSingleObject is okay too. +TEXT checkstack4<>(SB),7,$4 + RET diff --git a/src/pkg/runtime/sys_windows_amd64.s b/src/pkg/runtime/sys_windows_amd64.s index fe88f3b75..c20a268b1 100644 --- a/src/pkg/runtime/sys_windows_amd64.s +++ b/src/pkg/runtime/sys_windows_amd64.s @@ -346,3 +346,35 @@ TEXT runtime·install_exception_handler(SB),7,$0 TEXT runtime·remove_exception_handler(SB),7,$0 RET + +TEXT runtime·osyield(SB),7,$8 + // Tried NtYieldExecution but it doesn't yield hard enough. + // NtWaitForSingleObject being used here as Sleep(0). + // The CALL is safe because NtXxx is a system call wrapper: + // it puts the right system call number in AX, then does + // a SYSENTER and a RET. + MOVQ runtime·NtWaitForSingleObject(SB), AX + MOVQ $1, BX + NEGQ BX + MOVQ SP, R8 // ptime + MOVQ BX, (R8) + MOVQ $-1, CX // handle + MOVQ $0, DX // alertable + CALL AX + RET + +TEXT runtime·usleep(SB),7,$8 + // The CALL is safe because NtXxx is a system call wrapper: + // it puts the right system call number in AX, then does + // a SYSENTER and a RET. + MOVQ runtime·NtWaitForSingleObject(SB), AX + // Have 1us units; want negative 100ns units. + MOVL usec+0(FP), BX + IMULQ $10, BX + NEGQ BX + MOVQ SP, R8 // ptime + MOVQ BX, (R8) + MOVQ $-1, CX // handle + MOVQ $0, DX // alertable + CALL AX + RET diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc index 2babb173d..6de989f51 100644 --- a/src/pkg/runtime/time.goc +++ b/src/pkg/runtime/time.goc @@ -15,7 +15,6 @@ package time static Timers timers; static void addtimer(Timer*); -static bool deltimer(Timer*); // Package time APIs. // Godoc uses the comments in package time, not these. @@ -31,15 +30,13 @@ func Sleep(ns int64) { func startTimer(t *Timer) { if(raceenabled) runtime·racerelease(t); - runtime·lock(&timers); - addtimer(t); - runtime·unlock(&timers); + runtime·addtimer(t); } // stopTimer removes t from the timer heap if it is there. // It returns true if t was removed, false if t wasn't even there. func stopTimer(t *Timer) (stopped bool) { - stopped = deltimer(t); + stopped = runtime·deltimer(t); } // C runtime. @@ -79,6 +76,14 @@ runtime·tsleep(int64 ns, int8 *reason) static FuncVal timerprocv = {timerproc}; +void +runtime·addtimer(Timer *t) +{ + runtime·lock(&timers); + addtimer(t); + runtime·unlock(&timers); +} + // Add a timer to the heap and start or kick the timer proc // if the new timer is earlier than any of the others. static void @@ -121,8 +126,8 @@ addtimer(Timer *t) // Delete timer t from the heap. // Do not need to update the timerproc: // if it wakes up early, no big deal. -static bool -deltimer(Timer *t) +bool +runtime·deltimer(Timer *t) { int32 i; diff --git a/src/pkg/runtime/time_plan9_386.c b/src/pkg/runtime/time_plan9_386.c index a29d45715..fc08a90d6 100644 --- a/src/pkg/runtime/time_plan9_386.c +++ b/src/pkg/runtime/time_plan9_386.c @@ -24,7 +24,7 @@ runtime·nanotime(void) // file descriptor) is roughly four times slower // in 9vx on a 2.16 GHz Intel Core 2 Duo. - if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0) + if(fd < 0 && (fd = runtime·open("/dev/bintime", OREAD|OCEXEC, 0)) < 0) return 0; if(runtime·pread(fd, b, sizeof b, 0) != sizeof b) return 0; diff --git a/src/pkg/runtime/vdso_linux_amd64.c b/src/pkg/runtime/vdso_linux_amd64.c index ab68c23c3..f55d312a0 100644 --- a/src/pkg/runtime/vdso_linux_amd64.c +++ b/src/pkg/runtime/vdso_linux_amd64.c @@ -4,6 +4,7 @@ #include "runtime.h" +#define AT_RANDOM 25 #define AT_SYSINFO_EHDR 33 #define AT_NULL 0 /* End of vector */ #define PT_LOAD 1 /* Loadable program segment */ @@ -319,11 +320,16 @@ runtime·linux_setup_vdso(int32 argc, uint8** argv) if(elf_auxv[i].a_type == AT_SYSINFO_EHDR) { if(elf_auxv[i].a_un.a_val == 0) { // Something went wrong - return; + continue; } vdso_init_from_sysinfo_ehdr(&vdso_info, (Elf64_Ehdr*)elf_auxv[i].a_un.a_val); vdso_parse_symbols(&vdso_info, vdso_find_version(&vdso_info, &linux26)); - return; + continue; + } + if(elf_auxv[i].a_type == AT_RANDOM) { + runtime·startup_random_data = (byte*)elf_auxv[i].a_un.a_val; + runtime·startup_random_data_len = 16; + continue; } } } |