Skip to content

Commit

Permalink
runtime: traceback running goroutines
Browse files Browse the repository at this point in the history
Introduce freezetheworld function that is a best-effort attempt to stop any concurrently running goroutines. Call it during crash.
Fixes #5873.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/12054044
  • Loading branch information
dvyukov committed Aug 9, 2013
1 parent e0405b7 commit 01f1e3d
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/pkg/runtime/panic.c
Expand Up @@ -419,6 +419,7 @@ runtime·startpanic(void)
g->writebuf = nil;
runtime·xadd(&runtime·panicking, 1);
runtime·lock(&paniclk);
runtime·freezetheworld();
}

void
Expand Down
54 changes: 47 additions & 7 deletions src/pkg/runtime/proc.c
Expand Up @@ -107,8 +107,8 @@ static G* globrunqget(P*, int32);
static P* pidleget(void);
static void pidleput(P*);
static void injectglist(G*);
static void preemptall(void);
static void preemptone(P*);
static bool preemptall(void);
static bool preemptone(P*);
static bool exitsyscallfast(void);

// The bootstrap sequence is:
Expand Down Expand Up @@ -374,6 +374,34 @@ runtime·helpgc(int32 nproc)
runtime·unlock(&runtime·sched);
}

// Similar to stoptheworld but best-effort and can be called several times.
// There is no reverse operation, used during crashing.
// This function must not lock any mutexes.
void
runtime·freezetheworld(void)
{
int32 i;

if(runtime·gomaxprocs == 1)
return;
// stopwait and preemption requests can be lost
// due to races with concurrently executing threads,
// so try several times
for(i = 0; i < 5; i++) {
// this should tell the scheduler to not start any new goroutines
runtime·sched.stopwait = 0x7fffffff;
runtime·atomicstore((uint32*)&runtime·gcwaiting, 1);
// this should stop running goroutines
if(!preemptall())
break; // no running goroutines
runtime·usleep(1000);
}
// to be sure
runtime·usleep(1000);
preemptall();
runtime·usleep(1000);
}

void
runtime·stoptheworld(void)
{
Expand Down Expand Up @@ -1518,6 +1546,12 @@ exitsyscallfast(void)
{
P *p;

// Freezetheworld sets stopwait but does not retake P's.
if(runtime·sched.stopwait) {
m->p = nil;
return false;
}

// Try to re-acquire the last P.
if(m->p && m->p->status == Psyscall && runtime·cas(&m->p->status, Psyscall, Prunning)) {
// There's a cpu for us, so we can run.
Expand Down Expand Up @@ -2243,18 +2277,22 @@ retake(int64 now)
// This function is purely best-effort. It can fail to inform a goroutine if a
// processor just started running it.
// No locks need to be held.
static void
// Returns true if preemption request was issued to at least one goroutine.
static bool
preemptall(void)
{
P *p;
int32 i;
bool res;

res = false;
for(i = 0; i < runtime·gomaxprocs; i++) {
p = runtime·allp[i];
if(p == nil || p->status != Prunning)
continue;
preemptone(p);
res |= preemptone(p);
}
return res;
}

// Tell the goroutine running on processor P to stop.
Expand All @@ -2263,20 +2301,22 @@ preemptall(void)
// correct goroutine, that goroutine might ignore the request if it is
// simultaneously executing runtime·newstack.
// No lock needs to be held.
static void
// Returns true if preemption request was issued.
static bool
preemptone(P *p)
{
M *mp;
G *gp;

mp = p->m;
if(mp == nil || mp == m)
return;
return false;
gp = mp->curg;
if(gp == nil || gp == mp->g0)
return;
return false;
gp->preempt = true;
gp->stackguard0 = StackPreempt;
return true;
}

// Put mp on midle list.
Expand Down
1 change: 1 addition & 0 deletions src/pkg/runtime/runtime.h
Expand Up @@ -837,6 +837,7 @@ int32 runtime·callers(int32, uintptr*, int32);
int64 runtime·nanotime(void);
void runtime·dopanic(int32);
void runtime·startpanic(void);
void runtime·freezetheworld(void);
void runtime·unwindstack(G*, byte*);
void runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp);
void runtime·resetcpuprofiler(int32);
Expand Down

0 comments on commit 01f1e3d

Please sign in to comment.