aboutsummaryrefslogtreecommitdiff
path: root/libgo/runtime/proc.c
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2018-12-05 23:09:51 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-12-05 23:09:51 +0000
commitc43137e800bb9ca2ecda0a6b6189e0eb5c22f0d7 (patch)
treedf5d750d82dff84b98ec03163cc8c2b2552a559a /libgo/runtime/proc.c
parente4a9a572770b48375561c7ca424eb94eb45a9fcb (diff)
downloadgcc-c43137e800bb9ca2ecda0a6b6189e0eb5c22f0d7.zip
gcc-c43137e800bb9ca2ecda0a6b6189e0eb5c22f0d7.tar.gz
gcc-c43137e800bb9ca2ecda0a6b6189e0eb5c22f0d7.tar.bz2
runtime: add precise stack scan support
This CL adds support of precise stack scan using stack maps to the runtime. The stack maps are generated by the compiler (if supported). Each safepoint is associated with a (real or dummy) landing pad, and its "type info" in the exception table is a pointer to the stack map. When a stack is scanned, the stack map is found by the stack unwinding code by inspecting the exception table (LSDA). For precise stack scan we need to unwind the stack. There are three cases: - If a goroutine is scanning its own stack, it can unwind the stack and scan the frames. - If a goroutine is scanning another, stopped, goroutine, it cannot directly unwind the target stack. We handle this by switching (runtime.gogo) to the target g, letting it unwind and scan the stack, and switch back. - If we are scanning a goroutine that is blocked in a syscall, we send a signal to the target goroutine's thread, and let the signal handler unwind and scan the stack. Extra care is needed as this races with enter/exit syscall. Currently this is only implemented on linux. Reviewed-on: https://go-review.googlesource.com/c/140518 From-SVN: r266832
Diffstat (limited to 'libgo/runtime/proc.c')
-rw-r--r--libgo/runtime/proc.c73
1 files changed, 71 insertions, 2 deletions
diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c
index 7bd95a0..99b2cb1 100644
--- a/libgo/runtime/proc.c
+++ b/libgo/runtime/proc.c
@@ -59,6 +59,8 @@ uintptr runtime_stacks_sys;
void gtraceback(G*)
__asm__(GOSYM_PREFIX "runtime.gtraceback");
+static void gscanstack(G*);
+
#ifdef __rtems__
#define __thread
#endif
@@ -340,6 +342,8 @@ runtime_mcall(FuncVal *fv)
if(gp->traceback != 0)
gtraceback(gp);
+ if(gp->scang != 0)
+ gscanstack(gp);
}
if (gp == nil || !gp->fromgogo) {
#ifdef USING_SPLIT_STACK
@@ -469,6 +473,66 @@ gtraceback(G* gp)
runtime_gogo(traceback->gp);
}
+void doscanstackswitch(G*, G*) __asm__(GOSYM_PREFIX "runtime.doscanstackswitch");
+
+// Switch to gp and let it scan its stack.
+// The first time gp->scang is set (to me). The second time here
+// gp is done scanning, and has unset gp->scang, so we just return.
+void
+doscanstackswitch(G* me, G* gp)
+{
+ __go_assert(me->entry == nil);
+ me->fromgogo = false;
+
+#ifdef USING_SPLIT_STACK
+ __splitstack_getcontext((void*)(&me->stackcontext[0]));
+#endif
+ getcontext(ucontext_arg(&me->context[0]));
+
+ if(me->entry != nil) {
+ // Got here from mcall.
+ // The stack scanning code may call systemstack, which calls
+ // mcall, which calls setcontext.
+ // Run the function, which at the end will switch back to gp.
+ FuncVal *fv = me->entry;
+ void (*pfn)(G*) = (void (*)(G*))fv->fn;
+ G* gp1 = (G*)me->param;
+ __go_assert(gp1 == gp);
+ me->entry = nil;
+ me->param = nil;
+ __builtin_call_with_static_chain(pfn(gp1), fv);
+ abort();
+ }
+
+ if (gp->scang != 0)
+ runtime_gogo(gp);
+}
+
+// Do a stack scan, then switch back to the g that triggers this scan.
+// We come here from doscanstackswitch.
+static void
+gscanstack(G *gp)
+{
+ G *oldg, *oldcurg;
+ M* holdm;
+
+ oldg = (G*)gp->scang;
+ oldcurg = oldg->m->curg;
+ holdm = gp->m;
+ if(holdm != nil && holdm != g->m)
+ runtime_throw("gscanstack: m is not nil");
+ oldg->m->curg = gp;
+ gp->m = oldg->m;
+ gp->scang = 0;
+
+ doscanstack(gp, (void*)gp->scangcw);
+
+ gp->scangcw = 0;
+ gp->m = holdm;
+ oldg->m->curg = oldcurg;
+ runtime_gogo(oldg);
+}
+
// Called by pthread_create to start an M.
void*
runtime_mstart(void *arg)
@@ -516,6 +580,9 @@ runtime_mstart(void *arg)
// may always go to the getcontext call in mcall.
gtraceback(gp);
}
+ if(gp->scang != 0)
+ // Got here from doscanswitch. Should not happen.
+ runtime_throw("mstart with scang");
if(gp->entry != nil) {
// Got here from mcall.
@@ -630,7 +697,8 @@ runtime_entersyscall()
{
// Save the registers in the g structure so that any pointers
// held in registers will be seen by the garbage collector.
- getcontext(ucontext_arg(&g->gcregs[0]));
+ if (!runtime_usestackmaps)
+ getcontext(ucontext_arg(&g->gcregs[0]));
// Note that if this function does save any registers itself,
// we might store the wrong value in the call to getcontext.
@@ -676,7 +744,8 @@ runtime_entersyscallblock()
{
// Save the registers in the g structure so that any pointers
// held in registers will be seen by the garbage collector.
- getcontext(ucontext_arg(&g->gcregs[0]));
+ if (!runtime_usestackmaps)
+ getcontext(ucontext_arg(&g->gcregs[0]));
// See comment in runtime_entersyscall.
doentersyscallblock((uintptr)runtime_getcallerpc(),