JVMTI agent to print reference paths

To get started on JVMTI please read JVMTI reference guide JVMTI reference guide and a very good article on JVMTI Programming here

Here I will briefly describe how I used JVMTI to write an agent which prints the reference paths of instances of a given class in the running process.

The global data structure used in this agent:
/\* Global agent data structure \*/
typedef struct {
    /\* JVMTI Environment \*/
    jvmtiEnv      \*jvmti;

    /\* Data access Lock \*/
    jrawMonitorID  lock;

    /\* Fields to store the Options passed to the agent\*/
    char\* classname;   
    int  max_count;    

    /\* boolen to indicate if dump is in progress \*/	
    jboolean dumpInProgress;
    JavaVM\* jvm;
    jclass  klass;
    jlong  klassTag;
} GlobalAgentData;

This agent prints these reference paths when a SIGQUIT signal is sent to the process. For this, In Agent_OnLoad() function, set the callback for DataDumpRequest and enable the event notification for JVMTI_EVENT_DATA_DUMP_REQUEST event.
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM \*jvm, char \*options, void \*reserved)
{
  ....
  callbacks.DataDumpRequest = &dataDumpRequest;
  jvmti->SetEventNotificationMode(JVMTI_ENABLE,
            	    JVMTI_EVENT_DATA_DUMP_REQUEST, NULL); 
  ....
}

With the above code, when the process receives SIGQUIT (ctrl+\\ on solaris and ctrl+break on windows) signal, dataDumpRequest() function of this agent would get invoked.

Let's see how this function looks:
static void JNICALL dataDumpRequest(jvmtiEnv \*jvmti)
{
  enter_critical_section(jvmti); {
    if ( !gdata->dumpInProgress ) {
      gdata->dumpInProgress = JNI_TRUE;
      gdata->klassTag = 1;
      gdata->jvmti->SetTag(gdata->klass, gdata->klassTag);
      jint count = 0;
      void\* user_data = NULL;

      // iterate over all the reachable objects in heap
      jvmti->IterateOverReachableObjects(&heap_root_callback, &stack_ref_callback,
                 &object_ref_callback, user_data);

      // print ref paths
      RefPaths\* list = ref_paths;
      int max = gdata->max_count;

      printf("Reference paths of instances of %s ....\\n", gdata->classname);
      while ((list != NULL) && (max >= 0) ) {
        ObjectInfo\* object = (ObjectInfo\*)list->path;
        printf("\\n\\nReference Path:");
        dfsPrintRefPaths(object);
        list = list->next;
        max--;
      }

      // unset tags
      jvmti->IterateOverReachableObjects(&heap_root_callback, &stack_ref_callback,
            &object_ref_clean_callback, user_data);

      // delete object info list
      ObjectInfoList\* list1 = objList;
      while (list1) {
        ObjectInfoList\* node = list1;
        list1 = list1->next;
        DeallocateObject(node->obj);
        delete(node);
      }
      objList = NULL;
		
      // delete ref paths list
      list = ref_paths;
      RefPaths\* path;

      while (list != NULL) {
        path = list;
        list = list->next;
        delete(path);			
      }
      ref_paths = NULL;

      gdata->klassTag = 1;
      gdata->dumpInProgress = JNI_FALSE;
    }
  } exit_critical_section(jvmti);	
}
Here, we first set the tag of the class gdata->klass which was set to the classname passed as parameter to the agent. Then we call IterateOverReachableObjects function to go over all the reachable objects in heap. IterateOverReachableObjects function iterates over the root objects and all objects that are directly and indirectly reachable from the root objects. In dataDumpRequest we are making two calls to IterateOverReachableObjects, first one to create a list of reachable objects and their referrers and the second one to unset the tags set in the first call.

IterateOverReachableObjects accepts three callbacks, callback which gets called when any root object is visited, callback which gets called when any stack object is visited and a callback when any object reference is visited. This sample agent does not do anything in the root and stack callbacks.

