The Java Virtual Machine Profiler Interface 
by Christof Schmalenbach and Christian Hoefig

Listing One

// simpleprof.c - simple library, profiling agent JVMPI
// #include <string.h>
#include <jvmpi.h>
#include <jni.h>
#define ALLOC_LIMIT 2000    // object size for notification
void notifyEvent(JVMPI_Event *event);
static JVMPI_Interface *jvmpi_interface;

// profiler agent entry point
JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM *jvm,char *options,void *reserved) {
    // get jvmpi interface pointer
    int res = (*jvm)->GetEnv(jvm,(void **)&jvmpi_interface,JVMPI_VERSION_1);
    if (res < 0) {
      printf("Error obtaining jvmpi interface pointer\n");
      return JNI_ERR;
    }
    // enable minimum event notification, rest from notifyEvent()
    if (jvmpi_interface->EnableEvent(JVMPI_EVENT_JVM_INIT_DONE, NULL)
            != JVMPI_SUCCESS) {;
      printf("Failed to enable JVM_INIT_DONE.\n");
      return JNI_ERR;
    }
    // initialize jvmpi interface
    jvmpi_interface->NotifyEvent = notifyEvent;
    return JNI_OK;
}
// function for handling event notification - our own function
void notifyEvent(JVMPI_Event *event) {
   switch(event->event_type) {
        case JVMPI_EVENT_JVM_INIT_DONE:
            printf("\nSIMPLEPROF: INIT_DONE\n");
            jvmpi_interface->EnableEvent(JVMPI_EVENT_OBJECT_ALLOC, NULL);
            return;
        case JVMPI_EVENT_OBJECT_ALLOC:
        if (event->u.obj_alloc.size >= ALLOC_LIMIT) {
             printf("\nSIMPLEPROF: Large object size %d (>= %d) allocated\n",
                        event->u.obj_alloc.size,ALLOC_LIMIT);
                return;
        }
       return;
    }
}


Listing Two

/*
*     loadInstrumentationClass instantiate our BCEL based Instrumentation Class
*     Note : this class is determined at run time
*     with
*     -Xrunjvmpi4ddj:<package_and_classname>
*     However, the instrumentation class have to provide a method:
*     public static byte[] instrumentClass(byte[] buf)
*/
int loadInstrumentationClass(JNIEnv *env)
{
   /*
    * instrumentation class and the method may already have been located,
    * no need to do it more than once. To remember, we need a GLOBAL
reference,
    * not a local one ! Otherwise, reference may have been garbage
collected.
    * Will create a global one with NewGlobalRef().
    * See java.sun.com/docs/books/tutorial/native1.1/implementing/refs.html
    * or http://java.sun.com/docs/books/jni/html/refs.html
   */

   jclass localref_instrumentationClass = 0;

   if ( instrumentationClass != 0 ) {
      fprintf(stderr,"native:instrumentationClass is 0\n");
      return 1;
   }
   /*
    * Use the JNI Function FindClass
    * see http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/functions.html
    * load a locally defined class
    * "name: a fully-qualified class name (that is, a package name,
    * delimited by "/", followed by the class name).
    * If the name begins with "[" (the array signature character),
    * it returns an array class.
   */

   localref_instrumentationClass = env->FindClass(ppatchclass);
   if ( localref_instrumentationClass == NULL ) {
      fprintf(stderr,"native:loadInstrumentationClass:INFO:could not
FindClass(%s)\n",
         ppatchclass);
      return 0;
   }
   instrumentationClass =
(jclass)env->NewGlobalRef(localref_instrumentationClass);   // now cached
   if (instrumentationClass == NULL) {
      fprintf(stderr,"native:loadInstrumentationClass:INFO:could not create
global ref to (%s)\n",
         ppatchclass);
      return 0;
   }
    /* The local reference is no longer useful */
    env->DeleteLocalRef(localref_instrumentationClass);

   /*
    * this may throw Java exceptions:
    * NoSuchMethodError
    * ExceptionInInitializerError
    * OutOfMemoryError
   */
      instrumentClassMethodID = env->GetStaticMethodID(instrumentationClass,
"instrumentClass","([B)[B");
   if ( instrumentClassMethodID == NULL ) {
      fprintf(stderr,"native:loadInstrumentationClass:INFO:could not get static
method instrumentClass in %s\n",
         ppatchclass);
      return 0;
   }
   return 1;
}


