| // Copyright 2020 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // GC checkmarks | |
| // | |
| // In a concurrent garbage collector, one worries about failing to mark | |
| // a live object due to mutations without write barriers or bugs in the | |
| // collector implementation. As a sanity check, the GC has a 'checkmark' | |
| // mode that retraverses the object graph with the world stopped, to make | |
| // sure that everything that should be marked is marked. | |
| package runtime | |
| import ( | |
| "internal/goarch" | |
| "runtime/internal/atomic" | |
| "runtime/internal/sys" | |
| "unsafe" | |
| ) | |
| // A checkmarksMap stores the GC marks in "checkmarks" mode. It is a | |
| // per-arena bitmap with a bit for every word in the arena. The mark | |
| // is stored on the bit corresponding to the first word of the marked | |
| // allocation. | |
| type checkmarksMap struct { | |
| _ sys.NotInHeap | |
| b [heapArenaBytes / goarch.PtrSize / 8]uint8 | |
| } | |
| // If useCheckmark is true, marking of an object uses the checkmark | |
| // bits instead of the standard mark bits. | |
| var useCheckmark = false | |
| // startCheckmarks prepares for the checkmarks phase. | |
| // | |
| // The world must be stopped. | |
| func startCheckmarks() { | |
| assertWorldStopped() | |
| // Clear all checkmarks. | |
| for _, ai := range mheap_.allArenas { | |
| arena := mheap_.arenas[ai.l1()][ai.l2()] | |
| bitmap := arena.checkmarks | |
| if bitmap == nil { | |
| // Allocate bitmap on first use. | |
| bitmap = (*checkmarksMap)(persistentalloc(unsafe.Sizeof(*bitmap), 0, &memstats.gcMiscSys)) | |
| if bitmap == nil { | |
| throw("out of memory allocating checkmarks bitmap") | |
| } | |
| arena.checkmarks = bitmap | |
| } else { | |
| // Otherwise clear the existing bitmap. | |
| for i := range bitmap.b { | |
| bitmap.b[i] = 0 | |
| } | |
| } | |
| } | |
| // Enable checkmarking. | |
| useCheckmark = true | |
| } | |
| // endCheckmarks ends the checkmarks phase. | |
| func endCheckmarks() { | |
| if gcMarkWorkAvailable(nil) { | |
| throw("GC work not flushed") | |
| } | |
| useCheckmark = false | |
| } | |
| // setCheckmark throws if marking object is a checkmarks violation, | |
| // and otherwise sets obj's checkmark. It returns true if obj was | |
| // already checkmarked. | |
| func setCheckmark(obj, base, off uintptr, mbits markBits) bool { | |
| if !mbits.isMarked() { | |
| printlock() | |
| print("runtime: checkmarks found unexpected unmarked object obj=", hex(obj), "\n") | |
| print("runtime: found obj at *(", hex(base), "+", hex(off), ")\n") | |
| // Dump the source (base) object | |
| gcDumpObject("base", base, off) | |
| // Dump the object | |
| gcDumpObject("obj", obj, ^uintptr(0)) | |
| getg().m.traceback = 2 | |
| throw("checkmark found unmarked object") | |
| } | |
| ai := arenaIndex(obj) | |
| arena := mheap_.arenas[ai.l1()][ai.l2()] | |
| arenaWord := (obj / heapArenaBytes / 8) % uintptr(len(arena.checkmarks.b)) | |
| mask := byte(1 << ((obj / heapArenaBytes) % 8)) | |
| bytep := &arena.checkmarks.b[arenaWord] | |
| if atomic.Load8(bytep)&mask != 0 { | |
| // Already checkmarked. | |
| return true | |
| } | |
| atomic.Or8(bytep, mask) | |
| return false | |
| } |