Index: src/main/native/addField.c =================================================================== --- src/main/native/addField.c (revision 813372) +++ src/main/native/addField.c (working copy) @@ -16,141 +16,156 @@ #include "cjvmti.h" #include "addObject.h" #include "addClass.h" -FPOS_T addFieldValue(int type, jobject obj, jfieldID field, char * signature, - jthread thread, jint depth, jint slot) { - // Primitives +typedef union{ + jbyte * bArray; + jchar * cArray; + jdouble * dArray; + jfloat * fArray; + jint * iArray; + jlong * jArray; + jshort * sArray; + jboolean * zArray; +} primArrays; + +typedef union{ jbyte b; jchar c; jdouble d; jfloat f; jint i; jlong j; - jobject l; jshort s; jboolean z; +} primitives; + +// Saves a field to the dump file returning the position in the file it was saved to. +// type indicates where the field value came from instance, local or static. +FPOS_T addFieldValue(int type, jobject obj, jfieldID field, char * signature, + jthread thread, jint depth, jint slot, int referenceDepth) { + + // Primitives + + primitives prim; // Arrays - jbyte * bArray; - jchar * cArray; - jdouble * dArray; - jfloat * fArray; - jint * iArray; - jlong * jArray; - jshort * sArray; - jboolean * zArray; + + primArrays primArr; + int szArray; + int tempI; jobject arrayElement; - + jobject l = 0; jboolean isCopy; - jvmtiError err; int counter; FPOS_T position = -1; FPOS_T * arrayFields; struct JClass * arrayType; + tempI = 0; + memset(&primArr, 0, sizeof(primArr)); + memset(&prim, 0, sizeof(prim)); + position = FTELL(variableFile); switch (*signature) { case 'B': switch (type) { case CJVMTI_INSTANCE_VAR: - b = (*jniptr)->GetByteField(jniptr, obj, field); + prim.b = (*jniptr)->GetByteField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalInt(env, thread, depth, slot, &i); - b = (jbyte) i; + err = (*env)->GetLocalInt(env, thread, depth, slot, &tempI); + prim.b = (jbyte) tempI; break; case CJVMTI_STATIC_VAR: - b = (*jniptr)->GetStaticByteField(jniptr, obj, field); + prim.b = (*jniptr)->GetStaticByteField(jniptr, obj, field); break; } - printfd("Type %d %c %d \n", type, *signature, b); writeIDNoSize(CJVMTI_BYTE, variableFile); - fwrite(&b, sizeof(jbyte), 1, variableFile); + fwrite(&prim.b, sizeof(jbyte), 1, variableFile); break; case 'C': switch (type) { case CJVMTI_INSTANCE_VAR: - c = (*jniptr)->GetCharField(jniptr, obj, field); + prim.c = (*jniptr)->GetCharField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalInt(env, thread, depth, slot, &i); - c = (jchar) i; + err = (*env)->GetLocalInt(env, thread, depth, slot, &tempI); + prim.c = (jchar) tempI; break; case CJVMTI_STATIC_VAR: - c = (*jniptr)->GetStaticCharField(jniptr, obj, field); + prim.c = (*jniptr)->GetStaticCharField(jniptr, obj, field); break; } - printfd("Type %d %c %c \n", type, *signature, c); writeIDNoSize(CJVMTI_CHAR, variableFile); - fwrite(&c, sizeof(jchar), 1, variableFile); + fwrite(&prim.c, sizeof(jchar), 1, variableFile); break; case 'D': switch (type) { case CJVMTI_INSTANCE_VAR: - d = (*jniptr)->GetDoubleField(jniptr, obj, field); + prim.d = (*jniptr)->GetDoubleField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalDouble(env, thread, depth, slot, &d); + err = (*env)->GetLocalDouble(env, thread, depth, slot, &prim.d); break; case CJVMTI_STATIC_VAR: - d = (*jniptr)->GetStaticDoubleField(jniptr, obj, field); + prim.d = (*jniptr)->GetStaticDoubleField(jniptr, obj, field); break; } - printfd("Type %d %c %f \n", type, *signature, d); writeIDNoSize(CJVMTI_DOUBLE, variableFile); - fwrite(&d, sizeof(jdouble), 1, variableFile); + fwrite(&prim.d, sizeof(jdouble), 1, variableFile); break; case 'F': switch (type) { case CJVMTI_INSTANCE_VAR: - f = (*jniptr)->GetFloatField(jniptr, obj, field); + prim.f = (*jniptr)->GetFloatField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalFloat(env, thread, depth, slot, &f); + err = (*env)->GetLocalFloat(env, thread, depth, slot, &prim.f); break; case CJVMTI_STATIC_VAR: - f = (*jniptr)->GetStaticFloatField(jniptr, obj, field); + prim.f = (*jniptr)->GetStaticFloatField(jniptr, obj, field); break; } - printfd("Type %d %c %f \n", type, *signature, f); writeIDNoSize(CJVMTI_FLOAT, variableFile); - fwrite(&f, sizeof(jfloat), 1, variableFile); + fwrite(&prim.f, sizeof(jfloat), 1, variableFile); break; case 'I': switch (type) { case CJVMTI_INSTANCE_VAR: - i = (*jniptr)->GetIntField(jniptr, obj, field); + prim.i = (*jniptr)->GetIntField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalInt(env, thread, depth, slot, &i); + err = (*env)->GetLocalInt(env, thread, depth, slot, &prim.i); break; case CJVMTI_STATIC_VAR: - i = (*jniptr)->GetStaticIntField(jniptr, obj, field); + prim.i = (*jniptr)->GetStaticIntField(jniptr, obj, field); break; } - printfd("Type %d %c %d \n", type, *signature, i); writeIDNoSize(CJVMTI_INT, variableFile); - fwrite(&i, sizeof(jint), 1, variableFile); + fwrite(&prim.i, sizeof(jint), 1, variableFile); break; case 'J': switch (type) { case CJVMTI_INSTANCE_VAR: - j = (*jniptr)->GetLongField(jniptr, obj, field); + prim.j = (*jniptr)->GetLongField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalLong(env, thread, depth, slot, &j); + err = (*env)->GetLocalLong(env, thread, depth, slot, &prim.j); break; case CJVMTI_STATIC_VAR: - j = (*jniptr)->GetStaticLongField(jniptr, obj, field); + prim.j = (*jniptr)->GetStaticLongField(jniptr, obj, field); break; } - printfd("Type %d %c %ld \n", type, *signature, j); writeIDNoSize(CJVMTI_LONG, variableFile); - fwrite(&j, sizeof(jlong), 1, variableFile); + fwrite(&prim.j, sizeof(jlong), 1, variableFile); break; case 'L': + if (maxReferenceDepth != 0 && maxReferenceDepth <= referenceDepth){ + writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); + break; + } switch (type) { case CJVMTI_INSTANCE_VAR: l = (*jniptr)->GetObjectField(jniptr, obj, field); @@ -158,7 +173,7 @@ case CJVMTI_LOCAL_VAR: err = (*env)->GetLocalObject(env, thread, depth, slot, &l); if (err != JVMTI_ERROR_NONE) { - printfd("error %d\n", err); + printf("Error getting local object %d\n", err); } break; case CJVMTI_STATIC_VAR: @@ -169,65 +184,57 @@ } if (l != 0) { - printfd("Type %d %s %d\n", type, signature, l); - position = addObject(l)->positionInFile; - printfd("Position %lld \n", position); + position = addObject(l, referenceDepth+1)->positionInFile; } else { - printfd( - "Null Object, need to standardise how to represent this..\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } break; case 'S': switch (type) { case CJVMTI_INSTANCE_VAR: - s = (*jniptr)->GetShortField(jniptr, obj, field); + prim.s = (*jniptr)->GetShortField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalInt(env, thread, depth, slot, &i); - s = (jshort) i; + err = (*env)->GetLocalInt(env, thread, depth, slot, &tempI); + prim.s = (jshort) tempI; break; case CJVMTI_STATIC_VAR: - s = (*jniptr)->GetStaticShortField(jniptr, obj, field); + prim.s = (*jniptr)->GetStaticShortField(jniptr, obj, field); break; } - printfd("Type %d %c %hd \n", type, *signature, s); writeIDNoSize(CJVMTI_SHORT, variableFile); - fwrite(&s, sizeof(jshort), 1, variableFile); + fwrite(&prim.s, sizeof(jshort), 1, variableFile); break; case 'Z': switch (type) { case CJVMTI_INSTANCE_VAR: - z = (*jniptr)->GetBooleanField(jniptr, obj, field); + prim.z = (*jniptr)->GetBooleanField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: - err = (*env)->GetLocalInt(env, thread, depth, slot, &i); - z = (jboolean) i; + err = (*env)->GetLocalInt(env, thread, depth, slot, &tempI); + prim.z = (jboolean) tempI; break; case CJVMTI_STATIC_VAR: - z = (*jniptr)->GetStaticBooleanField(jniptr, obj, field); + prim.z = (*jniptr)->GetStaticBooleanField(jniptr, obj, field); break; } - if (z) { - printfd("Type %d %c true \n", type, *signature); - } else { - printfd("Type %d %c false \n", type, *signature); - } writeIDNoSize(CJVMTI_BOOLEAN, variableFile); - fwrite(&z, sizeof(jboolean), 1, variableFile); + fwrite(&prim.z, sizeof(jboolean), 1, variableFile); break; case '[': signature++; if ((*signature == 'L') || (*signature == '[')) { + if (maxReferenceDepth != 0 && maxReferenceDepth <= referenceDepth){ + writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); + break; + } switch (type) { case CJVMTI_INSTANCE_VAR: l = (*jniptr)->GetObjectField(jniptr, obj, field); break; case CJVMTI_LOCAL_VAR: err = (*env)->GetLocalObject(env, thread, depth, slot, &l); - if (err != JVMTI_ERROR_NONE) - printfd("Error %d \n", err); break; case CJVMTI_STATIC_VAR: l = (*jniptr)->GetStaticObjectField(jniptr, obj, field); @@ -237,7 +244,6 @@ break; } if (l == 0) { - printfd("NULL array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { FSEEK(variableFile, 0, SEEK_END); @@ -246,24 +252,29 @@ szArray = (*jniptr)->GetArrayLength(jniptr, l); arrayFields = NULL; if (szArray > 0) { + // Save field references in memory arrayFields = malloc(sizeof(FPOS_T) * szArray); for (counter = 0; counter < szArray; counter++) { FSEEK(variableFile, 0, SEEK_END); arrayElement = (*jniptr)->GetObjectArrayElement(jniptr, l, counter); if (arrayElement == 0) { - printfd("Null array element \n"); arrayFields[counter] = CJVMTI_NULL_OBJECT; } else { - printfd("Follow array\n"); arrayFields[counter] = addFieldValue( CJVMTI_ARRAY_VAR, arrayElement, 0, - (arrayType->name) + 1, 0, 0, 0); + (arrayType->name) + 1, 0, 0, 0, referenceDepth+1); + if (arrayFields[counter] == 0){ + printf("Found left over ref\n"); + (*jniptr)->DeleteLocalRef(jniptr, arrayElement); + } } } } FSEEK(variableFile, 0, SEEK_END); position = FTELL(variableFile); + + // Write out array references / array writeIDNoSize(CJVMTI_OBJECT_ARRAY, variableFile); writeReference(arrayType->positionInFile, variableFile); writeJint(szArray, variableFile); @@ -286,8 +297,6 @@ break; case CJVMTI_LOCAL_VAR: err = (*env)->GetLocalObject(env, thread, depth, slot, &l); - if (err != JVMTI_ERROR_NONE) - printfd("Error %d \n", err); break; case CJVMTI_STATIC_VAR: l = (*jniptr)->GetStaticObjectField(jniptr, obj, field); @@ -297,9 +306,7 @@ break; } // TODO correct this, do we have a null object before we declare the class type, or use the signature.. - printfd("get byte array \n"); if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); break; } @@ -316,160 +323,135 @@ // Z boolean true or false case 'B': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_BYTE_ARRAY, variableFile); writeJint(szArray, variableFile); - bArray + primArr.bArray = (*jniptr)->GetByteArrayElements(jniptr, l, &isCopy); - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%d", bArray[counter]); - fwrite(&bArray[counter], sizeof(jbyte), 1, variableFile); + fwrite(&primArr.bArray[counter], sizeof(jbyte), 1, variableFile); } - (*jniptr)->ReleaseByteArrayElements(jniptr, l, bArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseByteArrayElements(jniptr, l, primArr.bArray,JNI_ABORT); } break; case 'C': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_CHAR_ARRAY, variableFile); writeJint(szArray, variableFile); - cArray + primArr.cArray = (*jniptr)->GetCharArrayElements(jniptr, l, &isCopy); - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%c", cArray[counter]); - fwrite(&cArray[counter], sizeof(jchar), 1, variableFile); + fwrite(&primArr.cArray[counter], sizeof(jchar), 1, variableFile); } - (*jniptr)->ReleaseCharArrayElements(jniptr, l, cArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseCharArrayElements(jniptr, l, primArr.cArray,JNI_ABORT); } break; case 'D': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_DOUBLE_ARRAY, variableFile); writeJint(szArray, variableFile); - dArray = (*jniptr)->GetDoubleArrayElements(jniptr, l, + primArr.dArray = (*jniptr)->GetDoubleArrayElements(jniptr, l, &isCopy); - - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%f", dArray[counter]); - fwrite(&dArray[counter], sizeof(jdouble), 1, + fwrite(&primArr.dArray[counter], sizeof(jdouble), 1, variableFile); } - (*jniptr)->ReleaseDoubleArrayElements(jniptr, l, dArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseDoubleArrayElements(jniptr, l, primArr.dArray,JNI_ABORT); } break; case 'F': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_FLOAT_ARRAY, variableFile); writeJint(szArray, variableFile); - fArray = (*jniptr)->GetFloatArrayElements(jniptr, l, + primArr.fArray = (*jniptr)->GetFloatArrayElements(jniptr, l, &isCopy); - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%f", fArray[counter]); - fwrite(&fArray[counter], sizeof(jfloat), 1, + fwrite(&primArr.fArray[counter], sizeof(jfloat), 1, variableFile); } - (*jniptr)->ReleaseFloatArrayElements(jniptr, l, fArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseFloatArrayElements(jniptr, l, primArr.fArray,JNI_ABORT); } break; case 'I': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_INT_ARRAY, variableFile); writeJint(szArray, variableFile); - iArray = (*jniptr)->GetIntArrayElements(jniptr, l, &isCopy); - printfd(""); + primArr.iArray = (*jniptr)->GetIntArrayElements(jniptr, l, &isCopy); for (counter = 0; counter < szArray; counter++) { - printfR("%d", iArray[counter]); - fwrite(&iArray[counter], sizeof(jint), 1, variableFile); + fwrite(&primArr.iArray[counter], sizeof(jint), 1, variableFile); } - (*jniptr)->ReleaseIntArrayElements(jniptr, l, iArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseIntArrayElements(jniptr, l, primArr.iArray,JNI_ABORT); } break; case 'J': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_LONG_ARRAY, variableFile); writeJint(szArray, variableFile); - jArray + primArr.jArray = (*jniptr)->GetLongArrayElements(jniptr, l, &isCopy); - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%ld ", jArray[counter]); - fwrite(&jArray[counter], sizeof(jlong), 1, variableFile); + fwrite(&primArr.jArray[counter], sizeof(jlong), 1, variableFile); } - (*jniptr)->ReleaseLongArrayElements(jniptr, l, jArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseLongArrayElements(jniptr, l, primArr.jArray,JNI_ABORT); } break; case 'S': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_SHORT_ARRAY, variableFile); writeJint(szArray, variableFile); - sArray = (*jniptr)->GetShortArrayElements(jniptr, l, + primArr.sArray = (*jniptr)->GetShortArrayElements(jniptr, l, &isCopy); - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%d ", sArray[counter]); - fwrite(&sArray[counter], sizeof(jshort), 1, + fwrite(&primArr.sArray[counter], sizeof(jshort), 1, variableFile); } - (*jniptr)->ReleaseShortArrayElements(jniptr, l, sArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseShortArrayElements(jniptr, l, primArr.sArray,JNI_ABORT); } break; case 'Z': if (l == 0) { - printfd("NULL primitive array\n"); writeIDNoSize(CJVMTI_NULL_OBJECT, variableFile); } else { writeIDNoSize(CJVMTI_BOOLEAN_ARRAY, variableFile); writeJint(szArray, variableFile); - zArray = (*jniptr)->GetBooleanArrayElements(jniptr, l, + primArr.zArray = (*jniptr)->GetBooleanArrayElements(jniptr, l, &isCopy); - printfd(""); for (counter = 0; counter < szArray; counter++) { - printfR("%ld ", zArray[counter]); - fwrite(&zArray[counter], sizeof(jboolean), 1, + fwrite(&primArr.zArray[counter], sizeof(jboolean), 1, variableFile); } - (*jniptr)->ReleaseBooleanArrayElements(jniptr, l, zArray,JNI_ABORT); - printfR("\n"); + (*jniptr)->ReleaseBooleanArrayElements(jniptr, l, primArr.zArray,JNI_ABORT); } break; } } } + /*free(bArray); + free(cArray); + free(dArray); + free(fArray); + free(iArray); + free(jArray); + free(sArray); + free(zArray); */ return position; } Index: src/main/native/cjvmti.c =================================================================== --- src/main/native/cjvmti.c (revision 813372) +++ src/main/native/cjvmti.c (working copy) @@ -12,6 +12,27 @@ * limitations under the License. ******************************************************************************/ +/**** + +CJVMTI v0.1 +-> indicates what this can possibly call +addThreads -> addObject & addClass +addObject -> addClass & addObject & queues objects +addClass -> addClass & queues classes +objectQueues -> getObjectReferences +getObjectReferences -> addField +addField -> addObject +classQueue -> getStaticReferences +getStaticReferences -> addObject + +addObject and addClass both can function without following any references and allocate the required space in the dump file +This means calling them can happen at any point with no adverse effects (running out of stack.) +They are tagged so that any references to them are resolved to their position in the dump file. +Its only once the dump is completed that the references are followed by clearing the class and object queue. These both generate +many more references that need to be added to the queues. This two step process means all reference loops are easily avoided as references +can always be written as the reference is only a pointer inside the file. + +****/ #include "cjvmti.h" #include "addThreads.h" @@ -19,15 +40,15 @@ #include "addClass.h" #include "addObject.h" - -static int logFile = 0; +int jvmtiVersion = 11; +int dumping = 0; // Used to ensure two dumps aren't attempted at the same time +static int logFile = 0; int initAgent(); // Provides printing to log/stdout void printfR(const char * _Format, ...) { va_list args; va_start(args, _Format); - assert(printDepth > -1); //vprintf(_Format, args); va_end(args); @@ -66,28 +87,7 @@ return 1; } -// Write a CJVMTI constant and make space to write the size of the following structure, returns the location in the file where the size needs to be written -FPOS_T writeID(char id, FILE * fp) { - FPOS_T pos; - FPOS_T ident = 0x8888888888888888LL; - fwrite(&id, sizeof(char), 1, fp); - pos = FTELL(fp); - fwrite(&ident, sizeof(FPOS_T), 1, fp); - return pos; -} - -// Works out and writes the size of the enclosed structure, sizeWrite is the position to write the size into -int writeSize(FPOS_T sizeWrite, FILE * fp) { - FPOS_T pos; - FGETPOS(fp, &pos); - FSETPOS(fp, &sizeWrite); - sizeWrite = pos - sizeWrite; - fwrite(&sizeWrite, sizeof(FPOS_T), 1, fp); - FSEEK(fp, 0, SEEK_END); - return 1; -} - -// Writes a null terminated string, needs replacing +// Writes a null terminated string, needs replacing to specifying the number of characters int writeString(char * tString, FILE * fp) { fputs(tString, fp); fputc(0, fp); @@ -101,9 +101,6 @@ // Used for writing longs and file position, needs splitting up int writeReference(FPOS_T pt, FILE * fp) { - //if (pt == waitingToBeWritten) { - // abort(); - //} fwrite(&pt, sizeof(FPOS_T), 1, fp); return 1; } @@ -118,7 +115,43 @@ return 1; } +void JNICALL +Exception(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location){ + jvmtiPhase phas; + char * sig; + char * gSig; + jclass clazz; + if (catch_method == NULL){ + if (!dumping){ + dumping = 1; + printf("Exception %d \n \n \n", catch_method); + dataDumpRequest(jvmti_env); + } + }/*else{ // For apache tomcat demo. + if (exception != NULL ){ + clazz = (*jni_env)->GetObjectClass(jni_env, exception); + (*env)->GetClassSignature(env, clazz, &sig, &gSig); + if (!strcmp(sig, "Ljava/lang/ArrayIndexOutOfBoundsException;")){ + if (!dumping){ + dumping = 1; + printf("Exception %d \n \n \n", catch_method); + dataDumpRequest(jvmti_env); + } + } + } + }*/ + return; +} + +// Clears through reference queue int clearObjectQueue() { jclass clazz; jobject obj; @@ -129,13 +162,13 @@ getObjectReferences(obj); } if(clazz != 0){ - addStaticFields(clazz); + getClassReferences(clazz); } } return 1; } -// Ensures references are within range, useful for testing +// Ensures references are within the files range, useful for testing int checkReference(FPOS_T loc, FILE * fp) { FPOS_T fileSize; FPOS_T currentLoc = FTELL(fp); @@ -158,7 +191,7 @@ static void JNICALL dataDumpRequest(jvmtiEnv *jvmti) { int i; jint rc; - jthread currentThread; + jthread currentThread = 0; jlong tag; jobject deTagObj; struct JObject * deAlObj; @@ -166,18 +199,30 @@ jthread * suspendedThreads; jint numSuspendedThreads; env = jvmti; - rc = (*vmptr)->GetEnv(vmptr, (void **) &jniptr, JNI_VERSION_1_6); - if (rc != JVMTI_ERROR_NONE) { - printf("Unable to load JNI environment"); - return; + dumping = 1; + if (jniptr == NULL){ + rc = (*vmptr)->GetEnv(vmptr, (void **) &jniptr, JNI_VERSION_1_4); + if (rc != JVMTI_ERROR_NONE) { + printf("Unable to load JNI environment"); + return; + } } + + (*jniptr)->PushLocalFrame(jniptr, 5000); // Automatically deallocates local references when popped + nullObject.positionInFile = CJVMTI_NULL_OBJECT; - initAgent(); + initAgent(); // Prepare dump file + + + printf("Get current thread \n"); (*jvmti)->GetCurrentThread(jvmti, ¤tThread); - - (*env)->SetTag(env, currentThread, 1); + if (currentThread == 0){ + printf("Null thread returned \n"); + }else{ + } + (*env)->SetTag(env, currentThread, 1); // So we can skip pausing the agent thread rc = (*env)->GetAllThreads(env, &numSuspendedThreads, &suspendedThreads); printf("Suspend the VM threads \n"); printf("Current thread is %llx \n", currentThread); @@ -190,13 +235,15 @@ } } (*env)->SetTag(env,currentThread, 0); - printf("Creating dump file \n"); + printf("Creating dump \n"); qObjects = Q_initQueue(100, sizeof(jobject)); qStaticFields = Q_initQueue(100, sizeof(jclass)); toDetag = Q_initQueue(200, sizeof(jobject)); - followAllThreads(); + + // Dump finished + if (fp!=NULL)fclose(fp); fp = NULL; fclose(variableFile); @@ -207,9 +254,7 @@ if (tag == 0)continue; // We sometimes free an object from earlier in the queue if it went from an object to a class object deAlObj = (struct JObject *)tag; if (deAlObj->isClass){ - // TODO isClass is set before add object deAlClass = (struct JClass *) tag; - for (i = 0; i < deAlClass->numInstanceFields; i++){ (*env)->Deallocate(env, (unsigned char*)deAlClass->instanceFieldGenSig[i]); (*env)->Deallocate(env, (unsigned char*)deAlClass->instanceFieldSignatures[i]); @@ -219,9 +264,6 @@ free(deAlClass->instanceFields); free(deAlClass->instanceFieldSignatures); free(deAlClass->instanceModifiers); - free(deAlClass->staticFieldLoc); - - } free((void*)tag); (*env)->SetTag(env, deTagObj, 0); @@ -237,14 +279,73 @@ Q_free(qStaticFields); (*env)->Deallocate(env, (unsigned char *)suspendedThreads); - printf("Left over object %d \n", objectCount); - printf("Left over static fields %d \n", staticsCount); printf("Finished creating dump file \n"); + (*jniptr)->PopLocalFrame(jniptr, NULL); // Clears local references + dumping = 0; +} + +int setEnvironment(JavaVM * vm){ + jint rc; + jvmtiError err; + char * errorName; + jvmtiCapabilities caps; + jvmtiEventCallbacks callbacks; + errorName = NULL; + rc = (*vm)->GetEnv(vm, (void **) &env, JVMTI_VERSION_1_1); + if (rc != JNI_OK) { + printf("--- Cannot connect to JNI ---\n"); + if(rc == JNI_EVERSION){ + printf(" Incompatible version \n"); + } + return rc; + } + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.DataDumpRequest = &dataDumpRequest; + callbacks.Exception = &Exception; + + err = (*env)->SetEventCallbacks(env, &callbacks, sizeof(callbacks)); + if (err != JVMTI_ERROR_NONE) + printf("Could not set callbacks"); + + + err = (*env)->SetEventNotificationMode(env, JVMTI_ENABLE, + JVMTI_EVENT_DATA_DUMP_REQUEST, (jthread) NULL); + + memset(&caps, 0, sizeof(jvmtiCapabilities)); + + caps.can_get_owned_monitor_info = 1; + caps.can_get_current_contended_monitor = 1; + caps.can_get_monitor_info = 1; + caps.can_access_local_variables = 1; + caps.can_tag_objects = 1; + caps.can_generate_exception_events = 1; + caps.can_get_source_file_name = 1; + caps.can_get_line_numbers = 1; + caps.can_signal_thread = 1; + caps.can_suspend = 1; + + err = (*env)->AddCapabilities(env, &caps); + if (err != JVMTI_ERROR_NONE) + printf("can't turn on required capabilites\n"); + + err = (*env)->SetEventNotificationMode(env, JVMTI_ENABLE, + JVMTI_EVENT_EXCEPTION, (jthread) NULL); + + if (err != JVMTI_ERROR_NONE) + printf("Could not set event notification %d \n", err); + + + err = (*env)->CreateRawMonitor(env, "cbmonitor", &lock); + if (err != JVMTI_ERROR_NONE) + printf("cannot create monitor"); + fp = NULL; + variableFile = NULL; + return 1; } + int initAgent(){ static int dumpCount; - time_t creationTime; struct tm * dumpTime; char fileName[50]; @@ -254,16 +355,19 @@ staticsCount = 0; printDepth = 0; logFile = 0; -#ifdef __DEBUG_CJVMTI__ - _CrtDumpMemoryLeaks(); -#endif + + #ifdef __DEBUG_CJVMTI__ + _RPT0(_CRT_WARN,"Trace\n"); + _CrtDumpMemoryLeaks(); + #endif + if (logFile) { printf("Open log file\n"); fp = fopen("CJVMTI_debug.txt", "wb+"); fprintf(fp, "CJVMTI LOG FILE\n"); printf("Opened\n"); } - + printf("Creating dump file "); creationTime = time(NULL); dumpTime = localtime(&creationTime); @@ -285,70 +389,52 @@ return 1; } -JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { - jint rc; - jvmtiError err; - jvmtiCapabilities caps; - jvmtiEventCallbacks callbacks; +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { + char * frameDepthChar; + char * referenceDepthChar; + jniptr = NULL; printf("CJVMTI v0.1 \n"); - - // print header info - vmptr = vm; - rc = (*vm)->GetEnv(vm, (void **) &env, JVMTI_VERSION); - if (rc != JVMTI_ERROR_NONE) { - printf("--- Cannot connect to JNI ---\n"); - return rc; - } - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.DataDumpRequest = &dataDumpRequest; - - err = (*env)->SetEventCallbacks(env, &callbacks, sizeof(callbacks)); - if (err != JVMTI_ERROR_NONE) - printf("Could not set callbacks"); - - - err = (*env)->SetEventNotificationMode(env, JVMTI_ENABLE, - JVMTI_EVENT_DATA_DUMP_REQUEST, (jthread) NULL); - if (err != JVMTI_ERROR_NONE) - printf("Could not set event notification"); - - memset(&caps, 0, sizeof(jvmtiCapabilities)); - - caps.can_get_owned_monitor_info = 1; - caps.can_get_current_contended_monitor = 1; - caps.can_get_monitor_info = 1; - caps.can_access_local_variables = 1; - caps.can_tag_objects = 1; - caps.can_generate_exception_events = 1; - caps.can_get_source_file_name = 1; - caps.can_get_line_numbers = 1; - caps.can_signal_thread = 1; - caps.can_suspend = 1; - - err = (*env)->AddCapabilities(env, &caps); - if (err != JVMTI_ERROR_NONE) - printf("can't turn on required capabilites\n"); - - err = (*env)->CreateRawMonitor(env, "cbmonitor", &lock); - if (err != JVMTI_ERROR_NONE) - printf("cannot create monitor"); - fp = NULL; - variableFile = NULL; + setEnvironment(vm); #ifdef __DEBUG_CJVMTI__ - hLogFile = CreateFile("c:\\memLeakCJVMTI.txt", GENERIC_WRITE, - FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, NULL); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, hLogFile); - - printf("Memory leak \n"); + hLogFile = CreateFile("c:\\memLeak.txt", GENERIC_WRITE, + FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, hLogFile); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); _CrtDumpMemoryLeaks(); #endif + printf("Agent loaded \n"); + maxReferenceDepth = 4; + maxFrameDepth = 3; + if (options != NULL){ + frameDepthChar = strtok(options, ","); + referenceDepthChar = strtok(NULL, ","); + if (frameDepthChar == NULL || referenceDepthChar == NULL) + { + maxReferenceDepth = 0; + maxFrameDepth = 0; + printf("Unlimited frame depth and reference depth set\n"); + return JNI_OK; + } + + maxFrameDepth = atoi(frameDepthChar); + maxReferenceDepth = atoi(referenceDepthChar); + if (maxFrameDepth != 0 || maxReferenceDepth != 0) + { + printf("Frame Depth %s ReferenceDepth %s \n", frameDepthChar, referenceDepthChar); + }else{ + printf("Unlimited frame depth and reference depth set\n"); + } + }else{ + maxReferenceDepth = 0; + maxFrameDepth = 0; + printf("Unlimited frame depth and reference depth set\n"); + } return JNI_OK; } @@ -356,8 +442,11 @@ Agent_OnUnload(JavaVM *vm) { #ifdef __DEBUG_CJVMTI__ _RPT0(_CRT_WARN,"file message\n"); - CloseHandle(hLogFile); + CloseHandle(hLogFile); #endif if (fp != NULL) fclose(fp); - if (fp != NULL) fclose(variableFile); + if (variableFile != NULL) fclose(variableFile); + //(*env)->DisposeEnvironment(env); + + printf("Agent released \n"); } Index: src/main/native/include/addObject.h =================================================================== --- src/main/native/include/addObject.h (revision 813372) +++ src/main/native/include/addObject.h (working copy) @@ -16,7 +16,6 @@ #ifndef addObject_H_ #define addObject_H_ #include "cjvmti.h" -struct JObject * addObject(jobject obj); -struct JObject * getObjectInfo(jclass class, jobject obj); +struct JObject * addObject(jobject obj, int depth); struct JObject * getObjectReferences(jobject obj); #endif Index: src/main/native/include/addThreads.h =================================================================== --- src/main/native/include/addThreads.h (revision 813372) +++ src/main/native/include/addThreads.h (working copy) @@ -16,8 +16,5 @@ #ifndef addThreads_H_ #define addThreads_H_ #include "cjvmti.h" -int getLocVars(jmethodID methodID, jlocation loc, jthread * thread, int depth); -FPOS_T getFrameInfo(int depth, jvmtiFrameInfo * info, jthread * thread); -FPOS_T getThreadInfo(jthread * thread); int followAllThreads(); #endif Index: src/main/native/include/cjvmti.h =================================================================== --- src/main/native/include/cjvmti.h (revision 813372) +++ src/main/native/include/cjvmti.h (working copy) @@ -18,7 +18,7 @@ #ifdef __linux__ #define _FILE_OFFSET_BITS 64 #elif WIN32 -// Uncomment to detect memory leaks +// Uncomment to detect memory leaks with visual c++ //#define __DEBUG_CJVMTI__ #endif @@ -36,6 +36,7 @@ #include #include +// Cross platform file operations all based on __int64 #ifdef __linux__ #define FPOS_T off_t #define FSEEK(x,y,z) fseeko((x),(y),(z)) @@ -55,24 +56,24 @@ struct JObject { int state; // 0 not prepared 1 prepared 2 is references filled - FPOS_T positionInFile; - FPOS_T referenceLocation; - int totalFieldCount; - char isClass; + FPOS_T positionInFile; // Actual object position in dump file + FPOS_T referenceLocation; // Used to store where references should be saved + int totalFieldCount; // Number of fields after following the superclass hierarchy + char isClass; // Identifies if this is a class object + char depth; // Used to limit depth references are followed to }; struct JClass { - struct JObject obj; - int filledIn; - char * name; // dealloc from jvmti - FPOS_T positionInFile; - FPOS_T classLoader; - FPOS_T staticFieldStart; - struct JClass * superClass; + struct JObject obj; // Class object + int filledIn; // Check class has been correctly initialised + char * name; // Class name + FPOS_T positionInFile; // Actual position in dump file + FPOS_T classLoader; // Class loader position + FPOS_T staticFieldStart; // Where static fields should be saved + struct JClass * superClass; int numStaticFields; - FPOS_T * staticFieldLoc; int numInstanceFields; - jfieldID * instanceFields; // + jfieldID * instanceFields; char * * instanceFieldSignatures; // each ref is jvmti, malloced normally char * * instanceFieldGenSig; // each ref is jvmti, malloced normally jint * instanceModifiers; // malloced normally @@ -131,12 +132,14 @@ JavaVM *vmptr; JNIEnv *jniptr; jrawMonitorID lock; -FILE *fp; -FILE *variableFile; +FILE *fp; // log file +FILE *variableFile; // dump file +int maxReferenceDepth; +int maxFrameDepth; int printDepth; -struct queueADT * qObjects; -struct queueADT * qStaticFields; -struct queueADT * toDetag; // JObjects that need to be detagged and freed +struct queueADT * qObjects; // References waiting to be followed in objects +struct queueADT * qStaticFields; // References waiting to be followed in classes +struct queueADT * toDetag; // Memory that needs to be freed/objects that need to be detagged long objectCount; long staticsCount; struct JObject nullObject; @@ -152,11 +155,10 @@ void printfR(const char * _Format, ...); void printfd(const char * _Format, ...); int writeIDNoSize(char id, FILE * fp); -FPOS_T writeID(char id, FILE * fp) ; -int writeSize(FPOS_T sizeWrite, FILE * fp); int writeString(char * tString, FILE * fp); int writeJint(jint i, FILE * fp); int writeReference(FPOS_T pt, FILE * fp); int writeLong(jlong j, FILE *fp); int writeBool(jboolean b, FILE * fp); +static void JNICALL dataDumpRequest(jvmtiEnv *jvmti); #endif Index: src/main/native/include/addClass.h =================================================================== --- src/main/native/include/addClass.h (revision 813372) +++ src/main/native/include/addClass.h (working copy) @@ -17,6 +17,5 @@ #define addClass_H_ #include "cjvmti.h" struct JClass * addClassDetails(jclass class); -struct JClass * addStaticFields(jclass class); -int addMethodDetails(jclass class); +struct JClass * getClassReferences(jclass class); #endif Index: src/main/native/include/addField.h =================================================================== --- src/main/native/include/addField.h (revision 813372) +++ src/main/native/include/addField.h (working copy) @@ -17,5 +17,5 @@ #define addField_H_ #include "cjvmti.h" FPOS_T addFieldValue(int type, jobject obj, jfieldID field, char * signature, - jthread thread, jint depth, jint slot); + jthread thread, jint depth, jint slot, int referenceDepth); #endif Index: src/main/native/addClass.c =================================================================== --- src/main/native/addClass.c (revision 813372) +++ src/main/native/addClass.c (working copy) @@ -18,6 +18,9 @@ #include "addField.h" #include "addObject.h" + +int addMethodDetails(jclass class); + struct JClass * addClassDetails(jclass class) { jint field_count; jvmtiError err; @@ -57,14 +60,14 @@ return NULL; } statusP = 0; - /// Try do something with this before the call... - + // Make a call to check for errors on this class err = (*env)->GetClassStatus(env, class, &statusP); if (err != JVMTI_ERROR_NONE){ printf("Class is not loaded \n"); return NULL; } - //printf("Status %d \n", statusP); + + // Check class is valid before attempting to save any information about it. (*env)->GetClassModifiers(env, class, &classMods); //if (statusP == 0 || statusP & 8){ if (!(statusP == 7 || statusP == 16 || statusP == 32 || (statusP == 3 && (classMods & 0x0200 || classMods & 0x0400)))){ @@ -75,9 +78,10 @@ err = (*env)->GetTag(env, class, &tag); if (err != JVMTI_ERROR_NONE) { - printDepth--; return NULL; } + + // Check if this has already been added, but as an object, not a class object. classObject = 0; if (tag!=0){ oldObj = (void *)tag; @@ -89,14 +93,20 @@ - // Follow class hierarchy from the bottom up, reduces recursion which isn't currently prevented on classes, only object + if (tag == 0 || classObject) { FSEEK(variableFile, 0, SEEK_END); + + // Follow class hierarchy from the bottom up, reduces recursion which isn't currently prevented on classes, only object + + // Calculate bottom depth superClazz = class; while (superClazz != 0) { level++; superClazz = (*jniptr)->GetSuperclass(jniptr, superClazz); } + + // Work back up adding the class details. superClazz = class; while (level != 0) { superClazz = class; @@ -113,66 +123,71 @@ FSEEK(variableFile, 0, SEEK_END); } - addClassDetails((*jniptr)->GetSuperclass(jniptr, class)); // Ensure we know everything about this class hierarchy before beginning + + addClassDetails((*jniptr)->GetSuperclass(jniptr, class)); // Ensure we know everything about + //this class hierarchy before beginning, should return immediately + + // Begin writing class details FSEEK(variableFile, 0, SEEK_END); err = (*env)->GetClassSignature(env, class, &name_ptr, &genSig_ptr); if (err != JVMTI_ERROR_NONE){ - printf(" ERROR ! \n "); + (*env)->Deallocate(env, (unsigned char*)genSig_ptr); + (*env)->Deallocate(env, (unsigned char*)name_ptr); + printf(" Unexpected JVMTI error %d \n", err); abort(); } - printfd("--- Not seen this class before, allocate %s ---\n", name_ptr); + tag = (long) (struct JClass *) calloc(1, sizeof(struct JClass)); + if (tag == (long) NULL) { - printfd("--- Failed to malloc ---\n"); - printDepth--; + printf("Failed to malloc class \n"); abort(); - return NULL; } err = (*env)->SetTag(env, class, tag); - Q_enqueue(&class, toDetag); + + Q_enqueue(&class, toDetag); // Add to deallocate queue + jcls = (void *) tag; - if (classObject){ - // TODO free old object memory! + + if (classObject){ // If this was previously just an object copy object information and deallocate old object. jcls->obj = (*oldObj); free(oldObj); } jcls->positionInFile = FTELL(variableFile); - jcls->obj.isClass = 1; + jcls->obj.isClass = 1; // Indicates its a class object staticsCount++; writeIDNoSize(CJVMTI_CLASS, variableFile); writeJint(classMods, variableFile); writeString(name_ptr, variableFile); - //printf(" %s \n", name_ptr); - sourceFileName = 0; err = (*env)->GetSourceFileName(env, class, &sourceFileName); if (err != JVMTI_ERROR_NONE || sourceFileName == 0) { + (*env)->Deallocate(env, (unsigned char*)sourceFileName); writeString("NoSource", variableFile); } else { writeString(sourceFileName, variableFile); (*env)->Deallocate(env, (unsigned char*)sourceFileName); } + if (genSig_ptr != 0){ writeString(genSig_ptr, variableFile); (*env)->Deallocate(env, (unsigned char*)genSig_ptr); }else { + (*env)->Deallocate(env, (unsigned char*)genSig_ptr); writeString(" ", variableFile); } } else { - jcls = (void *) tag; - printDepth--; + jcls = (void *) tag; // Already disocvered so return class. return jcls; } err = (*env)->GetClassFields(env, class, &field_count, &fields); if (err != JVMTI_ERROR_NONE) { jcls->numInstanceFields = 0; - printDepth--; - printf(" No class fields \n"); + printf("Unexpected JVMTI error %d \n", err); abort(); - return jcls; } addMethodDetails(class); @@ -191,6 +206,7 @@ } } jcls->numStaticFields = staticFieldCount; + // malloc space for them.. jcls->numInstanceFields = instanceFieldCount; jcls->instanceModifiers @@ -203,16 +219,14 @@ * instanceFieldCount); if (jcls->instanceFields == NULL) { - printfd("--- Malloc failed end Class details ---\n"); + printf("Failed to malloc instance fields\n"); abort(); - printDepth--; - return NULL; } writeIDNoSize(CJVMTI_CLASS_INSTANCE_FIELDS, variableFile); writeJint(instanceFieldCount, variableFile); - // occupy struct with instance fields so we can save objects correctly and also write out to file + // occupy struct with instance fields so we can save objects correctly countUp = 0; for (i = 0; i < field_count; i++) { (*env)->GetFieldModifiers(env, class, fields[i], &modifiers); @@ -220,9 +234,8 @@ err = (*env)->GetFieldName(env, class, fields[i], &name_ptr, &signature_ptr, &genSig_ptr); if (err != JVMTI_ERROR_NONE) { - printf("Error getting field name \n"); + printf("Error getting field name %d \n", err); abort(); - break; } writeJint(modifiers, variableFile); @@ -235,7 +248,6 @@ writeString(genSig_ptr, variableFile); } - printfd("Field %s type %s \n", name_ptr, signature_ptr); instanceFieldCount--; assert(instanceFieldCount > -1); jcls->instanceFields[countUp] = fields[i]; @@ -253,17 +265,16 @@ } } + // write out static field details, these only need saving to the dump file. writeIDNoSize(CJVMTI_CLASS_STATIC_FIELDS, variableFile); writeJint(staticFieldCount, variableFile); - - // write out static field details + genSig_ptr = NULL; for (i = 0; i < field_count; i++) { (*env)->GetFieldModifiers(env, class, fields[i], &modifiers); if ((modifiers & 0x0008)) { err = (*env)->GetFieldName(env, class, fields[i], &name_ptr, &signature_ptr, &genSig_ptr); if (err != JVMTI_ERROR_NONE) { - printfd("Getting field name for class error %d\n", err); break; } @@ -273,6 +284,7 @@ writeString(signature_ptr, variableFile); if (genSig_ptr == 0) { writeString(" ", variableFile); + (*env)->Deallocate(env, (unsigned char*)genSig_ptr); } else { writeString(genSig_ptr, variableFile); (*env)->Deallocate(env, (unsigned char*)genSig_ptr); @@ -282,9 +294,9 @@ } } - jcls->staticFieldLoc = (FPOS_T *) malloc(sizeof(FPOS_T) * staticFieldCount); // References to static fields // make space for all references in file. + fieldPos = FTELL(variableFile); writeIDNoSize(CJVMTI_SUPERCLASS_FIELDS, variableFile); fwrite(&waitingToBeWritten, sizeof(FPOS_T), 1, variableFile); // super class reference @@ -311,6 +323,8 @@ FGETPOS(variableFile, &fieldPosCheck); //Alignment check for end + + // Follow references FSEEK(variableFile, 0, SEEK_END); jcls->superClass = addClassDetails((*jniptr)->GetSuperclass(jniptr, class)); @@ -334,17 +348,19 @@ fieldPos = FTELL(variableFile); FSEEK(variableFile, 0, SEEK_END); } - if (interfaceCount > 0)(*env)->Deallocate(env, (unsigned char*)interfaces); + (*env)->Deallocate(env, (unsigned char*)interfaces); (*env)->Deallocate(env, (unsigned char*)fields); jcls->staticFieldStart = fieldPos; FSEEK(variableFile, 0, SEEK_END); - Q_enqueue(&class, qStaticFields); + Q_enqueue(&class, qStaticFields); // Add to class queue to resolve static field references return jcls; } -struct JClass * addStaticFields(jclass class) { +// Resolves static field references +struct JClass * getClassReferences(jclass class) { FPOS_T fieldPos; + FPOS_T reference; jfieldID * fields = NULL; jint field_count; int i; @@ -360,17 +376,20 @@ if (jcls == NULL) { return NULL; } - if (jcls->filledIn) { + if (jcls->filledIn) {// Indicates references have already been resolved return jcls; } + staticFieldCount = jcls->numStaticFields; FSEEK(variableFile, jcls->staticFieldStart, SEEK_SET); + writeIDNoSize(CJVMTI_CLASS_STATIC_FIELDS, variableFile); fieldPos = FTELL(variableFile); FSEEK(variableFile, 0, SEEK_END); err = (*env)->GetClassFields(env, class, &field_count, &fields); (*env)->GetClassLoader(env, class, &classloader); - + + // Follow each reference, then save for (i = 0; i < field_count; i++) { err = (*env)->GetFieldName(env, class, fields[i], &name_ptr, &signature_ptr, &genSig_ptr); @@ -383,15 +402,14 @@ (*env)->GetFieldModifiers(env, class, fields[i], &modifiers); if (modifiers & 0x0008) { staticFieldCount--; - - jcls->staticFieldLoc[staticFieldCount] - = addFieldValue(CJVMTI_STATIC_VAR, class, fields[i], - signature_ptr, 0, 0, 0); + reference = 0; + reference = addFieldValue(CJVMTI_STATIC_VAR, class, fields[i], + signature_ptr, 0, 0, 0, 0); FSETPOS(variableFile, &fieldPos); - writeReference(jcls->staticFieldLoc[staticFieldCount], variableFile); + writeReference(reference, variableFile); - // check its adding a true reference - if (jcls->staticFieldLoc[staticFieldCount] == waitingToBeWritten) { + // check its added a true reference + if (reference == waitingToBeWritten) { printf("Circular reference, abort \n"); abort(); } @@ -402,13 +420,16 @@ (*env)->Deallocate(env, (unsigned char *) name_ptr); (*env)->Deallocate(env, (unsigned char *) signature_ptr); (*env)->Deallocate(env, (unsigned char *) genSig_ptr); + name_ptr = NULL; + signature_ptr = NULL; + genSig_ptr = NULL; } // write class loader details to file FSEEK(variableFile, 0, SEEK_END); if (classloader != 0) { - jcls->classLoader = addObject(classloader)->positionInFile; + jcls->classLoader = addObject(classloader, 0)->positionInFile; } else { jcls->classLoader = CJVMTI_NULL_OBJECT; } @@ -419,7 +440,7 @@ FSEEK(variableFile, 0, SEEK_END); printDepth--; - jcls->filledIn = 1; + jcls->filledIn = 1; // Indicate class has been resolved staticsCount--; (*env)->Deallocate(env, (unsigned char *)fields); return jcls; @@ -433,7 +454,7 @@ int addMethodDetails(jclass class) { jint method_count; - jmethodID * method_ptr; + jmethodID * method_ptr = NULL; jvmtiError err; char * mname = NULL; char * msig = NULL; @@ -448,6 +469,7 @@ writeIDNoSize(CJVMTI_METHOD, variableFile); err = (*env)->GetClassMethods(env, class, &method_count, &method_ptr); if (err != JVMTI_ERROR_NONE) { + (*env)->Deallocate(env, (unsigned char *) method_ptr); method_count = 0; } writeJint(method_count, variableFile); @@ -470,6 +492,7 @@ (*env)->Deallocate(env, (unsigned char *) mgenSig); } else { writeString(" ", variableFile); + (*env)->Deallocate(env, (unsigned char *) mgenSig); } mname = NULL; msig = NULL; @@ -502,7 +525,7 @@ } - if (err == JVMTI_ERROR_NONE && varCount > 0) (*env)->Deallocate(env, (unsigned char *) table); + (*env)->Deallocate(env, (unsigned char *) table); table = NULL; // Line number table @@ -515,14 +538,14 @@ writeJint(lnt_ptr[i2].line_number, variableFile); writeReference(lnt_ptr[i2].start_location, variableFile); } - if (varCount > 0){ - (*env)->Deallocate(env, (unsigned char *) lnt_ptr); - } + } + (*env)->Deallocate(env, (unsigned char *) lnt_ptr); + lnt_ptr = NULL; } - if (method_count > 0)(*env)->Deallocate(env, (unsigned char *) method_ptr); + (*env)->Deallocate(env, (unsigned char *) method_ptr); return 1; } Index: src/main/native/addObject.c =================================================================== --- src/main/native/addObject.c (revision 813372) +++ src/main/native/addObject.c (working copy) @@ -18,35 +18,50 @@ #include "addObject.h" #include "addClass.h" #include "addField.h" -struct JObject * addObject(jobject obj) { - jclass clazz; - if (obj == 0){ - printf("NULL object! \n"); - abort(); - return &nullObject; - } - clazz = (*jniptr)->GetObjectClass(jniptr, obj); - return getObjectInfo(clazz, obj); -} - -struct JObject * getObjectInfo(jclass class, jobject obj) { +struct JObject * addObject(jobject obj, int depth) { int i; jlong tag; jvmtiError err; + jclass class; struct JClass * clazz; struct JClass * tempClazz; struct JObject * objp = NULL; FPOS_T fieldValLoc; jint totalFieldCount; int classObject = 0; + + if (maxReferenceDepth != 0 && maxReferenceDepth <= depth){ + return &nullObject; // We have followed to max reference depth so return null + } + + // Cover all cases of null classes / jni references / invalid classes + + if (obj == NULL) return &nullObject; // Unexpected. + + // Check if we have already referenced this object + err = (*env)->GetTag(env, obj, &tag); + if (tag == 0){ + class = (*jniptr)->GetObjectClass(jniptr, obj); + if (class == NULL){ + (*jniptr)->DeleteLocalRef(jniptr, obj); + (*jniptr)->DeleteLocalRef(jniptr, class); + return &nullObject; + } + clazz = addClassDetails(class); + }else{ + class = (*jniptr)->GetObjectClass(jniptr, obj); + clazz = addClassDetails(class); + //(*jniptr)->DeleteLocalRef(jniptr, class); + } - if (obj == NULL || class == NULL) return &nullObject; - clazz = addClassDetails(class); // grab the declaring class and ensure hierarchy is built for fields - //addClassDetails(obj); // check if this is a class object + + if (obj == NULL || class == NULL) return &nullObject; // Unexpected + if (clazz == NULL){ return &nullObject; } + if (clazz->positionInFile == 0) { objp = (void *) clazz; printf("Invalid class location %p %lld %lld \n", clazz, @@ -55,67 +70,62 @@ abort(); } - printDepth++; - - if (clazz == NULL) { - printfd("Null class, abort\n"); - abort(); - } - - err = (*env)->GetTag(env, obj, &tag); - if (err != JVMTI_ERROR_NONE) { - printfR("Could not retrieve tag/object %d \n", err); - printDepth--; - return objp; - } - + // Check if what is tagged is actually a class object and needs filling in if (tag != 0) { objp = (void *) tag; - if (!objp->state) { + if (!objp->state) { // Check it has not previously been filled in objp->isClass = 1; classObject = 1; }else{ + if (objp->depth > depth && maxReferenceDepth != 0){ // Check depth is not lower from this reference + objp->depth = depth; + //printf("Lower depth from here %d %s ! \n", objp->state, clazz->name); + objp->state = 1;} return objp; } } if (tag == 0 || classObject) { - printfd("--- Not seen this object before, save it ---\n"); if (!classObject) tag = (jlong) calloc(1, sizeof(struct JObject)); + if ((void *) tag == NULL) { - printfd("--- Failed to malloc ---\n"); - printDepth--; - return objp; + printf("Failed to malloc object \n"); + abort(); } if (!classObject){ err = (*env)->SetTag(env, obj, tag); - Q_enqueue(&obj, toDetag); + Q_enqueue(&obj, toDetag); // Add object to deallocation queue } - if (err != JVMTI_ERROR_NONE) { + + if (err != JVMTI_ERROR_NONE) { // Check tag was set printf("JVMTI Error %d \n", err); - printDepth--; return objp; } + FSEEK(variableFile, 0, SEEK_END); // Work out how many fields we need to save across the hierarchy tempClazz = clazz; totalFieldCount = 0; - while (tempClazz) { totalFieldCount += tempClazz->numInstanceFields; tempClazz = tempClazz->superClass; } - objp = (void *) tag; + objp = (void *) tag; // Set pointer if (!classObject) objp->isClass = 0; - objp->state = 1; + + objp->state = 1; // Indicates object has been written (but references not followed) + + if (maxReferenceDepth!=0)objp->depth = depth; // Set reference depth + FSEEK(variableFile, 0, SEEK_END); objp->positionInFile = FTELL(variableFile); - writeIDNoSize(CJVMTI_OBJECT, variableFile); + // Prepare space in dump file + writeIDNoSize(CJVMTI_OBJECT, variableFile); writeReference(clazz->positionInFile, variableFile); writeJint(totalFieldCount, variableFile); fieldValLoc = FTELL(variableFile); @@ -123,11 +133,11 @@ fwrite(&waitingToBeWritten, sizeof(FPOS_T), 1, variableFile); } - objp->referenceLocation = fieldValLoc; - objp->totalFieldCount = totalFieldCount; + objp->referenceLocation = fieldValLoc; // used to write references into file + objp->totalFieldCount = totalFieldCount; // used to write references into file FSEEK(variableFile, 0, SEEK_END); objectCount++; - Q_enqueue(&obj, qObjects); + Q_enqueue(&obj, qObjects); // Queue this object to have its references followed } return objp; } @@ -137,28 +147,33 @@ FPOS_T fieldValLoc; FPOS_T fieldValue; jlong tag; + jclass tempClass; struct JObject * objp; struct JClass * clazz; struct JClass * tempClazz; int totalFieldCount; + (*env)->GetTag(env, obj, &tag); objp = (struct JObject *) tag; - if (objp->state == 2) { + if (objp->state == 2) { // Already had its reference resolved objectCount--; return objp; } - clazz = addClassDetails((*jniptr)->GetObjectClass(jniptr, obj)); + + tempClass = (*jniptr)->GetObjectClass(jniptr, obj); + clazz = addClassDetails(tempClass); + (*jniptr)->DeleteLocalRef(jniptr, tempClass); totalFieldCount = objp->totalFieldCount; fieldValLoc = objp->referenceLocation; FSEEK(variableFile, 0, SEEK_END); + // Loop through saving the references to file tempClazz = clazz; while (tempClazz) { - // TODO We only need an array of max size of a superclass fields for (i = 0; i < tempClazz->numInstanceFields; i++) { fieldValue = addFieldValue(CJVMTI_INSTANCE_VAR, obj, tempClazz->instanceFields[i], - tempClazz->instanceFieldSignatures[i], 0, 0, 0); + tempClazz->instanceFieldSignatures[i], 0, 0, 0, objp->depth+1); if (fieldValue == waitingToBeWritten){ printf("Error unwritten reference\n"); abort(); @@ -166,7 +181,7 @@ FSETPOS(variableFile, &fieldValLoc); writeReference(fieldValue, variableFile); if (fieldValue == waitingToBeWritten) { - printfd("Circular reference \n"); + printf("Circular reference \n"); abort(); } fieldValLoc = FTELL(variableFile); @@ -175,7 +190,7 @@ tempClazz = tempClazz->superClass; } objectCount--; - objp->state = 2; + objp->state = 2; // Object full resolved printDepth--; return objp; } Index: src/main/native/queue.c =================================================================== --- src/main/native/queue.c (revision 813372) +++ src/main/native/queue.c (working copy) @@ -30,6 +30,7 @@ static int id; int doubleSize(queueADT); +// Call this to initialise the array list struct queueADT * Q_initQueue(int qsize, int sizeofElement){ void * pt; struct queueADT * q = calloc(1, sizeof(struct queueADT)); @@ -83,6 +84,8 @@ return q->size; } + +// Expands the size of the queue (array list), copies values and deallocs the old array int doubleSize(struct queueADT * q){ void * pt; int i; @@ -121,6 +124,7 @@ return 1; } + int Q_free(struct queueADT * q){ id--; free(q->queue); Index: src/main/native/addThreads.c =================================================================== --- src/main/native/addThreads.c (revision 813372) +++ src/main/native/addThreads.c (working copy) @@ -23,22 +23,22 @@ int aliveVarCount; jvmtiError err; jint varCount; - jvmtiLocalVariableEntry * table; + jvmtiLocalVariableEntry * table = NULL; FPOS_T refLoc; FPOS_T tempLoc; aliveVarCount = 0; printDepth++; - err = (*env)->GetLocalVariableTable(env, methodID, &varCount, &table); - if (err != JVMTI_ERROR_NONE) { - printfd("JVMTI Error \n"); - writeIDNoSize(CJVMTI_JVMTI_ERROR, variableFile); + + if (loc == -1) { printDepth--; + writeIDNoSize(CJVMTI_LOCAL_NATIVECALL, variableFile); return 0; } - if (loc == -1) { - printfd("Executing native method\n"); + err = (*env)->GetLocalVariableTable(env, methodID, &varCount, &table); + if (err != JVMTI_ERROR_NONE) { + writeIDNoSize(CJVMTI_JVMTI_ERROR, variableFile); printDepth--; - writeIDNoSize(CJVMTI_LOCAL_NATIVECALL, variableFile); + (*env)->Deallocate(env, (unsigned char*)table); return 0; } writeIDNoSize(CJVMTI_LOCAL_VARIABLE, variableFile); @@ -51,7 +51,6 @@ aliveVarCount++; } } - writeJint(aliveVarCount, variableFile); refLoc = FTELL(variableFile); for (i = 0; i < aliveVarCount; i++) { @@ -68,14 +67,16 @@ refLoc = FTELL(variableFile); FSEEK(variableFile, 0, SEEK_END); tempLoc = addFieldValue(CJVMTI_LOCAL_VAR, NULL, NULL, - table[i].signature, *thread, depth, table[i].slot); + table[i].signature, *thread, depth, table[i].slot, 0); FSEEK(variableFile, refLoc, SEEK_SET); writeReference(tempLoc, variableFile); (*env)->Deallocate(env, (unsigned char*)table[i].generic_signature); (*env)->Deallocate(env, (unsigned char*)table[i].name); (*env)->Deallocate(env, (unsigned char*)table[i].signature); } else { - printfd("Not currently in slot, ignore\n"); + (*env)->Deallocate(env, (unsigned char*)table[i].generic_signature); + (*env)->Deallocate(env, (unsigned char*)table[i].name); + (*env)->Deallocate(env, (unsigned char*)table[i].signature); } } (*env)->Deallocate(env, (unsigned char*)table); @@ -89,7 +90,7 @@ FPOS_T dClassPos; FPOS_T position; - char* methodName; + char* methodName = NULL; struct JClass * declClazz; printDepth++; err = (*env)->GetMethodName(env, info->method, &methodName, NULL, NULL); @@ -97,9 +98,13 @@ if (err != JVMTI_ERROR_NONE) { position = FTELL(variableFile); writeIDNoSize(CJVMTI_JVMTI_ERROR, variableFile); - return position; + (*env)->Deallocate(env, (unsigned char*)methodName); + printf("Unexpected JVMTI error %d \n", err); + abort(); } FSEEK(variableFile, 0, SEEK_END); + + // Add declaring class reference if (declClass != 0) { declClazz = addClassDetails(declClass); if (declClazz != NULL){ @@ -118,6 +123,7 @@ writeReference(dClassPos, variableFile); getLocVars(info->method, info->location, thread, depth); + printDepth--; (*env)->Deallocate(env, (unsigned char*)methodName); return position; @@ -126,13 +132,13 @@ FPOS_T getThreadInfo(jthread * thread) { int i; - int maxDepth = 5; + int maxDepth = 0; jvmtiThreadInfo info; - jvmtiFrameInfo frames[5]; + jvmtiFrameInfo * frames; jvmtiError err; jint ownedMonitorCount; - jobject * ownedObjects; + jobject * ownedObjects = NULL; jobject contendedObject; struct JObject * monObj; @@ -142,11 +148,18 @@ FPOS_T position; FPOS_T refLoc; FPOS_T tempRef; - + if (maxFrameDepth != 0){ + maxDepth = maxFrameDepth; + frames = malloc(sizeof (jvmtiFrameInfo) * maxDepth); + }else{ + maxDepth = 20; + frames = malloc(sizeof (jvmtiFrameInfo) * maxDepth); + } printDepth++; - + info.name = NULL; err = (*env)->GetThreadInfo(env, *thread, &info); if (err != JVMTI_ERROR_NONE) { + (*env)->Deallocate(env, (unsigned char*)info.name); printf("No thread info %d \n ", err); abort(); } @@ -160,14 +173,29 @@ ownedMonitorCount = 0; } err = (*env)->GetStackTrace(env, *thread, 0, maxDepth, frames, &frameCount); - if (err != JVMTI_ERROR_NONE) { - frameCount = 0; + if (err != JVMTI_ERROR_NONE) { + frameCount = 0; + } + while (maxFrameDepth == 0 && frameCount == maxDepth ){ + printf("Had to double frame buffer, recommend limiting stack depth current buffer size: %d\n", maxDepth *2); + free(frames); + maxDepth = maxDepth * 2; + frames = malloc(sizeof (jvmtiFrameInfo) * maxDepth); + err = (*env)->GetStackTrace(env, *thread, 0, maxDepth, frames, &frameCount); + if (err != JVMTI_ERROR_NONE) { + frameCount = 0; + } } + printf("Thread %s Number of frames %d\n", info.name, frameCount); + + // Prepare space in dump file for all information queried position = FTELL(variableFile); writeIDNoSize(CJVMTI_THREAD, variableFile); writeString(info.name, variableFile); - writeReference(addObject(*thread)->positionInFile, variableFile); + (*env)->Deallocate(env, (unsigned char*)info.name); + info.name = NULL; + writeReference(addObject(*thread, 0)->positionInFile, variableFile); writeJint(info.priority, variableFile); writeBool(info.is_daemon, variableFile); writeJint(ownedMonitorCount, variableFile); @@ -181,11 +209,13 @@ for (i = 0; i < frameCount; i++) { writeReference(waitingToBeWritten, variableFile); // Stack frames } - - // Allocated space, fill references + + // Allocated space, now follow references FSEEK(variableFile, 0, SEEK_END); for (i = 0; i < ownedMonitorCount; i++) { - monObj = addObject(ownedObjects[i]); + + monObj = addObject(ownedObjects[i], 0); + FSEEK(variableFile, refLoc, SEEK_SET); writeReference(monObj->positionInFile, variableFile); refLoc = FTELL(variableFile); @@ -194,7 +224,9 @@ (*env)->Deallocate(env, (unsigned char*)ownedObjects); ownedObjects = NULL; if (contendedObject != 0) { - monObj = addObject(contendedObject); + + monObj = addObject(contendedObject, 0); + FSEEK(variableFile, refLoc, SEEK_SET); writeReference(monObj->positionInFile, variableFile); } else { @@ -206,13 +238,15 @@ refLoc = FTELL(variableFile); FSEEK(variableFile, 0, SEEK_END); for (i = 0; i < frameCount; i++) { + tempRef = getFrameInfo(i, &frames[i], thread); + FSEEK(variableFile, refLoc, SEEK_SET); writeReference(tempRef, variableFile); refLoc = FTELL(variableFile); FSEEK(variableFile, 0, SEEK_END); } - + free(frames); return position; } @@ -226,31 +260,35 @@ int i; err = (*env)->GetAllThreads(env, &threadCount, &threads); + + // Prepare space in file writeIDNoSize(CJVMTI_THREAD, variableFile); writeJint(threadCount, variableFile); refLoc = FTELL(variableFile); for (i = 0; i < threadCount; i++) { - writeReference(waitingToBeWritten, variableFile); // Prepare reference space in file + writeReference(waitingToBeWritten, variableFile); } + printf("%d threads \n", threadCount); check = FTELL(variableFile); for (i = 0; i < threadCount; i++) { - clearObjectQueue(); - addObject(threads[i]); // Add all thread objects + //clearObjectQueue(); + addObject(threads[i], 0); // Add all thread objects } - printf(" Q size %d \n", Q_size(qObjects)); + + // Now fill in all references FSEEK(variableFile, 0, SEEK_END); for (i = 0; i < threadCount; i++) { + ref = getThreadInfo(&threads[i]); + FSEEK(variableFile, refLoc, SEEK_SET); writeReference(ref, variableFile); refLoc = FTELL(variableFile); FSEEK(variableFile, 0, SEEK_END); } - clearObjectQueue(); (*env)->Deallocate(env, (unsigned char*)threads); - printfd("--- Finished following threads ---\n"); return 1; }