Listing Three

/**
 * jvmpi4ddj.cc
 * The one and only module
 * For our BCEL based C profiler
 * The sense of this C-code is the minimization of C-code for JVMPI 
 * project : jvmpi profiler; article for ddj 2004
 * @version 1.00 02/02/04
 * @author Chr. Hoefig / Chr. Schmalenbach
 */

#include <jvmpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
Enable this precompilerflag if the first instrumented
class should be written to the file system for debugging purposes
*/
//#define WRITEFIRSTHOOKEDCLASSTOFILE 1

#ifdef WRITEFIRSTHOOKEDCLASSTOFILE
FILE *fp = 0;
int filewritten = 0;
#endif // #ifdef WRITEFIRSTHOOKEDCLASSTOFILE

// global jvmpi interface pointer
static JVMPI_Interface *jvmpi_interface;

// pointer to the JVM
JavaVM *jvm;

// instrumentation class and Method
jclass instrumentationClass = 0;
jmethodID instrumentClassMethodID = 0;

/*
*     it seems that our instrumentaion strategy has reentrance
*     problems, if we instrument two classes at the same time.
*     So we avoid this through instrumentation_active flag
*/
int instrumentation_active = 0;

/*
*     We use the opions field to allow dynamic configuration
*     of the instrumentation class.
*/
char* ppatchclass;

/*
*     loadInstrumentationClass instantiate our BCEL based Instrumentation Class
*     Note : this class is determined at run time
*     with
*     -Xrunjvmpi4ddj:<package_and_classname>
*     However, the instrumentation class have to provide a method:
*     public static byte[] instrumentClass(byte[] buf)
*/
int loadInstrumentationClass(JNIEnv *env)
{
   /*
    * instrumentation class and the method may already have been located,
    * no need to do it more than once. To remember, we need a GLOBAL
reference,
    * not a local one ! Otherwise, reference may have been garbage
collected.
    * Will create a global one with NewGlobalRef().
    * See java.sun.com/docs/books/tutorial/native1.1/implementing/refs.html
    * or http://java.sun.com/docs/books/jni/html/refs.html
   */

   jclass localref_instrumentationClass = 0;

   if ( instrumentationClass != 0 ) {
      fprintf(stderr,"native:instrumentationClass is 0\n");
      return 1;
   }
   /*
    * Use the JNI Function FindClass
    * see http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/functions.html
    * load a locally defined class
    * "name: a fully-qualified class name (that is, a package name,
    * delimited by "/", followed by the class name).
    * If the name begins with "[" (the array signature character),
    * it returns an array class.
   */

   localref_instrumentationClass = env->FindClass(ppatchclass);
   if ( localref_instrumentationClass == NULL ) {
      fprintf(stderr,"native:loadInstrumentationClass:INFO:could not
FindClass(%s)\n",
         ppatchclass);
      return 0;
   }
   instrumentationClass =
(jclass)env->NewGlobalRef(localref_instrumentationClass);   // now cached
   if (instrumentationClass == NULL) {
      fprintf(stderr,"native:loadInstrumentationClass:INFO:could not create
global ref to (%s)\n",
         ppatchclass);
      return 0;
   }
    /* The local reference is no longer useful */
    env->DeleteLocalRef(localref_instrumentationClass);

   /*
    * this may throw Java exceptions:
    * NoSuchMethodError
    * ExceptionInInitializerError
    * OutOfMemoryError
   */
      instrumentClassMethodID = env->GetStaticMethodID(instrumentationClass,
"instrumentClass","([B)[B");
   if ( instrumentClassMethodID == NULL ) {
      fprintf(stderr,"native:loadInstrumentationClass:INFO:could not get static
method instrumentClass in %s\n",
         ppatchclass);
      return 0;
   }
   return 1;
}


