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}