/* * Copyright (c) 2017, Google and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include #include #include #include "jvmti.h" #ifdef __cplusplus extern "C" { #endif #ifndef JNI_ENV_ARG #ifdef __cplusplus #define JNI_ENV_ARG(x, y) y #define JNI_ENV_PTR(x) x #else #define JNI_ENV_ARG(x,y) x, y #define JNI_ENV_PTR(x) (*x) #endif #endif #define TRUE 1 #define FALSE 0 #define PRINT_OUT 1 #define MAX_TRACES 400 static const char *EXC_CNAME = "java/lang/Exception"; static jvmtiEnv *jvmti = NULL; static int check_error(jvmtiError err, const char *s) { if (err != JVMTI_ERROR_NONE) { printf(" ## %s error: %d\n", s, err); return 1; } return 0; } static int check_capability_error(jvmtiError err, const char *s) { if (err != JVMTI_ERROR_NONE) { if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) { return 0; } printf(" ## %s error: %d\n", s, err); return 1; } return 1; } static jint throw_exc(JNIEnv *env, char *msg) { jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME)); if (exc_class == NULL) { printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME); return -1; } return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg); } static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { return Agent_Initialize(jvm, options, reserved); } JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { return Agent_Initialize(jvm, options, reserved); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { return JNI_VERSION_1_8; } static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { jint res; res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), JVMTI_VERSION_9); if (res != JNI_OK || jvmti == NULL) { printf(" Error: wrong result of a valid call to GetEnv!\n"); return JNI_ERR; } jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); // Get line numbers, sample heap, and filename for the test. caps.can_get_line_numbers = 1; caps.can_sample_heap = 1; caps.can_get_source_file_name = 1; if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities\n")){ return JNI_ERR; } if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(jvmtiEventCallbacks)), " Set Event Callbacks")) { return JNI_ERR; } return JNI_OK; } // Given a method and a location, this method gets the line number. static jint get_line_number(jvmtiEnv *jvmti, jmethodID method, jlocation location) { // Read the line number table. jvmtiLineNumberEntry *table_ptr = 0; jint line_number_table_entries; int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, &line_number_table_entries, &table_ptr); if (JVMTI_ERROR_NONE != jvmti_error) { return -1; } if (line_number_table_entries <= 0) { return -1; } if (line_number_table_entries == 1) { return table_ptr[0].line_number; } // Go through all the line numbers... jint last_location = table_ptr[0].start_location; int l; for (l = 1; l < line_number_table_entries; l++) { // ... and if you see one that is in the right place for your // location, you've found the line number! if ((location < table_ptr[l].start_location) && (location >= last_location)) { return table_ptr[l - 1].line_number; } last_location = table_ptr[l].start_location; } if (location >= last_location) { return table_ptr[line_number_table_entries - 1].line_number; } else { return -1; } } typedef struct _ExpectedContentFrame { const char *name; const char *signature; const char *file_name; int line_number; } ExpectedContentFrame; static jboolean check_sample_content(JNIEnv *env, jvmtiAllocTraceInfo* trace, ExpectedContentFrame *expected, int expected_count, int print_out_comparisons) { int i; jvmtiStackInfo* stack_info = trace->stack_info; if (expected_count > stack_info->frame_count) { return FALSE; } jvmtiFrameInfo* frames = stack_info->frame_buffer; for (i = 0; i < expected_count; i++) { // Get basic information out of the trace. int bci = frames[i].location; jmethodID methodid = frames[i].method; char *name = NULL, *signature = NULL, *file_name = NULL; if (bci < 0) { return FALSE; } // Transform into usable information. int line_number = get_line_number(jvmti, methodid, bci); (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); jclass declaring_class; if (JVMTI_ERROR_NONE != (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { return FALSE; } jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, &file_name); if (err != JVMTI_ERROR_NONE) { return FALSE; } // Compare now, none should be NULL. if (name == NULL) { return FALSE; } if (file_name == NULL) { return FALSE; } if (signature == NULL) { return FALSE; } if (print_out_comparisons) { fprintf(stderr, "\tComparing:\n"); fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name); fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature); fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name); fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number); fprintf(stderr, "\t\tResult is %d\n", (strcmp(name, expected[i].name) || strcmp(signature, expected[i].signature) || strcmp(file_name, expected[i].file_name) || line_number != expected[i].line_number)); } if (strcmp(name, expected[i].name) || strcmp(signature, expected[i].signature) || strcmp(file_name, expected[i].file_name) || line_number != expected[i].line_number) { return FALSE; } } return TRUE; } static jboolean compare_samples(JNIEnv* env, jvmtiAllocTraceInfo* traces, int trace_count, ExpectedContentFrame* expected_content, size_t size, int print_out_comparisons) { // We expect the code to record correctly the bci, retrieve the line // number, have the right method and the class name of the first frames. int i; if (print_out_comparisons) { fprintf(stderr, "\tNumber of traces: %d\n", print_out_comparisons); } for (i = 0; i < trace_count; i++) { jvmtiAllocTraceInfo* trace = traces + i; if (check_sample_content(env, trace, expected_content, size, print_out_comparisons)) { // At least one frame matched what we were looking for. return TRUE; } } return FALSE; } static jboolean check_samples(JNIEnv* env, ExpectedContentFrame* expected, size_t size, jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiAllocTraceInfo**, jint*), int print_out_comparisons) { jvmtiAllocTraceInfo *traces; jint trace_counter; jvmtiError error = get_traces(jvmti, &traces, &trace_counter); if (error != JVMTI_ERROR_NONE) { return FALSE; } int result = compare_samples(env, traces, trace_counter, expected, size, print_out_comparisons); if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { return FALSE; } return result; } static jboolean frames_exist_live(JNIEnv* env, ExpectedContentFrame* expected, size_t size, int print_out_comparisons) { return check_samples(env, expected, size, (*jvmti)->GetObjectAllocTraces, print_out_comparisons); } static jboolean frames_exist_recent(JNIEnv* env, ExpectedContentFrame* expected, size_t size, int print_out_comparisons) { return check_samples(env, expected, size, (*jvmti)->GetGarbageTraces, print_out_comparisons); } static jboolean frames_exist_frequent(JNIEnv* env, ExpectedContentFrame* expected, size_t size, int print_out_comparisons) { return check_samples(env, expected, size, (*jvmti)->GetFrequentGarbageTraces, print_out_comparisons); } // Static native API for various tests. static void fill_native_frames(JNIEnv* env, jobjectArray frames, ExpectedContentFrame* native_frames, size_t size) { size_t i; for(i = 0; i < size; i++) { jobject obj = (*env)->GetObjectArrayElement(env, frames, i); jclass frame_class = (*env)->GetObjectClass(env, obj); jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, "lineNumber", "I"); int line_number = (*env)->GetIntField(env, obj, line_number_field_id); jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method", "Ljava/lang/String;"); jstring string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); const char* method = (*env)->GetStringUTFChars(env, string_object, 0); string_id = (*env)->GetFieldID(env, frame_class, "fileName", "Ljava/lang/String;"); string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0); string_id = (*env)->GetFieldID(env, frame_class, "signature", "Ljava/lang/String;"); string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); const char* signature= (*env)->GetStringUTFChars(env, string_object, 0); native_frames[i].name = method; native_frames[i].file_name = file_name; native_frames[i].signature = signature; native_frames[i].line_number = line_number; } } static jboolean check_and(JNIEnv *env, jobjectArray frames, int live, int recent, int frequent, int print_out) { jobject loader = NULL; if (frames == NULL) { return FALSE; } // Start by transforming the frames into a C-friendly structure. jsize size = (*env)->GetArrayLength(env, frames); ExpectedContentFrame native_frames[size]; fill_native_frames(env, frames, native_frames, size); if (jvmti == NULL) { throw_exc(env, "JVMTI client was not properly loaded!\n"); return FALSE; } int result = TRUE; if (live) { result = frames_exist_live(env, native_frames, size, print_out); } if (recent) { result = result && frames_exist_recent(env, native_frames, size, print_out); } if (frequent) { result = result && frames_exist_frequent(env, native_frames, size, print_out); } return result; } static jboolean check_or(JNIEnv *env, jobjectArray frames, int live, int recent, int frequent, int print_out) { jobject loader = NULL; if (frames == NULL) { return FALSE; } // Start by transforming the frames into a C-friendly structure. jsize size = (*env)->GetArrayLength(env, frames); ExpectedContentFrame native_frames[size]; fill_native_frames(env, frames, native_frames, size); if (jvmti == NULL) { throw_exc(env, "JVMTI client was not properly loaded!\n"); return FALSE; } jboolean result = FALSE; if (live) { result = frames_exist_live(env, native_frames, size, print_out); } if (recent) { result = result || frames_exist_recent(env, native_frames, size, print_out); } if (frequent) { result = result || frames_exist_frequent(env, native_frames, size, print_out); } return result; } static jboolean checkAll(JNIEnv *env, jobjectArray frames, int print_out) { return check_and(env, frames, 1, 1, 1, print_out); } static jboolean checkNone(JNIEnv *env, jobjectArray frames, int print_out) { jobject loader = NULL; if (frames == NULL) { return FALSE; } // Start by transforming the frames into a C-friendly structure. jsize size = (*env)->GetArrayLength(env, frames); ExpectedContentFrame native_frames[size]; fill_native_frames(env, frames, native_frames, size); if (jvmti == NULL) { throw_exc(env, "JVMTI client was not properly loaded!\n"); return FALSE ; } if ((!frames_exist_live(env, native_frames, size, print_out)) && (!frames_exist_recent(env, native_frames, size, print_out)) && (!frames_exist_frequent(env, native_frames, size, print_out))) { return TRUE; } return FALSE; } JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_enableSampling(JNIEnv *env, jclass cls, int rate, int max_traces) { check_error((*jvmti)->StartHeapSampling(jvmti, rate, max_traces), "Start Heap Sampling"); } JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_disableSampling(JNIEnv *env, jclass cls) { check_error((*jvmti)->StopHeapSampling(jvmti), "Stop Heap Sampling"); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_areSamplingStatisticsZero(JNIEnv *env, jclass cls) { jvmtiHeapSamplingStats stats; check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), "Heap Sampling Statistics"); jvmtiHeapSamplingStats zero; memset(&zero, 0, sizeof(zero)); return memcmp(&stats, &zero, sizeof(zero)) == 0; } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_framesExistEverywhere(JNIEnv *env, jclass cls, jobjectArray frames) { // We want the frames in each part. return checkAll(env, frames, PRINT_OUT); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_framesExistNowhere(JNIEnv *env, jclass cls, jobjectArray frames) { // We want the frames in none of the parts. return checkNone(env, frames, PRINT_OUT); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_framesExistSomewhere(JNIEnv *env, jclass cls, jobjectArray frames) { return check_or(env, frames, TRUE, TRUE, TRUE, PRINT_OUT); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorRecentTest_framesNotInLiveOrRecent(JNIEnv *env, jclass cls, jobjectArray frames) { return !check_or(env, frames, TRUE, TRUE, FALSE, PRINT_OUT); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorRecentTest_framesExistInRecent(JNIEnv *env, jclass cls, jobjectArray frames) { return check_and(env, frames, FALSE, TRUE, FALSE, PRINT_OUT); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorFrequentTest_framesExistInFrequent(JNIEnv *env, jclass cls, jobjectArray frames) { return check_and(env, frames, FALSE, FALSE, TRUE, PRINT_OUT); } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, jclass cls) { jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); caps.can_sample_heap= 1; if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), "Add capabilities\n")){ return FALSE; } if (check_capability_error((*jvmti)->StartHeapSampling(jvmti, 1<<19, MAX_TRACES), "Start Heap Sampling")) { return FALSE; } if (check_capability_error((*jvmti)->StopHeapSampling(jvmti), "Stop Heap Sampling")) { return FALSE; } if (check_capability_error((*jvmti)->GetHeapSamplingStats(jvmti, NULL), "Get Heap Sampling Stats")) { return FALSE; } if (check_capability_error((*jvmti)->GetGarbageTraces(jvmti, NULL, NULL), "Get Garbage Traces")) { return FALSE; } if (check_capability_error((*jvmti)->GetFrequentGarbageTraces(jvmti, NULL, NULL), "Get Frequent Garbage Traces")) { return FALSE; } if (check_capability_error((*jvmti)->GetObjectAllocTraces(jvmti, NULL, NULL), "Get Object Allocated Traces")) { return FALSE; } if (check_capability_error((*jvmti)->GetObjectAllocTraces(jvmti, NULL, NULL), "Get Cached Object Allocated Traces")) { return FALSE; } return TRUE; } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_statsHaveExpectedNumberSamples(JNIEnv *env, jclass cls, int expected, int percent_error) { jvmtiHeapSamplingStats stats; check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), "Heap Sampling Statistics"); double diff_ratio = (stats.sample_count - expected); diff_ratio = (diff_ratio < 0) ? -diff_ratio : diff_ratio; diff_ratio /= expected; if (diff_ratio * 100 >= percent_error) { fprintf(stderr, "Problem with sample count, obtained %ld and expected %d\n", stats.sample_count, expected); } return diff_ratio * 100 < percent_error; } JNIEXPORT jdouble JNICALL Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { jvmtiHeapSamplingStats stats; check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), "Heap Sampling Statistics"); return ((double) stats.sample_rate_accumulation) / stats.sample_rate_count; } static double calculate_average_stack_depth( jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiAllocTraceInfo**, jint*)) { jvmtiAllocTraceInfo* traces = NULL; jint trace_counter; jvmtiError error = get_traces(jvmti, &traces, &trace_counter);; if (error != JVMTI_ERROR_NONE) { return 0; } if (trace_counter == 0) { return 0; } int i; double sum = 0; for (i = 0; i < trace_counter; i++) { jvmtiAllocTraceInfo* trace = traces + i; jvmtiStackInfo* stack_info = trace->stack_info; sum += stack_info->frame_count; } if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { return 0; } return sum / i; } JNIEXPORT jdouble JNICALL Java_MyPackage_HeapMonitorStackDepthTest_getAverageStackDepth(JNIEnv *env, jclass cls) { double result = calculate_average_stack_depth((*jvmti)->GetObjectAllocTraces); if (result != 0) { return result; } // It is possible all the live objects got collected, check the garbage traces // in case. return calculate_average_stack_depth((*jvmti)->GetGarbageTraces); } typedef struct sThreadsFound { jint* threads; int num_threads; } ThreadsFound; static void find_threads_in_traces(jvmtiAllocTraceInfo* traces, jint trace_counter, ThreadsFound* thread_data) { int i; jint* threads = thread_data->threads; int num_threads = thread_data->num_threads; // We are looking for at last expected_num_threads different traces. for (i = 0; i < trace_counter; i++) { jvmtiAllocTraceInfo* trace = traces + i; jvmtiStackInfo* stack_info = trace->stack_info; jint thread_id = trace->thread_id; // Check it is the right frame: only accept helper top framed traces. if (stack_info->frame_count == 0) { continue; } jmethodID methodid = stack_info->frame_buffer[0].method; char *name = NULL, *signature = NULL, *file_name = NULL; (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); if (strcmp(name, "helper")) { continue; } // Really not efficient look-up but it's for a test... int found = 0; int j; for (j = 0; j < num_threads; j++) { if (thread_id == threads[j]) { found = 1; break; } } if (!found) { threads[num_threads] = thread_id; num_threads++; } } thread_data->num_threads = num_threads; } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, jintArray threads) { jvmtiAllocTraceInfo* traces; jint trace_counter; ThreadsFound thread_data; thread_data.threads = (*env)->GetIntArrayElements(env, threads, 0); thread_data.num_threads = 0; // Get live and garbage traces to ensure we capture all the threads that have // been sampled. if ((*jvmti)->GetObjectAllocTraces(jvmti, &traces, &trace_counter) != JVMTI_ERROR_NONE) { return FALSE; } find_threads_in_traces(traces, trace_counter, &thread_data); if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { return FALSE; } if ((*jvmti)->GetGarbageTraces(jvmti, &traces, &trace_counter) != JVMTI_ERROR_NONE) { return FALSE; } find_threads_in_traces(traces, trace_counter, &thread_data); if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { return FALSE; } (*env)->ReleaseIntArrayElements(env, threads, thread_data.threads, 0); return TRUE; } JNIEXPORT void JNICALL Java_MyPackage_HeapMonitorCachedTest_getLiveTracesToForceGc(JNIEnv *env, jclass cls) { jvmtiAllocTraceInfo* traces; jint trace_counter; jvmtiError error = (*jvmti)->GetObjectAllocTraces(jvmti, &traces, &trace_counter); if (error != JVMTI_ERROR_NONE) { return; } (*jvmti)->Deallocate(jvmti, (unsigned char*) traces); } static jboolean compare_traces(jvmtiAllocTraceInfo* traces, int trace_count, jvmtiAllocTraceInfo* other_traces, int other_trace_count, int print_out_comparisons) { if (trace_count != other_trace_count) { return FALSE; } int i; for (i = 0; i < trace_count; i++) { jvmtiAllocTraceInfo* trace = traces + i; jvmtiAllocTraceInfo* other_trace = other_traces + i; jvmtiStackInfo* stack_info = trace->stack_info; jvmtiStackInfo* other_stack_info = trace->stack_info; if (stack_info->frame_count != other_stack_info->frame_count) { return FALSE; } if (trace->size != other_trace->size) { return FALSE; } if (trace->thread_id != other_trace->thread_id) { return FALSE; } jvmtiFrameInfo* frames = stack_info->frame_buffer; jvmtiFrameInfo* other_frames = other_stack_info->frame_buffer; if (memcmp(frames, other_frames, sizeof(*frames) * stack_info->frame_count)) { return FALSE; } } return TRUE; } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorCachedTest_cachedAndLiveAreSame(JNIEnv *env, jclass cls) { // Get cached first, then get live (since live performs a GC). jvmtiAllocTraceInfo* cached_traces; jint cached_trace_counter; jvmtiError error = (*jvmti)->GetCachedObjectAllocTraces(jvmti, &cached_traces, &cached_trace_counter); if (error != JVMTI_ERROR_NONE) { return FALSE; } jvmtiAllocTraceInfo* live_traces; jint live_trace_counter; error = (*jvmti)->GetObjectAllocTraces(jvmti, &live_traces, &live_trace_counter); if (error != JVMTI_ERROR_NONE) { return FALSE; } int result = compare_traces(cached_traces, cached_trace_counter, live_traces, live_trace_counter, PRINT_OUT); if ((*jvmti)->Deallocate(jvmti, (unsigned char*) cached_traces) != JVMTI_ERROR_NONE) { return FALSE; } if ((*jvmti)->Deallocate(jvmti, (unsigned char*) live_traces) != JVMTI_ERROR_NONE) { return FALSE; } return result; } static long hash(long hash_code, long value) { return hash_code * 31 + value; } static long get_hash_code(jvmtiAllocTraceInfo* traces, jint trace_counter) { int hash_code = 17; int i, j; hash_code = hash(hash_code, trace_counter); for (i = 0; i < trace_counter; i++) { jvmtiAllocTraceInfo* trace = traces + i; hash_code = hash(hash_code, trace->size); hash_code = hash(hash_code, trace->thread_id); jvmtiStackInfo* stack_info = trace->stack_info; hash_code = hash(hash_code, stack_info->frame_count); int frame_count = stack_info->frame_count; jvmtiFrameInfo* frames = stack_info->frame_buffer; hash_code = hash(hash_code, frame_count); for (j = 0; j < frame_count; j++) { hash_code = hash(hash_code, (long) frames[i].method); hash_code = hash(hash_code, frames[i].location); } } return TRUE; } JNIEXPORT jlong JNICALL Java_MyPackage_HeapMonitorCachedTest_getCachedHashCode(JNIEnv *env, jclass cls) { // Get cached first, then get live. jvmtiAllocTraceInfo* cached_traces; jint cached_trace_counter; jvmtiError error = (*jvmti)->GetCachedObjectAllocTraces(jvmti, &cached_traces, &cached_trace_counter); if (error != JVMTI_ERROR_NONE) { return 0; } long hash_code = get_hash_code(cached_traces, cached_trace_counter); if ((*jvmti)->Deallocate(jvmti, (unsigned char*) cached_traces) != JVMTI_ERROR_NONE) { return FALSE; } return hash_code; } JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorTest_framesAreNotLive(JNIEnv *env, jclass cls, jobjectArray frames) { return !check_and(env, frames, FALSE, FALSE, TRUE, PRINT_OUT); } #ifdef __cplusplus } #endif