001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.mmtk.utility.sanitychecker; 014 015import org.mmtk.plan.Plan; 016import org.mmtk.plan.Trace; 017import org.mmtk.plan.Simple; 018import org.mmtk.plan.TraceLocal; 019import org.mmtk.policy.Space; 020import org.mmtk.utility.Log; 021 022import org.mmtk.vm.VM; 023 024import org.vmmagic.pragma.*; 025import org.vmmagic.unboxed.*; 026 027/** 028 * This class performs sanity checks for Simple collectors. 029 */ 030@Uninterruptible 031public final class SanityChecker { 032 033 /* Counters */ 034 public static long referenceCount; 035 public static long rootReferenceCount; 036 public static long danglingReferenceCount; 037 public static long nullReferenceCount; 038 public static long liveObjectCount; 039 040 public static final int DEAD = -2; 041 public static final int ALIVE = -1; 042 public static final int UNSURE = 0; 043 044 public static final int LOG_SANITY_DATA_SIZE = 24; 045 046 /* Tracing */ 047 public Trace rootTrace; 048 public Trace checkTrace; 049 private final SanityDataTable sanityTable; 050 private boolean preGCSanity; 051 052 /* Local, but we only run the check trace single-threaded. */ 053 final SanityTraceLocal checkTraceLocal; 054 055 /* Linear scanning */ 056 private final SanityLinearScan scanner = new SanityLinearScan(this); 057 058 /**************************************************************************** 059 * Constants 060 */ 061 062 /** 063 * 064 */ 065 public SanityChecker() { 066 sanityTable = new SanityDataTable(Plan.sanitySpace, LOG_SANITY_DATA_SIZE); 067 checkTrace = new Trace(Plan.sanitySpace); 068 rootTrace = new Trace(Plan.sanitySpace); 069 checkTraceLocal = new SanityTraceLocal(checkTrace, this); 070 } 071 072 /** 073 * Perform any sanity checking collection phases. 074 * 075 * @param phaseId The id to proces 076 * @return {@code true} if the phase was handled. 077 */ 078 @NoInline 079 public boolean collectionPhase(int phaseId) { 080 if (phaseId == Simple.SANITY_SET_PREGC) { 081 preGCSanity = true; 082 return true; 083 } 084 085 if (phaseId == Simple.SANITY_SET_POSTGC) { 086 preGCSanity = false; 087 return true; 088 } 089 090 if (phaseId == Simple.SANITY_PREPARE) { 091 Log.writeln(""); 092 Log.write("============================== GC Sanity Checking "); 093 Log.writeln("=============================="); 094 Log.writeln(preGCSanity ? "Performing Pre-GC Sanity Checks..." : "Performing Post-GC Sanity Checks..."); 095 096 // Reset counters 097 referenceCount = 0; 098 nullReferenceCount = 0; 099 liveObjectCount = 0; 100 danglingReferenceCount = 0; 101 rootReferenceCount = 0; 102 103 // Clear data space 104 sanityTable.acquireTable(); 105 106 // Root trace 107 rootTrace.prepareNonBlocking(); 108 109 // Checking trace 110 checkTrace.prepareNonBlocking(); 111 checkTraceLocal.prepare(); 112 return true; 113 } 114 115 if (phaseId == Simple.SANITY_ROOTS) { 116 VM.scanning.resetThreadCounter(); 117 return true; 118 } 119 120 if (phaseId == Simple.SANITY_BUILD_TABLE) { 121 // Trace, checking for dangling pointers 122 checkTraceLocal.completeTrace(); 123 return true; 124 } 125 126 if (phaseId == Simple.SANITY_CHECK_TABLE) { 127 // Iterate over the reachable objects. 128 Address curr = sanityTable.getFirst(); 129 while (!curr.isZero()) { 130 ObjectReference ref = SanityDataTable.getObjectReference(curr); 131 int normalRC = SanityDataTable.getNormalRC(curr); 132 int rootRC = SanityDataTable.getRootRC(curr); 133 if (!preGCSanity) { 134 int expectedRC = VM.activePlan.global().sanityExpectedRC(ref, rootRC); 135 switch (expectedRC) { 136 case SanityChecker.ALIVE: 137 case SanityChecker.UNSURE: 138 // Always ok. 139 break; 140 case SanityChecker.DEAD: 141 // Never ok. 142 Log.write("ERROR: SanityRC = "); 143 Log.write(normalRC); 144 Log.write(", SpaceRC = 0 "); 145 SanityChecker.dumpObjectInformation(ref); 146 break; 147 default: 148 // A mismatch in an RC space 149 if (normalRC != expectedRC && VM.activePlan.global().lastCollectionFullHeap()) { 150 Log.write("WARNING: SanityRC = "); 151 Log.write(normalRC); 152 Log.write(", SpaceRC = "); 153 Log.write(expectedRC); 154 Log.write(" "); 155 SanityChecker.dumpObjectInformation(ref); 156 break; 157 } 158 } 159 } 160 curr = sanityTable.getNext(curr); 161 } 162 163 if (!preGCSanity && VM.activePlan.global().lastCollectionFullHeap()) { 164 VM.activePlan.global().sanityLinearScan(scanner); 165 } 166 return true; 167 } 168 169 if (phaseId == Simple.SANITY_RELEASE) { 170 checkTrace.release(); 171 sanityTable.releaseTable(); 172 checkTraceLocal.release(); 173 174 Log.writeln("roots\tobjects\trefs\tnull"); 175 Log.write(rootReferenceCount);Log.write("\t"); 176 Log.write(liveObjectCount);Log.write("\t"); 177 Log.write(referenceCount);Log.write("\t"); 178 Log.writeln(nullReferenceCount); 179 180 Log.write("========================================"); 181 Log.writeln("========================================"); 182 183 return true; 184 } 185 186 return false; 187 } 188 189 /** 190 * Process an object during a linear scan of the heap. We have already checked 191 * all objects in the table. So we are only interested in objects that are not in 192 * the sanity table here. We are therefore only looking for leaks here. 193 * 194 * @param object The object being scanned. 195 */ 196 public void scanProcessObject(ObjectReference object) { 197 if (sanityTable.getEntry(object, false).isZero()) { 198 // Is this a leak? 199 int expectedRC = VM.activePlan.global().sanityExpectedRC(object, 0); 200 201 if (expectedRC == SanityChecker.UNSURE) { 202 // Probably not. 203 return; 204 } 205 206 // Possibly 207 Log.write("WARNING: Possible leak, SpaceRC = "); 208 Log.write(expectedRC); 209 Log.write(" "); 210 SanityChecker.dumpObjectInformation(object); 211 } 212 } 213 214 /** 215 * Process an object during sanity checking, validating data, 216 * incrementing counters and enqueuing if this is the first 217 * visit to the object. 218 * 219 * @param trace the trace to use for processing 220 * @param object The object to mark. 221 * @param root {@code true} If the object is a root. 222 */ 223 public void processObject(TraceLocal trace, ObjectReference object, boolean root) { 224 SanityChecker.referenceCount++; 225 if (root) SanityChecker.rootReferenceCount++; 226 227 if (object.isNull()) { 228 SanityChecker.nullReferenceCount++; 229 return; 230 } 231 232 if (Plan.SCAN_BOOT_IMAGE && Space.isInSpace(Plan.VM_SPACE, object)) { 233 return; 234 } 235 236 // Get the table entry. 237 Address tableEntry = sanityTable.getEntry(object, true); 238 239 if (SanityDataTable.incRC(tableEntry, root)) { 240 SanityChecker.liveObjectCount++; 241 trace.processNode(object); 242 } 243 } 244 245 /** 246 * Print out object information (used for warning and error messages) 247 * 248 * @param object The object to dump info for. 249 */ 250 public static void dumpObjectInformation(ObjectReference object) { 251 Log.write(object); 252 Log.write(" ["); 253 Log.write(Space.getSpaceForObject(object).getName()); 254 Log.write("] "); 255 Log.writeln(VM.objectModel.getTypeDescriptor(object)); 256 } 257}