// function for handling event notification
void notifyEvent(JVMPI_Event *event) {

  JNIEnv *jni_interface;
  switch(event->event_type) {
            case JVMPI_EVENT_CLASS_LOAD_HOOK:
            {
                  jint class_data_length =
event->u.class_load_hook.class_data_len;
                  /*
                        in most cases we aren't interested in instrumentation.
                        that's why we use the class as read by the jvm
                  */
                  event->u.class_load_hook.new_class_data =
event->u.class_load_hook.class_data;
                  event->u.class_load_hook.new_class_data_len =
event->u.class_load_hook.class_data_len;

                  jvm->GetEnv((void **)&jni_interface, JNI_VERSION_1_2);

                  if ( instrumentation_active == 1 )
                        return;

                  instrumentation_active = 1;
                  jbyteArray buf =
jni_interface->NewByteArray(event->u.class_load_hook.class_data_len);
                  jni_interface->SetByteArrayRegion(
                                                                        buf,
                                                                        0,

event->u.class_load_hook.class_data_len,
                                                                        (jbyte
*)event->u.class_load_hook.class_data
                                                                  );
                  jbyteArray buf_new;
                  buf_new = (jbyteArray)jni_interface->CallStaticObjectMethod(

instrumentationClass,

instrumentClassMethodID,

buf
                                                                              );
                  // contract between this shared library and the
instrumentation class:
                  // return buf_new != 0 iff instrumented
                  if( buf_new != 0 )
                  {
                        int new_len = jni_interface->GetArrayLength(buf_new);
                        event->u.class_load_hook.new_class_data_len = new_len;
                        event->u.class_load_hook.new_class_data = (unsigned
char*)event->u.class_load_hook.malloc_f(new_len);
                        jni_interface->GetByteArrayRegion(buf_new, 0, new_len,
(jbyte*)event->u.class_load_hook.new_class_data);

#ifdef WRITEFIRSTHOOKEDCLASSTOFILE

                      fp = fopen("jvmpi4ddj_dump", "wb");
                        if( !filewritten )
                        {
                              fwrite( event->u.class_load_hook.new_class_data,
                                          sizeof(char),

event->u.class_load_hook.new_class_data_len,
                                          fp );
                              fclose(fp);
                              filewritten = 1;
                        }
#endif //#ifdef WRITEFIRSTHOOKEDCLASSTOFILE
                  }
                  instrumentation_active = 0;

                  break;
            }
            case JVMPI_EVENT_JVM_INIT_DONE:
            {
                  jvm->GetEnv((void **)&jni_interface, JNI_VERSION_1_2);
                  // try to load our BCEL based instrumentation class
                  if ( loadInstrumentationClass( jni_interface ) == 0 ) {
                        return;
                  }
                  else
                  {
                        // class and method successfully estimated, so we can
use it in
                        // event JVMPI_EVENT_CLASS_LOAD_HOOK

jvmpi_interface->EnableEvent(JVMPI_EVENT_CLASS_LOAD_HOOK, NULL);
                  }
                  break;
            }
  }
  return;
}

// profiler agent entry point
extern "C" {
  JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM *_jvm, char *options, void *reserved)
{

    ppatchclass = (char *)malloc( sizeof(char)*strlen(options) + 1 );
    strcpy( ppatchclass,options );
    jvm = _jvm;

    if ((jvm->GetEnv((void **)&jvmpi_interface, JVMPI_VERSION_1_1)) < 0) {
      return JNI_ERR;
    }


    jvmpi_interface->NotifyEvent = notifyEvent;
    /*
      initially we are only interested in notification
      of successfull VM start
    */
      jvmpi_interface->EnableEvent(JVMPI_EVENT_JVM_INIT_DONE, NULL);

    return JNI_OK;
  }
}


Listing Four

public class CFoo
{
    public Long mfoo(long l)
    {
        return new Long(l);
    }
}
Listing Five
import jvmpi4ddj.PerfMeasure;
public class CFoo
{
    public CFoo()
    {
    }
    private Long jvmpi4ddj_mfoo(long l)
    {
        return new Long(l);
    }
    public Long mfoo(long arg0)
    {
        String s = "mfoo";
        Object obj = ";";
        Object obj1 = "CFoo";
        obj = new PerfMeasure(obj1 + obj + s + obj + arg0 + obj);
        obj1 = jvmpi4ddj_mfoo(arg0);
        ((PerfMeasure) (obj)).closeMeasure();
        return ((Long) (obj1));
    }
}






