/* Lookup or create a new TraceInfo */ static TraceInfo * lookupOrEnter(jvmtiEnv *jvmti, Trace *trace, TraceFlavor flavor) { TraceInfo *tinfo; jlong hashCode; /* Calculate hash code (outside critical section to lessen contention) */ hashCode = hashTrace(trace); /* Do a lookup in the hash table */ enterCriticalSection(jvmti); { TraceInfo *prev; int hashIndex; /* Start with first item in hash buck chain */ prev = NULL; hashIndex = (int)(hashCode & HASH_INDEX_MASK); tinfo = gdata->hashBuckets[hashIndex]; while ( tinfo != NULL ) { if ( tinfo->hashCode == hashCode && memcmp(trace, &(tinfo->trace), sizeof(Trace))==0 ) { /* We found one that matches, move to head of bucket chain */ if ( prev != NULL ) { /* Remove from list and add to head of list */ prev->next = tinfo->next; tinfo->next = gdata->hashBuckets[hashIndex]; gdata->hashBuckets[hashIndex] = tinfo; } /* Break out */ break; } prev = tinfo; tinfo = tinfo->next; } /* If we didn't find anything we need to enter a new entry */ if ( tinfo == NULL ) { /* Create new hash table element */ tinfo = newTraceInfo(trace, hashCode, flavor); } /* Update stats */ (void)updateStats(tinfo); } exitCriticalSection(jvmti); return tinfo; }
/* Callback for JVMTI_EVENT_VM_START */ static void JNICALL cbVMStart(jvmtiEnv *jvmti, JNIEnv *env) { enterCriticalSection(jvmti); { jclass klass; jfieldID field; jint rc; /* Java Native Methods for class */ static JNINativeMethod registry[2] = { { STRING(HEAP_TRACKER_native_newobj), "(Ljava/lang/Object;Ljava/lang/Object;)V", (void*)&HEAP_TRACKER_native_newobj }, { STRING(HEAP_TRACKER_native_newarr), "(Ljava/lang/Object;Ljava/lang/Object;)V", (void*)&HEAP_TRACKER_native_newarr } }; /* Register Natives for class whose methods we use */ klass = (*env)->FindClass(env, STRING(HEAP_TRACKER_class)); if ( klass == NULL ) { fatal_error("ERROR: JNI: Cannot find %s with FindClass\n", STRING(HEAP_TRACKER_class)); } rc = (*env)->RegisterNatives(env, klass, registry, 2); if ( rc != 0 ) { fatal_error("ERROR: JNI: Cannot register natives for class %s\n", STRING(HEAP_TRACKER_class)); } /* Engage calls. */ field = (*env)->GetStaticFieldID(env, klass, STRING(HEAP_TRACKER_engaged), "I"); if ( field == NULL ) { fatal_error("ERROR: JNI: Cannot get field from %s\n", STRING(HEAP_TRACKER_class)); } (*env)->SetStaticIntField(env, klass, field, 1); /* Indicate VM has started */ gdata->vmStarted = JNI_TRUE; } exitCriticalSection(jvmti); }
/* Callback for JVMTI_EVENT_VM_INIT */ static void JNICALL cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) { jvmtiHeapCallbacks heapCallbacks; jvmtiError error; /* Iterate through heap, find all untagged objects allocated before this */ (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks)); heapCallbacks.heap_iteration_callback = &cbObjectTagger; error = (*jvmti)->IterateThroughHeap(jvmti, JVMTI_HEAP_FILTER_TAGGED, NULL, &heapCallbacks, NULL); check_jvmti_error(jvmti, error, "Cannot iterate through heap"); enterCriticalSection(jvmti); { /* Indicate VM is initialized */ gdata->vmInitialized = JNI_TRUE; } exitCriticalSection(jvmti); }
/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */ static void JNICALL cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env, jclass class_being_redefined, jobject loader, const char* name, jobject protection_domain, jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) { enterCriticalSection(jvmti); { /* It's possible we get here right after VmDeath event, be careful */ if ( !gdata->vmDead ) { const char * classname; /* Name can be NULL, make sure we avoid SEGV's */ if ( name == NULL ) { classname = java_crw_demo_classname(class_data, class_data_len, NULL); if ( classname == NULL ) { fatal_error("ERROR: No classname in classfile\n"); } } else { classname = strdup(name); if ( classname == NULL ) { fatal_error("ERROR: Ran out of malloc() space\n"); } } *new_class_data_len = 0; *new_class_data = NULL; /* The tracker class itself? */ if ( strcmp(classname, STRING(HEAP_TRACKER_class)) != 0 ) { jint cnum; int systemClass; unsigned char *newImage; long newLength; /* Get number for every class file image loaded */ cnum = gdata->ccount++; /* Is it a system class? If the class load is before VmStart * then we will consider it a system class that should * be treated carefully. (See java_crw_demo) */ systemClass = 0; if ( !gdata->vmStarted ) { systemClass = 1; } newImage = NULL; newLength = 0; /* Call the class file reader/write demo code */ java_crw_demo(cnum, classname, class_data, class_data_len, systemClass, STRING(HEAP_TRACKER_class), "L" STRING(HEAP_TRACKER_class) ";", NULL, NULL, NULL, NULL, STRING(HEAP_TRACKER_newobj), "(Ljava/lang/Object;)V", STRING(HEAP_TRACKER_newarr), "(Ljava/lang/Object;)V", &newImage, &newLength, NULL, NULL); /* If we got back a new class image, return it back as "the" * new class image. This must be JVMTI Allocate space. */ if ( newLength > 0 ) { unsigned char *jvmti_space; jvmti_space = (unsigned char *)allocate(jvmti, (jint)newLength); (void)memcpy((void*)jvmti_space, (void*)newImage, (int)newLength); *new_class_data_len = (jint)newLength; *new_class_data = jvmti_space; /* VM will deallocate */ } /* Always free up the space we get from java_crw_demo() */ if ( newImage != NULL ) { (void)free((void*)newImage); /* Free malloc() space with free() */ } } (void)free((void*)classname); } } exitCriticalSection(jvmti); }
/* Callback for JVMTI_EVENT_VM_DEATH */ static void JNICALL cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env) { jvmtiHeapCallbacks heapCallbacks; jvmtiError error; /* These are purposely done outside the critical section */ /* Force garbage collection now so we get our ObjectFree calls */ error = (*jvmti)->ForceGarbageCollection(jvmti); check_jvmti_error(jvmti, error, "Cannot force garbage collection"); /* Iterate through heap and find all objects */ (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks)); heapCallbacks.heap_iteration_callback = &cbObjectSpaceCounter; error = (*jvmti)->IterateThroughHeap(jvmti, 0, NULL, &heapCallbacks, NULL); check_jvmti_error(jvmti, error, "Cannot iterate through heap"); /* Process VM Death */ enterCriticalSection(jvmti); { jclass klass; jfieldID field; jvmtiEventCallbacks callbacks; /* Disengage calls in HEAP_TRACKER_class. */ klass = (*env)->FindClass(env, STRING(HEAP_TRACKER_class)); if ( klass == NULL ) { fatal_error("ERROR: JNI: Cannot find %s with FindClass\n", STRING(HEAP_TRACKER_class)); } field = (*env)->GetStaticFieldID(env, klass, STRING(HEAP_TRACKER_engaged), "I"); if ( field == NULL ) { fatal_error("ERROR: JNI: Cannot get field from %s\n", STRING(HEAP_TRACKER_class)); } (*env)->SetStaticIntField(env, klass, field, 0); /* The critical section here is important to hold back the VM death * until all other callbacks have completed. */ /* Clear out all callbacks. */ (void)memset(&callbacks,0, sizeof(callbacks)); error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks)); check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks"); /* Since this critical section could be holding up other threads * in other event callbacks, we need to indicate that the VM is * dead so that the other callbacks can short circuit their work. * We don't expect an further events after VmDeath but we do need * to be careful that existing threads might be in our own agent * callback code. */ gdata->vmDead = JNI_TRUE; /* Dump all objects */ if ( gdata->traceInfoCount > 0 ) { TraceInfo **list; int count; int i; stdout_message("Dumping heap trace information\n"); /* Create single array of pointers to TraceInfo's, sort, and * print top gdata->maxDump top space users. */ list = (TraceInfo**)calloc(gdata->traceInfoCount, sizeof(TraceInfo*)); if ( list == NULL ) { fatal_error("ERROR: Ran out of malloc() space\n"); } count = 0; for ( i = 0 ; i < HASH_BUCKET_COUNT ; i++ ) { TraceInfo *tinfo; tinfo = gdata->hashBuckets[i]; while ( tinfo != NULL ) { if ( count < gdata->traceInfoCount ) { list[count++] = tinfo; } tinfo = tinfo->next; } } if ( count != gdata->traceInfoCount ) { fatal_error("ERROR: Count found by iterate doesn't match ours:" " count=%d != traceInfoCount==%d\n", count, gdata->traceInfoCount); } qsort(list, count, sizeof(TraceInfo*), &compareInfo); for ( i = 0 ; i < count ; i++ ) { if ( i >= gdata->maxDump ) { break; } printTraceInfo(jvmti, i+1, list[i]); } (void)free(list); } } exitCriticalSection(jvmti); }