Now let's see what the object reference callback does:
static jvmtiIterationControl JNICALL
object_ref_callback(jvmtiObjectReferenceKind reference_kind,
		jlong class_tag, jlong size, jlong\* tag_ptr,
		jlong referrer_tag, jint referrer_index, void \*user_data)
{
  ObjectInfo\* obj_info = NULL;
  // if this object's tag is set which can be for the class we want to create
  // reference paths for or if we are visiting this object again.
  if (\*tag_ptr != NULL) { 
    if (gdata->klassTag == 1) {
      if (\*tag_ptr == gdata->klassTag) {
        obj_info = new ObjectInfo();
	memset(obj_info, 0 , sizeof(ObjectInfo));
	obj_info->size = size;
	obj_info->visited = 1;
	obj_info->kind = reference_kind;

	\*tag_ptr = (jlong)(ptrdiff_t)(void\*)obj_info;
	gdata->klassTag = \*tag_ptr;
      }
    } else {
      obj_info = (ObjectInfo\*)\*tag_ptr;
      if (obj_info->visited == 1)
	return JVMTI_ITERATION_CONTINUE;
    }
  }
  // if tag is not present, then create ObjectInfo and set it as it's tag.
  else {
    obj_info = new ObjectInfo();
    memset(obj_info, 0 , sizeof(ObjectInfo));
    obj_info->size = size;
    obj_info->visited = 1;
    obj_info->kind = reference_kind;

    \*tag_ptr = (jlong)(ptrdiff_t)(void\*)obj_info;

    //Add the new ObjectInfo to ObjectInfo's list
    if (objList == NULL) {
      objList = new ObjectInfoList();
      objList->obj = obj_info;
      objList->next = NULL;
    } else {
      ObjectInfoList\* list = objList;
      while (list->next != NULL) {
        list = list->next;
      }
      ObjectInfoList\* objinfo = new ObjectInfoList();
      objinfo->obj = obj_info;
      objinfo->next = NULL;

      list->next = objinfo;
    }	
  }
	
  // If this object has the referrer then put it in the referrers list
  if (referrer_tag != NULL) {
    if (obj_info->referrers == NULL) {
      obj_info->referrers = new Referrer();
      obj_info->referrers->tag = referrer_tag;
      obj_info->referrers->refereeTag = \*tag_ptr;
      obj_info->referrers->next = NULL;
    } else {
      Referrer\* referrer = obj_info->referrers;
      while (referrer->next != NULL) {
        referrer = referrer->next;
      }
      Referrer\* ref = new Referrer();
      ref->tag = referrer_tag;
      ref->refereeTag = \*tag_ptr;
      ref->next = NULL;
      referrer->next = ref;			
    }
  } 

  if (class_tag == gdata->klassTag) {
    AddToPathsList(obj_info);
  }

  return JVMTI_ITERATION_CONTINUE;
}

Here the ObjectInfo class represents an object in the heap. We tag every visited reference with the ObjectInfo pointer and when it's refree is visited, the referer_tag contains this ObjectInfo pointer. So by using tag_ptr and referer_tag, this callback creates list of reference paths (RefPaths) of all the instances of the given class.

After returning from the callback, function dfsPrintRefPaths prints prints these reference paths. Then we make another call to IterateOverReachableObjects to unset the tags we had set in the first iteration.

The full code of this agent can be downloaded from here.

How this works:
-----------------
Build ref_paths.cpp into a shared library. Copy the library refpaths.dll or refpaths.so in the current working directory or set the LD_LIBRARY_PATH on solaris and PATH env variable on Windows to the loaction of this library.

Start your java program with options:
java -agentlib:refpaths=classname="java/lang/String",max=40 TestClass

where -agentlib:refpaths specifies that 'refpaths' is the jvmti based shared library
and following that are the arguments passed to this agent.
o classname - name of the class for which we want to print the ref-paths
o max - the maximum number of instances

The above commnad will print the 40 ref-paths of instances of java/lang/String class.

To print the ref-paths at any point, send Sigquit signal to the running process
and it will print the paths though which the requested instances are reachable.

