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    }