Move allocg and handling of allgs slice from C to Go.
Reviewed-on: https://go-review.googlesource.com/34797
From-SVN: r244036
-eac28020ee4b2532d4cd43f448fe612e84e0a108
+dfe446c5a54ca0febabb81b542cc4e634c6f5c30
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
stopTheWorld("profile")
n = 1
- for _, gp1 := range allgs() {
+ for _, gp1 := range allgs {
if isOK(gp1) {
n++
}
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
// 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,
// 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
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)
+}
var (
// emptystring string
- // allglen uintptr
+
+ allglen uintptr
+
// allm *m
- // allp [_MaxGomaxprocs + 1]*p
+
+ allp [_MaxGomaxprocs + 1]*p
+
// gomaxprocs int32
panicking uint32
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
// 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
// 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.
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()
// 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
+}
// 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)
+}
#include <unistd.h>
#include "runtime.h"
-#include "go-alloc.h"
#include "array.h"
#include "arch.h"
#include "malloc.h"
#endif
#include "runtime.h"
-#include "go-alloc.h"
#include "array.h"
#include "arch.h"
#include "malloc.h"
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"
#include <stdlib.h>
#include "runtime.h"
-#include "go-alloc.h"
#include "go-assert.h"
#include "go-type.h"
#include "unwind-pe.h"
#include "runtime.h"
-#include "go-alloc.h"
/* The code for a Go exception. */
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);
#include <stddef.h>
#include <errno.h>
#include <stdlib.h>
-#include "go-alloc.h"
#include "runtime.h"
#include "arch.h"
#include "malloc.h"
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<<PageShift);
- // NOTE(rsc,dvyukov): The original implementation of efence
- // in CL 22060046 used SysFree instead of SysFault, so that
- // the operating system would eventually give the memory
- // back to us again, so that an efence program could run
- // longer without running out of memory. Unfortunately,
- // calling SysFree here without any kind of adjustment of the
- // heap data structures means that when the memory does
- // come back to us, we have the wrong metadata for it, either in
- // the MSpan structures or in the garbage collection bitmap.
- // Using SysFault here means that the program will run out of
- // memory fairly quickly in efence mode, but at least it won't
- // have mysterious crashes due to confused memory reuse.
- // It should be possible to switch back to SysFree if we also
- // implement and then call some kind of MHeap_DeleteSpan.
- if(runtime_debug.efence)
- runtime_SysFault((void*)(s->start<<PageShift), size);
- else
- runtime_MHeap_Free(&runtime_mheap, s, 1);
- c->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)
{
// 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*
// 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});
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)
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);
// Parallel for algorithm.
#include "runtime.h"
+#include "malloc.h"
#include "arch.h"
struct ParForThread
// 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;
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;
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*);
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*);
void allgadd(G*)
__asm__(GOSYM_PREFIX "runtime.allgadd");
+void checkdead(void)
+ __asm__(GOSYM_PREFIX "runtime.checkdead");
bool runtime_isstarted;
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.
*(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
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.
newg = runtime_malg(true, false, &sp, &malsize);
spsize = (size_t)malsize;
+ newg->atomicstatus = _Gdead;
allgadd(newg);
}
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
return g->lockedm != nil && g->m->lockedg != nil;
}
-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)
{
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)
{
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
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
#include "go-assert.h"
#include <complex.h>
#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#endif
-#include "go-alloc.h"
-
#define _STRINGIFY2_(x) #x
#define _STRINGIFY_(x) _STRINGIFY2_(x)
#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
*/
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;
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*)
struct gotraceback_ret {
int32 level;
- bool crash;
+ bool all;
+ bool crash;
};
extern struct gotraceback_ret gotraceback(void)