This libgo patch by Cherry Zhang stops the stack scan when the stack
can not be unwound.  In a signal-triggered stack scan, if the signal
is delivered at certain bad time (e.g. in vdso, or in the middle of
setcontext?), the unwinder may not be able to unwind the whole stack,
while it still reports _URC_END_OF_STACK.  So we cannot rely on
_URC_END_OF_STACK to tell if it successfully scanned the stack.
Instead, we check the last Go frame to see it actually reached the end
of the stack.  For Go-created stack, this is runtime.kickoff.  For
C-created stack, we need to record the outermost Go frame when it
enters the Go side.

Also we cannot unwind the stack if the signal is delivered in the
middle of runtime.gogo, halfway through a goroutine switch, where the
g and the stack don't match. Give up in this case as well.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 268952)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -1,4 +1,4 @@
-9605c2efd99aa9c744652a9153e208e0653b8596
+672572130ba7a7b277a4c9c8f93576fc42accf63
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/go/runtime/cgo_gccgo.go
===================================================================
--- libgo/go/runtime/cgo_gccgo.go       (revision 268949)
+++ libgo/go/runtime/cgo_gccgo.go       (working copy)
@@ -80,6 +80,10 @@ func CgocallBack() {
                gp = getg()
                mp := gp.m
                mp.dropextram = true
+
+               // This is a C-created stack.
+               // Record the outermost Go frame to help stack scan.
+               gp.entrysp = getcallersp()
        }
 
        lockOSThread()
Index: libgo/go/runtime/proc.go
===================================================================
--- libgo/go/runtime/proc.go    (revision 268949)
+++ libgo/go/runtime/proc.go    (working copy)
@@ -1192,6 +1192,9 @@ func kickoff() {
                gp.param = nil
        }
 
+       // Record the entry SP to help stack scan.
+       gp.entrysp = getsp()
+
        fv(param)
        goexit1()
 }
Index: libgo/go/runtime/runtime2.go
===================================================================
--- libgo/go/runtime/runtime2.go        (revision 268949)
+++ libgo/go/runtime/runtime2.go        (working copy)
@@ -433,6 +433,7 @@ type g struct {
 
        entry    func(unsafe.Pointer) // goroutine function to run
        entryfn  uintptr              // function address passed to __go_go
+       entrysp  uintptr              // the stack pointer of the outermost Go 
frame
        fromgogo bool                 // whether entered from gogo function
 
        scanningself bool // whether goroutine is scanning its own stack
Index: libgo/go/runtime/stubs.go
===================================================================
--- libgo/go/runtime/stubs.go   (revision 268949)
+++ libgo/go/runtime/stubs.go   (working copy)
@@ -231,6 +231,10 @@ func getcallerpc() uintptr
 //go:noescape
 func getcallersp() uintptr // implemented as an intrinsic on all platforms
 
+// getsp returns the stack pointer (SP) of the caller of getsp.
+//go:noinline
+func getsp() uintptr { return getcallersp() }
+
 func asmcgocall(fn, arg unsafe.Pointer) int32 {
        throw("asmcgocall")
        return 0
Index: libgo/runtime/go-unwind.c
===================================================================
--- libgo/runtime/go-unwind.c   (revision 268949)
+++ libgo/runtime/go-unwind.c   (working copy)
@@ -649,6 +649,19 @@ findstackmaps (struct _Unwind_Context *c
   _sleb128_t index;
   int size;
 
+#ifdef HAVE_GETIPINFO
+  ip1 = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+  ip1 = _Unwind_GetIP (context);
+#endif
+  if (! ip_before_insn)
+    --ip1;
+
+  if (ip != NULL)
+    *ip = ip1;
+  if (sp != NULL)
+    *sp = _Unwind_GetCFA (context);
+
 #ifdef __ARM_EABI_UNWINDER__
   {
     _Unwind_Control_Block *ucbp;
@@ -672,14 +685,6 @@ findstackmaps (struct _Unwind_Context *c
   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;
@@ -738,15 +743,16 @@ findstackmaps (struct _Unwind_Context *c
   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;
 }
 
+struct scanstate {
+  void* gcw;      // the GC worker, passed into scanstackwithmap_callback
+  uintptr lastsp; // the last (outermost) SP of Go function seen in a 
traceback, set by the callback
+};
+
 // Callback function to scan a stack frame with stack maps.
 // It skips non-Go functions.
 static _Unwind_Reason_Code
@@ -755,7 +761,11 @@ scanstackwithmap_callback (struct _Unwin
   struct _stackmap *stackmap;
   _Unwind_Ptr ip, sp;
   G* gp;
-  void *gcw = arg;
+  struct scanstate* state = (struct scanstate*) arg;
+  void *gcw;
+
+  gp = runtime_g ();
+  gcw = state->gcw;
 
   switch (findstackmaps (context, &ip, &sp, &stackmap))
     {
@@ -767,7 +777,6 @@ scanstackwithmap_callback (struct _Unwin
           // 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
@@ -781,6 +790,7 @@ scanstackwithmap_callback (struct _Unwin
         abort ();
     }
 
+  state->lastsp = sp;
   runtime_scanstackblockwithmap (ip, sp, (uintptr)(stackmap->len) * 
sizeof(uintptr), stackmap->data, gcw);
 
   return _URC_NO_REASON;
@@ -792,10 +802,30 @@ bool
 scanstackwithmap (void *gcw)
 {
   _Unwind_Reason_Code code;
+  bool ret;
+  struct scanstate state;
+  G* gp;
+  G* curg;
+
+  state.gcw = gcw;
+  state.lastsp = 0;
+  gp = runtime_g ();
+  curg = gp->m->curg;
+
   runtime_xadd (&__go_runtime_in_callers, 1);
-  code = _Unwind_Backtrace (scanstackwithmap_callback, gcw);
+  code = _Unwind_Backtrace (scanstackwithmap_callback, (void*)&state);
   runtime_xadd (&__go_runtime_in_callers, -1);
-  return code == _URC_END_OF_STACK;
+  ret = (code == _URC_END_OF_STACK);
+  if (ret && gp == gp->m->gsignal)
+    {
+      // For signal-triggered scan, the unwinder may not be able to unwind
+      // the whole stack while it still reports _URC_END_OF_STACK (e.g.
+      // signal is delivered in vdso). Check that we actually reached the
+      // the end of the stack, that is, the SP on entry.
+      if (state.lastsp != curg->entrysp)
+        ret = false;
+    }
+  return ret;
 }
 
 // Returns whether stack map is enabled.

Reply via email to