From 243acd49980309a543857c88732b44e5413bd23b Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 3 Jan 2017 22:58:48 +0000 Subject: [PATCH] runtime: remove __go_alloc and __go_free Move allocg and handling of allgs slice from C to Go. Reviewed-on: https://go-review.googlesource.com/34797 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@244036 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/go/gofrontend/MERGE | 2 +- libgo/go/runtime/mprof.go | 4 +- libgo/go/runtime/proc.go | 205 ++++++++++++++++++++++- libgo/go/runtime/runtime2.go | 8 +- libgo/go/runtime/stubs.go | 59 +++++-- libgo/go/runtime/traceback_gccgo.go | 55 ++++++ libgo/runtime/go-libmain.c | 1 - libgo/runtime/go-main.c | 1 - libgo/runtime/go-new.c | 1 - libgo/runtime/go-reflect-call.c | 1 - libgo/runtime/go-unwind.c | 1 - libgo/runtime/heapdump.c | 4 +- libgo/runtime/malloc.goc | 105 ------------ libgo/runtime/mgc0.c | 7 +- libgo/runtime/parfor.c | 3 +- libgo/runtime/proc.c | 322 +++--------------------------------- libgo/runtime/runtime.h | 13 +- libgo/runtime/runtime_c.c | 3 +- 18 files changed, 352 insertions(+), 443 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index a48719637f4..1efd7ee659c 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -eac28020ee4b2532d4cd43f448fe612e84e0a108 +dfe446c5a54ca0febabb81b542cc4e634c6f5c30 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/libgo/go/runtime/mprof.go b/libgo/go/runtime/mprof.go index a2701e32f76..cc0a673c8b4 100644 --- a/libgo/go/runtime/mprof.go +++ b/libgo/go/runtime/mprof.go @@ -556,7 +556,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { stopTheWorld("profile") n = 1 - for _, gp1 := range allgs() { + for _, gp1 := range allgs { if isOK(gp1) { n++ } @@ -571,7 +571,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { r = r[1:] // Save other goroutines. - for _, gp1 := range allgs() { + for _, gp1 := range allgs { if isOK(gp1) { if len(r) == 0 { // Should be impossible, but better to return a diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index fa90a282866..78cc6ee7d8a 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -11,15 +11,18 @@ import ( // Functions temporarily called by C code. //go:linkname newextram runtime.newextram +//go:linkname checkdead runtime.checkdead +//go:linkname schedtrace runtime.schedtrace +//go:linkname allgadd runtime.allgadd // Functions temporarily in C that have not yet been ported. func allocm(*p, bool, *unsafe.Pointer, *uintptr) *m func malg(bool, bool, *unsafe.Pointer, *uintptr) *g -func allgadd(*g) // C functions for ucontext management. func setGContext() func makeGContext(*g, unsafe.Pointer, uintptr) +func getTraceback(me, gp *g) // main_init_done is a signal used by cgocallbackg that initialization // has been completed. It is made before _cgo_notify_runtime_init_done, @@ -27,6 +30,39 @@ func makeGContext(*g, unsafe.Pointer, uintptr) // it is closed, meaning cgocallbackg can reliably receive from it. var main_init_done chan bool +var ( + allgs []*g + allglock mutex +) + +func allgadd(gp *g) { + if readgstatus(gp) == _Gidle { + throw("allgadd: bad status Gidle") + } + + lock(&allglock) + allgs = append(allgs, gp) + allglen = uintptr(len(allgs)) + + // Grow GC rescan list if necessary. + if len(allgs) > cap(work.rescan.list) { + lock(&work.rescan.lock) + l := work.rescan.list + // Let append do the heavy lifting, but keep the + // length the same. + work.rescan.list = append(l[:cap(l)], 0)[:len(l)] + unlock(&work.rescan.lock) + } + unlock(&allglock) +} + +// All reads and writes of g's status go through readgstatus, casgstatus +// castogscanstatus, casfrom_Gscanstatus. +//go:nosplit +func readgstatus(gp *g) uint32 { + return atomic.Load(&gp.atomicstatus) +} + // If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus // and casfrom_Gscanstatus instead. // casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that @@ -328,3 +364,170 @@ func lockextra(nilokay bool) *m { func unlockextra(mp *m) { atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp))) } + +// Check for deadlock situation. +// The check is based on number of running M's, if 0 -> deadlock. +func checkdead() { + // For -buildmode=c-shared or -buildmode=c-archive it's OK if + // there are no running goroutines. The calling program is + // assumed to be running. + if islibrary || isarchive { + return + } + + // If we are dying because of a signal caught on an already idle thread, + // freezetheworld will cause all running threads to block. + // And runtime will essentially enter into deadlock state, + // except that there is a thread that will call exit soon. + if panicking > 0 { + return + } + + // -1 for sysmon + run := sched.mcount - sched.nmidle - sched.nmidlelocked - 1 + if run > 0 { + return + } + if run < 0 { + print("runtime: checkdead: nmidle=", sched.nmidle, " nmidlelocked=", sched.nmidlelocked, " mcount=", sched.mcount, "\n") + throw("checkdead: inconsistent counts") + } + + grunning := 0 + lock(&allglock) + for i := 0; i < len(allgs); i++ { + gp := allgs[i] + if isSystemGoroutine(gp) { + continue + } + s := readgstatus(gp) + switch s &^ _Gscan { + case _Gwaiting: + grunning++ + case _Grunnable, + _Grunning, + _Gsyscall: + unlock(&allglock) + print("runtime: checkdead: find g ", gp.goid, " in status ", s, "\n") + throw("checkdead: runnable g") + } + } + unlock(&allglock) + if grunning == 0 { // possible if main goroutine calls runtime·Goexit() + throw("no goroutines (main called runtime.Goexit) - deadlock!") + } + + // Maybe jump time forward for playground. + gp := timejump() + if gp != nil { + // Temporarily commented out for gccgo. + // For gccgo this code will never run anyhow. + // casgstatus(gp, _Gwaiting, _Grunnable) + // globrunqput(gp) + // _p_ := pidleget() + // if _p_ == nil { + // throw("checkdead: no p for timer") + // } + // mp := mget() + // if mp == nil { + // // There should always be a free M since + // // nothing is running. + // throw("checkdead: no m for timer") + // } + // nmp.nextp.set(_p_) + // notewakeup(&mp.park) + // return + } + + getg().m.throwing = -1 // do not dump full stacks + throw("all goroutines are asleep - deadlock!") +} + +var starttime int64 + +func schedtrace(detailed bool) { + now := nanotime() + if starttime == 0 { + starttime = now + } + + gomaxprocs := int32(GOMAXPROCS(0)) + + lock(&sched.lock) + print("SCHED ", (now-starttime)/1e6, "ms: gomaxprocs=", gomaxprocs, " idleprocs=", sched.npidle, " threads=", sched.mcount, " spinningthreads=", sched.nmspinning, " idlethreads=", sched.nmidle, " runqueue=", sched.runqsize) + if detailed { + print(" gcwaiting=", sched.gcwaiting, " nmidlelocked=", sched.nmidlelocked, " stopwait=", sched.stopwait, " sysmonwait=", sched.sysmonwait, "\n") + } + // We must be careful while reading data from P's, M's and G's. + // Even if we hold schedlock, most data can be changed concurrently. + // E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil. + for i := int32(0); i < gomaxprocs; i++ { + _p_ := allp[i] + if _p_ == nil { + continue + } + mp := _p_.m.ptr() + h := atomic.Load(&_p_.runqhead) + t := atomic.Load(&_p_.runqtail) + if detailed { + id := int32(-1) + if mp != nil { + id = mp.id + } + print(" P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gfreecnt, "\n") + } else { + // In non-detailed mode format lengths of per-P run queues as: + // [len1 len2 len3 len4] + print(" ") + if i == 0 { + print("[") + } + print(t - h) + if i == gomaxprocs-1 { + print("]\n") + } + } + } + + if !detailed { + unlock(&sched.lock) + return + } + + for mp := allm(); mp != nil; mp = mp.alllink { + _p_ := mp.p.ptr() + gp := mp.curg + lockedg := mp.lockedg + id1 := int32(-1) + if _p_ != nil { + id1 = _p_.id + } + id2 := int64(-1) + if gp != nil { + id2 = gp.goid + } + id3 := int64(-1) + if lockedg != nil { + id3 = lockedg.goid + } + print(" M", mp.id, ": p=", id1, " curg=", id2, " mallocing=", mp.mallocing, " throwing=", mp.throwing, " preemptoff=", mp.preemptoff, ""+" locks=", mp.locks, " dying=", mp.dying, " helpgc=", mp.helpgc, " spinning=", mp.spinning, " blocked=", mp.blocked, " lockedg=", id3, "\n") + } + + lock(&allglock) + for gi := 0; gi < len(allgs); gi++ { + gp := allgs[gi] + mp := gp.m + lockedm := gp.lockedm + id1 := int32(-1) + if mp != nil { + id1 = mp.id + } + id2 := int32(-1) + if lockedm != nil { + id2 = lockedm.id + } + print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n") + } + unlock(&allglock) + unlock(&sched.lock) +} diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go index 6686e1f29b3..571972c1a83 100644 --- a/libgo/go/runtime/runtime2.go +++ b/libgo/go/runtime/runtime2.go @@ -755,9 +755,13 @@ const _TracebackMaxFrames = 100 var ( // emptystring string - // allglen uintptr + + allglen uintptr + // allm *m - // allp [_MaxGomaxprocs + 1]*p + + allp [_MaxGomaxprocs + 1]*p + // gomaxprocs int32 panicking uint32 diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index c299ae0e8eb..3d184083d55 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -476,12 +476,6 @@ func UnlockOSThread() func lockOSThread() func unlockOSThread() func allm() *m -func allgs() []*g - -//go:nosplit -func readgstatus(gp *g) uint32 { - return atomic.Load(&gp.atomicstatus) -} // Temporary for gccgo until we port malloc.go func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer @@ -489,9 +483,6 @@ func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer // Temporary for gccgo until we port mheap.go func setprofilebucket(p unsafe.Pointer, b *bucket) -// Currently in proc.c. -func tracebackothers(*g) - // Temporary for gccgo until we port mgc.go. func setgcpercent(int32) int32 @@ -530,9 +521,7 @@ func getZerobase() *uintptr { // Temporary for gccgo until we port proc.go. func sigprof() func mcount() int32 -func gcount() int32 func goexit1() -func schedtrace(bool) func freezetheworld() // Get signal trampoline, written in C. @@ -562,6 +551,30 @@ func getCgoHasExtraM() *bool { return &cgoHasExtraM } +// Temporary for gccgo until we port proc.go. +//go:linkname getAllP runtime.getAllP +func getAllP() **p { + return &allp[0] +} + +// Temporary for gccgo until we port proc.go. +//go:linkname allocg runtime.allocg +func allocg() *g { + return new(g) +} + +// Temporary for gccgo until we port the garbage collector. +//go:linkname getallglen runtime.getallglen +func getallglen() uintptr { + return allglen +} + +// Temporary for gccgo until we port the garbage collector. +//go:linkname getallg runtime.getallg +func getallg(i int) *g { + return allgs[i] +} + // Throw and rethrow an exception. func throwException() func rethrowException() @@ -579,3 +592,27 @@ func getPanicking() uint32 { // Temporary for gccgo until we port mcache.go. func allocmcache() *mcache + +// Temporary for gccgo until we port mgc.go. +// This is just so that allgadd will compile. +var work struct { + rescan struct { + lock mutex + list []guintptr + } +} + +// gcount is temporary for gccgo until more of proc.go is ported. +// This is a copy of the C function we used to use. +func gcount() int32 { + n := int32(0) + lock(&allglock) + for _, gp := range allgs { + s := readgstatus(gp) + if s == _Grunnable || s == _Grunning || s == _Gsyscall || s == _Gwaiting { + n++ + } + } + unlock(&allglock) + return n +} diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go index f61f9a0f2dd..611aba91a4d 100644 --- a/libgo/go/runtime/traceback_gccgo.go +++ b/libgo/go/runtime/traceback_gccgo.go @@ -171,3 +171,58 @@ func isSystemGoroutine(gp *g) bool { // FIXME. return false } + +func tracebackothers(me *g) { + var tb tracebackg + tb.gp = me + + level, _, _ := gotraceback() + + // Show the current goroutine first, if we haven't already. + g := getg() + gp := g.m.curg + if gp != nil && gp != me { + print("\n") + goroutineheader(gp) + gp.traceback = &tb + getTraceback(me, gp) + printtrace(tb.locbuf[:tb.c], nil) + printcreatedby(gp) + } + + lock(&allglock) + for _, gp := range allgs { + if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 { + continue + } + print("\n") + goroutineheader(gp) + + // gccgo's only mechanism for doing a stack trace is + // _Unwind_Backtrace. And that only works for the + // current thread, not for other random goroutines. + // So we need to switch context to the goroutine, get + // the backtrace, and then switch back. + // + // This means that if g is running or in a syscall, we + // can't reliably print a stack trace. FIXME. + + // Note: gp.m == g.m occurs when tracebackothers is + // called from a signal handler initiated during a + // systemstack call. The original G is still in the + // running state, and we want to print its stack. + if gp.m != g.m && readgstatus(gp)&^_Gscan == _Grunning { + print("\tgoroutine running on other thread; stack unavailable\n") + printcreatedby(gp) + } else if readgstatus(gp)&^_Gscan == _Gsyscall { + print("\tgoroutine in C code; stack unavailable\n") + printcreatedby(gp) + } else { + gp.traceback = &tb + getTraceback(me, gp) + printtrace(tb.locbuf[:tb.c], nil) + printcreatedby(gp) + } + } + unlock(&allglock) +} diff --git a/libgo/runtime/go-libmain.c b/libgo/runtime/go-libmain.c index a404eb9b681..8e07e90178c 100644 --- a/libgo/runtime/go-libmain.c +++ b/libgo/runtime/go-libmain.c @@ -13,7 +13,6 @@ #include #include "runtime.h" -#include "go-alloc.h" #include "array.h" #include "arch.h" #include "malloc.h" diff --git a/libgo/runtime/go-main.c b/libgo/runtime/go-main.c index b8c9af1e79b..dba80852603 100644 --- a/libgo/runtime/go-main.c +++ b/libgo/runtime/go-main.c @@ -15,7 +15,6 @@ #endif #include "runtime.h" -#include "go-alloc.h" #include "array.h" #include "arch.h" #include "malloc.h" diff --git a/libgo/runtime/go-new.c b/libgo/runtime/go-new.c index 01bc2af3121..da44074a5d5 100644 --- a/libgo/runtime/go-new.c +++ b/libgo/runtime/go-new.c @@ -4,7 +4,6 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ -#include "go-alloc.h" #include "runtime.h" #include "arch.h" #include "malloc.h" diff --git a/libgo/runtime/go-reflect-call.c b/libgo/runtime/go-reflect-call.c index dc2f60575ec..6a9a7f35a1a 100644 --- a/libgo/runtime/go-reflect-call.c +++ b/libgo/runtime/go-reflect-call.c @@ -9,7 +9,6 @@ #include #include "runtime.h" -#include "go-alloc.h" #include "go-assert.h" #include "go-type.h" diff --git a/libgo/runtime/go-unwind.c b/libgo/runtime/go-unwind.c index 9e85b4b8147..4c9fb49c994 100644 --- a/libgo/runtime/go-unwind.c +++ b/libgo/runtime/go-unwind.c @@ -14,7 +14,6 @@ #include "unwind-pe.h" #include "runtime.h" -#include "go-alloc.h" /* The code for a Go exception. */ diff --git a/libgo/runtime/heapdump.c b/libgo/runtime/heapdump.c index 80d2b7bf850..c050541db9d 100644 --- a/libgo/runtime/heapdump.c +++ b/libgo/runtime/heapdump.c @@ -311,8 +311,8 @@ dumpgs(void) uint32 i; // goroutines & stacks - for(i = 0; i < runtime_allglen; i++) { - gp = runtime_allg[i]; + for(i = 0; i < runtime_getallglen(); i++) { + gp = runtime_getallg(i); switch(gp->atomicstatus){ default: runtime_printf("unexpected G.status %d\n", gp->atomicstatus); diff --git a/libgo/runtime/malloc.goc b/libgo/runtime/malloc.goc index 5cbdc4632fb..1e6704c6290 100644 --- a/libgo/runtime/malloc.goc +++ b/libgo/runtime/malloc.goc @@ -10,7 +10,6 @@ package runtime #include #include #include -#include "go-alloc.h" #include "runtime.h" #include "arch.h" #include "malloc.h" @@ -308,107 +307,6 @@ runtime_profilealloc(void *v, uintptr size) runtime_MProf_Malloc(v, size); } -void* -__go_alloc(uintptr size) -{ - return runtime_mallocgc(size, 0, FlagNoInvokeGC); -} - -// Free the object whose base pointer is v. -void -__go_free(void *v) -{ - M *m; - int32 sizeclass; - MSpan *s; - MCache *c; - uintptr size; - - if(v == nil) - return; - - // If you change this also change mgc0.c:/^sweep, - // which has a copy of the guts of free. - - m = runtime_m(); - if(m->mallocing) - runtime_throw("malloc/free - deadlock"); - m->mallocing = 1; - - if(!runtime_mlookup(v, nil, nil, &s)) { - runtime_printf("free %p: not an allocated block\n", v); - runtime_throw("free runtime_mlookup"); - } - size = s->elemsize; - sizeclass = s->sizeclass; - // Objects that are smaller than TinySize can be allocated using tiny alloc, - // if then such object is combined with an object with finalizer, we will crash. - if(size < TinySize) - runtime_throw("freeing too small block"); - - if(runtime_debug.allocfreetrace) - runtime_tracefree(v, size); - - // Ensure that the span is swept. - // If we free into an unswept span, we will corrupt GC bitmaps. - runtime_MSpan_EnsureSwept(s); - - if(s->specials != nil) - runtime_freeallspecials(s, v, size); - - c = m->mcache; - if(sizeclass == 0) { - // Large object. - s->needzero = 1; - // Must mark v freed before calling unmarkspan and MHeap_Free: - // they might coalesce v into other spans and change the bitmap further. - runtime_markfreed(v); - runtime_unmarkspan(v, 1<start<local_nlargefree++; - c->local_largefree += size; - } else { - // Small object. - if(size > 2*sizeof(uintptr)) - ((uintptr*)v)[1] = (uintptr)0xfeedfeedfeedfeedll; // mark as "needs to be zeroed" - else if(size > sizeof(uintptr)) - ((uintptr*)v)[1] = 0; - // Must mark v freed before calling MCache_Free: - // it might coalesce v and other blocks into a bigger span - // and change the bitmap further. - c->local_nsmallfree[sizeclass]++; - c->local_cachealloc -= size; - if(c->alloc[sizeclass] == s) { - // We own the span, so we can just add v to the freelist - runtime_markfreed(v); - ((MLink*)v)->next = s->freelist; - s->freelist = v; - s->ref--; - } else { - // Someone else owns this span. Add to free queue. - runtime_MCache_Free(c, v, sizeclass, size); - } - } - m->mallocing = 0; -} - int32 runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp) { @@ -628,9 +526,6 @@ runtime_mallocinit(void) // Initialize the rest of the allocator. runtime_MHeap_Init(&runtime_mheap); runtime_m()->mcache = runtime_allocmcache(); - - // See if it works. - runtime_free(runtime_malloc(TinySize)); } void* diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index aa8404e2167..5d6275a6357 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -1279,7 +1279,6 @@ markroot(ParFor *desc, uint32 i) // For gccgo we use this for all the other global roots. enqueue1(&wbuf, (Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0}); - enqueue1(&wbuf, (Obj){(byte*)&runtime_allg, sizeof runtime_allg, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0}); enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0}); @@ -1334,9 +1333,9 @@ markroot(ParFor *desc, uint32 i) default: // the rest is scanning goroutine stacks - if(i - RootCount >= runtime_allglen) + if(i - RootCount >= runtime_getallglen()) runtime_throw("markroot: bad index"); - gp = runtime_allg[i - RootCount]; + gp = runtime_getallg(i - RootCount); // remember when we've first observed the G blocked // needed only to output in traceback if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince == 0) @@ -2243,7 +2242,7 @@ gc(struct gc_args *args) work.nwait = 0; work.ndone = 0; work.nproc = runtime_gcprocs(); - runtime_parforsetup(work.markfor, work.nproc, RootCount + runtime_allglen, false, &markroot_funcval); + runtime_parforsetup(work.markfor, work.nproc, RootCount + runtime_getallglen(), false, &markroot_funcval); if(work.nproc > 1) { runtime_noteclear(&work.alldone); runtime_helpgc(work.nproc); diff --git a/libgo/runtime/parfor.c b/libgo/runtime/parfor.c index ede921b9aaa..c2f10b9a987 100644 --- a/libgo/runtime/parfor.c +++ b/libgo/runtime/parfor.c @@ -5,6 +5,7 @@ // Parallel for algorithm. #include "runtime.h" +#include "malloc.h" #include "arch.h" struct ParForThread @@ -27,7 +28,7 @@ runtime_parforalloc(uint32 nthrmax) // The ParFor object is followed by CacheLineSize padding // and then nthrmax ParForThread. - desc = (ParFor*)runtime_malloc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread)); + desc = (ParFor*)runtime_mallocgc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread), 0, FlagNoInvokeGC); desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize); desc->nthrmax = nthrmax; return desc; diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 586a6323e6d..8a7a2d76ae6 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -361,6 +361,10 @@ enum extern Sched* runtime_getsched() __asm__ (GOSYM_PREFIX "runtime.getsched"); extern bool* runtime_getCgoHasExtraM() __asm__ (GOSYM_PREFIX "runtime.getCgoHasExtraM"); +extern P** runtime_getAllP() + __asm__ (GOSYM_PREFIX "runtime.getAllP"); +extern G* allocg(void) + __asm__ (GOSYM_PREFIX "runtime.allocg"); Sched* runtime_sched; int32 runtime_gomaxprocs; @@ -374,11 +378,6 @@ int32 runtime_ncpu; bool runtime_precisestack; static int32 newprocs; -static Lock allglock; // the following vars are protected by this lock or by stoptheworld -G** runtime_allg; -uintptr runtime_allglen; -static uintptr allgcap; - bool runtime_isarchive; void* runtime_mstart(void*); @@ -403,7 +402,6 @@ static void startlockedm(G*); static void sysmon(void); static uint32 retake(int64); static void incidlelocked(int32); -static void checkdead(void); static void exitsyscall0(G*); static void park0(G*); static void goexit0(G*); @@ -421,6 +419,8 @@ static bool exitsyscallfast(void); void allgadd(G*) __asm__(GOSYM_PREFIX "runtime.allgadd"); +void checkdead(void) + __asm__(GOSYM_PREFIX "runtime.checkdead"); bool runtime_isstarted; @@ -482,7 +482,7 @@ runtime_schedinit(void) n = _MaxGomaxprocs; procs = n; } - runtime_allp = runtime_malloc((_MaxGomaxprocs+1)*sizeof(runtime_allp[0])); + runtime_allp = runtime_getAllP(); procresize(procs); // Can not enable GC until all roots are registered. @@ -586,85 +586,25 @@ runtime_main(void* dummy __attribute__((unused))) *(int32*)0 = 0; } -void -runtime_tracebackothers(G * volatile me) -{ - G * volatile gp; - Traceback tb; - int32 traceback; - Slice slice; - volatile uintptr i; - - tb.gp = me; - traceback = runtime_gotraceback(nil); - - // Show the current goroutine first, if we haven't already. - if((gp = g->m->curg) != nil && gp != me) { - runtime_printf("\n"); - runtime_goroutineheader(gp); - gp->traceback = &tb; - -#ifdef USING_SPLIT_STACK - __splitstack_getcontext(&me->stackcontext[0]); -#endif - getcontext(ucontext_arg(&me->context[0])); - - if(gp->traceback != nil) { - runtime_gogo(gp); - } - - slice.__values = &tb.locbuf[0]; - slice.__count = tb.c; - slice.__capacity = tb.c; - runtime_printtrace(slice, nil); - runtime_printcreatedby(gp); - } - - runtime_lock(&allglock); - for(i = 0; i < runtime_allglen; i++) { - gp = runtime_allg[i]; - if(gp == me || gp == g->m->curg || gp->atomicstatus == _Gdead) - continue; - if(gp->issystem && traceback < 2) - continue; - runtime_printf("\n"); - runtime_goroutineheader(gp); - - // Our only mechanism for doing a stack trace is - // _Unwind_Backtrace. And that only works for the - // current thread, not for other random goroutines. - // So we need to switch context to the goroutine, get - // the backtrace, and then switch back. - - // This means that if g is running or in a syscall, we - // can't reliably print a stack trace. FIXME. - - if(gp->atomicstatus == _Grunning) { - runtime_printf("\tgoroutine running on other thread; stack unavailable\n"); - runtime_printcreatedby(gp); - } else if(gp->atomicstatus == _Gsyscall) { - runtime_printf("\tgoroutine in C code; stack unavailable\n"); - runtime_printcreatedby(gp); - } else { - gp->traceback = &tb; +void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback"); +// getTraceback stores a traceback of gp in the g's traceback field +// and then returns to me. We expect that gp's traceback is not nil. +// It works by saving me's current context, and checking gp's traceback field. +// If gp's traceback field is not nil, it starts running gp. +// In places where we call getcontext, we check the traceback field. +// If it is not nil, we collect a traceback, and then return to the +// goroutine stored in the traceback field, which is me. +void getTraceback(G* me, G* gp) +{ #ifdef USING_SPLIT_STACK - __splitstack_getcontext(&me->stackcontext[0]); + __splitstack_getcontext(&me->stackcontext[0]); #endif - getcontext(ucontext_arg(&me->context[0])); + getcontext(ucontext_arg(&me->stackcontext[0])); - if(gp->traceback != nil) { - runtime_gogo(gp); - } - - slice.__values = &tb.locbuf[0]; - slice.__count = tb.c; - slice.__capacity = tb.c; - runtime_printtrace(slice, nil); - runtime_printcreatedby(gp); - } + if (gp->traceback != nil) { + runtime_gogo(gp); } - runtime_unlock(&allglock); } static void @@ -1067,22 +1007,6 @@ runtime_allocm(P *p, bool allocatestack, byte** ret_g0_stack, uintptr* ret_g0_st return mp; } -static G* -allocg(void) -{ - G *gp; - // static Type *gtype; - - // if(gtype == nil) { - // Eface e; - // runtime_gc_g_ptr(&e); - // gtype = ((PtrType*)e.__type_descriptor)->__element_type; - // } - // gp = runtime_cnew(gtype); - gp = runtime_malloc(sizeof(G)); - return gp; -} - void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext"); // setGContext sets up a new goroutine context for the current g. @@ -2129,6 +2053,7 @@ __go_go(void (*fn)(void*), void* arg) newg = runtime_malg(true, false, &sp, &malsize); spsize = (size_t)malsize; + newg->atomicstatus = _Gdead; allgadd(newg); } @@ -2152,31 +2077,6 @@ __go_go(void (*fn)(void*), void* arg) return newg; } -void -allgadd(G *gp) -{ - G **new; - uintptr cap; - - runtime_lock(&allglock); - if(runtime_allglen >= allgcap) { - cap = 4096/sizeof(new[0]); - if(cap < 2*allgcap) - cap = 2*allgcap; - new = runtime_malloc(cap*sizeof(new[0])); - if(new == nil) - runtime_throw("runtime: cannot allocate memory"); - if(runtime_allg != nil) { - runtime_memmove(new, runtime_allg, runtime_allglen*sizeof(new[0])); - runtime_free(runtime_allg); - } - runtime_allg = new; - allgcap = cap; - } - runtime_allg[runtime_allglen++] = gp; - runtime_unlock(&allglock); -} - // Put on gfree list. // If local list is too long, transfer a batch to the global list. static void @@ -2352,29 +2252,6 @@ runtime_lockedOSThread(void) } int32 -runtime_gcount(void) -{ - G *gp; - int32 n, s; - uintptr i; - - n = 0; - runtime_lock(&allglock); - // TODO(dvyukov): runtime.NumGoroutine() is O(N). - // We do not want to increment/decrement centralized counter in newproc/goexit, - // just to make runtime.NumGoroutine() faster. - // Compromise solution is to introduce per-P counters of active goroutines. - for(i = 0; i < runtime_allglen; i++) { - gp = runtime_allg[i]; - s = gp->atomicstatus; - if(s == _Grunnable || s == _Grunning || s == _Gsyscall || s == _Gwaiting) - n++; - } - runtime_unlock(&allglock); - return n; -} - -int32 runtime_mcount(void) { return runtime_sched->mcount; @@ -2638,59 +2515,6 @@ incidlelocked(int32 v) runtime_unlock(&runtime_sched->lock); } -// Check for deadlock situation. -// The check is based on number of running M's, if 0 -> deadlock. -static void -checkdead(void) -{ - G *gp; - int32 run, grunning, s; - uintptr i; - - // For -buildmode=c-shared or -buildmode=c-archive it's OK if - // there are no running goroutines. The calling program is - // assumed to be running. - if(runtime_isarchive) { - return; - } - - // -1 for sysmon - run = runtime_sched->mcount - runtime_sched->nmidle - runtime_sched->nmidlelocked - 1; - if(run > 0) - return; - // If we are dying because of a signal caught on an already idle thread, - // freezetheworld will cause all running threads to block. - // And runtime will essentially enter into deadlock state, - // except that there is a thread that will call runtime_exit soon. - if(runtime_panicking() > 0) - return; - if(run < 0) { - runtime_printf("runtime: checkdead: nmidle=%d nmidlelocked=%d mcount=%d\n", - runtime_sched->nmidle, runtime_sched->nmidlelocked, runtime_sched->mcount); - runtime_throw("checkdead: inconsistent counts"); - } - grunning = 0; - runtime_lock(&allglock); - for(i = 0; i < runtime_allglen; i++) { - gp = runtime_allg[i]; - if(gp->isbackground) - continue; - s = gp->atomicstatus; - if(s == _Gwaiting) - grunning++; - else if(s == _Grunnable || s == _Grunning || s == _Gsyscall) { - runtime_unlock(&allglock); - runtime_printf("runtime: checkdead: find g %D in status %d\n", gp->goid, s); - runtime_throw("checkdead: runnable g"); - } - } - runtime_unlock(&allglock); - if(grunning == 0) // possible if main goroutine calls runtime_Goexit() - runtime_throw("no goroutines (main called runtime.Goexit) - deadlock!"); - g->m->throwing = -1; // do not dump full stacks - runtime_throw("all goroutines are asleep - deadlock!"); -} - static void sysmon(void) { @@ -2832,94 +2656,6 @@ preemptall(void) return false; } -void -runtime_schedtrace(bool detailed) -{ - static int64 starttime; - int64 now; - int64 id1, id2, id3; - int32 i, t, h; - uintptr gi; - const char *fmt; - M *mp, *lockedm; - G *gp, *lockedg; - P *p; - - now = runtime_nanotime(); - if(starttime == 0) - starttime = now; - - runtime_lock(&runtime_sched->lock); - runtime_printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d idlethreads=%d runqueue=%d", - (now-starttime)/1000000, runtime_gomaxprocs, runtime_sched->npidle, runtime_sched->mcount, - runtime_sched->nmidle, runtime_sched->runqsize); - if(detailed) { - runtime_printf(" gcwaiting=%d nmidlelocked=%d nmspinning=%d stopwait=%d sysmonwait=%d\n", - runtime_sched->gcwaiting, runtime_sched->nmidlelocked, runtime_sched->nmspinning, - runtime_sched->stopwait, runtime_sched->sysmonwait); - } - // We must be careful while reading data from P's, M's and G's. - // Even if we hold schedlock, most data can be changed concurrently. - // E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil. - for(i = 0; i < runtime_gomaxprocs; i++) { - p = runtime_allp[i]; - if(p == nil) - continue; - mp = (M*)p->m; - h = runtime_atomicload(&p->runqhead); - t = runtime_atomicload(&p->runqtail); - if(detailed) - runtime_printf(" P%d: status=%d schedtick=%d syscalltick=%d m=%d runqsize=%d gfreecnt=%d\n", - i, p->status, p->schedtick, p->syscalltick, mp ? mp->id : -1, t-h, p->gfreecnt); - else { - // In non-detailed mode format lengths of per-P run queues as: - // [len1 len2 len3 len4] - fmt = " %d"; - if(runtime_gomaxprocs == 1) - fmt = " [%d]\n"; - else if(i == 0) - fmt = " [%d"; - else if(i == runtime_gomaxprocs-1) - fmt = " %d]\n"; - runtime_printf(fmt, t-h); - } - } - if(!detailed) { - runtime_unlock(&runtime_sched->lock); - return; - } - for(mp = runtime_allm; mp; mp = mp->alllink) { - p = (P*)mp->p; - gp = mp->curg; - lockedg = mp->lockedg; - id1 = -1; - if(p) - id1 = p->id; - id2 = -1; - if(gp) - id2 = gp->goid; - id3 = -1; - if(lockedg) - id3 = lockedg->goid; - runtime_printf(" M%d: p=%D curg=%D mallocing=%d throwing=%d gcing=%d" - " locks=%d dying=%d helpgc=%d spinning=%d blocked=%d lockedg=%D\n", - mp->id, id1, id2, - mp->mallocing, mp->throwing, mp->gcing, mp->locks, mp->dying, mp->helpgc, - mp->spinning, mp->blocked, id3); - } - runtime_lock(&allglock); - for(gi = 0; gi < runtime_allglen; gi++) { - gp = runtime_allg[gi]; - mp = gp->m; - lockedm = gp->lockedm; - runtime_printf(" G%D: status=%d(%S) m=%d lockedm=%d\n", - gp->goid, gp->atomicstatus, gp->waitreason, mp ? mp->id : -1, - lockedm ? lockedm->id : -1); - } - runtime_unlock(&allglock); - runtime_unlock(&runtime_sched->lock); -} - // Put mp on midle list. // Sched must be locked. static void @@ -3357,20 +3093,6 @@ runtime_go_allm() return &runtime_allm; } -extern Slice runtime_go_allgs(void) - __asm__ (GOSYM_PREFIX "runtime.allgs"); - -Slice -runtime_go_allgs() -{ - Slice s; - - s.__values = runtime_allg; - s.__count = runtime_allglen; - s.__capacity = allgcap; - return s; -} - intgo NumCPU(void) __asm__ (GOSYM_PREFIX "runtime.NumCPU"); intgo diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 54bdcf8ce72..c8f490f20de 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -7,6 +7,7 @@ #include "go-assert.h" #include #include +#include #include #include #include @@ -22,8 +23,6 @@ #include #endif -#include "go-alloc.h" - #define _STRINGIFY2_(x) #x #define _STRINGIFY_(x) _STRINGIFY2_(x) #define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__) @@ -233,8 +232,10 @@ enum */ extern uintptr* runtime_getZerobase(void) __asm__(GOSYM_PREFIX "runtime.getZerobase"); -extern G** runtime_allg; -extern uintptr runtime_allglen; +extern G* runtime_getallg(intgo) + __asm__(GOSYM_PREFIX "runtime.getallg"); +extern uintptr runtime_getallglen(void) + __asm__(GOSYM_PREFIX "runtime.getallglen"); extern G* runtime_lastg; extern M* runtime_allm; extern P** runtime_allp; @@ -309,13 +310,9 @@ MCache* runtime_allocmcache(void) void runtime_freemcache(MCache*); void runtime_mallocinit(void); void runtime_mprofinit(void); -#define runtime_malloc(s) __go_alloc(s) -#define runtime_free(p) __go_free(p) #define runtime_getcallersp(p) __builtin_frame_address(0) int32 runtime_mcount(void) __asm__ (GOSYM_PREFIX "runtime.mcount"); -int32 runtime_gcount(void) - __asm__ (GOSYM_PREFIX "runtime.gcount"); void runtime_mcall(void(*)(G*)); uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1"); int32 runtime_timediv(int64, int32, int32*) diff --git a/libgo/runtime/runtime_c.c b/libgo/runtime/runtime_c.c index 464531263f5..67d5ee2a424 100644 --- a/libgo/runtime/runtime_c.c +++ b/libgo/runtime/runtime_c.c @@ -25,7 +25,8 @@ extern volatile intgo runtime_MemProfileRate struct gotraceback_ret { int32 level; - bool crash; + bool all; + bool crash; }; extern struct gotraceback_ret gotraceback(void) -- 2.11.4.GIT