runtime: record extra information in throwOnGCWork crashes

Currently we only know the slot address and the value being written in
the throwOnGCWork crash tracebacks, and we have to infer the old value
from what's dumped by gcWork.checkPut. Sometimes these old values
don't make sense, like when we see a write of a nil pointer to a
freshly-allocated object, yet we observe marking a value (where did
that pointer come from?).

This CL adds the old value of the slot and the first two pointers in
the buffer to the traceback.

For #27993.

Change-Id: Ib70eead1afb9c06e8099e520172c3a2acaa45f80
Reviewed-on: https://go-review.googlesource.com/c/154597
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Austin Clements 2018-12-17 14:17:20 -05:00
parent 503091a77c
commit 3c255e8bc6

View file

@ -197,10 +197,32 @@ func wbBufFlush(dst *uintptr, src uintptr) {
// Switch to the system stack so we don't have to worry about
// the untyped stack slots or safe points.
systemstack(func() {
wbBufFlush1(getg().m.p.ptr())
if debugCachedWork {
// For debugging, include the old value of the
// slot and some other data in the traceback.
wbBuf := &getg().m.p.ptr().wbBuf
var old uintptr
if dst != nil {
// dst may be nil in direct calls to wbBufFlush.
old = *dst
}
wbBufFlush1Debug(old, wbBuf.buf[0], wbBuf.buf[1], &wbBuf.buf[0], wbBuf.next)
} else {
wbBufFlush1(getg().m.p.ptr())
}
})
}
// wbBufFlush1Debug is a temporary function for debugging issue
// #27993. It exists solely to add some context to the traceback.
//
//go:nowritebarrierrec
//go:systemstack
//go:noinline
func wbBufFlush1Debug(old, buf1, buf2 uintptr, start *uintptr, next uintptr) {
wbBufFlush1(getg().m.p.ptr())
}
// wbBufFlush1 flushes p's write barrier buffer to the GC work queue.
//
// This must not have write barriers because it is part of the write