With stack objects, when we scan the stack, it scans defers with
tracebackdefers, but it seems to me that tracebackdefers doesn't
include the func value itself, which could be a stack allocated
closure. Scan it explicitly.
Alternatively, we can change tracebackdefers to include the func
value, which in turn needs to change the type of stkframe.
Fixes #30453.
Change-Id: I55a6e43264d6952ab2fa5c638bebb89fdc410e2b
Reviewed-on: https://go-review.googlesource.com/c/164118
Reviewed-by: Keith Randall <khr@golang.org>
// Find additional pointers that point into the stack from the heap.
// Currently this includes defers and panics. See also function copystack.
tracebackdefers(gp, scanframe, nil)
+ for d := gp._defer; d != nil; d = d.link {
+ // tracebackdefers above does not scan the func value, which could
+ // be a stack allocated closure. See issue 30453.
+ if d.fn != nil {
+ scanblock(uintptr(unsafe.Pointer(&d.fn)), sys.PtrSize, &oneptrmask[0], gcw, &state)
+ }
+ }
if gp._panic != nil {
state.putPtr(uintptr(unsafe.Pointer(gp._panic)))
}
}
}
}
+
+// Test that defer closure is correctly scanned when the stack is scanned.
+func TestDeferLiveness(t *testing.T) {
+ output := runTestProg(t, "testprog", "DeferLiveness", "GODEBUG=clobberfree=1")
+ if output != "" {
+ t.Errorf("output:\n%s\n\nwant no output", output)
+ }
+}
register("GCFairness2", GCFairness2)
register("GCSys", GCSys)
register("GCPhys", GCPhys)
+ register("DeferLiveness", DeferLiveness)
}
func GCSys() {
fmt.Println("OK")
runtime.KeepAlive(saved)
}
+
+// Test that defer closure is correctly scanned when the stack is scanned.
+func DeferLiveness() {
+ var x [10]int
+ escape(&x)
+ fn := func() {
+ if x[0] != 42 {
+ panic("FAIL")
+ }
+ }
+ defer fn()
+
+ x[0] = 42
+ runtime.GC()
+ runtime.GC()
+ runtime.GC()
+}
+
+//go:noinline
+func escape(x interface{}) { sink2 = x; sink2 = nil }
+
+var sink2 interface{}