Package com.starlight.io.hprof

Source Code of com.starlight.io.hprof.HPROFParser

/*
* Copyright (c) 2012 Rob Eden.
* All Rights Reserved.
*/

package com.starlight.io.hprof;

import com.starlight.IOKit;
import com.starlight.ValidationKit;
import com.starlight.io.PositionTrackingInputStream;

import java.io.*;


/**
* A parser for processing HPROF files. The parser will read the file and call
* {@link HPROFParseHandler} methods as the file is read, in the same style as a SAX
* parser.
* <pre>
*     File hprof_file = new File( "my_dump.hprof" );
*     HPROFParseHandler my_handler = // implementation of handler
*     HPROFParser parser = new HPROFParser( file, my_handler );
*     parser.parse();
* </pre>
*/
public class HPROFParser {
  private final File file;
  private final HPROFParseHandler handler;

  private boolean inside_heap_dump = false;


  /**
   * Instantiate a new parser that will read the given file.
   */
  public HPROFParser( File file, HPROFParseHandler handler )
    throws FileNotFoundException {

    ValidationKit.checkNonnull( file, "file" );
    ValidationKit.checkNonnull( handler, "handler" );

    this.file = file;
    this.handler = handler;

    if ( !file.exists() ) {
      throw new FileNotFoundException( "File \"" + file + "\" does not exist." );
    }
  }


  /**
   * Parse the file, calling the handler during processing. This method will return
   * at the completion of processing. Parsing may be done multiple times and will
   * parse the entire file each time.
   *
   * @throws IOException          If a read error occurs during parsing.
   */
  public synchronized void parse() throws IOException {
    if ( !file.exists() ) {
      throw new FileNotFoundException( "File \"" + file + "\" does not exist." );
    }

    FileInputStream fin = null;
    BufferedInputStream bin = null;
    PositionTrackingInputStream pin = null;
    DataInputStream din = null;
    try {
      fin = new FileInputStream( file );
      bin = new BufferedInputStream( fin );
      pin = new PositionTrackingInputStream( bin );
      din = new DataInputStream( pin );

      parse_internal( din, pin );
    }
    finally {
      IOKit.close( din );
      IOKit.close( pin );
      IOKit.close( bin );
      IOKit.close( fin );
    }
  }


  private void parse_internal( DataInputStream in, PositionTrackingInputStream pin )
    throws IOException {

    ///////////////////////////////////////////////////////////////
    // Header
    StringBuilder format_name_buf = new StringBuilder();

    int i;
    while( ( i = in.readByte() & 0xff ) != 0 ) {
      format_name_buf.append( ( char ) i );
    }

//    System.out.println( "Format name: " + format_name_buf );

    int identifier_size = in.readInt();
//    System.out.println( "Identifier size: " + identifier_size );

    long time = in.readInt() & 0xFFFFFFFFL;
    time = time << 32;
    time |= in.readInt() & 0xFFFFFFFFL;
//    System.out.println( "Time: " + new Date( time ) );

    handler.header( format_name_buf.toString(), identifier_size, time );



    ////////////////////////////////////////////////////////////////
    // Tags
    while( true ) {
      byte tag_type;
      try {
        tag_type = in.readByte();
      }
      catch( EOFException ex ) {
        // If we're inside a heap dump and there was no HEAP DUMP END marker,
        // act as though there was.
        if ( inside_heap_dump ) {
          handler.recordHeapDumpEnd();
        }

        // This is a place we expect to hit the end of the file.
        // Just stop processing.
        handler.end();
        return;
      }

      long micros_since_header = in.readInt() & 0xFFFFFFFFL;
      long length = in.readInt() & 0xFFFFFFFFL;

      handler.beginRecord( tag_type, micros_since_header, length );
      parseTag( in, pin, tag_type, length, identifier_size );
      handler.endRecord( tag_type );
    }
  }


