// 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 }