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 }