aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCherry Zhang <cherryyz@google.com>2019-02-27 12:34:20 -0500
committerIan Lance Taylor <iant@golang.org>2019-03-01 18:39:38 +0000
commit91fd14b82493e592730a3e459ef6610195b854c2 (patch)
tree2b77c9ed12b67fb250de7718f358a6ff2663adb0
parent162b3610e62f2e55e4048f2f72273a58e152236e (diff)
downloadgo-91fd14b82493e592730a3e459ef6610195b854c2.tar.gz
go-91fd14b82493e592730a3e459ef6610195b854c2.zip
[release-branch.go1.12] runtime: scan defer closure in stack scan
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. Updates #30453. Fixes #30470. Change-Id: I55a6e43264d6952ab2fa5c638bebb89fdc410e2b Reviewed-on: https://go-review.googlesource.com/c/164118 Reviewed-by: Keith Randall <khr@golang.org> (cherry picked from commit 4f4c2a79d4f952b96d58aec2926b4c894245071b) Reviewed-on: https://go-review.googlesource.com/c/164629 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
-rw-r--r--src/runtime/mgcmark.go7
-rw-r--r--src/runtime/stack_test.go8
-rw-r--r--src/runtime/testdata/testprog/gc.go23
3 files changed, 38 insertions, 0 deletions
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 022cc8d7d7..cc4e7d06d3 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -713,6 +713,13 @@ func scanstack(gp *g, gcw *gcWork) {
// 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)))
}
diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go
index f52381710d..7bc63967bb 100644
--- a/src/runtime/stack_test.go
+++ b/src/runtime/stack_test.go
@@ -787,3 +787,11 @@ func TestTracebackAncestors(t *testing.T) {
}
}
}
+
+// 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)
+ }
+}
diff --git a/src/runtime/testdata/testprog/gc.go b/src/runtime/testdata/testprog/gc.go
index fdf08be7e9..ea6604f132 100644
--- a/src/runtime/testdata/testprog/gc.go
+++ b/src/runtime/testdata/testprog/gc.go
@@ -18,6 +18,7 @@ func init() {
register("GCFairness2", GCFairness2)
register("GCSys", GCSys)
register("GCPhys", GCPhys)
+ register("DeferLiveness", DeferLiveness)
}
func GCSys() {
@@ -207,3 +208,25 @@ func GCPhys() {
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{}