X

Poonam Bajaj's Blog

JVMTI agent to print reference paths

Poonam Parhar
Consulting Member of Technical Staff
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.

Join the discussion

Comments ( 7 )
  • Wei Friday, June 29, 2007
    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!
  • Poonam Saturday, June 30, 2007
    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
  • Fredrik Wahlgren Friday, February 24, 2012

    Regrettably, the ref_paths.cpp file is missing.


  • guest Monday, July 29, 2013

    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


  • guest Tuesday, August 6, 2013

    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!


  • Bin Wednesday, August 7, 2013

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


  • guest Wednesday, August 7, 2013

    Corrected the link for the source code of the agent.

    Thanks,

    Poonam


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.