// // Copyright (c) Microsoft Corporation. All rights reserved. // /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ /* depots. Master copy resides in Bartok Depot. Changes should be */ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ //#define DEBUG_OFFSETTABLE namespace System.GCs { using Microsoft.Bartok.Runtime; using System.Threading; using System.Runtime.CompilerServices; [CCtorIsRunDuringStartup] internal class Verifier { internal abstract class StackVerifier { internal abstract void Verify(NonNullReferenceVisitor threadReferenceVisitor, Thread thread); } private NonNullReferenceVisitor referenceVisitor; private NonNullReferenceVisitor threadReferenceVisitor; private ObjectLayout.ObjectVisitor objectVisitor; private StackVerifier stackVerifier; internal static readonly NonNullReferenceVisitor genericReferenceVisitor; internal static readonly ObjectLayout.ObjectVisitor genericObjectVisitor; internal static readonly NonNullReferenceVisitor genericThreadReferenceVisitor; internal static readonly StackVerifier genericStackVerifier; internal static readonly Verifier bumpAllocatorVerifier; internal static readonly Verifier segregatedFreeListVerifier; static Verifier() { genericReferenceVisitor = new GenericReferenceVisitor(); genericThreadReferenceVisitor = new GenericThreadReferenceVisitor(); genericObjectVisitor = new GenericObjectVisitor(genericReferenceVisitor); genericStackVerifier = new GenericStackVerifier(); bumpAllocatorVerifier = new Verifier(genericReferenceVisitor, genericThreadReferenceVisitor, genericObjectVisitor, genericStackVerifier); segregatedFreeListVerifier = new Verifier(genericReferenceVisitor, genericThreadReferenceVisitor, new SegregatedFreeList.ObjectVisitorWrapper(genericObjectVisitor), genericStackVerifier); } internal Verifier(NonNullReferenceVisitor referenceVisitor, NonNullReferenceVisitor threadReferenceVisitor, ObjectLayout.ObjectVisitor objectVisitor, StackVerifier stackVerifier) { this.referenceVisitor = referenceVisitor;; this.threadReferenceVisitor = threadReferenceVisitor; this.objectVisitor = objectVisitor; this.stackVerifier = stackVerifier; } public void VerifyHeap() { // Temporarily set the thread allocation point to the unused // space token, if necessary. Thread currentThread = Thread.CurrentThread; VerifyAllThreadsGCControlled(currentThread); // Do the real work VerifyPages(this.objectVisitor); StaticData.ScanStaticData(this.referenceVisitor); VTable.Deny(PageTable.IsUnusedPage(PageTable.Page(Magic.addressOf(Thread.threadTable)))); for (int i = 0; i < Thread.threadTable.Length; i++) { Thread t = Thread.threadTable[i]; if (t != null) { VTable.Deny(PageTable.IsUnusedPage(PageTable.Page(Magic.addressOf(t)))); this.stackVerifier.Verify(this.threadReferenceVisitor, t); } } } private static void VerifyAllThreadsGCControlled(Thread thread) { int limit = Thread.threadTable.Length; for (int i = 0; i < limit; i++) { Thread t = Thread.threadTable[i]; if (t != null && t != thread) { #if !SINGULARITY VTable.Assert(Transitions.UnderGCControl(t.threadIndex)); #endif } } } //========================================= // Verification of heap and page management internal static void VerifyPages(ObjectLayout.ObjectVisitor objectVisitor) { UIntPtr page = UIntPtr.Zero; while (page < PageTable.pageTableCount) { UIntPtr startPage = page; if (!PageTable.IsMyPage(startPage)) { page++; continue; } PageType pageType = PageTable.Type(page); uint pageProcess = PageTable.Process(page); do { page++; } while (page < PageTable.pageTableCount && PageTable.Type(page) == pageType && PageTable.Process(page) == pageProcess); UIntPtr endPage = page; switch (pageType) { case PageType.Unallocated: case PageType.Unknown: case PageType.Shared: { // The region does not belong to us, so there is // nothing to check. break; } case PageType.UnusedClean: case PageType.UnusedDirty: { PageManager.VerifyUnusedRegion(startPage, endPage); break; } case PageType.System: { // We have looked at the region, but it is off-limits // for the verifier. break; } case PageType.NonGC: { // Since there may be non-objects in the static data // pages, we cannot apply the heapVerifier to the // region. break; } case PageType.Stack: { // The page contains (part of) the activation record // stack for one or more threads. break; } default: { // We have found a data region VTable.Assert(PageTable.IsGcPage(startPage)); UIntPtr startAddr = PageTable.PageAddr(startPage); UIntPtr endAddr = PageTable.PageAddr(endPage); GC.installedGC.VisitObjects(objectVisitor, startAddr, endAddr); break; } } } } private class GenericReferenceVisitor : NonNullReferenceVisitor { internal unsafe override void Visit(UIntPtr *loc) { UIntPtr addr = *loc; UIntPtr page = PageTable.Page(addr); PageType pageType = PageTable.Type(page); if (!PageTable.IsGcPage(pageType)) { VTable.Assert(PageTable.IsNonGcPage(pageType) || PageTable.IsStackPage(pageType) || PageTable.IsSharedPage(pageType)); return; } VTable.Assert(PageTable.IsMyPage(page)); UIntPtr objectAddr = GC.installedGC.FindObjectAddr(addr); VTable.Assert(objectAddr == addr); UIntPtr vtableAddr = Magic.addressOf(Magic.fromAddress(objectAddr).vtable); VTable.Assert(PageTable.IsNonGcPage(PageTable.Type(PageTable.Page(vtableAddr)))); VTable.Assert(Magic.fromAddress(vtableAddr) is VTable); } } internal class GenericThreadReferenceVisitor : NonNullReferenceVisitor { internal unsafe override void Visit(UIntPtr *loc) { UIntPtr addr = *loc; UIntPtr page = PageTable.Page(addr); PageType pageType = PageTable.Type(page); if (!PageTable.IsGcPage(pageType)) { VTable.Assert(PageTable.IsNonGcPage(pageType) || PageTable.IsStackPage(pageType) || PageTable.IsSharedPage(pageType)); return; } VTable.Assert(PageTable.IsMyPage(page)); UIntPtr objectAddr = GC.installedGC.FindObjectAddr(addr); VTable.Assert(objectAddr <= addr); UIntPtr vtableAddr = Magic.addressOf(Magic.fromAddress(objectAddr).vtable); VTable.Assert(PageTable.IsNonGcPage(PageTable.Type(PageTable.Page(vtableAddr)))); VTable.Assert(Magic.fromAddress(vtableAddr) is VTable); } } internal class GenericObjectVisitor : ObjectLayout.ObjectVisitor { private NonNullReferenceVisitor referenceVisitor; #if DEBUG_OFFSETTABLE private static UIntPtr lastObjPtr; #endif internal GenericObjectVisitor(NonNullReferenceVisitor referenceVisitor) { this.referenceVisitor = referenceVisitor; #if DEBUG_OFFSETTABLE lastObjPtr = UIntPtr.Zero; #endif } internal override UIntPtr Visit(Object obj) { #if DEBUG_OFFSETTABLE VTable.DebugPrint("visit obj {0:x8}\n", __arglist(Magic.addressOf(obj))); if (lastObjPtr != UIntPtr.Zero) { UIntPtr lastCard = CardTable.CardNo(lastObjPtr); if (lastCard != CardTable.CardNo(Magic.addressOf(obj))) { if (!OffsetTable.NoObjectPtrToTheCard(lastCard)) { UIntPtr realOffst = lastObjPtr - CardTable.CardAddr(lastCard); UIntPtr currOffst = OffsetTable.GetOffset(lastCard); if (realOffst != currOffst ) { VTable.DebugPrint("Verifier: wrong offset. Card {0:x8} Offset {1:x8} Should be {2:x8}\n", __arglist(lastCard, currOffst, realOffst)); VTable.Assert(false); } } } } #endif return this.referenceVisitor.VisitReferenceFields(obj); } } internal class GenericStackVerifier : StackVerifier { internal override void Verify(NonNullReferenceVisitor threadReferenceVisitor, Thread thread) { CallStack.ScanStack(thread, threadReferenceVisitor, threadReferenceVisitor); } } } }