/*
* 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.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
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.Address;
import org.vmmagic.unboxed.ObjectReference;
/**
* A snapshot of the heap, taken by the harness without using core MMTk code.
*/
public class HeapSnapshot implements HeapVisitor {
private static final boolean VERBOSE = true;
/** The objects in the heap, by address */
private final Map<ObjectReference,HeapEntry> byAddress = new HashMap<ObjectReference,HeapEntry>();
/** The objects in the heap, by ID */
private final Map<Integer,HeapEntry> byId = new HashMap<Integer,HeapEntry>();
/** Statistics: objects in each space */
private final Map<String,Integer> spaceStats = new TreeMap<String,Integer>();
/** Duplicate objects */
private final Map<Integer,Set<HeapEntry>> duplicates = new HashMap<Integer,Set<HeapEntry>>();
/**
* Take a snapshot of the current heap
*/
public HeapSnapshot() {
Traversal.traverse(this);
}
/** @return the space statistics */
public Map<String, Integer> getSpaceStats() {
return spaceStats;
}
/** @return the number of distinct object addresses in the heap */
public int sizeByAddress() {
return byAddress.size();
}
/** @return the number of distinct objects in the heap */
public int sizeById() {
return byId.size();
}
/** @return the set of live object IDs */
public Set<Integer> getLive() {
return byId.keySet();
}
/** @return the set of live object references */
public Set<ObjectReference> getLiveObjects() {
return byAddress.keySet();
}
/**
* Get an entry - fails with a runtime exception if the object is duplicated.
* @param id Entry ID
* @return The entry
*/
public HeapEntry getEntry(int id) {
return byId.get(id);
}
/**
* @return the set of duplicate objects (live objects that are pointed to
* at more than one address
*/
public Set<Set<HeapEntry>> getDuplicates() {
return Collections.unmodifiableSet(new HashSet<Set<HeapEntry>>(duplicates.values()));
}
private void addDuplicate(HeapEntry original, HeapEntry dup) {
Set<HeapEntry> entries = duplicates.get(original.getId());
if (entries == null) {
entries = new TreeSet<HeapEntry>();
entries.addAll(Arrays.asList(original,dup));
duplicates.put(original.getId(), entries);
} else {
entries.add(dup);
}
}
private void addEntryById(HeapEntry entry) {
if (VERBOSE) Trace.trace(Item.SANITY,"Found object %d at address %s",entry.getId(),entry.getObject());
HeapEntry oldEntry = byId.get(entry.getId());
if (oldEntry == null) {
byId.put(entry.getId(), entry);
} else if (!oldEntry.equals(entry)) {
addDuplicate(oldEntry,entry);
Trace.printf(Item.SANITY,"Duplicate object found in heap %n%s",entry.toString());
}
}
private void addSpaceStats(ObjectReference object) {
String name = Space.getSpaceForObject(object).getName();
Integer count = spaceStats.get(name);
if (count == null) {
count = Integer.valueOf(0);
}
spaceStats.put(name, count+1);
}
/**
* @see HeapVisitor#visitObject(ObjectReference, boolean, boolean)
*/
@Override
public void visitObject(ObjectReference object, boolean root, boolean marked) {
if (VERBOSE) {
Trace.trace(Item.SANITY,"Visiting %sobject at address %s",root ? "root ":"",object);
}
int id = ObjectModel.getId(object);
if (!ObjectModel.hasValidId(object)) {
System.err.printf("### Invalid %sobject id=%d found at address %s%n",root ? "root ":"", id, object);
}
if (VERBOSE) {
Trace.trace(Item.SANITY,"Visiting %sobject %d at address %s",root ? "root ":"",id,object);
}
HeapEntry entry = byAddress.get(object);
if (!marked) {
assert entry == null;
entry = new HeapEntry(object);
byAddress.put(object, entry);
addEntryById(entry);
addSpaceStats(object);
}
if (root) entry.setRootReachable();
entry.incRefCount();
}
/**
* @see HeapVisitor#visitPointer(ObjectReference, Address, ObjectReference)
*/
@Override
public void visitPointer(ObjectReference source, Address slot, ObjectReference target) {
// Do nothing
}
}