  private void parseTag( DataInputStream in, PositionTrackingInputStream pin,
    byte tag_type, long length, long identifier_size ) throws IOException {


    switch( tag_type ) {
      // STRING IN UTF8
      case 0x01:
      {
        ID id = readID( in, identifier_size );
        String string = readString( in, length - identifier_size );

        handler.recordStringInUTF8( id, string );
        break;
      }

      // LOAD CLASS
      case 0x02:
      {
        int serial_number = in.readInt();
        ID object_id = readID( in, identifier_size );
        int stack_trace_serial = in.readInt();
        ID class_name_string_id = readID( in, identifier_size );

        handler.recordLoadClass( serial_number, object_id, stack_trace_serial,
          class_name_string_id );
        break;
      }

      // UNLOAD CLASS
      case 0x03:
      {
        int serial_number = in.readInt();
        handler.recordUnloadClass( serial_number );
        break;
      }

      // STACK FRAME
      case 0x04:
      {
        ID frame_id = readID( in, identifier_size );
        ID method_name_id = readID( in, identifier_size );
        ID method_signature_id = readID( in, identifier_size );
        ID source_file_name_id = readID( in, identifier_size );
        int class_serial_number = in.readInt();
        // >0 - line number
        // 0  - no line info available
        // -1 - unknown location
        // -2 - compiled method
        // -3 - native method
        int line = in.readInt();

        handler.recordStackFrame( frame_id, method_name_id,
          method_signature_id, source_file_name_id, class_serial_number,
          line );
        break;
      }

      // STACK TRACE
      case 0x05:
      {
        int trace_serial_number = in.readInt();
        int thread_serial_number = in.readInt();
        long frame_count = in.readInt() & 0xFFFFFFFFL;
        ID[] frame_ids = new ID[ ( int ) frame_count ];
        for( long frame = 0; frame < frame_count; frame++ ) {
          ID frame_id = readID( in, identifier_size );
          frame_ids[ ( int ) frame ] = frame_id;
        }
        handler.recordStackTrace( trace_serial_number, thread_serial_number,
          frame_ids );
        break;
      }

      // ALLOC SITES
      case 0x06:
      {
        short bit_mask_flags = in.readShort();
        float cutoff_ratio = in.readFloat();
        int total_live_bytes = in.readInt();
        int total_live_instances = in.readInt();
        long total_bytes_allocated = in.readLong();
        long total_instances_allocated = in.readLong();

        int site_count = in.readInt();
        for( int i = 0; i < site_count; i++ ) {
          byte array_indicator = in.readByte();
          long class_serial_number = in.readInt() & 0xFFFFFFFFL;
          long stack_trace_serial_number = in.readInt() & 0xFFFFFFFFL;

          int site_total_live_bytes = in.readInt();
          int site_total_live_instances = in.readInt();
          int site_total_bytes_allocated = in.readInt();
          int site_total_instances_allocated = in.readInt();
        }

        // TODO: call handler
        break;
      }

      // HEAP SUMMARY
      case 0x07:
      {
        int total_live_bytes = in.readInt();
        int total_live_instances = in.readInt();
        long total_bytes_allocated = in.readLong();
        long total_instances_allocated = in.readLong();

        // TODO: call handler
        break;
      }

      // START THREAD
      case 0x0A:
      {
        long thread_serial_number = in.readInt() & 0xFFFFFFFFL;
        ID thread_object_id = readID( in, identifier_size );
        long stack_trace_serial_number = in.readInt() & 0xFFFFFFFFL;
        ID thread_name_string_id = readID( in, identifier_size );
        ID thread_group_name_id = readID( in, identifier_size );
        ID thread_parent_group_name_id = readID( in, identifier_size );

        // TODO: call handler
        break;
      }

      // END THREAD
      case 0x0B:
      {
        long thread_serial_number = in.readInt() & 0xFFFFFFFFL;

        // TODO: call handler
        break;
      }

      // HEAP DUMP
      case 0x0C:
      // HEAP DUMP SEGMENT
      case 0x1C:
      {
        inside_heap_dump = true;
        if ( tag_type == 0x0C ) handler.recordHeapDump();
        else handler.recordHeapDumpSegment();

        long starting_position = pin.position();
        while( pin.position() - starting_position < length ) {
          byte sub_tag = in.readByte();
          processHeapDumpSubTag( in, sub_tag, identifier_size );
        }

        break;
      }

      // HEAP DUMP END
      case 0x2C:
        inside_heap_dump = false;
        handler.recordHeapDumpEnd();
        break;

      // CPU SAMPLES
      case 0x0D:
      {
        long total_number_of_samples = in.readInt() & 0xFFFFFFFFL;
        long number_of_traces = in.readInt() & 0xFFFFFFFFL;
        int[] num_samples = new int[ ( int ) number_of_traces ];
        int[] trace_serial = new int[ ( int ) number_of_traces ];
        for( long n = 0; n < number_of_traces; n++ ) {
          num_samples[ ( int ) n ] = in.readInt()// number of samples
          trace_serial[ ( int ) n ] = in.readInt(); // stack trace serial number
        }
        handler.recordCPUSamples( total_number_of_samples, num_samples,
          trace_serial );
        break;
      }

      // CONTROL SETTINGS
      case 0x0E:
      {
        int bit_mask_flags = in.readInt();
        int stack_trace_depth = in.readUnsignedShort();
        handler.recordControlSettings( bit_mask_flags, stack_trace_depth );
        break;
      }

      default:
        System.out.println( "Hit unhandled type: 0x" +
          Integer.toHexString( tag_type )  );
        System.exit( -1 );
    }
  }


