summaryrefslogtreecommitdiff
path: root/src/runtime/panic.c
blob: 46683b2b0c705befebed3778b0e281f8ffc7b49d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright 2012 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.

#include "runtime.h"
#include "arch_GOARCH.h"
#include "stack.h"
#include "malloc.h"
#include "textflag.h"

// Code related to defer, panic and recover.

// TODO: remove once code is moved to Go
extern Defer* runtime·newdefer(int32 siz);
extern runtime·freedefer(Defer *d);

uint32 runtime·panicking;
static Mutex paniclk;

void
runtime·deferproc_m(void)
{
	int32 siz;
	FuncVal *fn;
	uintptr argp;
	uintptr callerpc;
	Defer *d;

	siz = g->m->scalararg[0];
	fn = g->m->ptrarg[0];
	argp = g->m->scalararg[1];
	callerpc = g->m->scalararg[2];
	g->m->ptrarg[0] = nil;
	g->m->scalararg[1] = 0;

	d = runtime·newdefer(siz);
	if(d->panic != nil)
		runtime·throw("deferproc: d->panic != nil after newdefer");
	d->fn = fn;
	d->pc = callerpc;
	d->argp = argp;
	runtime·memmove(d+1, (void*)argp, siz);
}

// Unwind the stack after a deferred function calls recover
// after a panic.  Then arrange to continue running as though
// the caller of the deferred function returned normally.
void
runtime·recovery_m(G *gp)
{
	void *argp;
	uintptr pc;
	
	// Info about defer passed in G struct.
	argp = (void*)gp->sigcode0;
	pc = (uintptr)gp->sigcode1;

	// d's arguments need to be in the stack.
	if(argp != nil && ((uintptr)argp < gp->stack.lo || gp->stack.hi < (uintptr)argp)) {
		runtime·printf("recover: %p not in [%p, %p]\n", argp, gp->stack.lo, gp->stack.hi);
		runtime·throw("bad recovery");
	}

	// Make the deferproc for this d return again,
	// this time returning 1.  The calling function will
	// jump to the standard return epilogue.
	// The -2*sizeof(uintptr) makes up for the
	// two extra words that are on the stack at
	// each call to deferproc.
	// (The pc we're returning to does pop pop
	// before it tests the return value.)
	// On the arm there are 2 saved LRs mixed in too.
	if(thechar == '5' || thechar == '9')
		gp->sched.sp = (uintptr)argp - 4*sizeof(uintptr);
	else
		gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
	gp->sched.pc = pc;
	gp->sched.lr = 0;
	gp->sched.ret = 1;
	runtime·gogo(&gp->sched);
}

void
runtime·startpanic_m(void)
{
	if(runtime·mheap.cachealloc.size == 0) { // very early
		runtime·printf("runtime: panic before malloc heap initialized\n");
		g->m->mallocing = 1; // tell rest of panic not to try to malloc
	} else if(g->m->mcache == nil) // can happen if called from signal handler or throw
		g->m->mcache = runtime·allocmcache();
	switch(g->m->dying) {
	case 0:
		g->m->dying = 1;
		if(g != nil) {
			g->writebuf.array = nil;
			g->writebuf.len = 0;
			g->writebuf.cap = 0;
		}
		runtime·xadd(&runtime·panicking, 1);
		runtime·lock(&paniclk);
		if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
			runtime·schedtrace(true);
		runtime·freezetheworld();
		return;
	case 1:
		// Something failed while panicing, probably the print of the
		// argument to panic().  Just print a stack trace and exit.
		g->m->dying = 2;
		runtime·printf("panic during panic\n");
		runtime·dopanic(0);
		runtime·exit(3);
	case 2:
		// This is a genuine bug in the runtime, we couldn't even
		// print the stack trace successfully.
		g->m->dying = 3;
		runtime·printf("stack trace unavailable\n");
		runtime·exit(4);
	default:
		// Can't even print!  Just exit.
		runtime·exit(5);
	}
}

void
runtime·dopanic_m(void)
{
	G *gp;
	uintptr sp, pc;
	static bool didothers;
	bool crash;
	int32 t;

	gp = g->m->ptrarg[0];
	g->m->ptrarg[0] = nil;
	pc = g->m->scalararg[0];
	sp = g->m->scalararg[1];
	g->m->scalararg[1] = 0;
	if(gp->sig != 0)
		runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
			gp->sig, gp->sigcode0, gp->sigcode1, gp->sigpc);

	if((t = runtime·gotraceback(&crash)) > 0){
		if(gp != gp->m->g0) {
			runtime·printf("\n");
			runtime·goroutineheader(gp);
			runtime·traceback(pc, sp, 0, gp);
		} else if(t >= 2 || g->m->throwing > 0) {
			runtime·printf("\nruntime stack:\n");
			runtime·traceback(pc, sp, 0, gp);
		}
		if(!didothers) {
			didothers = true;
			runtime·tracebackothers(gp);
		}
	}
	runtime·unlock(&paniclk);
	if(runtime·xadd(&runtime·panicking, -1) != 0) {
		// Some other m is panicking too.
		// Let it print what it needs to print.
		// Wait forever without chewing up cpu.
		// It will exit when it's done.
		static Mutex deadlock;
		runtime·lock(&deadlock);
		runtime·lock(&deadlock);
	}
	
	if(crash)
		runtime·crash();

	runtime·exit(2);
}

#pragma textflag NOSPLIT
bool
runtime·canpanic(G *gp)
{
	M *m;
	uint32 status;

	// Note that g is m->gsignal, different from gp.
	// Note also that g->m can change at preemption, so m can go stale
	// if this function ever makes a function call.
	m = g->m;

	// Is it okay for gp to panic instead of crashing the program?
	// Yes, as long as it is running Go code, not runtime code,
	// and not stuck in a system call.
	if(gp == nil || gp != m->curg)
		return false;
	if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
		return false;
	status = runtime·readgstatus(gp);
	if((status&~Gscan) != Grunning || gp->syscallsp != 0)
		return false;
#ifdef GOOS_windows
	if(m->libcallsp != 0)
		return false;
#endif
	return true;
}