-d3a98b7a9ea8032be22ebb3ea2f389ce22669d53
+edc7e7172e674b8c7e9c3caa30e24280cd289a9c
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
}
// We use a C function to find the stack.
-func doscanstack(*g, *gcWork)
+// Returns whether we succesfully scanned the stack.
+func doscanstack(*g, *gcWork) bool
+
+func doscanstackswitch(*g, *g)
// scanstack scans gp's stack, greying all pointers found on the stack.
//
return
case _Grunning:
// ok for gccgo, though not for gc.
- case _Grunnable, _Gsyscall, _Gwaiting:
+ if usestackmaps {
+ print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
+ throw("scanstack: goroutine not stopped")
+ }
+ case _Gsyscall:
+ if usestackmaps {
+ print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
+ throw("scanstack: goroutine in syscall")
+ }
+ case _Grunnable, _Gwaiting:
// ok
}
}
// Scan the stack.
- doscanstack(gp, gcw)
+ if usestackmaps {
+ g := getg()
+ if g == gp {
+ // Scan its own stack.
+ doscanstack(gp, gcw)
+ } else if gp.entry != nil {
+ // This is a newly created g that hasn't run. No stack to scan.
+ } else {
+ // Scanning another g's stack. We need to switch to that g
+ // to unwind its stack. And switch back after scan.
+ scanstackswitch(gp, gcw)
+ }
+ } else {
+ doscanstack(gp, gcw)
- // Conservatively scan the saved register values.
- scanstackblock(uintptr(unsafe.Pointer(&gp.gcregs)), unsafe.Sizeof(gp.gcregs), gcw)
- scanstackblock(uintptr(unsafe.Pointer(&gp.context)), unsafe.Sizeof(gp.context), gcw)
+ // Conservatively scan the saved register values.
+ scanstackblock(uintptr(unsafe.Pointer(&gp.gcregs)), unsafe.Sizeof(gp.gcregs), gcw)
+ scanstackblock(uintptr(unsafe.Pointer(&gp.context)), unsafe.Sizeof(gp.context), gcw)
+ }
gp.gcscanvalid = true
}
+// scanstackswitch scans gp's stack by switching (gogo) to gp and
+// letting it scan its own stack, and switching back upon finish.
+//
+//go:nowritebarrier
+func scanstackswitch(gp *g, gcw *gcWork) {
+ g := getg()
+
+ // We are on the system stack which prevents preemption. But
+ // we are going to switch to g stack. Lock m to block preemption.
+ mp := acquirem()
+
+ // The doscanstackswitch function will modify the current g's
+ // context. Preserve it.
+ // The stack scan code may call systemstack, which will modify
+ // gp's context. Preserve it as well so we can resume gp.
+ context := g.context
+ stackcontext := g.stackcontext
+ context2 := gp.context
+ stackcontext2 := gp.stackcontext
+
+ gp.scangcw = uintptr(unsafe.Pointer(gcw))
+ gp.scang = uintptr(unsafe.Pointer(g))
+ doscanstackswitch(g, gp)
+
+ // Restore the contexts.
+ g.context = context
+ g.stackcontext = stackcontext
+ gp.context = context2
+ gp.stackcontext = stackcontext2
+ gp.scangcw = 0
+ // gp.scang is already cleared in C code.
+
+ releasem(mp)
+}
+
type gcDrainFlags int
const (
// scanblock, but we scan the stack conservatively, so there is no
// bitmask of pointers.
func scanstackblock(b, n uintptr, gcw *gcWork) {
+ if usestackmaps {
+ throw("scanstackblock: conservative scan but stack map is used")
+ }
+
for i := uintptr(0); i < n; i += sys.PtrSize {
// Same work as in scanobject; see comments there.
obj := *(*uintptr)(unsafe.Pointer(b + i))
}
}
+// scanstackblockwithmap is like scanstackblock, but with an explicit
+// pointer bitmap. This is used only when precise stack scan is enabled.
+//go:linkname scanstackblockwithmap runtime.scanstackblockwithmap
+//go:nowritebarrier
+func scanstackblockwithmap(pc, b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
+ // Use local copies of original parameters, so that a stack trace
+ // due to one of the throws below shows the original block
+ // base and extent.
+ b := b0
+ n := n0
+
+ for i := uintptr(0); i < n; {
+ // Find bits for the next word.
+ bits := uint32(*addb(ptrmask, i/(sys.PtrSize*8)))
+ if bits == 0 {
+ i += sys.PtrSize * 8
+ continue
+ }
+ for j := 0; j < 8 && i < n; j++ {
+ if bits&1 != 0 {
+ // Same work as in scanobject; see comments there.
+ obj := *(*uintptr)(unsafe.Pointer(b + i))
+ if obj != 0 {
+ o, span, objIndex := findObject(obj, b, i, false)
+ if obj < minPhysPageSize ||
+ span != nil && span.state != _MSpanManual &&
+ (obj < span.base() || obj >= span.limit || span.state != mSpanInUse) {
+ print("runtime: found in object at *(", hex(b), "+", hex(i), ") = ", hex(obj), ", pc=", hex(pc), "\n")
+ name, file, line := funcfileline(pc, -1)
+ print(name, "\n", file, ":", line, "\n")
+ //gcDumpObject("object", b, i)
+ throw("found bad pointer in Go stack (incorrect use of unsafe or cgo?)")
+ }
+ if o != 0 {
+ greyobject(o, b, i, span, gcw, objIndex, false)
+ }
+ }
+ }
+ bits >>= 1
+ i += sys.PtrSize
+ }
+ }
+}
+
// Shade the object if it isn't already.
// The object is not nil and known to be in the heap.
// Preemption must be disabled.
func minit() {
minitSignals()
- // FIXME: We should set _g_.m.procid here.
+ // FIXME: only works on linux for now.
+ getg().m.procid = uint64(gettid())
}
// Called from dropm to undo the effect of an minit.
sched.maxmcount = 10000
+ usestackmaps = probestackmaps()
+
mallocinit()
mcommoninit(_g_.m)
cpuinit() // must run before alginit
case _Gcopystack:
// Stack being switched. Go around again.
- case _Grunnable, _Gsyscall, _Gwaiting:
+ case _Gsyscall:
+ if usestackmaps {
+ // Claim goroutine by setting scan bit.
+ // Racing with execution or readying of gp.
+ // The scan bit keeps them from running
+ // the goroutine until we're done.
+ if castogscanstatus(gp, s, s|_Gscan) {
+ if gp.scanningself {
+ // Don't try to scan the stack
+ // if the goroutine is going to do
+ // it itself.
+ // FIXME: can this happen?
+ restartg(gp)
+ break
+ }
+ if !gp.gcscandone {
+ // Send a signal to let the goroutine scan
+ // itself. This races with enter/exitsyscall.
+ // If the goroutine is not stopped at a safepoint,
+ // it will not scan the stack and we'll try again.
+ mp := gp.m
+ noteclear(&mp.scannote)
+ gp.scangcw = uintptr(unsafe.Pointer(gcw))
+ tgkill(getpid(), _pid_t(mp.procid), _SIGURG)
+
+ // Wait for gp to scan its own stack.
+ notesleep(&mp.scannote)
+
+ if !gp.gcscandone {
+ // The signal delivered at a bad time.
+ // Try again.
+ restartg(gp)
+ break
+ }
+ }
+ restartg(gp)
+ break loop
+ }
+ break
+ }
+ fallthrough
+
+ case _Grunnable, _Gwaiting:
// Claim goroutine by setting scan bit.
// Racing with execution or readying of gp.
// The scan bit keeps them from running
// The GC requests that this routine be moved from a scanmumble state to a mumble state.
func restartg(gp *g) {
+ if gp.scang != 0 || gp.scangcw != 0 {
+ print("g ", gp.goid, "is being scanned scang=", gp.scang, " scangcw=", gp.scangcw, "\n")
+ throw("restartg: being scanned")
+ }
+
s := readgstatus(gp)
switch s {
default:
scanningself bool // whether goroutine is scanning its own stack
+ scang uintptr // the g that wants to scan this g's stack (uintptr to avoid write barrier)
+ scangcw uintptr // gc worker for scanning stack (uintptr to avoid write barrier)
+
isSystemGoroutine bool // whether goroutine is a "system" goroutine
traceback uintptr // stack traceback buffer
exiting bool // thread is exiting
gcing int32
+
+ scannote note // synchonization for signal-based stack scanning
}
type p struct {
sigfault, sigpc := getSiginfo(info, ctxt)
+ if sig == _SIGURG && usestackmaps {
+ // We may be signaled to do a stack scan.
+ // The signal delivery races with enter/exitsyscall.
+ // We may be on g0 stack now. gp.m.curg is the g we
+ // want to scan.
+ // If we're not on g stack, give up. The sender will
+ // try again later.
+ // If we're not stopped at a safepoint (doscanstack will
+ // return false), also give up.
+ if s := readgstatus(gp.m.curg); s == _Gscansyscall {
+ if gp == gp.m.curg {
+ if doscanstack(gp, (*gcWork)(unsafe.Pointer(gp.scangcw))) {
+ gp.gcscanvalid = true
+ gp.gcscandone = true
+ }
+ }
+ gp.m.curg.scangcw = 0
+ notewakeup(&gp.m.scannote)
+ return
+ }
+ }
+
if sig == _SIGPROF {
sigprof(sigpc, gp, _g_.m)
return
// signal handler, which will attempt to tear down the runtime
// immediately.
func abort()
+
+// usestackmaps is true if stack map (precise stack scan) is enabled.
+var usestackmaps bool
+
+// probestackmaps detects whether there are stack maps.
+//go:linkname probestackmaps runtime.probestackmaps
+func probestackmaps() bool
package runtime
func sbrk0() uintptr
+
+func gettid() _pid_t {
+ return _pid_t(syscall(_SYS_gettid, 0, 0, 0, 0, 0, 0))
+}
+
+func tgkill(pid _pid_t, tid _pid_t, sig uint32) uint32 {
+ return uint32(syscall(_SYS_tgkill, uintptr(pid), uintptr(tid), uintptr(sig), 0, 0, 0))
+}
func sbrk0() uintptr {
return 0
}
+
+func gettid() _pid_t {
+ return 0
+}
+
+func tgkill(pid _pid_t, tid _pid_t, sig uint32) uint32 {
+ throw("tgkill not implemented")
+}
return p;
}
+static inline int
+value_size (uint8_t encoding)
+{
+ switch (encoding & 0x0f)
+ {
+ case DW_EH_PE_sdata2:
+ case DW_EH_PE_udata2:
+ return 2;
+ case DW_EH_PE_sdata4:
+ case DW_EH_PE_udata4:
+ return 4;
+ case DW_EH_PE_sdata8:
+ case DW_EH_PE_udata8:
+ return 8;
+ default:
+ break;
+ }
+ abort ();
+}
+
/* The rest of this code is really similar to gcc/unwind-c.c and
libjava/exception.cc. */
_Unwind_SetIP (context, landing_pad);
return _URC_INSTALL_CONTEXT;
}
+
+// A dummy personality function, which doesn't capture any exception
+// and simply passes by. This is used for functions that don't
+// capture exceptions but need LSDA for stack maps.
+_Unwind_Reason_Code
+__gccgo_personality_dummy (int, _Unwind_Action, _Unwind_Exception_Class,
+ struct _Unwind_Exception *, struct _Unwind_Context *)
+ __attribute__ ((no_split_stack));
+
+_Unwind_Reason_Code
+__gccgo_personality_dummy (int version __attribute__ ((unused)),
+ _Unwind_Action actions __attribute__ ((unused)),
+ _Unwind_Exception_Class exception_class __attribute__ ((unused)),
+ struct _Unwind_Exception *ue_header __attribute__ ((unused)),
+ struct _Unwind_Context *context __attribute__ ((unused)))
+{
+ CONTINUE_UNWINDING;
+}
+
+// A sentinel value for Go functions.
+// A function is a Go function if it has LSDA, which has type info,
+// and the first (dummy) landing pad's type info is a pointer to
+// this value.
+#define GO_FUNC_SENTINEL ((uint64)'G' | ((uint64)'O'<<8) | \
+ ((uint64)'.'<<16) | ((uint64)'.'<<24) | \
+ ((uint64)'F'<<32) | ((uint64)'U'<<40) | \
+ ((uint64)'N'<<48) | ((uint64)'C'<<56))
+
+struct _stackmap {
+ uint32 len;
+ uint8 data[1]; // variabe length
+};
+
+extern void
+ runtime_scanstackblockwithmap (uintptr ip, uintptr sp, uintptr size, uint8 *ptrmask, void* gcw)
+ __asm__ (GOSYM_PREFIX "runtime.scanstackblockwithmap");
+
+#define FOUND 0
+#define NOTFOUND_OK 1
+#define NOTFOUND_BAD 2
+
+// Helper function to search for stack maps in the unwinding records of a frame.
+// If found, populate ip, sp, and stackmap. Returns the #define'd values above.
+static int
+findstackmaps (struct _Unwind_Context *context, _Unwind_Ptr *ip, _Unwind_Ptr *sp, struct _stackmap **stackmap)
+{
+ lsda_header_info info;
+ const unsigned char *language_specific_data, *p, *action_record;
+ bool first;
+ struct _stackmap *stackmap1;
+ _Unwind_Ptr ip1;
+ int ip_before_insn = 0;
+ _sleb128_t index;
+ int size;
+
+ language_specific_data = (const unsigned char *)
+ _Unwind_GetLanguageSpecificData (context);
+
+ /* If no LSDA, then there is no stack maps. */
+ if (! language_specific_data)
+ return NOTFOUND_OK;
+
+ p = parse_lsda_header (context, language_specific_data, &info);
+
+ if (info.TType == NULL)
+ return NOTFOUND_OK;
+
+#ifdef HAVE_GETIPINFO
+ ip1 = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ ip1 = _Unwind_GetIP (context);
+#endif
+ if (! ip_before_insn)
+ --ip1;
+
+ size = value_size (info.ttype_encoding);
+
+ action_record = NULL;
+ first = true;
+
+ /* Search the call-site table for the action associated with this IP. */
+ while (p < info.action_table)
+ {
+ _Unwind_Ptr cs_start, cs_len, cs_lp;
+ _uleb128_t cs_action;
+
+ /* Note that all call-site encodings are "absolute" displacements. */
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+
+ if (first)
+ {
+ // For a Go function, the first entry points to the sentinel value.
+ // Check this here.
+ const unsigned char *p1, *action1;
+ uint64 *x;
+
+ if (!cs_action)
+ return NOTFOUND_OK;
+
+ action1 = info.action_table + cs_action - 1;
+ read_sleb128 (action1, &index);
+ p1 = info.TType - index*size;
+ read_encoded_value (context, info.ttype_encoding, p1, (_Unwind_Ptr*)&x);
+ if (x == NULL || *x != GO_FUNC_SENTINEL)
+ return NOTFOUND_OK;
+
+ first = false;
+ continue;
+ }
+
+ /* The table is sorted, so if we've passed the ip, stop. */
+ if (ip1 < info.Start + cs_start)
+ return NOTFOUND_BAD;
+ else if (ip1 < info.Start + cs_start + cs_len)
+ {
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ break;
+ }
+ }
+
+ if (action_record == NULL)
+ return NOTFOUND_BAD;
+
+ read_sleb128 (action_record, &index);
+ p = info.TType - index*size;
+ read_encoded_value (context, info.ttype_encoding, p, (_Unwind_Ptr*)&stackmap1);
+ if (stackmap1 == NULL)
+ return NOTFOUND_BAD;
+
+ if (ip != NULL)
+ *ip = ip1;
+ if (sp != NULL)
+ *sp = _Unwind_GetCFA (context);
+ if (stackmap != NULL)
+ *stackmap = stackmap1;
+ return FOUND;
+}
+
+// Callback function to scan a stack frame with stack maps.
+// It skips non-Go functions.
+static _Unwind_Reason_Code
+scanstackwithmap_callback (struct _Unwind_Context *context, void *arg)
+{
+ struct _stackmap *stackmap;
+ _Unwind_Ptr ip, sp;
+ G* gp;
+ void *gcw = arg;
+
+ switch (findstackmaps (context, &ip, &sp, &stackmap))
+ {
+ case NOTFOUND_OK:
+ // Not a Go function. Skip this frame.
+ return _URC_NO_REASON;
+ case NOTFOUND_BAD:
+ {
+ // No stack map found.
+ // If we're scanning from the signal stack, the goroutine
+ // may be not stopped at a safepoint. Allow this case.
+ gp = runtime_g ();
+ if (gp != gp->m->gsignal)
+ {
+ // TODO: print gp, pc, sp
+ runtime_throw ("no stack map");
+ }
+ return _URC_NORMAL_STOP;
+ }
+ case FOUND:
+ break;
+ default:
+ abort ();
+ }
+
+ runtime_scanstackblockwithmap (ip, sp, (uintptr)(stackmap->len) * sizeof(uintptr), stackmap->data, gcw);
+
+ return _URC_NO_REASON;
+}
+
+// Scan the stack with stack maps. Return whether the scan
+// succeeded.
+bool
+scanstackwithmap (void *gcw)
+{
+ _Unwind_Reason_Code code;
+ code = _Unwind_Backtrace (scanstackwithmap_callback, gcw);
+ return code == _URC_END_OF_STACK;
+}
+
+// Returns whether stack map is enabled.
+bool
+usestackmaps ()
+{
+ return runtime_usestackmaps;
+}
+
+// Callback function to probe if a stack frame has stack maps.
+static _Unwind_Reason_Code
+probestackmaps_callback (struct _Unwind_Context *context,
+ void *arg __attribute__ ((unused)))
+{
+ switch (findstackmaps (context, NULL, NULL, NULL))
+ {
+ case NOTFOUND_OK:
+ case NOTFOUND_BAD:
+ return _URC_NO_REASON;
+ case FOUND:
+ break;
+ default:
+ abort ();
+ }
+
+ // Found a stack map. No need to keep unwinding.
+ runtime_usestackmaps = true;
+ return _URC_NORMAL_STOP;
+}
+
+// Try to find a stack map, store the result in global variable runtime_usestackmaps.
+// Called in start-up time from Go code, so there is a Go frame on the stack.
+bool
+probestackmaps ()
+{
+ runtime_usestackmaps = false;
+ _Unwind_Backtrace (probestackmaps_callback, NULL);
+ return runtime_usestackmaps;
+}
void gtraceback(G*)
__asm__(GOSYM_PREFIX "runtime.gtraceback");
+static void gscanstack(G*);
+
#ifdef __rtems__
#define __thread
#endif
if(gp->traceback != 0)
gtraceback(gp);
+ if(gp->scang != 0)
+ gscanstack(gp);
}
if (gp == nil || !gp->fromgogo) {
#ifdef USING_SPLIT_STACK
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)
// 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.
{
// 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.
{
// 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(),
struct funcfileline_return
runtime_funcfileline (uintptr targetpc, int32 index)
__asm__ (GOSYM_PREFIX "runtime.funcfileline");
+
+/*
+ * helpers for stack scan.
+ */
+bool scanstackwithmap(void*)
+ __asm__(GOSYM_PREFIX "runtime.scanstackwithmap");
+bool doscanstack(G*, void*)
+ __asm__("runtime.doscanstack");
+
+bool runtime_usestackmaps;
+
+bool probestackmaps(void)
+ __asm__("runtime.probestackmaps");
extern void scanstackblock(void *addr, uintptr size, void *gcw)
__asm__("runtime.scanstackblock");
-void doscanstack(G*, void*)
- __asm__("runtime.doscanstack");
-
-static void doscanstack1(G*, void*)
+static bool doscanstack1(G*, void*)
__attribute__ ((noinline));
// Scan gp's stack, passing stack chunks to scanstackblock.
-void doscanstack(G *gp, void* gcw) {
+bool doscanstack(G *gp, void* gcw) {
// Save registers on the stack, so that if we are scanning our
// own stack we will see them.
- __builtin_unwind_init();
- flush_registers_to_secondary_stack();
+ if (!runtime_usestackmaps) {
+ __builtin_unwind_init();
+ flush_registers_to_secondary_stack();
+ }
- doscanstack1(gp, gcw);
+ return doscanstack1(gp, gcw);
}
// Scan gp's stack after saving registers.
-static void doscanstack1(G *gp, void *gcw) {
+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;
-
- if (gp == runtime_g()) {
+ 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 {
// The goroutine is usually asleep (the world is stopped).
bottom = (void*)gp->gcnextsp;
if(bottom == nil)
- return;
+ return true;
nextsp2 = (void*)gp->gcnextsp2;
}
top = (byte*)(void*)(gp->gcinitialsp) + gp->gcstacksize;
scanstackblock(initialsp2, (uintptr)(nextsp2 - initialsp2), gcw);
}
#endif
+ return true;
}