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 */ 013 package org.mmtk.utility.sanitychecker; 014 015 import org.mmtk.plan.Plan; 016 import org.mmtk.plan.Trace; 017 import org.mmtk.plan.Simple; 018 import org.mmtk.plan.TraceLocal; 019 import org.mmtk.policy.Space; 020 import org.mmtk.utility.Constants; 021 import org.mmtk.utility.Log; 022 023 import org.mmtk.vm.VM; 024 025 import org.vmmagic.pragma.*; 026 import org.vmmagic.unboxed.*; 027 028 /** 029 * This class performs sanity checks for Simple collectors. 030 */ 031 @Uninterruptible 032 public final class SanityChecker implements Constants { 033 034 /* Counters */ 035 public static long referenceCount; 036 public static long rootReferenceCount; 037 public static long danglingReferenceCount; 038 public static long nullReferenceCount; 039 public static long liveObjectCount; 040 041 public static final int DEAD = -2; 042 public static final int ALIVE = -1; 043 public static final int UNSURE = 0; 044 045 public static final int LOG_SANITY_DATA_SIZE = 24; 046 047 /* Tracing */ 048 public Trace rootTrace; 049 public Trace checkTrace; 050 private final SanityDataTable sanityTable; 051 private boolean preGCSanity; 052 053 /* Local, but we only run the check trace single-threaded. */ 054 final SanityTraceLocal checkTraceLocal; 055 056 /* Linear scanning */ 057 private final SanityLinearScan scanner = new SanityLinearScan(this); 058 059 /**************************************************************************** 060 * Constants 061 */ 062 063 /** 064 * 065 */ 066 public SanityChecker() { 067 sanityTable = new SanityDataTable(Plan.sanitySpace, LOG_SANITY_DATA_SIZE); 068 checkTrace = new Trace(Plan.sanitySpace); 069 rootTrace = new Trace(Plan.sanitySpace); 070 checkTraceLocal = new SanityTraceLocal(checkTrace, this); 071 } 072 073 /** 074 * Perform any sanity checking collection phases. 075 * 076 * @param phaseId The id to proces 077 * @return {@code true} if the phase was handled. 078 */ 079 @NoInline 080 public boolean collectionPhase(int phaseId) { 081 if (phaseId == Simple.SANITY_SET_PREGC) { 082 preGCSanity = true; 083 return true; 084 } 085 086 if (phaseId == Simple.SANITY_SET_POSTGC) { 087 preGCSanity = false; 088 return true; 089 } 090 091 if (phaseId == Simple.SANITY_PREPARE) { 092 Log.writeln(""); 093 Log.write("============================== GC Sanity Checking "); 094 Log.writeln("=============================="); 095 Log.writeln(preGCSanity ? "Performing Pre-GC Sanity Checks..." : "Performing Post-GC Sanity Checks..."); 096 097 // Reset counters 098 referenceCount = 0; 099 nullReferenceCount = 0; 100 liveObjectCount = 0; 101 danglingReferenceCount = 0; 102 rootReferenceCount = 0; 103 104 // Clear data space 105 sanityTable.acquireTable(); 106 107 // Root trace 108 rootTrace.prepareNonBlocking(); 109 110 // Checking trace 111 checkTrace.prepareNonBlocking(); 112 checkTraceLocal.prepare(); 113 return true; 114 } 115 116 if (phaseId == Simple.SANITY_ROOTS) { 117 VM.scanning.resetThreadCounter(); 118 return true; 119 } 120 121 if (phaseId == Simple.SANITY_BUILD_TABLE) { 122 // Trace, checking for dangling pointers 123 checkTraceLocal.completeTrace(); 124 return true; 125 } 126 127 if (phaseId == Simple.SANITY_CHECK_TABLE) { 128 // Iterate over the reachable objects. 129 Address curr = sanityTable.getFirst(); 130 while (!curr.isZero()) { 131 ObjectReference ref = SanityDataTable.getObjectReference(curr); 132 int normalRC = SanityDataTable.getNormalRC(curr); 133 int rootRC = SanityDataTable.getRootRC(curr); 134 if (!preGCSanity) { 135 int expectedRC = VM.activePlan.global().sanityExpectedRC(ref, rootRC); 136 switch (expectedRC) { 137 case SanityChecker.ALIVE: 138 case SanityChecker.UNSURE: 139 // Always ok. 140 break; 141 case SanityChecker.DEAD: 142 // Never ok. 143 Log.write("ERROR: SanityRC = "); 144 Log.write(normalRC); 145 Log.write(", SpaceRC = 0 "); 146 SanityChecker.dumpObjectInformation(ref); 147 break; 148 default: 149 // A mismatch in an RC space 150 if (normalRC != expectedRC && VM.activePlan.global().lastCollectionFullHeap()) { 151 Log.write("WARNING: SanityRC = "); 152 Log.write(normalRC); 153 Log.write(", SpaceRC = "); 154 Log.write(expectedRC); 155 Log.write(" "); 156 SanityChecker.dumpObjectInformation(ref); 157 break; 158 } 159 } 160 } 161 curr = sanityTable.getNext(curr); 162 } 163 164 if (!preGCSanity && VM.activePlan.global().lastCollectionFullHeap()) { 165 VM.activePlan.global().sanityLinearScan(scanner); 166 } 167 return true; 168 } 169 170 if (phaseId == Simple.SANITY_RELEASE) { 171 checkTrace.release(); 172 sanityTable.releaseTable(); 173 checkTraceLocal.release(); 174 175 Log.writeln("roots\tobjects\trefs\tnull"); 176 Log.write(rootReferenceCount);Log.write("\t"); 177 Log.write(liveObjectCount);Log.write("\t"); 178 Log.write(referenceCount);Log.write("\t"); 179 Log.writeln(nullReferenceCount); 180 181 Log.write("========================================"); 182 Log.writeln("========================================"); 183 184 return true; 185 } 186 187 return false; 188 } 189 190 /** 191 * Process an object during a linear scan of the heap. We have already checked 192 * all objects in the table. So we are only interested in objects that are not in 193 * the sanity table here. We are therefore only looking for leaks here. 194 * 195 * @param object The object being scanned. 196 */ 197 public void scanProcessObject(ObjectReference object) { 198 if (sanityTable.getEntry(object, false).isZero()) { 199 // Is this a leak? 200 int expectedRC = VM.activePlan.global().sanityExpectedRC(object, 0); 201 202 if (expectedRC == SanityChecker.UNSURE) { 203 // Probably not. 204 return; 205 } 206 207 // Possibly 208 Log.write("WARNING: Possible leak, SpaceRC = "); 209 Log.write(expectedRC); 210 Log.write(" "); 211 SanityChecker.dumpObjectInformation(object); 212 } 213 } 214 215 /** 216 * Process an object during sanity checking, validating data, 217 * incrementing counters and enqueuing if this is the first 218 * visit to the object. 219 * 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 }