  private void processHeapDumpSubTag( DataInputStream in, byte sub_tag,
    long identifier_size ) throws IOException {

    ID object_id = readID( in, identifier_size );

    switch( sub_tag ) {
      // ROOT UNKNOWN
      case ( byte ) 0xFF:
        handler.heapDumpRootUnknown( object_id );
        break;

      // ROOT JNI GLOBAL
      case 0x01:
      {
        ID jni_global_ref_id = readID( in, identifier_size );
        handler.heapDumpRootJNIGlobal( object_id, jni_global_ref_id );
        break;
      }

      // ROOT JNI LOCAL
      case 0x02:
      {
        int thread_serial_number = in.readInt();
        int frame_number_in_trace = in.readInt();   // -1 for empty
        handler.heapDumpRootJNILocal( object_id, thread_serial_number,
          frame_number_in_trace );
        break;
      }

      // ROOT JAVA FRAME
      case 0x03:
      {
        int thread_serial_number = in.readInt();
        int frame_number_in_trace = in.readInt();   // -1 for empty
        handler.heapDumpRootJavaFrame( object_id, thread_serial_number,
          frame_number_in_trace );
        break;
      }

      // ROOT NATIVE STACK
      case 0x04:
      {
        int thread_serial_number = in.readInt();
        handler.heapDumpRootNativeStack( object_id, thread_serial_number );
        break;
      }

      // ROOT STICKY CLASS
      case 0x05:
        handler.heapDumpRootStickyClass( object_id );
        break;

      // ROOT THREAD BLOCK
      case 0x06:
      {
        int thread_serial_number = in.readInt();
        handler.heapDumpRootThreadBlock( object_id, thread_serial_number );
        break;
      }

      // ROOT MONITOR USED
      case 0x07:
        handler.heapDumpRootMonitorUsed( object_id );
        break;

      // ROOT THREAD OBJECT
      case 0x08:
      {
        int thread_serial_number = in.readInt();
        int stack_trace_serial_number = in.readInt();
        handler.heapDumpRootThreadObject( object_id, thread_serial_number,
          stack_trace_serial_number );
        break;
      }

      // CLASS DUMP
      case 0x20:
      {
        int stack_trace_serial_number = in.readInt();
        ID super_class_id = readID( in, identifier_size );
        ID class_loader_id = readID( in, identifier_size );
        ID signers_object_id = readID( in, identifier_size );
        ID protection_domain_object_id = readID( in, identifier_size );
        readID( in, identifier_size );       // reserved
        readID( in, identifier_size );       // reserved
        long instance_size = in.readInt() & 0xFFFFFFFFL;

        int constant_pool_size = in.readShort() & 0xFFFF;
        int[] constant_pool_indexes = new int[ constant_pool_size ];
        BasicType[] constant_pool_types = new BasicType[ constant_pool_size ];
        Object[] constant_pool_values = new Object[ constant_pool_size ];
        for( int i = 0; i < constant_pool_size; i++ ) {
          constant_pool_indexes[ i ] = in.readShort() & 0xFFFF;
          constant_pool_types[ i ] = BasicType.fromID( in.readByte() );
          constant_pool_values[ i ] = valueForType( in,
            constant_pool_types[ i ].getID(), identifier_size );
        }

        int static_field_count = in.readShort() & 0xFFFF;
        ID[] static_field_names = new ID[ static_field_count ];
        BasicType[] static_field_types = new BasicType[ static_field_count ];
        Object[] static_field_values = new Object[ static_field_count ];
        for( int i = 0; i < static_field_count; i++ ) {
          static_field_names[ i ] = readID( in, identifier_size );
          static_field_types[ i ] = BasicType.fromID( in.readByte() );
          static_field_values[ i ] = valueForType( in,
            static_field_types[ i ].getID(), identifier_size );
        }

        int instance_field_count = in.readShort() & 0xFFFF;
        ID[] instance_field_names = new ID[ instance_field_count ];
        BasicType[] instance_field_types = new BasicType[ instance_field_count ];
        for( int i = 0; i < instance_field_count; i++ ) {
          instance_field_names[ i ] = readID( in, identifier_size );
          instance_field_types[ i ] = BasicType.fromID( in.readByte() );
        }


        handler.heapDumpClassDump( object_id, stack_trace_serial_number,
          super_class_id, class_loader_id, signers_object_id,
          protection_domain_object_id, instance_size, constant_pool_indexes,
          constant_pool_types, constant_pool_values, static_field_names,
          static_field_types, static_field_values, instance_field_names,
          instance_field_types );
        break;
      }

      // INSTANCE DUMP
      case 0x21:
      {
        int stack_trace_serial_number = in.readInt();
        ID class_object_id = readID( in, identifier_size );
        long value_byte_length = in.readInt() & 0xFFFFFFFFL;
        for( long i = 0; i < value_byte_length; i++ ) {
          in.readByte();
        }
        handler.heapDumpInstanceDump( object_id, stack_trace_serial_number,
          class_object_id, value_byte_length );
        break;
      }

      // OBJECT ARRAY DUMP
      case 0x22:
      {
        int stack_trace_serial_number = in.readInt();
        int number_of_elements = in.readInt();
        ID array_class_object_id = readID( in, identifier_size );
        for( long i = 0; i < number_of_elements; i++ ) {
          readID( in, identifier_size );
        }

        handler.heapDumpArrayDump( object_id, stack_trace_serial_number,
          number_of_elements, array_class_object_id );
        break;
      }

      // PRIMITIVE ARRAY DUMP
      case 0x23:
      {
        int stack_trace_serial_number = in.readInt();
        int number_of_elements = in.readInt();
        byte entry_type = in.readByte();        // see Basic Type
        for( long i = 0; i < number_of_elements; i++ ) {
          valueForType( in, entry_type, identifier_size );
        }

        handler.headDumpPrimitiveArrayDump( object_id, stack_trace_serial_number,
          number_of_elements, entry_type );
        break;
      }

      default:
        System.out.println( "Hit unhandled heap dump sub-type: " +
          Integer.toHexString( sub_tag ) );
        System.exit( -1 );
    }
  }


