From da8cf5438aa676a99e8bb55c94011b2581743e1a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 16 Sep 2014 17:26:16 -0700 Subject: runtime: always run semacquire on the G stack semacquire might need to park the currently running G. It can only park if called from the G stack (because it has no way of saving the M stack state). So all calls to semacquire must come from the G stack. The three violators are GOMAXPROCS, ReadMemStats, and WriteHeapDump. This change moves the semacquire call earlier, out of their C code and into their Go code. This seldom caused bugs because semacquire seldom actually had to park the caller. But it did happen intermittently. Fixes #8749 LGTM=dvyukov R=golang-codereviews, dvyukov, bradfitz CC=golang-codereviews https://golang.org/cl/144940043 --- src/runtime/proc.c | 79 ++++-------------------------------------------------- 1 file changed, 5 insertions(+), 74 deletions(-) (limited to 'src/runtime/proc.c') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 25f9166403..0e677a9d28 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -24,42 +24,6 @@ // // Design doc at http://golang.org/s/go11sched. -typedef struct Sched Sched; -struct Sched { - Mutex lock; - - uint64 goidgen; - - M* midle; // idle m's waiting for work - int32 nmidle; // number of idle m's waiting for work - int32 nmidlelocked; // number of locked m's waiting for work - int32 mcount; // number of m's that have been created - int32 maxmcount; // maximum number of m's allowed (or die) - - P* pidle; // idle P's - uint32 npidle; - uint32 nmspinning; - - // Global runnable queue. - G* runqhead; - G* runqtail; - int32 runqsize; - - // Global cache of dead G's. - Mutex gflock; - G* gfree; - int32 ngfree; - - uint32 gcwaiting; // gc is waiting to run - int32 stopwait; - Note stopnote; - uint32 sysmonwait; - Note sysmonnote; - uint64 lastpoll; - - int32 profilehz; // cpu profiling rate -}; - enum { // Number of goroutine ids to grab from runtime·sched.goidgen to local per-P cache at once. @@ -67,7 +31,7 @@ enum GoidCacheBatch = 16, }; -Sched runtime·sched; +SchedType runtime·sched; int32 runtime·gomaxprocs; uint32 runtime·needextram; bool runtime·iscgo; @@ -79,7 +43,7 @@ M* runtime·extram; P* runtime·allp[MaxGomaxprocs+1]; int8* runtime·goos; int32 runtime·ncpu; -static int32 newprocs; +int32 runtime·newprocs; Mutex runtime·allglock; // the following vars are protected by this lock or by stoptheworld G** runtime·allg; @@ -763,9 +727,9 @@ runtime·starttheworld(void) injectglist(gp); add = needaddgcproc(); runtime·lock(&runtime·sched.lock); - if(newprocs) { - procresize(newprocs); - newprocs = 0; + if(runtime·newprocs) { + procresize(runtime·newprocs); + runtime·newprocs = 0; } else procresize(runtime·gomaxprocs); runtime·sched.gcwaiting = 0; @@ -2364,39 +2328,6 @@ runtime·Breakpoint(void) runtime·breakpoint(); } -// Implementation of runtime.GOMAXPROCS. -// delete when scheduler is even stronger -void -runtime·gomaxprocs_m(void) -{ - int32 n, ret; - - n = g->m->scalararg[0]; - g->m->scalararg[0] = 0; - - if(n > MaxGomaxprocs) - n = MaxGomaxprocs; - runtime·lock(&runtime·sched.lock); - ret = runtime·gomaxprocs; - if(n <= 0 || n == ret) { - runtime·unlock(&runtime·sched.lock); - g->m->scalararg[0] = ret; - return; - } - runtime·unlock(&runtime·sched.lock); - - runtime·semacquire(&runtime·worldsema, false); - g->m->gcing = 1; - runtime·stoptheworld(); - newprocs = n; - g->m->gcing = 0; - runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(); - - g->m->scalararg[0] = ret; - return; -} - // lockOSThread is called by runtime.LockOSThread and runtime.lockOSThread below // after they modify m->locked. Do not allow preemption during this call, // or else the m might be different in this function than in the caller. -- cgit v1.3 From 6c934238c93f8f60775409f1ab410ce9c9ea2357 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 17 Sep 2014 08:32:15 -0700 Subject: runtime: change minimum stack size to 2K. It will be 8K on windows because it needs 4K for the OS. Similarly, plan9 will be 4K. On linux/amd64, reduces size of 100,000 goroutines from ~819MB to ~245MB. Update #7514 LGTM=dvyukov R=golang-codereviews, dvyukov, khr, aram CC=golang-codereviews https://golang.org/cl/145790043 --- src/runtime/proc.c | 8 ++++---- src/runtime/stack.h | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'src/runtime/proc.c') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 0e677a9d28..4282a145e1 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -2162,11 +2162,11 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp siz = narg + nret; siz = (siz+7) & ~7; - // We could instead create a secondary stack frame - // and make it look like goexit was on the original but - // the call to the actual goroutine function was split. + // We could allocate a larger initial stack if necessary. // Not worth it: this is almost always an error. - if(siz > StackMin - 1024) + // 4*sizeof(uintreg): extra space added below + // sizeof(uintreg): caller's LR (arm) or return address (x86, in gostartcall). + if(siz >= StackMin - 4*sizeof(uintreg) - sizeof(uintreg)) runtime·throw("runtime.newproc: function arguments too large for new goroutine"); p = g->m->p; diff --git a/src/runtime/stack.h b/src/runtime/stack.h index b30e322166..f97dc4ed8d 100644 --- a/src/runtime/stack.h +++ b/src/runtime/stack.h @@ -69,16 +69,19 @@ enum { #endif // Plan 9 #endif // Windows - // The amount of extra stack to allocate beyond the size - // needed for the single frame that triggered the split. - StackExtra = 2048, + // The minimum size of stack used by Go code + StackMin = 2048, - // The minimum stack segment size to allocate. - // If the amount needed for the splitting frame + StackExtra - // is less than this number, the stack will have this size instead. - StackMin = 8192, - StackSystemRounded = StackSystem + (-StackSystem & (StackMin-1)), - FixedStack = StackMin + StackSystemRounded, + // The minimum stack size to allocate. + // The hackery here rounds FixedStack0 up to a power of 2. + FixedStack0 = StackMin + StackSystem, + FixedStack1 = FixedStack0 - 1, + FixedStack2 = FixedStack1 | (FixedStack1 >> 1), + FixedStack3 = FixedStack2 | (FixedStack2 >> 2), + FixedStack4 = FixedStack3 | (FixedStack3 >> 4), + FixedStack5 = FixedStack4 | (FixedStack4 >> 8), + FixedStack6 = FixedStack5 | (FixedStack5 >> 16), + FixedStack = FixedStack6 + 1, // Functions that need frames bigger than this use an extra // instruction to do the stack split check, to avoid overflow -- cgit v1.3 From c3b5db895b11ba28bc1546f37178efcb057ab3f0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 14:49:24 -0400 Subject: runtime: delete panicstring; move its checks into gopanic In Go 1.3 the runtime called panicstring to report errors like divide by zero or memory faults. Now we call panic (gopanic) with pre-allocated error values. That new path is missing the checking that panicstring did, so add it there. The only call to panicstring left is in cnew, which is problematic because if it fails, probably the heap is corrupt. In that case, calling panicstring creates a new errorCString (no allocation there), but then panic tries to print it, invoking errorCString.Error, which does a string concatenation (allocating), which then dies. Replace that one panicstring with a throw: cnew is for allocating runtime data structures and should never ask for an inappropriate amount of memory. With panicstring gone, delete newErrorCString, errorCString. While we're here, delete newErrorString, not called by anyone. (It can't be: that would be C code calling Go code that might block or grow the stack.) Found while debugging a malloc corruption. This resulted in 'panic during panic' instead of a more useful message. LGTM=khr R=khr CC=golang-codereviews https://golang.org/cl/138290045 --- src/runtime/error.go | 22 ------------------- src/runtime/malloc.c | 2 +- src/runtime/panic.go | 59 +++++++++++++++++++++++++-------------------------- src/runtime/proc.c | 7 ------ src/runtime/runtime.h | 3 --- 5 files changed, 30 insertions(+), 63 deletions(-) (limited to 'src/runtime/proc.c') diff --git a/src/runtime/error.go b/src/runtime/error.go index 3ea93680ce..0b40c702b0 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -71,28 +71,6 @@ func (e errorString) Error() string { return "runtime error: " + string(e) } -// For calling from C. -func newErrorString(s string, ret *interface{}) { - *ret = errorString(s) -} - -// An errorCString represents a runtime error described by a single C string. -// Not "type errorCString unsafe.Pointer" because of http://golang.org/issue/7084. -// Not uintptr because we want to avoid an allocation if interfaces can't hold -// uintptrs directly (and cstr _is_ a pointer). -type errorCString struct{ cstr unsafe.Pointer } - -func (e errorCString) RuntimeError() {} - -func (e errorCString) Error() string { - return "runtime error: " + gostringnocopy((*byte)(e.cstr)) -} - -// For calling from C. -func newErrorCString(s unsafe.Pointer, ret *interface{}) { - *ret = errorCString{s} -} - type stringer interface { String() string } diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c index d5f2b9ab80..60d20a992d 100644 --- a/src/runtime/malloc.c +++ b/src/runtime/malloc.c @@ -335,7 +335,7 @@ static void* cnew(Type *typ, intgo n) { if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size)) - runtime·panicstring("runtime: allocation size out of range"); + runtime·throw("runtime: allocation size out of range"); return runtime·mallocgc(typ->size*n, typ, typ->kind&KindNoPointers ? FlagNoScan : 0); } diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 927b6db44b..3cc31053e8 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -281,6 +281,35 @@ func gopanic(e interface{}) { if gp.m.curg != gp { gothrow("panic on m stack") } + + // m.softfloat is set during software floating point. + // It increments m.locks to avoid preemption. + // We moved the memory loads out, so there shouldn't be + // any reason for it to panic anymore. + if gp.m.softfloat != 0 { + gp.m.locks-- + gp.m.softfloat = 0 + gothrow("panic during softfloat") + } + if gp.m.mallocing != 0 { + print("panic: ") + printany(e) + print("\n") + gothrow("panic during malloc") + } + if gp.m.gcing != 0 { + print("panic: ") + printany(e) + print("\n") + gothrow("panic during gc") + } + if gp.m.locks != 0 { + print("panic: ") + printany(e) + print("\n") + gothrow("panic holding locks") + } + var p _panic p.arg = e p.link = gp._panic @@ -431,33 +460,3 @@ func gothrow(s string) { dopanic(0) *(*int)(nil) = 0 // not reached } - -func panicstring(s *int8) { - // m.softfloat is set during software floating point, - // which might cause a fault during a memory load. - // It increments m.locks to avoid preemption. - // If we're panicking, the software floating point frames - // will be unwound, so decrement m.locks as they would. - gp := getg() - if gp.m.softfloat != 0 { - gp.m.locks-- - gp.m.softfloat = 0 - } - - if gp.m.mallocing != 0 { - print("panic: ", s, "\n") - gothrow("panic during malloc") - } - if gp.m.gcing != 0 { - print("panic: ", s, "\n") - gothrow("panic during gc") - } - if gp.m.locks != 0 { - print("panic: ", s, "\n") - gothrow("panic holding locks") - } - - var err interface{} - newErrorCString(unsafe.Pointer(s), &err) - gopanic(err) -} diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 4282a145e1..860701ee58 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -123,7 +123,6 @@ runtime·schedinit(void) { int32 n, procs; byte *p; - Eface i; // raceinit must be the first call to race detector. // In particular, it must be done before mallocinit below calls racemapshadow. @@ -137,12 +136,6 @@ runtime·schedinit(void) runtime·mallocinit(); mcommoninit(g->m); - // Initialize the itable value for newErrorCString, - // so that the next time it gets called, possibly - // in a fault during a garbage collection, it will not - // need to allocated memory. - runtime·newErrorCString(0, &i); - runtime·goargs(); runtime·goenvs(); runtime·parsedebugvars(); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index c034f3aa97..386b09b96b 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -804,7 +804,6 @@ void runtime·goenvs(void); void runtime·goenvs_unix(void); void* runtime·getu(void); void runtime·throw(int8*); -void runtime·panicstring(int8*); bool runtime·canpanic(G*); void runtime·prints(int8*); void runtime·printf(int8*, ...); @@ -1063,8 +1062,6 @@ void runtime·panicdivide(void); */ void runtime·printany(Eface); void runtime·newTypeAssertionError(String*, String*, String*, String*, Eface*); -void runtime·newErrorString(String, Eface*); -void runtime·newErrorCString(int8*, Eface*); void runtime·fadd64c(uint64, uint64, uint64*); void runtime·fsub64c(uint64, uint64, uint64*); void runtime·fmul64c(uint64, uint64, uint64*); -- cgit v1.3 From c7f6bd795acf002d60f712f0f4e2701051e74e4a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 23:51:22 -0400 Subject: runtime: rename SchedType to SchedT CL 144940043 renamed it from Sched to SchedType to avoid a lowercasing conflict in the Go code with the variable named sched. We've been using just T resolve those conflicts, not Type. The FooType pattern is already taken for the kind-specific variants of the runtime Type structure: ChanType, MapType, and so on. SchedType isn't a Type. LGTM=bradfitz, khr R=khr, bradfitz CC=golang-codereviews https://golang.org/cl/145180043 --- src/runtime/proc.c | 2 +- src/runtime/runtime.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/runtime/proc.c') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 860701ee58..3f4179d473 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -31,7 +31,7 @@ enum GoidCacheBatch = 16, }; -SchedType runtime·sched; +SchedT runtime·sched; int32 runtime·gomaxprocs; uint32 runtime·needextram; bool runtime·iscgo; diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 386b09b96b..7fefbc2997 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -60,7 +60,7 @@ typedef struct SudoG SudoG; typedef struct Mutex Mutex; typedef struct M M; typedef struct P P; -typedef struct SchedType SchedType; +typedef struct SchedT SchedT; typedef struct Note Note; typedef struct Slice Slice; typedef struct String String; @@ -434,7 +434,7 @@ enum { MaxGomaxprocs = 1<<8, }; -struct SchedType +struct SchedT { Mutex lock; @@ -753,7 +753,7 @@ extern DebugVars runtime·debug; extern uintptr runtime·maxstacksize; extern Note runtime·signote; extern ForceGCState runtime·forcegc; -extern SchedType runtime·sched; +extern SchedT runtime·sched; extern int32 runtime·newprocs; /* -- cgit v1.3 From 7283e08cbf06bcd32a391183e26080cff301e7f9 Mon Sep 17 00:00:00 2001 From: Hector Martin Cantero Date: Wed, 24 Sep 2014 13:20:25 -0400 Subject: runtime: keep g->syscallsp consistent after cgo->Go callbacks Normally, the caller to runtime.entersyscall() must not return before calling runtime.exitsyscall(), lest g->syscallsp become a dangling pointer. runtime.cgocallbackg() violates this constraint. To work around this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then restore them after calling runtime.entersyscall(), which restores the syscall stack frame pointer saved by cgocall. This allows the GC to correctly trace a goroutine that is currently returning from a Go->cgo->Go chain. This also adds a check to proc.c that panics if g->syscallsp is clearly invalid. It is not 100% foolproof, as it will not catch a case where the stack was popped then pushed back beyond g->syscallsp, but it does catch the present cgo issue and makes existing tests fail without the bugfix. Fixes #7978. LGTM=dvyukov, rsc R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc CC=golang-codereviews, rsc https://golang.org/cl/131910043 --- misc/cgo/test/cgo_test.go | 1 + misc/cgo/test/issue7978.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++ src/run.bash | 2 + src/run.bat | 7 ++++ src/runtime/cgocall.go | 12 +++++- src/runtime/proc.c | 39 ++++++++++++------ src/runtime/runtime.h | 1 + src/runtime/stubs.go | 1 + 8 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 misc/cgo/test/issue7978.go (limited to 'src/runtime/proc.c') diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 3783af061c..1899d46053 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -56,5 +56,6 @@ func TestNaming(t *testing.T) { testNaming(t) } func Test7560(t *testing.T) { test7560(t) } func Test5242(t *testing.T) { test5242(t) } func Test8092(t *testing.T) { test8092(t) } +func Test7978(t *testing.T) { test7978(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/misc/cgo/test/issue7978.go b/misc/cgo/test/issue7978.go new file mode 100644 index 0000000000..39864476ce --- /dev/null +++ b/misc/cgo/test/issue7978.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. + +// Issue 7978. Stack tracing didn't work during cgo code after calling a Go +// callback. Make sure GC works and the stack trace is correct. + +package cgotest + +/* +#include + +void issue7978cb(void); + +// use ugly atomic variable sync since that doesn't require calling back into +// Go code or OS dependencies +static void issue7978c(uint32_t *sync) { + while(__sync_fetch_and_add(sync, 0) != 0) + ; + __sync_fetch_and_add(sync, 1); + while(__sync_fetch_and_add(sync, 0) != 2) + ; + issue7978cb(); + __sync_fetch_and_add(sync, 1); + while(__sync_fetch_and_add(sync, 0) != 6) + ; +} +*/ +import "C" + +import ( + "runtime" + "strings" + "sync/atomic" + "testing" +) + +var issue7978sync uint32 + +func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) { + runtime.GC() + buf := make([]byte, 65536) + trace := string(buf[:runtime.Stack(buf, true)]) + for _, goroutine := range strings.Split(trace, "\n\n") { + if strings.Contains(goroutine, "test.issue7978go") { + trace := strings.Split(goroutine, "\n") + // look for the expected function in the stack + for i := 0; i < depth; i++ { + if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) { + t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine) + return + } + if strings.Contains(trace[1+2*i], wantFunc) { + return + } + } + t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine) + return + } + } + t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace) +} + +func issue7978wait(store uint32, wait uint32) { + if store != 0 { + atomic.StoreUint32(&issue7978sync, store) + } + for atomic.LoadUint32(&issue7978sync) != wait { + runtime.Gosched() + } +} + +//export issue7978cb +func issue7978cb() { + issue7978wait(3, 4) +} + +func issue7978go() { + C.issue7978c((*C.uint32_t)(&issue7978sync)) + issue7978wait(7, 8) +} + +func test7978(t *testing.T) { + issue7978sync = 0 + go issue7978go() + // test in c code, before callback + issue7978wait(0, 1) + issue7978check(t, "runtime.cgocall_errno(", "", 1) + // test in go code, during callback + issue7978wait(2, 3) + issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3) + // test in c code, after callback + issue7978wait(4, 5) + issue7978check(t, "runtime.cgocall_errno(", "runtime.cgocallback", 1) + // test in go code, after return from cgo + issue7978wait(6, 7) + issue7978check(t, "test.issue7978go(", "", 3) + atomic.StoreUint32(&issue7978sync, 8) +} diff --git a/src/run.bash b/src/run.bash index b5f061d885..d6e53304d8 100755 --- a/src/run.bash +++ b/src/run.bash @@ -119,6 +119,8 @@ go run $GOROOT/test/run.go - . || exit 1 [ "$CGO_ENABLED" != 1 ] || (xcd ../misc/cgo/test +# cgo tests inspect the traceback for runtime functions +export GOTRACEBACK=2 go test -ldflags '-linkmode=auto' || exit 1 # linkmode=internal fails on dragonfly since errno is a TLS relocation. [ "$GOHOSTOS" == dragonfly ] || go test -ldflags '-linkmode=internal' || exit 1 diff --git a/src/run.bat b/src/run.bat index 62692acaf2..309e06d507 100644 --- a/src/run.bat +++ b/src/run.bat @@ -90,11 +90,18 @@ go run "%GOROOT%\test\run.go" - ..\misc\cgo\stdio if errorlevel 1 goto fail echo. +# cgo tests inspect the traceback for runtime functions +set OLDGOTRACEBACK=%GOTRACEBACK% +set GOTRACEBACK=2 + echo # ..\misc\cgo\test go test ..\misc\cgo\test if errorlevel 1 goto fail echo. +set GOTRACEBACK=%OLDGOTRACEBACK% +set OLDGOTRACEBACK= + echo # ..\misc\cgo\testso cd ..\misc\cgo\testso set FAIL=0 diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index a21474b01f..7fd91469eb 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -177,14 +177,22 @@ func cfree(p unsafe.Pointer) { // Call from C back to Go. //go:nosplit func cgocallbackg() { - if gp := getg(); gp != gp.m.curg { + gp := getg() + if gp != gp.m.curg { println("runtime: bad g in cgocallback") exit(2) } + // entersyscall saves the caller's SP to allow the GC to trace the Go + // stack. However, since we're returning to an earlier stack frame and + // need to pair with the entersyscall() call made by cgocall, we must + // save syscall* and let reentersyscall restore them. + savedsp := unsafe.Pointer(gp.syscallsp) + savedpc := gp.syscallpc exitsyscall() // coming out of cgo call cgocallbackg1() - entersyscall() // going back to cgo call + // going back to cgo call + reentersyscall(savedpc, savedsp) } func cgocallbackg1() { diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 3f4179d473..564798be7b 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -1700,9 +1700,9 @@ goexit0(G *gp) #pragma textflag NOSPLIT static void -save(void *pc, uintptr sp) +save(uintptr pc, uintptr sp) { - g->sched.pc = (uintptr)pc; + g->sched.pc = pc; g->sched.sp = sp; g->sched.lr = 0; g->sched.ret = 0; @@ -1730,9 +1730,15 @@ static void entersyscall_gcwait(void); // In practice, this means that we make the fast path run through // entersyscall doing no-split things, and the slow path has to use onM // to run bigger things on the m stack. +// +// reentersyscall is the entry point used by cgo callbacks, where explicitly +// saved SP and PC are restored. This is needed when exitsyscall will be called +// from a function further up in the call stack than the parent, as g->syscallsp +// must always point to a valid stack frame. entersyscall below is the normal +// entry point for syscalls, which obtains the SP and PC from the caller. #pragma textflag NOSPLIT void -·entersyscall(int32 dummy) +runtime·reentersyscall(uintptr pc, uintptr sp) { void (*fn)(void); @@ -1748,9 +1754,9 @@ void g->throwsplit = 1; // Leave SP around for GC and traceback. - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); - g->syscallsp = g->sched.sp; - g->syscallpc = g->sched.pc; + save(pc, sp); + g->syscallsp = sp; + g->syscallpc = pc; runtime·casgstatus(g, Grunning, Gsyscall); if(g->syscallsp < g->stack.lo || g->stack.hi < g->syscallsp) { fn = entersyscall_bad; @@ -1760,7 +1766,7 @@ void if(runtime·atomicload(&runtime·sched.sysmonwait)) { // TODO: fast atomic fn = entersyscall_sysmon; runtime·onM(&fn); - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save(pc, sp); } g->m->mcache = nil; @@ -1769,7 +1775,7 @@ void if(runtime·sched.gcwaiting) { fn = entersyscall_gcwait; runtime·onM(&fn); - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save(pc, sp); } // Goroutines must not split stacks in Gsyscall status (it would corrupt g->sched). @@ -1779,6 +1785,14 @@ void g->m->locks--; } +// Standard syscall entry used by the go syscall library and normal cgo calls. +#pragma textflag NOSPLIT +void +·entersyscall(int32 dummy) +{ + runtime·reentersyscall((uintptr)runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); +} + static void entersyscall_bad(void) { @@ -1826,7 +1840,7 @@ void g->stackguard0 = StackPreempt; // see comment in entersyscall // Leave SP around for GC and traceback. - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save((uintptr)runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); g->syscallsp = g->sched.sp; g->syscallpc = g->sched.pc; runtime·casgstatus(g, Grunning, Gsyscall); @@ -1839,7 +1853,7 @@ void runtime·onM(&fn); // Resave for traceback during blocked call. - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save((uintptr)runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); g->m->locks--; } @@ -1856,12 +1870,15 @@ entersyscallblock_handoff(void) // from the low-level system calls used by the runtime. #pragma textflag NOSPLIT void -runtime·exitsyscall(void) +·exitsyscall(int32 dummy) { void (*fn)(G*); g->m->locks++; // see comment in entersyscall + if(runtime·getcallersp(&dummy) > g->syscallsp) + runtime·throw("exitsyscall: syscall frame is no longer valid"); + g->waitsince = 0; if(exitsyscallfast()) { // There's a cpu for us, so we can run. diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 7fefbc2997..3a6d3e3262 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -901,6 +901,7 @@ void runtime·goexit(void); void runtime·asmcgocall(void (*fn)(void*), void*); int32 runtime·asmcgocall_errno(void (*fn)(void*), void*); void runtime·entersyscall(void); +void runtime·reentersyscall(uintptr, uintptr); void runtime·entersyscallblock(void); void runtime·exitsyscall(void); G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*); diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 2e6aadca7a..1381c7efdb 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -164,6 +164,7 @@ func noescape(p unsafe.Pointer) unsafe.Pointer { } func entersyscall() +func reentersyscall(pc uintptr, sp unsafe.Pointer) func entersyscallblock() func exitsyscall() -- cgit v1.3 From 193daab9889708f7a20ff46efe0fa4b2bf0468d3 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 16:55:26 -0400 Subject: cmd/cc, cmd/ld, runtime: disallow conservative data/bss objects In linker, refuse to write conservative (array of pointers) as the garbage collection type for any variable in the data/bss GC program. In the linker, attach the Go type to an already-read C declaration during dedup. This gives us Go types for C globals for free as long as the cmd/dist-generated Go code contains the declaration. (Most runtime C declarations have a corresponding Go declaration. Both are bss declarations and so the linker dedups them.) In cmd/dist, add a few more C files to the auto-Go-declaration list in order to get Go type information for the C declarations into the linker. In C compiler, mark all non-pointer-containing global declarations and all string data as NOPTR. This allows them to exist in C files without any corresponding Go declaration. Count C function pointers as "non-pointer-containing", since we have no heap-allocated C functions. In runtime, add NOPTR to the remaining pointer-containing declarations, none of which refer to Go heap objects. In runtime, also move os.Args and syscall.envs data into runtime-owned variables. Otherwise, in programs that do not import os or syscall, the runtime variables named os.Args and syscall.envs will be missing type information. I believe that this CL eliminates the final source of conservative GC scanning in non-SWIG Go programs, and therefore... Fixes #909. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/149770043 --- src/cmd/cc/dcl.c | 9 ++++++--- src/cmd/cc/lex.c | 2 ++ src/cmd/cgo/out.go | 13 +++++++++++++ src/cmd/dist/buildruntime.c | 2 ++ src/cmd/ld/data.c | 1 + src/liblink/objfile.c | 6 ++++-- src/os/proc.go | 6 ++++++ src/runtime/asm_386.s | 2 -- src/runtime/asm_amd64.s | 2 -- src/runtime/asm_amd64p32.s | 2 -- src/runtime/heapdump.c | 3 +++ src/runtime/malloc.c | 23 ----------------------- src/runtime/malloc.h | 2 -- src/runtime/mcache.c | 2 +- src/runtime/mgc0.c | 5 ++--- src/runtime/os_windows.c | 6 +++--- src/runtime/proc.c | 30 +++++++++--------------------- src/runtime/proc.go | 8 ++++++++ src/runtime/runtime.c | 25 ++++++++++++++++--------- src/runtime/runtime.go | 11 +++++++++++ src/runtime/signals_darwin.h | 3 +++ src/runtime/signals_dragonfly.h | 3 +++ src/runtime/signals_freebsd.h | 3 +++ src/runtime/signals_linux.h | 3 +++ src/runtime/signals_nacl.h | 3 +++ src/runtime/signals_netbsd.h | 3 +++ src/runtime/signals_openbsd.h | 3 +++ src/runtime/signals_plan9.h | 3 +++ src/runtime/signals_solaris.h | 3 +++ src/runtime/stack.c | 4 ++-- src/runtime/thunk.s | 8 +++++++- src/syscall/env_unix.go | 4 +++- 32 files changed, 126 insertions(+), 77 deletions(-) (limited to 'src/runtime/proc.c') diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index 292717d688..117508fd6d 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -30,6 +30,9 @@ #include #include "cc.h" +#include "../ld/textflag.h" + +static int haspointers(Type*); Node* dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n) @@ -123,7 +126,8 @@ loop: if(dataflag) { s->dataflag = dataflag; dataflag = 0; - } + } else if(s->type != T && !haspointers(s->type)) + s->dataflag = NOPTR; firstbit = 0; n->sym = s; n->type = s->type; @@ -568,9 +572,8 @@ haspointers(Type *t) return 0; case TARRAY: return haspointers(t->link); - case TFUNC: case TIND: - return 1; + return t->link->etype != TFUNC; default: return 0; } diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 55fc36b1e0..7c9f718c09 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -31,6 +31,7 @@ #include #include "cc.h" #include "y.tab.h" +#include "../ld/textflag.h" #ifndef CPP #define CPP "cpp" @@ -1317,6 +1318,7 @@ cinit(void) t->width = 0; symstring = slookup(".string"); symstring->class = CSTATIC; + symstring->dataflag = NOPTR; symstring->type = t; t = typ(TARRAY, types[TCHAR]); diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 6586531ada..2d14f766fc 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -129,6 +129,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C) } else { + fmt.Fprintf(fc, "#pragma dataflag NOPTR /* C pointer, not heap pointer */ \n") fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C) } fmt.Fprintf(fc, "\n") @@ -397,6 +398,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { // C wrapper calls into gcc, passing a pointer to the argument frame. fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname) fmt.Fprintf(fc, "void %s(void*);\n", cname) + fmt.Fprintf(fc, "#pragma dataflag NOPTR\n") fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname) nret := 0 @@ -1151,20 +1153,31 @@ void *_CMalloc(size_t); const cProlog = ` #include "runtime.h" #include "cgocall.h" +#include "textflag.h" +#pragma dataflag NOPTR static void *cgocall_errno = runtime·cgocall_errno; +#pragma dataflag NOPTR void *·_cgo_runtime_cgocall_errno = &cgocall_errno; +#pragma dataflag NOPTR static void *runtime_gostring = runtime·gostring; +#pragma dataflag NOPTR void *·_cgo_runtime_gostring = &runtime_gostring; +#pragma dataflag NOPTR static void *runtime_gostringn = runtime·gostringn; +#pragma dataflag NOPTR void *·_cgo_runtime_gostringn = &runtime_gostringn; +#pragma dataflag NOPTR static void *runtime_gobytes = runtime·gobytes; +#pragma dataflag NOPTR void *·_cgo_runtime_gobytes = &runtime_gobytes; +#pragma dataflag NOPTR static void *runtime_cmalloc = runtime·cmalloc; +#pragma dataflag NOPTR void *·_cgo_runtime_cmalloc = &runtime_cmalloc; void ·_Cerrno(void*, int32); diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index 1257d5b811..bb774e05fc 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -330,9 +330,11 @@ mkzsys(char *dir, char *file) static char *runtimedefs[] = { "defs.c", "malloc.c", + "mcache.c", "mgc0.c", "proc.c", "parfor.c", + "stack.c", }; // mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 71624c3304..9d224d9eb9 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -818,6 +818,7 @@ proggenaddsym(ProgGen *g, LSym *s) if(s->gotype == nil && s->size >= PtrSize) { // conservative scan + diag("missing Go type information for global symbol: %s", s->name); if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c index 9b1e1b7a8f..15d602df92 100644 --- a/src/liblink/objfile.c +++ b/src/liblink/objfile.c @@ -550,7 +550,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) static int ndup; char *name; Reloc *r; - LSym *s, *dup; + LSym *s, *dup, *typ; Pcln *pc; Auto *a; @@ -586,7 +586,9 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) s->type = t; if(s->size < size) s->size = size; - s->gotype = rdsym(ctxt, f, pkg); + typ = rdsym(ctxt, f, pkg); + if(typ != nil) // if bss sym defined multiple times, take type from any one def + s->gotype = typ; rddata(f, &s->p, &s->np); s->maxp = s->np; n = rdint(f); diff --git a/src/os/proc.go b/src/os/proc.go index 38c436ec54..b63c85ad90 100644 --- a/src/os/proc.go +++ b/src/os/proc.go @@ -11,6 +11,12 @@ import "syscall" // Args hold the command-line arguments, starting with the program name. var Args []string +func init() { + Args = runtime_args() +} + +func runtime_args() []string // in package runtime + // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 2961f10f2a..846a214d55 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -903,8 +903,6 @@ TEXT runtime·emptyfunc(SB),0,$0-0 TEXT runtime·abort(SB),NOSPLIT,$0-0 INT $0x3 -GLOBL runtime·tls0(SB), $32 - // hash function using AES hardware instructions TEXT runtime·aeshash(SB),NOSPLIT,$0-16 MOVL p+0(FP), AX // ptr to data diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 44159bb57e..7304d79a2f 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -871,8 +871,6 @@ TEXT runtime·gocputicks(SB),NOSPLIT,$0-8 MOVQ AX, ret+0(FP) RET -GLOBL runtime·tls0(SB), $64 - // 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 bbbd886a53..13a1642568 100644 --- a/src/runtime/asm_amd64p32.s +++ b/src/runtime/asm_amd64p32.s @@ -674,8 +674,6 @@ TEXT runtime·gocputicks(SB),NOSPLIT,$0-8 MOVQ AX, ret+0(FP) RET -GLOBL runtime·tls0(SB), $64 - // 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/heapdump.c b/src/runtime/heapdump.c index 75897c3d35..54b9666b55 100644 --- a/src/runtime/heapdump.c +++ b/src/runtime/heapdump.c @@ -59,6 +59,8 @@ static BitVector makeheapobjbv(byte *p, uintptr size); // fd to write the dump to. static uintptr dumpfd; + +#pragma dataflag NOPTR /* tmpbuf not a heap pointer at least */ static byte *tmpbuf; static uintptr tmpbufsize; @@ -109,6 +111,7 @@ typedef struct TypeCacheBucket TypeCacheBucket; struct TypeCacheBucket { Type *t[TypeCacheAssoc]; }; +#pragma dataflag NOPTR /* only initialized and used while world is stopped */ static TypeCacheBucket typecache[TypeCacheBuckets]; // dump a uint64 in a varint format parseable by encoding/binary diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c index 60d20a992d..b79c30b720 100644 --- a/src/runtime/malloc.c +++ b/src/runtime/malloc.c @@ -329,29 +329,6 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n) return p; } -// Runtime stubs. - -static void* -cnew(Type *typ, intgo n) -{ - if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size)) - runtime·throw("runtime: allocation size out of range"); - return runtime·mallocgc(typ->size*n, typ, typ->kind&KindNoPointers ? FlagNoScan : 0); -} - -// same as runtime·new, but callable from C -void* -runtime·cnew(Type *typ) -{ - return cnew(typ, 1); -} - -void* -runtime·cnewarray(Type *typ, intgo n) -{ - return cnew(typ, n); -} - void runtime·setFinalizer_m(void) { diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h index 410a007173..b90f1baf29 100644 --- a/src/runtime/malloc.h +++ b/src/runtime/malloc.h @@ -526,8 +526,6 @@ uintptr runtime·sweepone(void); void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover); void runtime·unmarkspan(void *v, uintptr size); void runtime·purgecachedstats(MCache*); -void* runtime·cnew(Type*); -void* runtime·cnewarray(Type*, intgo); void runtime·tracealloc(void*, uintptr, Type*); void runtime·tracefree(void*, uintptr); void runtime·tracegc(void); diff --git a/src/runtime/mcache.c b/src/runtime/mcache.c index 17ea5d2e26..5fdbe32667 100644 --- a/src/runtime/mcache.c +++ b/src/runtime/mcache.c @@ -13,7 +13,7 @@ extern volatile intgo runtime·MemProfileRate; // dummy MSpan that contains no free objects. -static MSpan runtime·emptymspan; +MSpan runtime·emptymspan; MCache* runtime·allocmcache(void) diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 54728d5ada..c92fa1db73 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -120,7 +120,7 @@ FinBlock* runtime·finc; // cache of free blocks static byte finptrmask[FinBlockSize/PtrSize/PointersPerByte]; bool runtime·fingwait; bool runtime·fingwake; -static FinBlock *runtime·allfin; // list of all blocks +FinBlock *runtime·allfin; // list of all blocks BitVector runtime·gcdatamask; BitVector runtime·gcbssmask; @@ -140,7 +140,7 @@ static BitVector unrollglobgcprog(byte *prog, uintptr size); void runtime·bgsweep(void); static FuncVal bgsweepv = {runtime·bgsweep}; -static struct { +struct { uint64 full; // lock-free list of full blocks uint64 empty; // lock-free list of empty blocks byte pad0[CacheLineSize]; // prevents false-sharing between full/empty and nproc/nwait @@ -1038,7 +1038,6 @@ runtime·MSpan_Sweep(MSpan *s, bool preserve) // State of background runtime·sweep. // Protected by runtime·gclock. -// Must match mgc0.go. struct { G* g; diff --git a/src/runtime/os_windows.c b/src/runtime/os_windows.c index 62d94b65a0..6546d51d33 100644 --- a/src/runtime/os_windows.c +++ b/src/runtime/os_windows.c @@ -147,7 +147,7 @@ runtime·get_random_data(byte **rnd, int32 *rnd_len) void runtime·goenvs(void) { - extern Slice syscall·envs; + extern Slice runtime·envs; uint16 *env; String *s; @@ -160,8 +160,8 @@ runtime·goenvs(void) for(p=env; *p; n++) p += runtime·findnullw(p)+1; - syscall·envs = runtime·makeStringSlice(n); - s = (String*)syscall·envs.array; + runtime·envs = runtime·makeStringSlice(n); + s = (String*)runtime·envs.array; p = env; for(i=0; im->locks++; // disable GC because it can be called from sysmon if(g->m->p == nil) acquirep(p); // temporarily borrow p for mallocs in this function - if(mtype == nil) { - Eface e; - runtime·gc_m_ptr(&e); - mtype = ((PtrType*)e.type)->elem; - } - - mp = runtime·cnew(mtype); + mp = runtime·newM(); mcommoninit(mp); // In case of cgo or Solaris, pthread_create will make us a stack. @@ -889,19 +884,12 @@ runtime·allocm(P *p) return mp; } +G *runtime·newG(void); // in proc.go + static G* allocg(void) { - G *gp; - static Type *gtype; - - if(gtype == nil) { - Eface e; - runtime·gc_g_ptr(&e); - gtype = ((PtrType*)e.type)->elem; - } - gp = runtime·cnew(gtype); - return gp; + return runtime·newG(); } static M* lockextra(bool nilokay); diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 9b95868594..4bb661b54b 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -202,6 +202,14 @@ func newP() *p { return new(p) } +func newM() *m { + return new(m) +} + +func newG() *g { + return new(g) +} + func allgadd(gp *g) { if readgstatus(gp) == _Gidle { gothrow("allgadd: bad status Gidle") diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index aa8dd8f7a0..b3503fb909 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -62,10 +62,12 @@ runtime·mchr(byte *p, byte c, byte *ep) } static int32 argc; + +#pragma dataflag NOPTR /* argv not a heap pointer */ static uint8** argv; -Slice os·Args; -Slice syscall·envs; +extern Slice runtime·argslice; +extern Slice runtime·envs; void (*runtime·sysargs)(int32, uint8**); @@ -97,8 +99,8 @@ runtime·goargs(void) if(Windows) return; - os·Args = runtime·makeStringSlice(argc); - s = (String*)os·Args.array; + runtime·argslice = runtime·makeStringSlice(argc); + s = (String*)runtime·argslice.array; for(i=0; i Date: Mon, 29 Sep 2014 21:21:36 -0700 Subject: runtime: initialize traceback variables earlier Our traceback code needs to know the PC of several special functions, including goexit, mcall, etc. Make sure that these PCs are initialized before any traceback occurs. Fixes #8766 LGTM=rsc R=golang-codereviews, rsc, khr, bradfitz CC=golang-codereviews https://golang.org/cl/145570043 --- src/runtime/proc.c | 1 + src/runtime/runtime.h | 1 + src/runtime/traceback.go | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 10 deletions(-) (limited to 'src/runtime/proc.c') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 1f0a79098b..e84dc1d048 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -131,6 +131,7 @@ runtime·schedinit(void) runtime·sched.maxmcount = 10000; + runtime·tracebackinit(); runtime·symtabinit(); runtime·stackinit(); runtime·mallocinit(); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 3a6d3e3262..aa300d7bb8 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -841,6 +841,7 @@ void runtime·mpreinit(M*); void runtime·minit(void); void runtime·unminit(void); void runtime·signalstack(byte*, int32); +void runtime·tracebackinit(void); void runtime·symtabinit(void); Func* runtime·findfunc(uintptr); int32 runtime·funcline(Func*, uintptr, String*); diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index a93c42186b..24dc3eea95 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -31,20 +31,36 @@ import "unsafe" const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386" var ( - deferprocPC = funcPC(deferproc) - goexitPC = funcPC(goexit) - jmpdeferPC = funcPC(jmpdefer) - mcallPC = funcPC(mcall) - morestackPC = funcPC(morestack) - mstartPC = funcPC(mstart) - newprocPC = funcPC(newproc) - newstackPC = funcPC(newstack) - rt0_goPC = funcPC(rt0_go) - sigpanicPC = funcPC(sigpanic) + // initialized in tracebackinit + deferprocPC uintptr + goexitPC uintptr + jmpdeferPC uintptr + mcallPC uintptr + morestackPC uintptr + mstartPC uintptr + newprocPC uintptr + rt0_goPC uintptr + sigpanicPC uintptr externalthreadhandlerp uintptr // initialized elsewhere ) +func tracebackinit() { + // Go variable initialization happens late during runtime startup. + // Instead of initializing the variables above in the declarations, + // schedinit calls this function so that the variables are + // initialized and available earlier in the startup sequence. + deferprocPC = funcPC(deferproc) + goexitPC = funcPC(goexit) + jmpdeferPC = funcPC(jmpdefer) + mcallPC = funcPC(mcall) + morestackPC = funcPC(morestack) + mstartPC = funcPC(mstart) + newprocPC = funcPC(newproc) + rt0_goPC = funcPC(rt0_go) + sigpanicPC = funcPC(sigpanic) +} + // Traceback over the deferred function calls. // Report them like calls that have been invoked but not started executing yet. func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) { @@ -81,6 +97,9 @@ func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v uns // collector (callback != nil). A little clunky to merge these, but avoids // duplicating the code and all its subtlety. func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, printall bool) int { + if goexitPC == 0 { + gothrow("gentraceback before goexitPC initialization") + } g := getg() gotraceback := gotraceback(nil) if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp. -- cgit v1.3