c43137e800
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
126 lines
3.8 KiB
C
126 lines
3.8 KiB
C
// Copyright 2009 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.
|
|
|
|
// Stack scanning code for the garbage collector.
|
|
|
|
#include "runtime.h"
|
|
|
|
#ifdef USING_SPLIT_STACK
|
|
|
|
extern void * __splitstack_find (void *, void *, size_t *, void **, void **,
|
|
void **);
|
|
|
|
extern void * __splitstack_find_context (void *context[10], size_t *, void **,
|
|
void **, void **);
|
|
|
|
#endif
|
|
|
|
// Calling unwind_init in doscanstack only works if it does not do a
|
|
// tail call to doscanstack1.
|
|
#pragma GCC optimize ("-fno-optimize-sibling-calls")
|
|
|
|
extern void scanstackblock(void *addr, uintptr size, void *gcw)
|
|
__asm__("runtime.scanstackblock");
|
|
|
|
static bool doscanstack1(G*, void*)
|
|
__attribute__ ((noinline));
|
|
|
|
// Scan gp's stack, passing stack chunks to scanstackblock.
|
|
bool doscanstack(G *gp, void* gcw) {
|
|
// Save registers on the stack, so that if we are scanning our
|
|
// own stack we will see them.
|
|
if (!runtime_usestackmaps) {
|
|
__builtin_unwind_init();
|
|
flush_registers_to_secondary_stack();
|
|
}
|
|
|
|
return doscanstack1(gp, gcw);
|
|
}
|
|
|
|
// Scan gp's stack after saving registers.
|
|
static bool doscanstack1(G *gp, void *gcw) {
|
|
#ifdef USING_SPLIT_STACK
|
|
void* sp;
|
|
size_t spsize;
|
|
void* next_segment;
|
|
void* next_sp;
|
|
void* initial_sp;
|
|
G* _g_;
|
|
|
|
_g_ = runtime_g();
|
|
if (runtime_usestackmaps) {
|
|
// If stack map is enabled, we get here only when we can unwind
|
|
// the stack being scanned. That is, either we are scanning our
|
|
// own stack, or we are scanning through a signal handler.
|
|
__go_assert((_g_ == gp) || ((_g_ == gp->m->gsignal) && (gp == gp->m->curg)));
|
|
return scanstackwithmap(gcw);
|
|
}
|
|
if (_g_ == gp) {
|
|
// Scanning our own stack.
|
|
// If we are on a signal stack, it can unwind through the signal
|
|
// handler and see the g stack, so just scan our own stack.
|
|
sp = __splitstack_find(nil, nil, &spsize, &next_segment,
|
|
&next_sp, &initial_sp);
|
|
} else {
|
|
// Scanning another goroutine's stack.
|
|
// The goroutine is usually asleep (the world is stopped).
|
|
|
|
// The exception is that if the goroutine is about to enter or might
|
|
// have just exited a system call, it may be executing code such
|
|
// as schedlock and may have needed to start a new stack segment.
|
|
// Use the stack segment and stack pointer at the time of
|
|
// the system call instead, since that won't change underfoot.
|
|
if(gp->gcstack != 0) {
|
|
sp = (void*)(gp->gcstack);
|
|
spsize = gp->gcstacksize;
|
|
next_segment = (void*)(gp->gcnextsegment);
|
|
next_sp = (void*)(gp->gcnextsp);
|
|
initial_sp = (void*)(gp->gcinitialsp);
|
|
} else {
|
|
sp = __splitstack_find_context((void**)(&gp->stackcontext[0]),
|
|
&spsize, &next_segment,
|
|
&next_sp, &initial_sp);
|
|
}
|
|
}
|
|
if(sp != nil) {
|
|
scanstackblock(sp, (uintptr)(spsize), gcw);
|
|
while((sp = __splitstack_find(next_segment, next_sp,
|
|
&spsize, &next_segment,
|
|
&next_sp, &initial_sp)) != nil)
|
|
scanstackblock(sp, (uintptr)(spsize), gcw);
|
|
}
|
|
#else
|
|
byte* bottom;
|
|
byte* top;
|
|
byte* nextsp2;
|
|
byte* initialsp2;
|
|
|
|
if(gp == runtime_g()) {
|
|
// Scanning our own stack.
|
|
bottom = (byte*)&gp;
|
|
nextsp2 = secondary_stack_pointer();
|
|
} else {
|
|
// Scanning another goroutine's stack.
|
|
// The goroutine is usually asleep (the world is stopped).
|
|
bottom = (void*)gp->gcnextsp;
|
|
if(bottom == nil)
|
|
return true;
|
|
nextsp2 = (void*)gp->gcnextsp2;
|
|
}
|
|
top = (byte*)(void*)(gp->gcinitialsp) + gp->gcstacksize;
|
|
if(top > bottom)
|
|
scanstackblock(bottom, (uintptr)(top - bottom), gcw);
|
|
else
|
|
scanstackblock(top, (uintptr)(bottom - top), gcw);
|
|
if (nextsp2 != nil) {
|
|
initialsp2 = (byte*)(void*)(gp->gcinitialsp2);
|
|
if(initialsp2 > nextsp2)
|
|
scanstackblock(nextsp2, (uintptr)(initialsp2 - nextsp2), gcw);
|
|
else
|
|
scanstackblock(initialsp2, (uintptr)(nextsp2 - initialsp2), gcw);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|