runtime: avoid write barriers with traceback info

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
This commit is contained in:
Ian Lance Taylor 2018-09-13 17:30:00 +00:00
parent 5f54d5fee4
commit f0d89c7759
6 changed files with 14 additions and 12 deletions

View 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.

View 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")

View 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

View 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]

View 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)

View 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.