/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.harness.sanity; import java.util.Map; import java.util.Set; import org.mmtk.harness.lang.Trace; import org.mmtk.harness.lang.Trace.Item; import org.mmtk.harness.vm.ObjectModel; import org.mmtk.policy.Space; import org.vmmagic.unboxed.ObjectReference; /** * Collection sanity checker for the MMTk Harness. This class is used to * take heap snapshots before and after a collection, and ensure that the * objects live after the collection exactly those live before collection. * * Note that this is in no way problematic for partial-heap collectors that * only collect part of the heap - uncollected objects in the mature space * don't affect the live set either way. * * We also check: * - That no objects are duplicated */ public class GcSanity { private HeapSnapshot before; private HeapSnapshot after = null; /** * Maximum #errors of each type that will be reported */ private static final int ERROR_LIMIT = 10; /** Take a snapshot of the heap before collection */ public void snapshotBefore() { before = new HeapSnapshot(); } /** Take a snapshot of the heap after collection */ public void snapshotAfter() { after = new HeapSnapshot(); } /** @return The set of live objects */ public Set<ObjectReference> liveSet() { return (after == null ? before : after).getLiveObjects(); } /** * Assert that the heap state is sane (immediately after a collection) */ public void assertSanity() { Trace.trace(Item.SANITY,"Heap size by ID before: %d, after: %d", before.sizeById(), after.sizeById()); Trace.trace(Item.SANITY,"Heap size by Addr before: %d, after: %d", before.sizeByAddress(), after.sizeByAddress()); /* * Assert that there are no duplicated objects, ie objects with the same * object ID in more than one place. */ if (after.getDuplicates().size() > 0) { int errors = 0; for (Set<HeapEntry> aliasSet : after.getDuplicates()) { HeapEntry first = aliasSet.iterator().next(); boolean firstDup = true; System.err.printf("### Object %s is duplicated in the heap: ",first.getId()); for (HeapEntry entry : aliasSet) { if (firstDup) { firstDup = false; } else { System.err.printf(", "); } ObjectReference object = entry.getObject(); System.err.printf("%s/%s",object,Space.getSpaceForObject(object).getName()); } System.err.printf("%n"); if (errors++ > ERROR_LIMIT) { System.err.printf("[SANITY] Error limit reached...%n"); break; } } throw new AssertionError("Duplicate objects found in heap"); } /* * Assert that collection preserved the number of objects in the heap */ if (before.sizeById() != after.sizeById()) { System.err.printf("[SANITY] ERROR: Live object population has changed, before : %d objects, after: %d objects%n", before.sizeById(),after.sizeById()); dumpDifferencesAndDie(); } assert before.sizeByAddress() == after.sizeByAddress() : "before : "+before.sizeByAddress()+" objects, after: "+after.sizeByAddress()+" objects"; /* * Assert that the collection preserved the live set */ if (!before.getLive().equals(after.getLive())) { System.err.printf("[SANITY] ERROR: Live object set has changed%n"); dumpDifferencesAndDie(); } if (Trace.isEnabled(Item.SANITY)) { printSpaceStats("before", before); printSpaceStats("after", after); } } private void dumpDifferencesAndDie() throws AssertionError { /* Dump any objects killed by the collector */ int errors = 0; for (int id : before.getLive()) { if (!after.getLive().contains(id)) { System.err.printf("[SANITY] Object %d was killed by the collector%n",id); HeapEntry entry = before.getEntry(id); System.err.printf("[SANITY] %s%n",ObjectModel.getString(entry.getObject())); if (errors++ > ERROR_LIMIT) { System.err.printf("[SANITY] Error limit reached...%n"); break; } } } /* Dump any objects created by the collector */ errors = 0; for (int id : after.getLive()) { if (!before.getLive().contains(id)) { System.err.printf("[SANITY] Object %d was created by the collector%n",id); HeapEntry entry = after.getEntry(id); System.err.printf("[SANITY] %s%n",ObjectModel.getString(entry.getObject())); if (errors++ > ERROR_LIMIT) { System.err.printf("[SANITY] Error limit reached...%n"); break; } } } throw new AssertionError("Live set has changed"); } private void printSpaceStats(String tag, HeapSnapshot snapshot) { Trace.printf(Item.SANITY,tag+" "); for (Map.Entry<String,Integer> statistic : snapshot.getSpaceStats().entrySet()) { String name = statistic.getKey(); int count = statistic.getValue(); if (count > 0) Trace.printf("%-8s:%6d ",name,count); } Trace.printf("%n"); } }