aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-10-29 11:54:48 -0400
committerRuss Cox <rsc@golang.org>2014-10-29 11:54:48 -0400
commit8e171e196615bc40a3a804811acf768eee6b2aa8 (patch)
tree843fd3e672b94483e5684f537518b544124486d0 /src/runtime
parent5550249ad3030e563ba810f3c30a1ba6b01f448c (diff)
parent3bbc8638d5dd2564b3c60a603c094c0e570bd301 (diff)
downloadgo-8e171e196615bc40a3a804811acf768eee6b2aa8.tar.xz
[dev.garbage] all: merge default (dd5014ed9b01) into dev.garbage
LGTM=rlh R=rlh CC=golang-codereviews https://golang.org/cl/170730043
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/asm_386.s20
-rw-r--r--src/runtime/asm_amd64.s21
-rw-r--r--src/runtime/asm_amd64p32.s7
-rw-r--r--src/runtime/asm_arm.s18
-rw-r--r--src/runtime/cgo/gcc_arm.S15
-rw-r--r--src/runtime/chan_test.go29
-rw-r--r--src/runtime/crash_cgo_test.go52
-rw-r--r--src/runtime/crash_test.go17
-rw-r--r--src/runtime/debug/garbage.go30
-rw-r--r--src/runtime/debug/garbage_test.go13
-rw-r--r--src/runtime/env_plan9.go6
-rw-r--r--src/runtime/extern.go6
-rw-r--r--src/runtime/funcdata.h3
-rw-r--r--src/runtime/malloc.h3
-rw-r--r--src/runtime/mem.go3
-rw-r--r--src/runtime/mgc0.c35
-rw-r--r--src/runtime/mprof.go6
-rw-r--r--src/runtime/pprof/mprof_test.go99
-rw-r--r--src/runtime/pprof/pprof_test.go2
-rw-r--r--src/runtime/print1.go27
-rw-r--r--src/runtime/proc.c20
-rw-r--r--src/runtime/rt0_nacl_amd64p32.s2
-rw-r--r--src/runtime/runtime.c4
-rw-r--r--src/runtime/runtime.h2
-rw-r--r--src/runtime/select.go8
-rw-r--r--src/runtime/stack.c4
-rw-r--r--src/runtime/stubs.go4
-rw-r--r--src/runtime/symtab.go11
-rw-r--r--src/runtime/sys_nacl_amd64p32.s6
-rw-r--r--src/runtime/time.go29
30 files changed, 401 insertions, 101 deletions
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s
index b0ed2d8ceb..0d46a9eff7 100644
--- a/src/runtime/asm_386.s
+++ b/src/runtime/asm_386.s
@@ -732,6 +732,20 @@ needm:
MOVL g(CX), BP
MOVL g_m(BP), BP
+ // Set m->sched.sp = SP, so that if a panic happens
+ // during the function we are about to execute, it will
+ // have a valid SP to run on the g0 stack.
+ // The next few lines (after the havem label)
+ // will save this SP onto the stack and then write
+ // the same SP back to m->sched.sp. That seems redundant,
+ // but if an unrecovered panic happens, unwindm will
+ // restore the g->sched.sp from the stack location
+ // and then onM will try to use it. If we don't set it here,
+ // that restored SP will be uninitialized (typically 0) and
+ // will not be usable.
+ MOVL m_g0(BP), SI
+ MOVL SP, (g_sched+gobuf_sp)(SI)
+
havem:
// Now there's a valid m, and we're running on its m->g0.
// Save current m->g0->sched.sp on stack and then set it to SP.
@@ -871,12 +885,6 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-8
MOVL DX, ret_hi+4(FP)
RET
-TEXT runtime·gocputicks(SB),NOSPLIT,$0-8
- RDTSC
- MOVL AX, ret_lo+0(FP)
- MOVL DX, ret_hi+4(FP)
- RET
-
TEXT runtime·ldt0setup(SB),NOSPLIT,$16-0
// set up ldt 7 to point at tls0
// ldt 1 would be fine on Linux, but on OS X, 7 is as low as we can go.
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 2ee3312086..a9b082beb8 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -717,6 +717,20 @@ needm:
get_tls(CX)
MOVQ g(CX), BP
MOVQ g_m(BP), BP
+
+ // Set m->sched.sp = SP, so that if a panic happens
+ // during the function we are about to execute, it will
+ // have a valid SP to run on the g0 stack.
+ // The next few lines (after the havem label)
+ // will save this SP onto the stack and then write
+ // the same SP back to m->sched.sp. That seems redundant,
+ // but if an unrecovered panic happens, unwindm will
+ // restore the g->sched.sp from the stack location
+ // and then onM will try to use it. If we don't set it here,
+ // that restored SP will be uninitialized (typically 0) and
+ // will not be usable.
+ MOVQ m_g0(BP), SI
+ MOVQ SP, (g_sched+gobuf_sp)(SI)
havem:
// Now there's a valid m, and we're running on its m->g0.
@@ -855,13 +869,6 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-0
MOVQ AX, ret+0(FP)
RET
-TEXT runtime·gocputicks(SB),NOSPLIT,$0-8
- RDTSC
- SHLQ $32, DX
- ADDQ DX, AX
- MOVQ AX, ret+0(FP)
- RET
-
// hash function using AES hardware instructions
TEXT runtime·aeshash(SB),NOSPLIT,$0-32
MOVQ p+0(FP), AX // ptr to data
diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s
index e27f67e1ee..28875bc55a 100644
--- a/src/runtime/asm_amd64p32.s
+++ b/src/runtime/asm_amd64p32.s
@@ -657,13 +657,6 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-0
MOVQ AX, ret+0(FP)
RET
-TEXT runtime·gocputicks(SB),NOSPLIT,$0-8
- RDTSC
- SHLQ $32, DX
- ADDQ DX, AX
- MOVQ AX, ret+0(FP)
- RET
-
// hash function using AES hardware instructions
// For now, our one amd64p32 system (NaCl) does not
// support using AES instructions, so have not bothered to
diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s
index b21441488a..e94b4c1ff6 100644
--- a/src/runtime/asm_arm.s
+++ b/src/runtime/asm_arm.s
@@ -556,6 +556,21 @@ TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12
MOVW $runtime·needm(SB), R0
BL (R0)
+ // Set m->sched.sp = SP, so that if a panic happens
+ // during the function we are about to execute, it will
+ // have a valid SP to run on the g0 stack.
+ // The next few lines (after the havem label)
+ // will save this SP onto the stack and then write
+ // the same SP back to m->sched.sp. That seems redundant,
+ // but if an unrecovered panic happens, unwindm will
+ // restore the g->sched.sp from the stack location
+ // and then onM will try to use it. If we don't set it here,
+ // that restored SP will be uninitialized (typically 0) and
+ // will not be usable.
+ MOVW g_m(g), R8
+ MOVW m_g0(R8), R3
+ MOVW R13, (g_sched+gobuf_sp)(R3)
+
havem:
MOVW g_m(g), R8
MOVW R8, savedm-4(SP)
@@ -1275,9 +1290,6 @@ TEXT runtime·fastrand1(SB),NOSPLIT,$-4-4
MOVW R0, ret+0(FP)
RET
-TEXT runtime·gocputicks(SB),NOSPLIT,$0
- B runtime·cputicks(SB)
-
TEXT runtime·return0(SB),NOSPLIT,$0
MOVW $0, R0
RET
diff --git a/src/runtime/cgo/gcc_arm.S b/src/runtime/cgo/gcc_arm.S
index 2e4b3528ba..d5833bfad0 100644
--- a/src/runtime/cgo/gcc_arm.S
+++ b/src/runtime/cgo/gcc_arm.S
@@ -12,13 +12,6 @@
#endif
/*
- * Because the assembler might target an earlier revision of the ISA
- * by default, we must explicitly specify the ISA revision to ensure
- * BLX is recognized as a valid instruction.
- */
-.arch armv5t
-
-/*
* void crosscall_arm1(void (*fn)(void), void (*setg_gcc)(void *g), void *g)
*
* Calling into the 5c tool chain, where all registers are caller save.
@@ -31,8 +24,12 @@ EXT(crosscall_arm1):
mov r4, r0
mov r5, r1
mov r0, r2
- blx r5 // setg(g)
- blx r4 // fn()
+
+ // Because the assembler might target an earlier revision of the ISA
+ // by default, we encode BLX as a .word.
+ .word 0xe12fff35 // blx r5 // setg(g)
+ .word 0xe12fff34 // blx r4 // fn()
+
pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
.globl EXT(__stack_chk_fail_local)
diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go
index 01632892ed..e689ceaed1 100644
--- a/src/runtime/chan_test.go
+++ b/src/runtime/chan_test.go
@@ -482,6 +482,35 @@ func TestShrinkStackDuringBlockedSend(t *testing.T) {
<-done
}
+func TestSelectDuplicateChannel(t *testing.T) {
+ // This test makes sure we can queue a G on
+ // the same channel multiple times.
+ c := make(chan int)
+ d := make(chan int)
+ e := make(chan int)
+
+ // goroutine A
+ go func() {
+ select {
+ case <-c:
+ case <-c:
+ case <-d:
+ }
+ e <- 9
+ }()
+ time.Sleep(time.Millisecond) // make sure goroutine A gets qeueued first on c
+
+ // goroutine B
+ go func() {
+ <-c
+ }()
+ time.Sleep(time.Millisecond) // make sure goroutine B gets queued on c before continuing
+
+ d <- 7 // wake up A, it dequeues itself from c. This operation used to corrupt c.recvq.
+ <-e // A tells us it's done
+ c <- 8 // wake up B. This operation used to fail because c.recvq was corrupted (it tries to wake up an already running G instead of B)
+}
+
func BenchmarkChanNonblocking(b *testing.B) {
myc := make(chan int)
b.RunParallel(func(pb *testing.PB) {
diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go
index 4ff0084c22..5958ad8914 100644
--- a/src/runtime/crash_cgo_test.go
+++ b/src/runtime/crash_cgo_test.go
@@ -8,6 +8,7 @@ package runtime_test
import (
"runtime"
+ "strings"
"testing"
)
@@ -34,6 +35,17 @@ func TestCgoTraceback(t *testing.T) {
}
}
+func TestCgoExternalThreadPanic(t *testing.T) {
+ if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+ t.Skipf("no pthreads on %s", runtime.GOOS)
+ }
+ got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", cgoExternalThreadPanicC)
+ want := "panic: BOOM"
+ if !strings.Contains(got, want) {
+ t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
+ }
+}
+
const cgoSignalDeadlockSource = `
package main
@@ -117,3 +129,43 @@ func main() {
fmt.Printf("OK\n")
}
`
+
+const cgoExternalThreadPanicSource = `
+package main
+
+// void start(void);
+import "C"
+
+func main() {
+ C.start()
+ select {}
+}
+
+//export gopanic
+func gopanic() {
+ panic("BOOM")
+}
+`
+
+const cgoExternalThreadPanicC = `
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+
+void gopanic(void);
+
+static void*
+die(void* x)
+{
+ gopanic();
+ return 0;
+}
+
+void
+start(void)
+{
+ pthread_t t;
+ if(pthread_create(&t, 0, die, 0) != 0)
+ printf("pthread_create failed\n");
+}
+`
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index 783b4c48f5..211a0476fd 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -31,7 +31,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd {
return cmd
}
-func executeTest(t *testing.T, templ string, data interface{}) string {
+func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
switch runtime.GOOS {
case "android", "nacl":
t.Skipf("skipping on %s", runtime.GOOS)
@@ -61,7 +61,20 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
t.Fatalf("failed to close file: %v", err)
}
- got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput()
+ for i := 0; i < len(extra); i += 2 {
+ if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ cmd := exec.Command("go", "build", "-o", "a.exe")
+ cmd.Dir = dir
+ out, err := testEnv(cmd).CombinedOutput()
+ if err != nil {
+ t.Fatalf("building source: %v\n%s", err, out)
+ }
+
+ got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
return string(got)
}
diff --git a/src/runtime/debug/garbage.go b/src/runtime/debug/garbage.go
index 30994f2196..4a77dcfcd6 100644
--- a/src/runtime/debug/garbage.go
+++ b/src/runtime/debug/garbage.go
@@ -16,6 +16,7 @@ type GCStats struct {
NumGC int64 // number of garbage collections
PauseTotal time.Duration // total pause for all collections
Pause []time.Duration // pause history, most recent first
+ PauseEnd []time.Time // pause end times history, most recent first
PauseQuantiles []time.Duration
}
@@ -30,25 +31,36 @@ type GCStats struct {
func ReadGCStats(stats *GCStats) {
// Create a buffer with space for at least two copies of the
// pause history tracked by the runtime. One will be returned
- // to the caller and the other will be used as a temporary buffer
- // for computing quantiles.
+ // to the caller and the other will be used as transfer buffer
+ // for end times history and as a temporary buffer for
+ // computing quantiles.
const maxPause = len(((*runtime.MemStats)(nil)).PauseNs)
- if cap(stats.Pause) < 2*maxPause {
- stats.Pause = make([]time.Duration, 2*maxPause)
+ if cap(stats.Pause) < 2*maxPause+3 {
+ stats.Pause = make([]time.Duration, 2*maxPause+3)
}
- // readGCStats fills in the pause history (up to maxPause entries)
- // and then three more: Unix ns time of last GC, number of GC,
- // and total pause time in nanoseconds. Here we depend on the
- // fact that time.Duration's native unit is nanoseconds, so the
- // pauses and the total pause time do not need any conversion.
+ // readGCStats fills in the pause and end times histories (up to
+ // maxPause entries) and then three more: Unix ns time of last GC,
+ // number of GC, and total pause time in nanoseconds. Here we
+ // depend on the fact that time.Duration's native unit is
+ // nanoseconds, so the pauses and the total pause time do not need
+ // any conversion.
readGCStats(&stats.Pause)
n := len(stats.Pause) - 3
stats.LastGC = time.Unix(0, int64(stats.Pause[n]))
stats.NumGC = int64(stats.Pause[n+1])
stats.PauseTotal = stats.Pause[n+2]
+ n /= 2 // buffer holds pauses and end times
stats.Pause = stats.Pause[:n]
+ if cap(stats.PauseEnd) < maxPause {
+ stats.PauseEnd = make([]time.Time, 0, maxPause)
+ }
+ stats.PauseEnd = stats.PauseEnd[:0]
+ for _, ns := range stats.Pause[n : n+n] {
+ stats.PauseEnd = append(stats.PauseEnd, time.Unix(0, int64(ns)))
+ }
+
if len(stats.PauseQuantiles) > 0 {
if n == 0 {
for i := range stats.PauseQuantiles {
diff --git a/src/runtime/debug/garbage_test.go b/src/runtime/debug/garbage_test.go
index 149bafc6f3..54c33bd4f3 100644
--- a/src/runtime/debug/garbage_test.go
+++ b/src/runtime/debug/garbage_test.go
@@ -70,6 +70,19 @@ func TestReadGCStats(t *testing.T) {
t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1])
}
}
+
+ // compare memory stats with gc stats:
+ if len(stats.PauseEnd) != n {
+ t.Fatalf("len(stats.PauseEnd) = %d, want %d", len(stats.PauseEnd), n)
+ }
+ off := (int(mstats.NumGC) + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd)
+ for i := 0; i < n; i++ {
+ dt := stats.PauseEnd[i]
+ if dt.UnixNano() != int64(mstats.PauseEnd[off]) {
+ t.Errorf("stats.PauseEnd[%d] = %d, want %d", i, dt, mstats.PauseEnd[off])
+ }
+ off = (off + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd)
+ }
}
var big = make([]byte, 1<<20)
diff --git a/src/runtime/env_plan9.go b/src/runtime/env_plan9.go
index 76e9867e03..e442c34835 100644
--- a/src/runtime/env_plan9.go
+++ b/src/runtime/env_plan9.go
@@ -30,7 +30,7 @@ func gogetenv(key string) string {
if fd < 0 {
return ""
}
- n := seek(fd, 0, 2) - 1
+ n := seek(fd, 0, 2)
if n <= 0 {
close(fd)
return ""
@@ -44,6 +44,10 @@ func gogetenv(key string) string {
return ""
}
+ if p[r-1] == 0 {
+ r--
+ }
+
var s string
sp := (*_string)(unsafe.Pointer(&s))
sp.str = &p[0]
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index b8db5d0c4b..1b8052bb56 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -39,6 +39,12 @@ a comma-separated list of name=val pairs. Supported names are:
gcdead: setting gcdead=1 causes the garbage collector to clobber all stack slots
that it thinks are dead.
+ invalidptr: defaults to invalidptr=1, causing the garbage collector and stack
+ copier to crash the program if an invalid pointer value (for example, 1)
+ is found in a pointer-typed location. Setting invalidptr=0 disables this check.
+ This should only be used as a temporary workaround to diagnose buggy code.
+ The real fix is to not store integers in pointer-typed locations.
+
scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
detailed multiline info every X milliseconds, describing state of the scheduler,
processors, threads and goroutines.
diff --git a/src/runtime/funcdata.h b/src/runtime/funcdata.h
index a2667a4c02..d6c14fcb41 100644
--- a/src/runtime/funcdata.h
+++ b/src/runtime/funcdata.h
@@ -28,6 +28,9 @@
// defines the pointer map for the function's arguments.
// GO_ARGS should be the first instruction in a function that uses it.
// It can be omitted if there are no arguments at all.
+// GO_ARGS is inserted implicitly by the linker for any function
+// that also has a Go prototype and therefore is usually not necessary
+// to write explicitly.
#define GO_ARGS FUNCDATA $FUNCDATA_ArgsPointerMaps, go_args_stackmap(SB)
// GO_RESULTS_INITIALIZED indicates that the assembly function
diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h
index e606b0c7af..522b11bbad 100644
--- a/src/runtime/malloc.h
+++ b/src/runtime/malloc.h
@@ -268,7 +268,8 @@ struct MStats
uint64 next_gc; // next GC (in heap_alloc time)
uint64 last_gc; // last GC (in absolute time)
uint64 pause_total_ns;
- uint64 pause_ns[256];
+ uint64 pause_ns[256]; // circular buffer of recent GC pause lengths
+ uint64 pause_end[256]; // circular buffer of recent GC end times (nanoseconds since 1970)
uint32 numgc;
bool enablegc;
bool debuggc;
diff --git a/src/runtime/mem.go b/src/runtime/mem.go
index 438f22ec09..e6f1eb0e64 100644
--- a/src/runtime/mem.go
+++ b/src/runtime/mem.go
@@ -44,7 +44,8 @@ type MemStats struct {
NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount
LastGC uint64 // end time of last collection (nanoseconds since 1970)
PauseTotalNs uint64
- PauseNs [256]uint64 // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
+ PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
+ PauseEnd [256]uint64 // circular buffer of recent GC pause end times
NumGC uint32
EnableGC bool
DebugGC bool
diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c
index cc1f811234..bcc5a2f39d 100644
--- a/src/runtime/mgc0.c
+++ b/src/runtime/mgc0.c
@@ -687,7 +687,7 @@ putpartial(Workbuf *b)
else if (b->nobj == nelem(b->obj))
runtime·lfstackpush(&runtime·work.full, &b->node);
else {
- runtime·printf("b=%p, b->nobj=%d, nelem(b->obj)=%d\n", b, b->nobj, (uint32)nelem(b->obj));
+ runtime·printf("b=%p, b->nobj=%d, nelem(b->obj)=%d\n", b, (uint32)b->nobj, (uint32)nelem(b->obj));
runtime·throw("putpartial: bad Workbuf b->nobj");
}
}
@@ -1725,6 +1725,7 @@ gc(struct gc_args *args)
t4 = runtime·nanotime();
runtime·atomicstore64(&mstats.last_gc, runtime·unixnanotime()); // must be Unix time to make sense to user
mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
+ mstats.pause_end[mstats.numgc%nelem(mstats.pause_end)] = t4;
mstats.pause_total_ns += t4 - t0;
mstats.numgc++;
if(mstats.debuggc)
@@ -1773,7 +1774,6 @@ gc(struct gc_args *args)
runtime·sweep.spanidx = 0;
runtime·unlock(&runtime·mheap.lock);
- // Temporary disable concurrent sweep, because we see failures on builders.
if(ConcurrentSweep && !args->eagersweep) {
runtime·lock(&runtime·gclock);
if(runtime·sweep.g == nil)
@@ -1787,6 +1787,8 @@ gc(struct gc_args *args)
// Sweep all spans eagerly.
while(runtime·sweepone() != -1)
runtime·sweep.npausesweep++;
+ // Do an additional mProf_GC, because all 'free' events are now real as well.
+ runtime·mProf_GC();
}
runtime·mProf_GC();
@@ -1834,7 +1836,7 @@ readgcstats_m(void)
{
Slice *pauses;
uint64 *p;
- uint32 i, n;
+ uint32 i, j, n;
pauses = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
@@ -1843,25 +1845,29 @@ readgcstats_m(void)
if(pauses->cap < nelem(mstats.pause_ns)+3)
runtime·throw("runtime: short slice passed to readGCStats");
- // Pass back: pauses, last GC (absolute time), number of GC, total pause ns.
+ // Pass back: pauses, pause ends, last gc (absolute time), number of gc, total pause ns.
p = (uint64*)pauses->array;
runtime·lock(&runtime·mheap.lock);
+
n = mstats.numgc;
if(n > nelem(mstats.pause_ns))
n = nelem(mstats.pause_ns);
-
+
// The pause buffer is circular. The most recent pause is at
// pause_ns[(numgc-1)%nelem(pause_ns)], and then backward
// from there to go back farther in time. We deliver the times
// most recent first (in p[0]).
- for(i=0; i<n; i++)
- p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)];
+ for(i=0; i<n; i++) {
+ j = (mstats.numgc-1-i)%nelem(mstats.pause_ns);
+ p[i] = mstats.pause_ns[j];
+ p[n+i] = mstats.pause_end[j];
+ }
- p[n] = mstats.last_gc;
- p[n+1] = mstats.numgc;
- p[n+2] = mstats.pause_total_ns;
+ p[n+n] = mstats.last_gc;
+ p[n+n+1] = mstats.numgc;
+ p[n+n+2] = mstats.pause_total_ns;
runtime·unlock(&runtime·mheap.lock);
- pauses->len = n+3;
+ pauses->len = n+n+3;
}
void
@@ -2041,7 +2047,7 @@ runtime·unrollgcprog_m(void)
Type *typ;
byte *mask, *prog;
uintptr pos;
- uint32 x;
+ uintptr x;
typ = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
@@ -2060,8 +2066,9 @@ runtime·unrollgcprog_m(void)
unrollgcprog1(mask, prog, &pos, false, true);
}
// atomic way to say mask[0] = 1
- x = ((uint32*)mask)[0];
- runtime·atomicstore((uint32*)mask, x|1);
+ x = *(uintptr*)mask;
+ ((byte*)&x)[0] = 1;
+ runtime·atomicstorep((void**)mask, (void*)x);
}
runtime·unlock(&lock);
}
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index 89e9915236..803da56670 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -234,7 +234,7 @@ func mProf_GC() {
// Called by malloc to record a profiled block.
func mProf_Malloc(p unsafe.Pointer, size uintptr) {
var stk [maxStack]uintptr
- nstk := callers(1, &stk[0], len(stk))
+ nstk := callers(4, &stk[0], len(stk))
lock(&proflock)
b := stkbucket(memProfile, size, stk[:nstk], true)
mp := b.mp()
@@ -284,6 +284,8 @@ func SetBlockProfileRate(rate int) {
var r int64
if rate <= 0 {
r = 0 // disable profiling
+ } else if rate == 1 {
+ r = 1 // profile everything
} else {
// convert ns to cycles, use float64 to prevent overflow during multiplication
r = int64(float64(rate) * float64(tickspersecond()) / (1000 * 1000 * 1000))
@@ -297,7 +299,7 @@ func SetBlockProfileRate(rate int) {
func blockevent(cycles int64, skip int) {
if cycles <= 0 {
- return
+ cycles = 1
}
rate := int64(atomicload64(&blockprofilerate))
if rate <= 0 || (rate > cycles && int64(fastrand1())%rate > cycles) {
diff --git a/src/runtime/pprof/mprof_test.go b/src/runtime/pprof/mprof_test.go
new file mode 100644
index 0000000000..ebf53dd66b
--- /dev/null
+++ b/src/runtime/pprof/mprof_test.go
@@ -0,0 +1,99 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pprof_test
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "runtime"
+ . "runtime/pprof"
+ "testing"
+ "unsafe"
+)
+
+var memSink interface{}
+
+func allocateTransient1M() {
+ for i := 0; i < 1024; i++ {
+ memSink = &struct{ x [1024]byte }{}
+ }
+}
+
+func allocateTransient2M() {
+ // prevent inlining
+ if memSink == nil {
+ panic("bad")
+ }
+ memSink = make([]byte, 2<<20)
+}
+
+type Obj32 struct {
+ link *Obj32
+ pad [32 - unsafe.Sizeof(uintptr(0))]byte
+}
+
+var persistentMemSink *Obj32
+
+func allocatePersistent1K() {
+ for i := 0; i < 32; i++ {
+ // Can't use slice because that will introduce implicit allocations.
+ obj := &Obj32{link: persistentMemSink}
+ persistentMemSink = obj
+ }
+}
+
+var memoryProfilerRun = 0
+
+func TestMemoryProfiler(t *testing.T) {
+ // Disable sampling, otherwise it's difficult to assert anything.
+ oldRate := runtime.MemProfileRate
+ runtime.MemProfileRate = 1
+ defer func() {
+ runtime.MemProfileRate = oldRate
+ }()
+
+ // Allocate a meg to ensure that mcache.next_sample is updated to 1.
+ for i := 0; i < 1024; i++ {
+ memSink = make([]byte, 1024)
+ }
+
+ // Do the interesting allocations.
+ allocateTransient1M()
+ allocateTransient2M()
+ allocatePersistent1K()
+ memSink = nil
+
+ runtime.GC() // materialize stats
+ var buf bytes.Buffer
+ if err := Lookup("heap").WriteTo(&buf, 1); err != nil {
+ t.Fatalf("failed to write heap profile: %v", err)
+ }
+
+ memoryProfilerRun++
+
+ tests := []string{
+ fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.allocatePersistent1K\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:43
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:66
+`, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
+
+ fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.allocateTransient1M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:21
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:64
+`, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
+
+ fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.allocateTransient2M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:30
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:65
+`, memoryProfilerRun, (2<<20)*memoryProfilerRun),
+ }
+
+ for _, test := range tests {
+ if !regexp.MustCompile(test).Match(buf.Bytes()) {
+ t.Fatalf("The entry did not match:\n%v\n\nProfile:\n%v\n", test, buf.String())
+ }
+ }
+}
diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go
index edd471a0c9..8677cb30c5 100644
--- a/src/runtime/pprof/pprof_test.go
+++ b/src/runtime/pprof/pprof_test.go
@@ -249,7 +249,7 @@ func TestGoroutineSwitch(t *testing.T) {
// exists to record a PC without a traceback. Those are okay.
if len(stk) == 2 {
f := runtime.FuncForPC(stk[1])
- if f != nil && (f.Name() == "System" || f.Name() == "ExternalCode") {
+ if f != nil && (f.Name() == "System" || f.Name() == "ExternalCode" || f.Name() == "GC") {
return
}
}
diff --git a/src/runtime/print1.go b/src/runtime/print1.go
index 0fa1fb63c4..8f8268873b 100644
--- a/src/runtime/print1.go
+++ b/src/runtime/print1.go
@@ -19,32 +19,17 @@ func bytes(s string) (ret []byte) {
return
}
-// goprintf is the function call that is actually deferred when you write
-// defer print(...)
-// It is otherwise unused. In particular it is not used for ordinary prints.
-// Right now a dynamically allocated string that is being passed as an
-// argument is invisible to the garbage collector and might be collected
-// if that argument list is the only reference. For now we ignore that possibility.
-// To fix, we should change to defer a call to vprintf with a pointer to
-// an argument list on the stack, stored in an appropriately typed
-// struct. golang.org/issue/8614.
-//go:nosplit
-func goprintf(s string) {
- vprintf(s, add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
-}
-
-// printf is only called from C code. It has the same problem as goprintf
-// with strings possibly being collected from underneath.
-// However, the runtime never prints dynamically allocated
-// Go strings using printf. The strings it prints come from the symbol
-// and type tables.
+// printf is only called from C code. It has no type information for the args,
+// but C stacks are ignored by the garbage collector anyway, so having
+// type information would not add anything.
//go:nosplit
func printf(s *byte) {
vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
}
-// sprintf is only called from C code.
-// It has the same problem as goprintf.
+// sprintf is only called from C code. It has no type information for the args,
+// but C stacks are ignored by the garbage collector anyway, so having
+// type information would not add anything.
//go:nosplit
func snprintf(dst *byte, n int32, s *byte) {
buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n]
diff --git a/src/runtime/proc.c b/src/runtime/proc.c
index b824f574d9..ab6812329f 100644
--- a/src/runtime/proc.c
+++ b/src/runtime/proc.c
@@ -2764,6 +2764,8 @@ static void
checkdead(void)
{
G *gp;
+ P *p;
+ M *mp;
int32 run, grunning, s;
uintptr i;
@@ -2805,6 +2807,24 @@ checkdead(void)
runtime·unlock(&runtime·allglock);
if(grunning == 0) // possible if main goroutine calls runtime·Goexit()
runtime·throw("no goroutines (main called runtime.Goexit) - deadlock!");
+
+ // Maybe jump time forward for playground.
+ if((gp = runtime·timejump()) != nil) {
+ runtime·casgstatus(gp, Gwaiting, Grunnable);
+ globrunqput(gp);
+ p = pidleget();
+ if(p == nil)
+ runtime·throw("checkdead: no p for timer");
+ mp = mget();
+ if(mp == nil)
+ newm(nil, p);
+ else {
+ mp->nextp = p;
+ runtime·notewakeup(&mp->park);
+ }
+ return;
+ }
+
g->m->throwing = -1; // do not dump full stacks
runtime·throw("all goroutines are asleep - deadlock!");
}
diff --git a/src/runtime/rt0_nacl_amd64p32.s b/src/runtime/rt0_nacl_amd64p32.s
index d8703dc0f0..54e4b1de89 100644
--- a/src/runtime/rt0_nacl_amd64p32.s
+++ b/src/runtime/rt0_nacl_amd64p32.s
@@ -26,5 +26,5 @@ TEXT _rt0_amd64p32_nacl(SB),NOSPLIT,$16
TEXT main(SB),NOSPLIT,$0
// Uncomment for fake time like on Go Playground.
//MOVQ $1257894000000000000, AX
- //MOVQ AX, runtime·timens(SB)
+ //MOVQ AX, runtime·faketime(SB)
JMP runtime·rt0_go(SB)
diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c
index b3503fb909..c823691ec5 100644
--- a/src/runtime/runtime.c
+++ b/src/runtime/runtime.c
@@ -276,9 +276,13 @@ struct DbgVar
int32* value;
};
+// Do we report invalid pointers found during stack or heap scans?
+int32 runtime·invalidptr = 1;
+
#pragma dataflag NOPTR /* dbgvar has no heap pointers */
static DbgVar dbgvar[] = {
{"allocfreetrace", &runtime·debug.allocfreetrace},
+ {"invalidptr", &runtime·invalidptr},
{"efence", &runtime·debug.efence},
{"gctrace", &runtime·debug.gctrace},
{"gcdead", &runtime·debug.gcdead},
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index c1bba423a1..6a02ef1d31 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -672,6 +672,8 @@ enum {
byte* runtime·startup_random_data;
uint32 runtime·startup_random_data_len;
+int32 runtime·invalidptr;
+
enum {
// hashinit wants this many random bytes
HashRandomBytes = 32
diff --git a/src/runtime/select.go b/src/runtime/select.go
index 2d0787bd96..d703e1d79b 100644
--- a/src/runtime/select.go
+++ b/src/runtime/select.go
@@ -393,9 +393,9 @@ loop:
} else {
c = k._chan
if k.kind == _CaseSend {
- c.sendq.dequeueg(gp)
+ c.sendq.dequeueSudoG(sglist)
} else {
- c.recvq.dequeueg(gp)
+ c.recvq.dequeueSudoG(sglist)
}
}
sgnext = sglist.waitlink
@@ -623,7 +623,7 @@ func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
return
}
-func (q *waitq) dequeueg(gp *g) {
+func (q *waitq) dequeueSudoG(s *sudog) {
var prevsgp *sudog
l := &q.first
for {
@@ -631,7 +631,7 @@ func (q *waitq) dequeueg(gp *g) {
if sgp == nil {
return
}
- if sgp.g == gp {
+ if sgp == s {
*l = sgp.next
if q.last == sgp {
q.last = prevsgp
diff --git a/src/runtime/stack.c b/src/runtime/stack.c
index e06e48a93d..f18171ea5c 100644
--- a/src/runtime/stack.c
+++ b/src/runtime/stack.c
@@ -401,12 +401,12 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
break;
case BitsPointer:
p = scanp[i];
- if(f != nil && (byte*)0 < p && (p < (byte*)PageSize || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) {
+ if(f != nil && (byte*)0 < p && (p < (byte*)PageSize && runtime·invalidptr || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) {
// Looks like a junk value in a pointer slot.
// Live analysis wrong?
g->m->traceback = 2;
runtime·printf("runtime: bad pointer in frame %s at %p: %p\n", runtime·funcname(f), &scanp[i], p);
- runtime·throw("bad pointer!");
+ runtime·throw("invalid stack pointer");
}
if(minp <= p && p < maxp) {
if(StackDebug >= 3)
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index 32dfed7d39..2d5e41c1c5 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -182,7 +182,11 @@ func exit(code int32)
func breakpoint()
func nanotime() int64
func usleep(usec uint32)
+
+// careful: cputicks is not guaranteed to be monotonic! In particular, we have
+// noticed drift between cpus on certain os/arch combinations. See issue 8976.
func cputicks() int64
+
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer
func munmap(addr unsafe.Pointer, n uintptr)
func madvise(addr unsafe.Pointer, n uintptr, flags int32)
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 48d4023b9a..45d107b777 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -84,10 +84,13 @@ func symtabinit() {
}
}
- // file table follows ftab.
+ // The ftab ends with a half functab consisting only of
+ // 'entry', followed by a uint32 giving the pcln-relative
+ // offset of the file table.
sp = (*sliceStruct)(unsafe.Pointer(&filetab))
- p = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff))
- sp.array = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff))
+ end := unsafe.Pointer(&ftab[nftab].funcoff) // just beyond ftab
+ fileoffset := *(*uint32)(end)
+ sp.array = unsafe.Pointer(&pclntable[fileoffset])
// length is in first element of array.
// set len to 1 so we can get first element.
sp.len = 1
@@ -224,7 +227,7 @@ func funcline(f *_func, targetpc uintptr, file *string) int32 {
func funcspdelta(f *_func, targetpc uintptr) int32 {
x := pcvalue(f, f.pcsp, targetpc, true)
if x&(ptrSize-1) != 0 {
- print("invalid spdelta ", f.pcsp, " ", x, "\n")
+ print("invalid spdelta ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
}
return x
}
diff --git a/src/runtime/sys_nacl_amd64p32.s b/src/runtime/sys_nacl_amd64p32.s
index c30c2a8933..4eb4aacdd5 100644
--- a/src/runtime/sys_nacl_amd64p32.s
+++ b/src/runtime/sys_nacl_amd64p32.s
@@ -60,7 +60,7 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $24-20
TEXT runtime·write(SB),NOSPLIT,$16-20
// If using fake time and writing to stdout or stderr,
// emit playback header before actual data.
- MOVQ runtime·timens(SB), AX
+ MOVQ runtime·faketime(SB), AX
CMPQ AX, $0
JEQ write
MOVL fd+0(FP), DI
@@ -242,7 +242,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$8
RET
TEXT time·now(SB),NOSPLIT,$16
- MOVQ runtime·timens(SB), AX
+ MOVQ runtime·faketime(SB), AX
CMPQ AX, $0
JEQ realtime
MOVQ $0, DX
@@ -277,7 +277,7 @@ TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
RET
TEXT runtime·nanotime(SB),NOSPLIT,$16
- MOVQ runtime·timens(SB), AX
+ MOVQ runtime·faketime(SB), AX
CMPQ AX, $0
JEQ 3(PC)
MOVQ AX, ret+0(FP)
diff --git a/src/runtime/time.go b/src/runtime/time.go
index 8cf9eecf83..11862c7e23 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -35,8 +35,8 @@ var timers struct {
t []*timer
}
-// nacl fake time support.
-var timens int64
+// nacl fake time support - time in nanoseconds since 1970
+var faketime int64
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -194,7 +194,7 @@ func timerproc() {
f(arg, seq)
lock(&timers.lock)
}
- if delta < 0 {
+ if delta < 0 || faketime > 0 {
// No timers left - put goroutine to sleep.
timers.rescheduling = true
goparkunlock(&timers.lock, "timer goroutine (idle)")
@@ -208,6 +208,29 @@ func timerproc() {
}
}
+func timejump() *g {
+ if faketime == 0 {
+ return nil
+ }
+
+ lock(&timers.lock)
+ if !timers.created || len(timers.t) == 0 {
+ unlock(&timers.lock)
+ return nil
+ }
+
+ var gp *g
+ if faketime < timers.t[0].when {
+ faketime = timers.t[0].when
+ if timers.rescheduling {
+ timers.rescheduling = false
+ gp = timers.gp
+ }
+ }
+ unlock(&timers.lock)
+ return gp
+}
+
// Heap maintenance algorithms.
func siftupTimer(i int) {