aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mgcstack.go
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2019-10-10 14:38:15 -0400
committerAustin Clements <austin@google.com>2019-11-02 21:51:16 +0000
commitd16ec137568fb20e674a99c265e7c340c065dd69 (patch)
tree7e61729e505094e6fc1c7336dd48da5fc9c3e041 /src/runtime/mgcstack.go
parenta3ffb0d9eb948409c0898c6b1803401c9bc68ed4 (diff)
downloadgo-d16ec137568fb20e674a99c265e7c340c065dd69.tar.xz
runtime: scan stacks conservatively at async safe points
This adds support for scanning the stack when a goroutine is stopped at an async safe point. This is not yet lit up because asyncPreempt is not yet injected, but prepares us for that. This works by conservatively scanning the registers dumped in the frame of asyncPreempt and its parent frame, which was stopped at an asynchronous safe point. Conservative scanning works by only marking words that are pointers to valid, allocated heap objects. One complication is pointers to stack objects. In this case, we can't determine if the stack object is still "allocated" or if it was freed by an earlier GC. Hence, we need to propagate the conservative-ness of scanning stack objects: if all pointers found to a stack object were found via conservative scanning, then the stack object itself needs to be scanned conservatively, since its pointers may point to dead objects. For #10958, #24543. Change-Id: I7ff84b058c37cde3de8a982da07002eaba126fd6 Reviewed-on: https://go-review.googlesource.com/c/go/+/201761 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
Diffstat (limited to 'src/runtime/mgcstack.go')
-rw-r--r--src/runtime/mgcstack.go76
1 files changed, 50 insertions, 26 deletions
diff --git a/src/runtime/mgcstack.go b/src/runtime/mgcstack.go
index baeaa4fd55..211d882fa6 100644
--- a/src/runtime/mgcstack.go
+++ b/src/runtime/mgcstack.go
@@ -175,12 +175,23 @@ type stackScanState struct {
// stack limits
stack stack
+ // conservative indicates that the next frame must be scanned conservatively.
+ // This applies only to the innermost frame at an async safe-point.
+ conservative bool
+
// buf contains the set of possible pointers to stack objects.
// Organized as a LIFO linked list of buffers.
// All buffers except possibly the head buffer are full.
buf *stackWorkBuf
freeBuf *stackWorkBuf // keep around one free buffer for allocation hysteresis
+ // cbuf contains conservative pointers to stack objects. If
+ // all pointers to a stack object are obtained via
+ // conservative scanning, then the stack object may be dead
+ // and may contain dead pointers, so it must be scanned
+ // defensively.
+ cbuf *stackWorkBuf
+
// list of stack objects
// Objects are in increasing address order.
head *stackObjectBuf
@@ -194,17 +205,21 @@ type stackScanState struct {
// Add p as a potential pointer to a stack object.
// p must be a stack address.
-func (s *stackScanState) putPtr(p uintptr) {
+func (s *stackScanState) putPtr(p uintptr, conservative bool) {
if p < s.stack.lo || p >= s.stack.hi {
throw("address not a stack address")
}
- buf := s.buf
+ head := &s.buf
+ if conservative {
+ head = &s.cbuf
+ }
+ buf := *head
if buf == nil {
// Initial setup.
buf = (*stackWorkBuf)(unsafe.Pointer(getempty()))
buf.nobj = 0
buf.next = nil
- s.buf = buf
+ *head = buf
} else if buf.nobj == len(buf.obj) {
if s.freeBuf != nil {
buf = s.freeBuf
@@ -213,8 +228,8 @@ func (s *stackScanState) putPtr(p uintptr) {
buf = (*stackWorkBuf)(unsafe.Pointer(getempty()))
}
buf.nobj = 0
- buf.next = s.buf
- s.buf = buf
+ buf.next = *head
+ *head = buf
}
buf.obj[buf.nobj] = p
buf.nobj++
@@ -222,30 +237,39 @@ func (s *stackScanState) putPtr(p uintptr) {
// Remove and return a potential pointer to a stack object.
// Returns 0 if there are no more pointers available.
-func (s *stackScanState) getPtr() uintptr {
- buf := s.buf
- if buf == nil {
- // Never had any data.
- return 0
- }
- if buf.nobj == 0 {
- if s.freeBuf != nil {
- // Free old freeBuf.
- putempty((*workbuf)(unsafe.Pointer(s.freeBuf)))
- }
- // Move buf to the freeBuf.
- s.freeBuf = buf
- buf = buf.next
- s.buf = buf
+//
+// This prefers non-conservative pointers so we scan stack objects
+// precisely if there are any non-conservative pointers to them.
+func (s *stackScanState) getPtr() (p uintptr, conservative bool) {
+ for _, head := range []**stackWorkBuf{&s.buf, &s.cbuf} {
+ buf := *head
if buf == nil {
- // No more data.
- putempty((*workbuf)(unsafe.Pointer(s.freeBuf)))
- s.freeBuf = nil
- return 0
+ // Never had any data.
+ continue
+ }
+ if buf.nobj == 0 {
+ if s.freeBuf != nil {
+ // Free old freeBuf.
+ putempty((*workbuf)(unsafe.Pointer(s.freeBuf)))
+ }
+ // Move buf to the freeBuf.
+ s.freeBuf = buf
+ buf = buf.next
+ *head = buf
+ if buf == nil {
+ // No more data in this list.
+ continue
+ }
}
+ buf.nobj--
+ return buf.obj[buf.nobj], head == &s.cbuf
+ }
+ // No more data in either list.
+ if s.freeBuf != nil {
+ putempty((*workbuf)(unsafe.Pointer(s.freeBuf)))
+ s.freeBuf = nil
}
- buf.nobj--
- return buf.obj[buf.nobj]
+ return 0, false
}
// addObject adds a stack object at addr of type typ to the set of stack objects.