/*******************************************************************************
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.kato.jvmti.reader;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.stream.FileImageInputStream;
import javax.tools.diagnostics.image.CorruptDataException;
import javax.tools.diagnostics.image.DataUnavailable;
import javax.tools.diagnostics.runtime.java.JavaStackFrame;
import javax.tools.diagnostics.runtime.java.JavaThread;
import javax.tools.diagnostics.runtime.java.JavaVariable;
import org.apache.kato.jvmti.javaruntime.model.DeferredReference;
import org.apache.kato.jvmti.javaruntime.model.JClass;
import org.apache.kato.jvmti.javaruntime.model.JClassLoader;
import org.apache.kato.jvmti.javaruntime.model.JField;
import org.apache.kato.jvmti.javaruntime.model.JLocalVariable;
import org.apache.kato.jvmti.javaruntime.model.JLocalVariableTableEntry;
import org.apache.kato.jvmti.javaruntime.model.JLocation;
import org.apache.kato.jvmti.javaruntime.model.JMethod;
import org.apache.kato.jvmti.javaruntime.model.JMonitor;
import org.apache.kato.jvmti.javaruntime.model.JObject;
import org.apache.kato.jvmti.javaruntime.model.JStackFrame;
import org.apache.kato.jvmti.javaruntime.model.JThread;
import org.apache.kato.jvmti.javaruntime.model.Model;
import org.apache.kato.jvmti.util.CachedRandomAccessFile;
/**
*
* CJVMTIBinReader - Reads cjvmti dump files
*
*/
public class CJVMTIBinReader {
// This is literally the String in the dump file. Also reported by JavaRuntime
public static final String CJVMTI_VERSION_STRING = "CJVMTI V0.01";
private static final byte CJVMTI_BYTE = 0x01;
private static final byte CJVMTI_CHAR = 0x02;
private static final byte CJVMTI_DOUBLE = 0x03;
private static final byte CJVMTI_FLOAT = 0x04;
private static final byte CJVMTI_INT = 0x05;
private static final byte CJVMTI_LONG = 0x06;
private static final byte CJVMTI_OBJECT = 0x07;
private static final byte CJVMTI_SHORT = 0x08;
private static final byte CJVMTI_BOOLEAN = 0x09;
private static final byte CJVMTI_OBJECTARRAY = 0x0a;
private static final byte CJVMTI_BYTE_ARRAY = 0x0b;
private static final byte CJVMTI_CHAR_ARRAY = 0x0c;
private static final byte CJVMTI_DOUBLE_ARRAY = 0x0d;
private static final byte CJVMTI_FLOAT_ARRAY = 0x0e;
private static final byte CJVMTI_INT_ARRAY = 0x0f;
private static final byte CJVMTI_LONG_ARRAY = 0x10;
private static final byte CJVMTI_OBJECT_ARRAY = 0x11;
private static final byte CJVMTI_SHORT_ARRAY = 0x12;
private static final byte CJVMTI_BOOLEAN_ARRAY = 0x13;
private static final byte CJVMTI_METHOD = 0x14;
private static final byte CJVMTI_THREAD_GROUP = 0x15;
private static final byte CJVMTI_THREAD = 0x16;
private static final byte CJVMTI_FRAME = 0x17;
private static final byte CJVMTI_FIELD_MODIFIERS = 0x18;
private static final byte CJVMTI_MONITORS = 0x19;
private static final byte CJVMTI_CLASSLOADER = 0x1a;
private static final byte CJVMTI_CLASSLOADERS = 0x1b;
private static final byte CJVMTI_LOCAL_NATIVECALL = 0x1c;
private static final byte CJVMTI_CLASS = 0x1d;
private static final byte CJVMTI_NULL_OBJECT = 0x00;
private static final byte CJVMTI_CLASS_INSTANCE_FIELDS = 0x1e;
private static final byte CJVMTI_CLASS_STATIC_FIELDS = 0x1f;
private static final byte CJVMTI_CLASS_FIELDS = 0x20;
private static final long CJVMTI_NULL_OBJECT_ARRAY = 0x00;
private static final byte CJVMTI_LOCAL_VARIABLE = 0x21;
private static final byte CJVMTI_JVMTI_ERROR = 0x22;
private static final byte CJVMTI_SUPERCLASS_FIELDS = 0x23;
private static final byte CJVMTI_CLASS_INTERFACES = 0x24;
private long selfGeneratedIDs;
private int selfGeneratedMonitorIDs = 1;
FileImageInputStream variablesIn = null;
Model model = new Model();
static Logger log = CLogger.logr;
JClassLoader readerJCL;// Class loader for reader generated classes
JClassLoader bootJCL;// Class loader for boot class loader (return nulls in
// jvmti
JClass intArray;// "[int"
//
JClass floatArray; // "[float"
//
JClass shortArray; // "[short"
//
JClass longArray; // "[long"
//
JClass byteArray; // "[byte"
//
JClass booleanArray; // "[boolean"
//
JClass doubleArray; // "[double"
//
JClass charArray; // "[char"
JObject nullObject;
HashMap<JObject, JClass> objectArrayClasses;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
File f = new File(args[0]);
log.setLevel(Level.ALL);
ConsoleHandler handle = new ConsoleHandler();
handle.setLevel(Level.ALL);
log.addHandler(handle);
log.log(Level.FINEST, f.toString());
CJVMTIBinReader cjvm = null;
try {
cjvm = new CJVMTIBinReader(f);
} catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
log.log(Level.FINER, "");
log.log(Level.FINER, "");
log.log(Level.FINER, "");
List<JavaThread> jthreads = cjvm.getModel().getThreads();
for (JavaThread t : jthreads) {
try {
log.log(Level.FINEST, t.getName());
for (JavaStackFrame jsf : t.getStackFrames()) {
for (JavaVariable jv : jsf.getVariables()) {
try {
log.log(Level.FINEST, "" + jv.getName() + " "
+ jv.getSignature() + " ");
} catch (DataUnavailable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
log.log(Level.FINEST, "" + jv.getValue());
}
}
} catch (CorruptDataException e) {
e.printStackTrace();
}
}
log.log(Level.INFO, "Finished test read");
}
/**
* Attempts to read in a cjvmti dump file
* @param base Target file
* @throws IOException
*/
public CJVMTIBinReader(File base) throws IOException {
boolean bbigEndian = true;
// Currently split file setout, open variable file
//RandomAccessFile raf2 = new RandomAccessFile(base, "r");
RandomAccessFile raf2 = new CachedRandomAccessFile(base);
variablesIn = new FileImageInputStream(raf2);
selfGeneratedIDs = variablesIn.length() + 1; // Ensure classes generated by the reader do not collide with dump file class references
variablesIn.readLong();
if (variablesIn.readInt() == 1) {
variablesIn.setByteOrder(ByteOrder.BIG_ENDIAN);
} else {
variablesIn.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
variablesIn.seek(0);
variablesIn.readLong();
// check Endian setting
if (variablesIn.readInt() != 1) {
System.err
.println("Incorrect format for reader/failed to set endian");
throw new IOException("Incorrect format for reader/failed to set endian");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 12; i++) {
sb.append((char) variablesIn.readByte());
}
if (!sb.toString().equals(CJVMTI_VERSION_STRING)) {
System.err.println("Incorrect format for reader _" + sb.toString()
+ "_");
throw new IOException();
}
log.log(Level.INFO, CJVMTI_VERSION_STRING);
log.log(Level.INFO, "Starting reading in..");
time = variablesIn.readLong();
Date creationDate = new Date((time * 1000));
log.log(Level.INFO, "Date of creation: " + creationDate.toString());
generateClassLoaderForReader();
generatePrimitiveArrayClasses();
byte recordGroup = 0;
try {
recordGroup = (byte) variablesIn.readUnsignedByte();
log.log(Level.FINEST, "RG: " + recordGroup);
} catch (EOFException ee) {
log.log(Level.FINEST, "Finished");
}
switch (recordGroup) {
case CJVMTI_THREAD:
int numThreads = variablesIn.readInt();
long threadRefs[] = new long[numThreads];
for (int i = 0; i < numThreads; i++){
threadRefs[i] = variablesIn.readLong();
}
for (int i = 0; i < numThreads; i++) {
log.finest("Reading thread at "+Long.toHexString(threadRefs[i]));
nreadThread(threadRefs[i]);
}
break;
default:
error("record group " + recordGroup + " is not understood");
}
// Resolve field references until empty.
while (!unresolvedReferences.isEmpty()) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "# unresolved refs="+unresolvedReferences.size());
}
// Remove first reference and process.
long pos = unresolvedReferences.remove(0);
JObject jobj = (JObject) model.getObjectAtAddress(pos);
if (jobj == null) {
model.putObject(pos, nreadObject(pos));
}
}
log.log(Level.INFO, "CJVMTI bin reader complete");
}
/**
* Local variable table information
* @return
* @throws IOException
*/
private JLocalVariableTableEntry nlocalVarTable()
throws IOException {
// log.log(Level.FINEST,"Get local var table");
String name = readUTFString(variablesIn);
String sig = readUTFString(variablesIn);
String genSig = readUTFString(variablesIn);
int length = variablesIn.readInt();
int slot = variablesIn.readInt();
long start_location = variablesIn.readLong();
JLocalVariableTableEntry jlvt = new JLocalVariableTableEntry();
jlvt.setName(name);
jlvt.sig = sig;
jlvt.gensig = genSig;
jlvt.length = length;
jlvt.slot = slot;
jlvt.start = (int) start_location;
return jlvt;
}
/**
* Method information
* @param c Defining class
* @throws IOException
*/
private void nmethodDetails(JClass c) throws IOException {
if (variablesIn.readByte() != CJVMTI_METHOD)
throw new IOException();
long methodID = variablesIn.readLong();
String mName = readUTFString(variablesIn);
String mSig = readUTFString(variablesIn);
String mGenSig = readUTFString(variablesIn);
int modifiers = variablesIn.readInt();
int count = variablesIn.readInt();
JMethod jm = model.getMethod(methodID);
jm.name = mName;
jm.signature = mSig;
if (mGenSig.equals(" ")) {
jm.setGenericsignature(null); // Not currently keeping it null, left as reminder
}
jm.setGenericsignature(mGenSig);
jm.mods = modifiers;
for (int i = 0; i < count; i++) {
JLocalVariableTableEntry jlte = nlocalVarTable();
jm.addLocalVariableTableEntry(jlte);
}
// Line number table
int lntLength = variablesIn.readInt();
for (int i = 0; i < lntLength; i++) {
int lNum = variablesIn.readInt();
long bytePos = variablesIn.readLong();
jm.addLineNumberEntry(lNum, bytePos);
}
c.addMethod(jm);
}
/**
* Static field details
* @param c Defining class
* @return Number of static fields
* @throws IOException
*/
private int nstaticFieldDetails(JClass c) throws IOException {
int fieldCount = variablesIn.readInt();
for (int i = 0; i < fieldCount; i++) {
int modifiers = variablesIn.readInt();
long fieldID = variablesIn.readLong();
String name = readUTFString(variablesIn);
String sig = readUTFString(variablesIn);
String genSig = readUTFString(variablesIn);
JField fl = c.getField(fieldID);
fl.name = name;
fl.genericsignature = genSig;
fl.signature = sig;
fl.staticField = true;
fl.modifiers = modifiers;
fl.index = i; // Used to ensure references are filled in the order when filling in static values
}
return fieldCount;
}
/**
* Instance field details
* @param c Defining class
* @throws IOException
*/
private void instanceFieldDetails(JClass c) throws IOException {
int fieldCount = variablesIn.readInt();
for (int i = 0; i < fieldCount; i++) {
int modifiers = variablesIn.readInt();
long fieldID = variablesIn.readLong();
String name = readUTFString(variablesIn);
String sig = readUTFString(variablesIn);
String genSig = readUTFString(variablesIn);
JField fl = c.getField(fieldID);
fl.name = name;
fl.genericsignature = genSig;
fl.signature = sig;
fl.staticField = false;
fl.modifiers = modifiers;
fl.index = i; // used to fill in fields
}
}
/**
* Read class at defined position
* @param ref Position in file
* @return
* @throws IOException
*/
private JClass nreadClass(long ref) throws IOException {
if (ref == 0L) {
return null;
}
variablesIn.seek(ref);
short tag = variablesIn.readByte();
if (tag == CJVMTI_CLASS) {
return nreadClass();
} else {
log.log(Level.FINEST, "nreadClass: incorrect tag"+tag+" at 0x" + Long.toHexString(ref));
throw new IOException("unable to read class at offset "+ref);
}
}
/**
* Read class at current position
* @return
* @throws IOException
*/
private JClass nreadClass() throws IOException {
long id = variablesIn.getStreamPosition();
id--; // Class id/ImagePointer
JClass c = model.getClass(id);
// Prevent reference loops (check if this class is partly filled in)
if (c.classSig != null)
return c;
int modifiers = variablesIn.readInt();
c.modifiers = (short) modifiers;
log.log(Level.FINEST, "mods " + modifiers + " " + c.modifiers);
String name = readUTFString(variablesIn);
String sourceFile = readUTFString(variablesIn);
String genSig = readUTFString(variablesIn);
// TODO move this to C implementation
if (sourceFile.equals("NoSource")) {
if (!(name.indexOf("[")>-1)&&name.indexOf("L")>-1){
sourceFile = name.substring(name.lastIndexOf("/")+1, name.indexOf(";")); // Small attempt to extend number of sourcefiles reported to Eclipse
}
}
c.classSig = name;
c.classid = id;
c.sourceFile = sourceFile;
if (variablesIn.readByte() == CJVMTI_METHOD) {
int num = variablesIn.readInt();
for (int i = 0; i < num; i++) {
nmethodDetails(c);
}
} else {
throw new IOException();
}
if (variablesIn.readByte() == CJVMTI_CLASS_INSTANCE_FIELDS) {
instanceFieldDetails(c);
} else {
throw new IOException();
}
int numStaticFields = 0;
if (variablesIn.readByte() == CJVMTI_CLASS_STATIC_FIELDS) {
numStaticFields = nstaticFieldDetails(c);
} else {
throw new IOException();
}
long supC = 0;
if (variablesIn.readByte() == CJVMTI_SUPERCLASS_FIELDS) {
supC = variablesIn.readLong();
} else {
throw new IOException();
}
long interfaceClasses[];
if (variablesIn.readByte() == CJVMTI_CLASS_INTERFACES) {
int numInterfaces = variablesIn.readInt();
interfaceClasses = new long[numInterfaces];
for (int i = 0; i < interfaceClasses.length; i++) {
interfaceClasses[i] = variablesIn.readLong();
}
} else {
throw new IOException();
}
if (!(variablesIn.readByte() == CJVMTI_CLASS_STATIC_FIELDS)) {
throw new IOException();
}
long refs[] = new long[numStaticFields];
for (int i = 0; i < numStaticFields; i++) {
refs[i] = variablesIn.readLong();
}
long classLoaderRef = 0;
if (variablesIn.readByte() == CJVMTI_CLASSLOADERS) {
classLoaderRef = variablesIn.readLong();
} else {
throw new IOException();
}
c.superClassID = supC;
if (supC != CJVMTI_NULL_OBJECT) {
c.superclass = nreadClass(supC);
}
// Read static field references
for (JField fl : (List<JField>) c.getDeclaredFields()) {
if (fl.staticField) {
fl.staticValue = nreadReference(refs[fl.index]);
}
}
log.log(Level.FINEST, "num interf: " + interfaceClasses.length + " "
+ c.classSig);
// Follow interface references
for (long interfaceRef : interfaceClasses) {
JClass interfaceC = nreadClass(interfaceRef);
c.addInterface(interfaceC);
}
{
JClassLoader jcl = nreadClassLoader(classLoaderRef);
c.classloader = jcl;
try {
log.log(Level.FINEST, "Class loader : "
+ c.getClassLoader().getObject() + " for " + c.getName());
} catch (CorruptDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return c;
}
/**
* Loads in a classloader.
* This contains an optional reference to an object, and references to all of the
* classes within.
*
* @param position position of CJVMTI_CLASSLOADER record.
* @return CClassLoader
* @throws IOException
*/
private JClassLoader nreadClassLoader(long position) throws IOException {
JClassLoader jcl = model.getLoader(position);
// We've already processed this classloader.
if (jcl != null) {
return jcl;
}
// This is important - otherwise other classes might try and load this.
variablesIn.seek(position);
short tag = variablesIn.readByte();
if (tag != CJVMTI_CLASSLOADER) {
throw new IOException("Missing CJVMTI_CLASSLOADER tag, got "+tag);
}
long loaderObject = variablesIn.readLong();
int classesCount = variablesIn.readInt();
// Reuse preexisiting system class laoder if this is what we are.
// This is necessary for artificially generated types.
if (loaderObject == CJVMTI_NULL_OBJECT) {
jcl = model.getSystemClassLoader();
} else {
jcl = new JClassLoader();
}
// Store loader.
model.putLoader(position, jcl );
// Read array of class references.
// This avoids seeking even more than we do already.
long[] classesRef = new long[classesCount];
for (int i=0; i < classesCount; i++) {
classesRef[i] = variablesIn.readLong();
}
jcl.obj = nreadObject(loaderObject);
// add all of the classes loaded by this classloader.
for (int i=0; i < classesCount; i++) {
long classPos = classesRef[i];
if (classPos != CJVMTI_NULL_OBJECT) {
jcl.addClass(nreadClass(classPos));
}
}
return jcl;
}
/**
* Conveniance class for loading objects
* @param position Position in file
* @return Populated JObject or null
* @throws IOException
*/
private JObject nreadObject(long position) throws IOException {
variablesIn.seek(position);
if (position == CJVMTI_NULL_OBJECT) {
return null;
}
short id = variablesIn.readByte();
if (id == CJVMTI_OBJECT) {
return nreadObject();
} else {
throw new IOException("Record at 0x"+Long.toHexString(position)+" is not CJVMTI_OBJECT("
+CJVMTI_OBJECT+"), it is ("+id+")");
}
}
private List<Long> unresolvedReferences = new LinkedList<Long>();
private Long time;
/**
* Reads an object at the current file position
* @return Populated java object
* @throws IOException
*/
private JObject nreadObject() throws IOException {
long objectID = variablesIn.getStreamPosition() - 1;
long classRef = variablesIn.readLong();
int numInstanceFields = variablesIn.readInt();
long offset = variablesIn.getStreamPosition();
// log.log(Level.FINEST,"objectID " + Long.toHexString(objectID));
JClass c = nreadClass(classRef);
log.log(Level.FINEST, "Obj id " + Long.toHexString(objectID));
JObject obj = model.getObject(classRef, objectID);
if (obj.isVisited())
return obj; // Prevent reference loop
obj.setVisited(true);
// Calculate how many instance fields the reader can see on the class hierarchy
int instanceFieldCount = 0;
JClass superClass = c;
while (superClass != null) {
for (JField jf : (List<JField>) superClass.getDeclaredFields()) {
if (!jf.staticField)
instanceFieldCount++;
}
superClass = model.getClass(superClass.superClassID);
}
// Check they match the dump files recoard
if (numInstanceFields != instanceFieldCount) {
log.log(Level.FINEST, "Non-matching " + numInstanceFields
+ " found " + instanceFieldCount);
System.exit(0);
}
// Now read in all the references
long instanceRefs[] = new long[instanceFieldCount];
variablesIn.seek(offset);
int countUp = 0;
while (countUp < instanceFieldCount) {
instanceRefs[countUp] = variablesIn.readLong();
countUp++;
}
offset = variablesIn.getStreamPosition();
// Follow the references grabbing the correct jfield for the hierarchy of classes
int fieldCount = 0;
int fieldOffset;
fieldOffset = 0;
superClass = c;
while (superClass != null) {
for (JField jf : (List<JField>) superClass.getDeclaredFields()) {
if (!jf.staticField) {
fieldCount++;
if (instanceRefs[fieldOffset +jf.index] == 0x9999999999999999L){
log.fine("Error, unwritten reference");
}
log.finest("Reading in "+fieldCount);
long position = instanceRefs[fieldOffset + jf.index];
variablesIn.seek(position);
byte type = variablesIn.readByte();
if (type == CJVMTI_OBJECT) {
// Don't follow field references. Instead store a "deferred" reference
// as the field value,
JObject refObj = (JObject) model.getObjectAtAddress(position);
if (refObj == null) {
unresolvedReferences.add(position);
}
obj.addValue(jf.getFieldID(), new DeferredReference(position));
} else {
obj.addValue(jf.getFieldID(),
nreadReference(position));
}
}
}
fieldOffset = fieldCount;
superClass = model.getClass(superClass.superClassID);
}
log.log(Level.FINEST, " " + fieldCount + " " + numInstanceFields + " "
+ instanceFieldCount + " " + c.classSig);
return obj;
}
/**
* Takes a reference (and thus a file position) and returns a populated object
* @param position Position in file
* @return
* @throws IOException
*/
private Object nreadReference(long position) throws IOException {
if (position == CJVMTI_NULL_OBJECT) {
return null;
}
try {
// log.log(Level.FINEST,"Read at "+Long.toHexString(position));
variablesIn.seek(position);
int length = 0;
JObject obj;
byte type = variablesIn.readByte();
switch (type) {
case CJVMTI_CLASS:
return nreadClass();
case CJVMTI_OBJECT:
return nreadObject();
case CJVMTI_BYTE:
return variablesIn.readByte();
case CJVMTI_CHAR:
return variablesIn.readChar();
case CJVMTI_DOUBLE:
return variablesIn.readDouble();
case CJVMTI_FLOAT:
return variablesIn.readFloat();
case CJVMTI_INT:
return variablesIn.readInt();
case CJVMTI_LONG:
return variablesIn.readLong();
case CJVMTI_SHORT:
return variablesIn.readShort();
case CJVMTI_BOOLEAN:
return variablesIn.readBoolean();
case CJVMTI_OBJECT_ARRAY:
long classType = variablesIn.readLong();
length = variablesIn.readInt();
long currentReadPos = variablesIn.getStreamPosition();
log.log(Level.FINEST, "Object Array " + classType);
JClass arrayClass = nreadClass(classType);
variablesIn.seek(currentReadPos);
JObject objA[] = new JObject[length];
currentReadPos = variablesIn.getStreamPosition();
for (int counter = 0; counter < length; counter++) {
long objReference = variablesIn.readLong();
if (objReference == CJVMTI_NULL_OBJECT_ARRAY) {
objA[counter] = (JObject) null;
continue;
}
log.log(Level.FINEST, "Reading " + counter + " at "
+ Long.toHexString(objReference));
currentReadPos = variablesIn.getStreamPosition();
objA[counter] = (JObject) nreadReference(objReference);
variablesIn.seek(currentReadPos);
}
obj = model
.getObject(arrayClass.getID().getAddress(), position);
obj.setObjArray(objA);
return obj;
case CJVMTI_BYTE_ARRAY:
length = variablesIn.readInt();
Byte ba[] = new Byte[length];
for (int counter = 0; counter < length; counter++) {
ba[counter] = variablesIn.readByte();
}
obj = model.getObject(byteArray.classid, position);
obj.setObjArray(ba);
return obj;
case CJVMTI_CHAR_ARRAY:
length = variablesIn.readInt();
Character ca[] = new Character[length];
for (int counter = 0; counter < length; counter++) {
ca[counter] = (char) variablesIn.readShort();
}
obj = model.getObject(charArray.classid, position);
obj.setObjArray(ca);
return obj;
case CJVMTI_DOUBLE_ARRAY:
length = variablesIn.readInt();
Double da[] = new Double[length];
for (int counter = 0; counter < length; counter++) {
da[counter] = variablesIn.readDouble();
}
obj = model.getObject(doubleArray.classid, position);
obj.setObjArray(da);
return obj;
case CJVMTI_FLOAT_ARRAY:
length = variablesIn.readInt();
Float fa[] = new Float[length];
for (int counter = 0; counter < length; counter++) {
fa[counter] = variablesIn.readFloat();
}
obj = model.getObject(floatArray.classid, position);
obj.setObjArray(fa);
return obj;
case CJVMTI_INT_ARRAY:
length = variablesIn.readInt();
Integer i[] = new Integer[length];
for (int counter = 0; counter < length; counter++) {
i[counter] = variablesIn.readInt();
}
obj = model.getObject(intArray.classid, position);
obj.setObjArray(i);
return obj;
case CJVMTI_LONG_ARRAY:
length = variablesIn.readInt();
Long la[] = new Long[length];
for (int counter = 0; counter < length; counter++) {
la[counter] = variablesIn.readLong();
}
obj = model.getObject(longArray.classid, position);
obj.setObjArray(la);
return obj;
case CJVMTI_SHORT_ARRAY:
length = variablesIn.readInt();
Short sa[] = new Short[length];
for (int counter = 0; counter < length; counter++) {
sa[counter] = variablesIn.readShort();
}
obj = model.getObject(shortArray.classid, position);
obj.setObjArray(sa);
return obj;
case CJVMTI_BOOLEAN_ARRAY:
length = variablesIn.readInt();
Boolean[] za = new Boolean[length];
for (int counter = 0; counter < length; counter++) {
za[counter] = variablesIn.readBoolean();
}
obj = model.getObject(booleanArray.classid, position);
obj.setObjArray(za);
return obj;
case CJVMTI_NULL_OBJECT:
return null;
}
try {
System.out
.println("Unknown case "
+ type
+ " "
+ Long.toHexString((variablesIn
.getStreamPosition() - 1)));
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
} catch (Exception e) {
log.log(Level.FINEST, e.toString());
e.printStackTrace();
log.finest("Tried to read at "+Long.toHexString(position));
System.exit(0);
}
return null;
}
/**
* This is used to simulate the java boot class loader
*/
private void generateClassLoaderForReader() {
// generate system classloader
selfGeneratedIDs++;
bootJCL = (JClassLoader) model.getSystemClassLoader();
JClass bootCLC = model.getClass(selfGeneratedIDs);
bootCLC.classSig = "Ljava/lang/CJVMTISystemClassLoader;";
JObject bootObj = model.getObject(selfGeneratedIDs, selfGeneratedIDs);
bootJCL.obj = bootObj;
// rootJCL.addClass(bootCLC);
bootCLC.classloader = null;
readerJCL = bootJCL;
}
/**
* Manually add the primitive array classes
*/
private void generatePrimitiveArrayClasses() {
// "[int"
selfGeneratedIDs++;
intArray = model.getClass(selfGeneratedIDs);
intArray.classSig = "[I";
readerJCL.addClass(intArray);
//
// "[float"
selfGeneratedIDs++;
floatArray = model.getClass(selfGeneratedIDs);
floatArray.classSig = "[F";
readerJCL.addClass(floatArray);
//
// "[short"
selfGeneratedIDs++;
shortArray = model.getClass(selfGeneratedIDs);
shortArray.classSig = "[S";
readerJCL.addClass(shortArray);
//
// "[long"
selfGeneratedIDs++;
longArray = model.getClass(selfGeneratedIDs);
longArray.classSig = "[J";
readerJCL.addClass(longArray);
//
// "[byte"
selfGeneratedIDs++;
byteArray = model.getClass(selfGeneratedIDs);
byteArray.classSig = "[B";
readerJCL.addClass(byteArray);
//
// "[boolean"
selfGeneratedIDs++;
booleanArray = model.getClass(selfGeneratedIDs);
booleanArray.classSig = "[Z";
readerJCL.addClass(booleanArray);
//
// "[double"
selfGeneratedIDs++;
doubleArray = model.getClass(selfGeneratedIDs);
doubleArray.classSig = "[D";
readerJCL.addClass(doubleArray);
//
// "[char"
selfGeneratedIDs++;
charArray = model.getClass(selfGeneratedIDs);
charArray.classSig = "[C";
readerJCL.addClass(charArray);
}
/**
* Read a C style null terminated string
* @param z Input stream pointing at the start of the string
* @return
* @throws IOException
*/
private String readCString(FileImageInputStream z) throws IOException {
StringBuilder sb = new StringBuilder();
char cur;
while ((cur = (char) z.readUnsignedByte()) != 0) {
sb.append(cur);
}
return sb.toString();
}
/**
* Read a UTF8 style null terminated string
* @param z Input stream pointing at the start of the string
* @return a String
* @throws IOException
*/
private String readUTFString(FileImageInputStream z) throws IOException {
z.mark();
// Determine array size
int len = 0;
int cur;
while ((cur = z.readUnsignedByte()) != 0) {
len++;
}
// then read it into byte array
z.reset();
byte array[] = new byte[len];
int cnt = 0;
while ((cur = z.readUnsignedByte()) != 0) {
array[cnt++] = (byte)cur;
}
// Convert bytes into unicode string.
return new String(array,"UTF-8");
}
/**
* Read a stack frame
* @param pos Position of frame in file
* @param t Thread the frame is contained in
* @throws IOException
*/
private void nreadFrames(long pos, JThread t) throws IOException {
variablesIn.seek(pos);
byte val = variablesIn.readByte();
if (val == CJVMTI_FRAME){
log.finest(("Reading frame"));
}else if (val == CJVMTI_JVMTI_ERROR) {
log.finest("No frame information!");
return;
}else{
log.finer("Error reading frame");
throw new IOException("Error reading frame "+pos+" in thread "+t);
}
long methodID = variablesIn.readLong();
String methodName = readUTFString(variablesIn);
log.log(Level.FINEST, "Method name is: " + methodName);
long declaringClass = variablesIn.readLong();
long cPos = variablesIn.getStreamPosition(); // Save where we are before reading in the declaring class
JClass clazz = (JClass) nreadReference(declaringClass);
variablesIn.seek(cPos); // Return to our read position
JStackFrame jsf = new JStackFrame();
JLocation loc = new JLocation();
JMethod jm = model.getMethod(methodID);
loc.method = jm;
jsf.setLocation(loc);
// SRDM
if( clazz != null) {
loc.filename = clazz.sourceFile;
}
t.addStackFrame(jsf);
byte depth = variablesIn.readByte();
if (depth == CJVMTI_LOCAL_NATIVECALL) {
log.log(Level.FINEST, "Native call");
loc.linenumber = -1;
loc.setAddress(-1);
if (loc.filename == null){
loc.filename = "nativeCall"; // ..
}
return;
} else if (depth == CJVMTI_JVMTI_ERROR) {
// If there are no local variables, we can still have a frame for the method.
log.log(Level.FINEST, "Data unavailable");
loc.linenumber = -1;
loc.setAddress(-1);
loc.setCompilationLevel(1);
return;
} else if (depth != CJVMTI_LOCAL_VARIABLE) {
log.finer("Error reading frame type. Got "+depth);
throw new IOException("Error reading frame "+pos+" in thread. "+t+
". Error reading frame type. Got "+depth);
}
log.log(Level.FINEST, "Local var");
loc.setCompilationLevel(0);
if (clazz != null) {
loc.filename = clazz.sourceFile;
}
long location = variablesIn.readLong();
log.log(Level.FINEST, "Location is " + location);
loc.setAddress(location);
loc.linenumber = loc.method.getLineNumber(location);
log.log(Level.FINEST, " Line number " + loc.linenumber + " to "
+ location);
int varCount = variablesIn.readInt();
int slots[] = new int[varCount];
long lvarReference[] = new long[varCount];
log.log(Level.FINEST, "vars: " + varCount);
// Read in all the variable references
for (int i2 = 0; i2 < varCount; i2++) {
slots[i2] = variablesIn.readInt();
lvarReference[i2] = variablesIn.readLong();
}
// Follow all the variable references
for (int i2 = 0; i2 < varCount; i2++){
log.finest("Slot "+slots[i2]+" at "+Long.toHexString(lvarReference[i2]));
JLocalVariable jlv = new JLocalVariable();
jlv.slot = slots[i2];
long cpos = variablesIn.getStreamPosition();
jlv.value = nreadReference(lvarReference[i2]);
variablesIn.seek(cpos);
jsf.addVariable(jlv);
}
t.addStackFrame(jsf);
}
/**
* Read monitor information
* @param t Thread context
* @throws IOException
*/
private void readMonitor(JThread t) throws IOException {
int ownedMonitorCount = variablesIn.readInt();
log.log(Level.FINEST, "Owned monitor count " + ownedMonitorCount);
if (ownedMonitorCount == 0) return;
for (int i = 0; i < ownedMonitorCount; i++) {
log.finest("Monitor "+i+" of "+ ownedMonitorCount);
long objID = variablesIn.readLong();
long cPos = variablesIn.getStreamPosition(); // Save current read
nreadReference(objID); // Follow reference
variablesIn.seek(cPos); // Return to read point
JMonitor monitor = model.getMonitor(objID);
monitor.setObject(monitor.getId());
monitor.setOwner(t.id);
}
}
/**
* Get contended monitor information
* @param t Thread context
* @throws IOException
*/
private void readContendedMonitor(JThread t) throws IOException {
long contendedObject = variablesIn.readLong();
log.log(Level.FINEST, "Contended object: "
+ Long.toHexString(contendedObject));
if (contendedObject != CJVMTI_NULL_OBJECT) {
JMonitor monitor = model.getMonitor(contendedObject);
long cPos = variablesIn.getStreamPosition();
nreadReference(contendedObject);
variablesIn.seek(cPos);
monitor.addWaiter(t.id);
monitor.setObject(contendedObject);
}
}
private void nreadThread(long id) throws IOException {
variablesIn.seek(id);
if (variablesIn.read() == CJVMTI_THREAD){
log.finest("Reading thread information");
}else{
log.fine("Error");
System.exit(0);
}
String threadname = readUTFString(variablesIn);
log.finest("thread name "+threadname);
long objectRef = variablesIn.readLong();
long cpos = variablesIn.getStreamPosition();
nreadReference(objectRef);
variablesIn.seek(cpos);
JThread t = model.getThread(objectRef);
t.threadName = threadname;
log.log(Level.FINEST, t.threadName);
t.groupID = 0;
t.priority = (short) variablesIn.readInt();
t.daemon = (short) variablesIn.readByte();
t.setState(variablesIn.readInt());
t.setObject((JObject) model.getObjectAtAddress(objectRef));
readMonitor(t);
readContendedMonitor(t);
if (variablesIn.read()==CJVMTI_FRAME){
log.finest("Reading frames");
}else{
log.fine("Error reading frames");
}
int frameCount = variablesIn.readInt();
long frameRefs[] = new long[frameCount];
for (int i = 0; i < frameCount; i ++){
frameRefs[i] = variablesIn.readLong();
}
for (int i = 0; i < frameCount; i++){
log.finest("Reading frame "+i+" of "+frameCount+ " at "+Long.toHexString(frameRefs[i]));
nreadFrames(frameRefs[i], t);
}
}
/**
* Conveniance method for printing stream position on error
* @param msg
* @throws IOException
*/
private void error(String msg) throws IOException {
long offset = -1;
throw new IOException("at offset "
+ Long.toHexString(variablesIn.getStreamPosition()) + ": " + msg);
}
/**
* Get java model
* @return JavaModel
*/
public Model getModel() {
return model;
}
/**
* Returns the creation time, in millis, since the epoch.
*
* @return long
*/
public long getCreationTime() {
return time;
}
}