/*
* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.max.vma.tools.qa;
import java.util.*;
import java.io.*;
import com.oracle.max.vma.tools.qa.TransientVMAdviceHandlerTypes.*;
/**
* This is essentially the raw data read by {@link ProcessLog} organized
* differently. The {@link #objects} map contains all objects present in the trace,
* including those whose construction was "unseen". The value in the map is the
* {@link ObjectRecord} for that object.
*
* To facilitate querying by class or classloader an additional data structure
* is maintained, rooted in a {@link Map} keyed by the ids of classloader objects. The
* associated value is a {@link SortedMap} of class names to {@link ClassRecords}.
*
* In principle, there can be multiple instances of this class corresponding to different traces.
* In practice, that feature was used mostly in the historic version that worked with the
* output from multiple "isolates" in the (MVM) multi-tasking virtual machine.
*
*/
public class TraceRun {
/**
* Uniquely identifies the trace.
*/
public final String name;
public final ArrayList<AdviceRecord> adviceRecordList;
/**
* The objects map. The key is the id, qualified by the allocation, aka {@link ObjectRecord#id}.
*/
public final Map<String, ObjectRecord> objects;
public final Map<String, ThreadRecord> threads;
/**
* The class loaders map. The key is the id of the class loader instance
* and the value is a map from class names
* to {@link ClassRecord class records}, denoting all the classes loaded
* by that class loader.
*/
public final Map<String, SortedMap<String, ClassRecord>> classLoaders;
public final Map<String, ObjectRecord> missingConstructors;
public final ArrayList<AllocationEpoch> allocationEpochs;
public final long objectCount;
public final long arrayCount;
public final int missingConstructorCount;
public final long lastTime;
public final long startTime;
/**
* The following fields are caches for performance reasons. A value of -1
* indicates an unset cache value.
*/
private static long immutableObjectCount = -1;
private static long immutableArrayCount = -1;
private static class CheckCounts {
long checkArrayCount;
long checkObjCount;
}
public TraceRun(String name, ArrayList<AdviceRecord> adviceRecordList, Map<String, ObjectRecord> objects, Map<String, ThreadRecord> threads, Map<String, SortedMap<String, ClassRecord>> classLoaders,
Map<String, ObjectRecord> missingConstructors, long objectCount, long arrayCount,
int missingConstructorCount, ArrayList<AllocationEpoch> allocationEpochs, long startTime, long lastTime) {
this.name = name;
this.adviceRecordList = adviceRecordList;
this.objects = objects;
this.threads = threads;
this.classLoaders = classLoaders;
this.missingConstructors = missingConstructors;
this.objectCount = objectCount;
this.arrayCount = arrayCount;
this.missingConstructorCount = missingConstructorCount;
this.allocationEpochs = allocationEpochs;
this.startTime = startTime;
this.lastTime = lastTime;
Visitor visitor = new Visitor() {
CheckCounts checkCounts = new CheckCounts();
@Override
public void visit(TraceRun traceRun, ObjectRecord td,
PrintStream ps, Object[] args) {
if (td.isArray()) {
checkCounts.checkArrayCount++;
} else {
checkCounts.checkObjCount++;
}
td.setImmutable();
}
@Override
public Object getResult() {
return checkCounts;
}
};
visit(visitor, null, null);
CheckCounts checkCounts = (CheckCounts) visitor.getResult();
assert checkCounts.checkArrayCount == arrayCount;
assert checkCounts.checkObjCount == objectCount;
}
public long relTime(long time) {
return time - startTime;
}
/**
* This is a convenience method for backwards compatibility with queries
* that assume that it is possible to iterate over all the loaded classes
* (in all classloaders).
*/
public Iterator<ClassRecord> getClassesIterator() {
return new ClassIterator(classLoaders.values().iterator());
}
public long getImmutableObjectCount() {
if (immutableObjectCount < 0) {
Visitor visitor = new Visitor() {
private long count = 0;
@Override
public void visit(TraceRun traceRun, ObjectRecord td,
PrintStream ps, Object[] args) {
long mlt = td.getModifyLifeTime();
if (mlt == 0) {
if (!(td.getClassRecord().isArray())) {
count++;
}
}
}
@Override
public Object getResult() {
return new Long(count);
}
};
immutableObjectCount = ((Long) visit(visitor, null, null)).longValue();
}
return immutableObjectCount;
}
public long getImmutableArrayCount() {
if (immutableArrayCount < 0) {
Visitor visitor = new Visitor() {
private long count = 0;
@Override
public void visit(TraceRun traceRun, ObjectRecord td,
PrintStream ps, Object[] args) {
long mlt = td.getModifyLifeTime();
if (mlt == 0) {
if (td.getClassRecord().isArray()) {
count++;
}
}
}
@Override
public Object getResult() {
return new Long(count);
}
};
immutableArrayCount = ((Long) visit(visitor, null, null)).longValue();
}
return immutableArrayCount;
}
public ClassRecord getClassRecord(String classLoaderId, String className) {
SortedMap<String, ClassRecord> classes = classLoaders.get(classLoaderId);
if (classes == null) {
return null;
} else {
ClassRecord cr = classes.get(className);
return cr;
}
}
/*
* Traversal (visitor) support for "objects" map
*/
public static abstract class Visitor {
public abstract void visit(TraceRun traceRun, ObjectRecord td,
PrintStream ps, Object[] args);
public Object getResult() {
return null;
}
}
public Object visit(Visitor visitor, PrintStream ps, Object[] args) {
Iterator<ObjectRecord> iter = objects.values().iterator();
while (iter.hasNext()) {
ObjectRecord td = iter.next();
visitor.visit(this, td, ps, args);
}
return visitor.getResult();
}
static class ClassIterator implements Iterator<ClassRecord> {
private Iterator<SortedMap<String, ClassRecord>> clIter; // underlying iterator on classLoaders map
private Iterator<ClassRecord> classesIter; // current iterator on current classes map
ClassIterator(Iterator<SortedMap<String, ClassRecord>> clIter) {
this.clIter = clIter;
nextClassesIter();
}
public boolean hasNext() {
if (classesIter == null) {
return false;
} else {
if (classesIter.hasNext()) {
return true;
} else {
nextClassesIter();
return (classesIter != null) && classesIter.hasNext();
}
}
}
public ClassRecord next() {
if (classesIter.hasNext()) {
return classesIter.next();
} else {
// move on to next classloader if any
nextClassesIter();
if ((classesIter != null) && classesIter.hasNext()) {
return classesIter.next();
} else {
throw new NoSuchElementException();
}
}
}
public void remove() {
throw new UnsupportedOperationException();
}
private void nextClassesIter() {
if (clIter.hasNext()) {
classesIter = clIter.next().values().iterator();
} else {
classesIter = null;
}
}
}
}