/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.utility.sanitychecker; import org.mmtk.plan.Plan; import org.mmtk.plan.Trace; import org.mmtk.plan.Simple; import org.mmtk.plan.TraceLocal; import org.mmtk.policy.Space; import org.mmtk.utility.Constants; import org.mmtk.utility.Log; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; import org.vmmagic.unboxed.*; /** * This class performs sanity checks for Simple collectors. */ @Uninterruptible public final class SanityChecker implements Constants { /* Counters */ public static long referenceCount; public static long rootReferenceCount; public static long danglingReferenceCount; public static long nullReferenceCount; public static long liveObjectCount; public static final int DEAD = -2; public static final int ALIVE = -1; public static final int UNSURE = 0; public static final int LOG_SANITY_DATA_SIZE = 24; /* Tracing */ public Trace rootTrace; public Trace checkTrace; private final SanityDataTable sanityTable; private boolean preGCSanity; /* Local, but we only run the check trace single-threaded. */ final SanityTraceLocal checkTraceLocal; /* Linear scanning */ private final SanityLinearScan scanner = new SanityLinearScan(this); /**************************************************************************** * Constants */ public SanityChecker() { sanityTable = new SanityDataTable(Plan.sanitySpace, LOG_SANITY_DATA_SIZE); checkTrace = new Trace(Plan.sanitySpace); rootTrace = new Trace(Plan.sanitySpace); checkTraceLocal = new SanityTraceLocal(checkTrace, this); } /** * Perform any sanity checking collection phases. * * @param phaseId The id to proces * @return True if the phase was handled. */ @NoInline public boolean collectionPhase(int phaseId) { if (phaseId == Simple.SANITY_SET_PREGC) { preGCSanity = true; return true; } if (phaseId == Simple.SANITY_SET_POSTGC) { preGCSanity = false; return true; } if (phaseId == Simple.SANITY_PREPARE) { Log.writeln(""); Log.write("============================== GC Sanity Checking "); Log.writeln("=============================="); Log.writeln(preGCSanity ? "Performing Pre-GC Sanity Checks..." : "Performing Post-GC Sanity Checks..."); // Reset counters referenceCount = 0; nullReferenceCount = 0; liveObjectCount = 0; danglingReferenceCount = 0; rootReferenceCount = 0; // Clear data space sanityTable.acquireTable(); // Root trace rootTrace.prepareNonBlocking(); // Checking trace checkTrace.prepareNonBlocking(); checkTraceLocal.prepare(); return true; } if (phaseId == Simple.SANITY_ROOTS) { VM.scanning.resetThreadCounter(); return true; } if (phaseId == Simple.SANITY_BUILD_TABLE) { // Trace, checking for dangling pointers checkTraceLocal.completeTrace(); return true; } if (phaseId == Simple.SANITY_CHECK_TABLE) { // Iterate over the reachable objects. Address curr = sanityTable.getFirst(); while (!curr.isZero()) { ObjectReference ref = SanityDataTable.getObjectReference(curr); int normalRC = SanityDataTable.getNormalRC(curr); int rootRC = SanityDataTable.getRootRC(curr); if (!preGCSanity) { int expectedRC = VM.activePlan.global().sanityExpectedRC(ref, rootRC); switch (expectedRC) { case SanityChecker.ALIVE: case SanityChecker.UNSURE: // Always ok. break; case SanityChecker.DEAD: // Never ok. Log.write("ERROR: SanityRC = "); Log.write(normalRC); Log.write(", SpaceRC = 0 "); SanityChecker.dumpObjectInformation(ref); break; default: // A mismatch in an RC space if (normalRC != expectedRC && VM.activePlan.global().lastCollectionFullHeap()) { Log.write("WARNING: SanityRC = "); Log.write(normalRC); Log.write(", SpaceRC = "); Log.write(expectedRC); Log.write(" "); SanityChecker.dumpObjectInformation(ref); break; } } } curr = sanityTable.getNext(curr); } if (!preGCSanity && VM.activePlan.global().lastCollectionFullHeap()) { VM.activePlan.global().sanityLinearScan(scanner); } return true; } if (phaseId == Simple.SANITY_RELEASE) { checkTrace.release(); sanityTable.releaseTable(); checkTraceLocal.release(); Log.writeln("roots\tobjects\trefs\tnull"); Log.write(rootReferenceCount);Log.write("\t"); Log.write(liveObjectCount);Log.write("\t"); Log.write(referenceCount);Log.write("\t"); Log.writeln(nullReferenceCount); Log.write("========================================"); Log.writeln("========================================"); return true; } return false; } /** * Process an object during a linear scan of the heap. We have already checked * all objects in the table. So we are only interested in objects that are not in * the sanity table here. We are therefore only looking for leaks here. * * @param object The object being scanned. */ public void scanProcessObject(ObjectReference object) { if (sanityTable.getEntry(object, false).isZero()) { // Is this a leak? int expectedRC = VM.activePlan.global().sanityExpectedRC(object, 0); if (expectedRC == SanityChecker.UNSURE) { // Probably not. return; } // Possibly Log.write("WARNING: Possible leak, SpaceRC = "); Log.write(expectedRC); Log.write(" "); SanityChecker.dumpObjectInformation(object); } } /** * Process an object during sanity checking, validating data, * incrementing counters and enqueuing if this is the first * visit to the object. * * @param object The object to mark. * @param root True If the object is a root. */ public void processObject(TraceLocal trace, ObjectReference object, boolean root) { SanityChecker.referenceCount++; if (root) SanityChecker.rootReferenceCount++; if (object.isNull()) { SanityChecker.nullReferenceCount++; return; } if (Plan.SCAN_BOOT_IMAGE && Space.isInSpace(Plan.VM_SPACE, object)) { return; } // Get the table entry. Address tableEntry = sanityTable.getEntry(object, true); if (SanityDataTable.incRC(tableEntry, root)) { SanityChecker.liveObjectCount++; trace.processNode(object); } } /** * Print out object information (used for warning and error messages) * * @param object The object to dump info for. */ public static void dumpObjectInformation(ObjectReference object) { Log.write(object); Log.write(" ["); Log.write(Space.getSpaceForObject(object).getName()); Log.write("] "); Log.writeln(VM.objectModel.getTypeDescriptor(object)); } }