The output of ref-paths printed by this agent looks like this:
Reference Path:
- Instance Field: /E:/jre1.5.0_10/lib/ext/dnsns.jar
- Array Element: file:/E:/jre1.5.0_10/lib/ext/dnsns.jar
- Instance Field: [Ljava.lang.Object;@1c78e57
- Instance Field: [file:/E:/jre1.5.0_10/lib/ext/dnsns.jar, file:/E:/jre1.5.0_10/lib/ext/localedata.jar, file:/E:/jre1.5.0_10/lib/ext/sunjce_provider.jar, file:/E:/jre1.5.0_10/lib/ext/sunpkcs11.jar]
- Instance Field: sun.misc.URLClassPath@5224ee
- Instance Field: sun.misc.Launcher$ExtClassLoader@92e78c
- ClassLoader: sun.misc.Launcher$AppClassLoader@a39137
- class TestClass

Reference Path:
- Instance Field: file:/E:/jre1.5.0_10/lib/ext/sunpkcs11.jar!/
- Instance Field: jar:file:/E:/jre1.5.0_10/lib/ext/sunpkcs11.jar!/
- Array Element: sun.misc.URLClassPath$JarLoader@f6a746
- Instance Field: [Ljava.lang.Object;@15ff48b
- Instance Field: [sun.misc.URLClassPath$JarLoader@affc70, sun.misc.URLClassPath
$JarLoader@1e63e3d,sun.misc.URLClassPath$JarLoader@1004901,sun.misc.URLClassPath$JarLoader@f6a746]
- Instance Field: sun.misc.URLClassPath@5224ee
- Instance Field: sun.misc.Launcher$ExtClassLoader@92e78c
- ClassLoader: sun.misc.Launcher$AppClassLoader@a39137
- class TestClass

where any entry is reachable through the entry below it.
Comments:

Hallo, I tried to compile your code to a shared lib like so:
$gcc -o ref_paths.o -I${JDK_HOME}/include -I${JDK_HOME}/include/linux -c -fPIC ref_paths.cpp
$gcc -shared -Wl,-soname,libref_paths.so.1 -o libref_paths.so.1.0.1 ref_paths.o $ln -s libref_paths.so.1.0.1 libref_paths.so
And then started JVM like so:
$java -showversion -agentpath:/home/i6stud/siweheee/examples/libref_paths.so SimpleThread

But got the error message:
Error occurred during initialization of VM
Could not find agent library in absolute path: /home/i6stud/siweheee/examples/libref_paths.so

Actually I can use the same syntax to access another lib like so:
$java -showversion -agentpath:/home/i6stud/siweheee/examples/liba.so
SimpleThread
So I don't think it is the path problem but may be anything else.

Would you remind me what's wrong? Thank you very much!

Posted by Wei on June 29, 2007 at 02:23 PM IST #

Hi Wei,

The syntax is correct and I don't see any reason
why it shouldn't work. Did you try with 
'-agentlib' option also (need to set 
LD_LIBRARY_PATH env with this) ?

Thanks,
Poonam

Posted by Poonam on June 30, 2007 at 04:41 PM IST #

Regrettably, the ref_paths.cpp file is missing.

Posted by Fredrik Wahlgren on February 24, 2012 at 06:44 PM IST #

Hi, i am trying to retrieve the full physical path of the main class file of a running java application. Like for an application launched by this command line :< java helloworld > , i can get c:\java app\app1\helloworld.class).
Can JVMTI allow to do so ? thanks in advance

Posted by guest on July 29, 2013 at 06:55 PM IST #

It is a pity that the link to the source code is broken. Is it still possible to get the source code? Thank you very much in advanced!

Posted by guest on August 06, 2013 at 02:30 PM IST #

Hello Poonam, is the source code still available? It seems like the link is already broken.

Posted by Bin on August 07, 2013 at 01:04 PM IST #

Corrected the link for the source code of the agent.

Thanks,
Poonam

Posted by guest on August 07, 2013 at 03:06 PM IST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

poonam

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today