Friday May 11, 2007
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;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
....
callbacks.DataDumpRequest = &dataDumpRequest;
jvmti->SetEventNotificationMode(JVMTI_ENABLE,
JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
....
}
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.
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;
}
Posted at 03:25PM May 11, 2007 by poonam in Sun | Comments[2]
$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 09:23 PM IST #
Posted by Poonam on June 30, 2007 at 11:41 PM IST #