  private ID readID( DataInputStream in, long identifier_size ) throws IOException {
    byte[] to_return = new byte[ ( int ) identifier_size ];
    in.readFully( to_return );
    return new ID( to_return );
  }


  private String readString( DataInputStream in, long length ) throws IOException {
    StringBuilder buf = new StringBuilder();

    for( long i = 0; i < length; i++ ) {
      buf.append( ( char ) ( in.readByte() & 0xFF ) );
    }
    return buf.toString();
  }


  private Object valueForType( DataInputStream in, byte type, long identifier_size )
    throws IOException {

    switch ( type ) {
      case 2:
        return readID( in, identifier_size );
      case 4:
        return Boolean.valueOf( in.readBoolean() );
      case 5:
        return Character.valueOf( in.readChar() );
      case 6:
        return Float.valueOf( in.readFloat() );
      case 7:
        return Double.valueOf( in.readDouble() );
      case 8:
        return Byte.valueOf( in.readByte() );
      case 9:
        return Short.valueOf( in.readShort() );
      case 10:
        return Integer.valueOf( in.readInt() );
      case 11:
        return Long.valueOf( in.readLong() );

      default:
        throw new IllegalArgumentException( "Unknown basic type: " + type );
    }
  }
}
TOP

Related Classes of com.starlight.io.hprof.HPROFParser

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.