runtime: avoid write barriers with traceback info
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 13 Sep 2018 17:30:00 +0000 (17:30 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 13 Sep 2018 17:30:00 +0000 (17:30 +0000)
    Unlike the gc runtime, libgo stores traceback information in location
    structs, which contain strings.  Therefore, copying location structs
    around appears to require write barriers, although in fact write
    barriers are never important because the strings are never allocated
    in Go memory.  They come from libbacktrace.

    Some of the generated write barriers come at times when write barriers
    are not permitted.  For example, exitsyscall, marked
    nowritebarrierrec, calls exitsyscallfast which calls traceGoSysExit
    which calls traceEvent which calls traceStackID which calls
    trace.stackTab.put which copies location values into memory allocated
    by tab.newStack.  This write barrier can be invoked when there is no
    p, causing a crash.

    This change fixes the problem by ensuring that location values are
    copied around in the tracing code with no write barriers.

    This was found by fixing the compiler to fully implement
    //go:nowritebarrierrec; CL to follow.

    Reviewed-on: https://go-review.googlesource.com/134226

From-SVN: r264282

gcc/go/gofrontend/MERGE
libgo/go/runtime/proc.go
libgo/go/runtime/runtime2.go
libgo/go/runtime/trace.go
libgo/go/runtime/traceback_gccgo.go
libgo/runtime/proc.c

index ca47d87b427d682d237db0b7823d5b3789906d00..205ead9d55385262d67610e46a32851413cf1bc6 100644 (file)
@@ -1,4 +1,4 @@
-70bd9801911f8ed27df410d36a928166c37a68fd
+baf07c40960dc4f8df9da97281870d80d4245b18
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 74325e3974b8f55c8d80ed3c8cc08feb9ab87006..12d7071c3c0fe8901b89bedac7e8c290fcfe63fc 100644 (file)
@@ -1140,7 +1140,7 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 {
 func kickoff() {
        gp := getg()
 
-       if gp.traceback != nil {
+       if gp.traceback != 0 {
                gtraceback(gp)
        }
 
@@ -3097,7 +3097,7 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
        } else {
                resetNewG(newg, &sp, &spsize)
        }
-       newg.traceback = nil
+       newg.traceback = 0
 
        if readgstatus(newg) != _Gdead {
                throw("newproc1: new g is not Gdead")
index 0299d5a788f38eb32b5bfb2158e5851006f318e1..2de1cc8d725dcdc5377c9e7d1a6439325e51a5e0 100644 (file)
@@ -431,7 +431,7 @@ type g struct {
 
        isSystemGoroutine bool // whether goroutine is a "system" goroutine
 
-       traceback *tracebackg // stack traceback buffer
+       traceback uintptr // stack traceback buffer
 
        context      g_ucontext_t // saved context for setcontext
        stackcontext [10]uintptr  // split-stack context
index 8427e76c5a38eb53680c80668caceb5454f03c57..e2bbb5d52ab52b6d08b1b23489c124299007e515 100644 (file)
@@ -135,6 +135,7 @@ var trace struct {
 }
 
 // traceBufHeader is per-P tracing buffer.
+//go:notinheap
 type traceBufHeader struct {
        link      traceBufPtr              // in trace.empty/full
        lastTicks uint64                   // when we wrote the last event
@@ -747,7 +748,8 @@ func (tab *traceStackTable) put(pcs []location) uint32 {
        stk.n = len(pcs)
        stkpc := stk.stack()
        for i, pc := range pcs {
-               stkpc[i] = pc
+               // Use memmove to avoid write barrier.
+               memmove(unsafe.Pointer(&stkpc[i]), unsafe.Pointer(&pc), unsafe.Sizeof(pc))
        }
        part := int(hash % uintptr(len(tab.tab)))
        stk.link = tab.tab[part]
index 8551ec19ac30ea7dad3d927870bcde81f3276419..9456b1f4b3d0b5d212f5cdad93a1db8ab9a8ee04 100644 (file)
@@ -186,7 +186,7 @@ func tracebackothers(me *g) {
        if gp != nil && gp != me {
                print("\n")
                goroutineheader(gp)
-               gp.traceback = (*tracebackg)(noescape(unsafe.Pointer(&tb)))
+               gp.traceback = (uintptr)(noescape(unsafe.Pointer(&tb)))
                getTraceback(me, gp)
                printtrace(tb.locbuf[:tb.c], nil)
                printcreatedby(gp)
@@ -220,7 +220,7 @@ func tracebackothers(me *g) {
                        print("\tgoroutine in C code; stack unavailable\n")
                        printcreatedby(gp)
                } else {
-                       gp.traceback = (*tracebackg)(noescape(unsafe.Pointer(&tb)))
+                       gp.traceback = (uintptr)(noescape(unsafe.Pointer(&tb)))
                        getTraceback(me, gp)
                        printtrace(tb.locbuf[:tb.c], nil)
                        printcreatedby(gp)
index d8d231bae30d848e67a07157d347d75a974c9fa7..7bd95a061c77b36dd821f2c5e13b8e2640d18a97 100644 (file)
@@ -338,7 +338,7 @@ runtime_mcall(FuncVal *fv)
                gp = runtime_g();
                mp = gp->m;
 
-               if(gp->traceback != nil)
+               if(gp->traceback != 0)
                        gtraceback(gp);
        }
        if (gp == nil || !gp->fromgogo) {
@@ -443,7 +443,7 @@ void getTraceback(G* me, G* gp)
 #endif
        getcontext(ucontext_arg(&me->context[0]));
 
-       if (gp->traceback != nil) {
+       if (gp->traceback != 0) {
                runtime_gogo(gp);
        }
 }
@@ -457,8 +457,8 @@ gtraceback(G* gp)
        Traceback* traceback;
        M* holdm;
 
-       traceback = gp->traceback;
-       gp->traceback = nil;
+       traceback = (Traceback*)gp->traceback;
+       gp->traceback = 0;
        holdm = gp->m;
        if(holdm != nil && holdm != g->m)
                runtime_throw("gtraceback: m is not nil");
@@ -510,7 +510,7 @@ runtime_mstart(void *arg)
        // multiple times via the setcontext call in mcall.
        getcontext(ucontext_arg(&gp->context[0]));
 
-       if(gp->traceback != nil) {
+       if(gp->traceback != 0) {
                // Got here from getTraceback.
                // I'm not sure this ever actually happens--getTraceback
                // may always go to the getcontext call in mcall.