Package org.apache.kato.jvmti.reader

Source Code of org.apache.kato.jvmti.reader.CJVMTIBinReader

/*******************************************************************************
* 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;
  }
}
TOP

Related Classes of org.apache.kato.jvmti.reader.CJVMTIBinReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.