| /* |
| * Copyright (c) 2013, 2018, Oracle 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 <wchar.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #include "jvmti.h" |
| #include "jni_tools.h" |
| #include "jvmti_tools.h" |
| #include "agent_common.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #define STATIC_FIELD 0x0008 |
| |
| /* |
| For this test tags have following format: |
| |63 48| 32| 16| 0| |
| |<not used> |tag type|obj idx|fld idx| |
| */ |
| |
| #define FIELD_TAG 1 |
| #define OBJECT_TAG 2 |
| #define CLASS_TAG 4 |
| |
| #define ENCODE_TAG(type, obj, fld) (((jlong)type)<<32 | ((jlong)obj)<<16 | (jlong)fld) |
| #define DECODE_TYPE(tag) tag>>32 |
| #define DECODE_OBJECT(tag) ((tag>>16)&0xFFFF) |
| #define DECODE_FIELD(tag) (tag&0xFFFF) |
| |
| #define TEST_OBJECTS_COUNT 2 |
| #define TAGGED_OBJECTS 1 |
| |
| static long timeout = 0; |
| static int filter_type = -1; |
| |
| // by default at least one object will be reported regardless to filter type |
| static int expected_object_count = 1; |
| static int reported_objects = 0; |
| |
| //expected values |
| #define INT_ARRAY_LENGTH 2 |
| |
| static jint TAGGED_STATIC_INT_VALUE = 0xC0DE01; |
| static jint TAGGED_INT_VALUE = 0xC0DE02; |
| static jint UNTAGGED_STATIC_INT_VALUE = 0xC0DE03; |
| static jint UNTAGGED_INT_VALUE = 0xC0DE04; |
| static jint TAGGED_INT_ARRAY_VALUE[] = {0xC0DE01, |
| 0xC0DE01+1}; |
| static jint UNTAGGED_INT_ARRAY_VALUE[] = {0xC0DE03, |
| 0xC0DE03+1}; |
| static wchar_t *TAGGED_STRING_VALUE = L"I'm a tagged string"; |
| static wchar_t *UNTAGGED_STRING_VALUE = L"I'm an untagged string"; |
| |
| //kind of field |
| #define TYPE_FIELD 1 |
| #define TYPE_ARRAY 2 |
| #define TYPE_STRING 4 |
| |
| //field info |
| typedef struct { |
| char *name; |
| char *signature; |
| int found; |
| int collected; |
| int primitive; |
| int expected; |
| int type; |
| void *value; |
| int size; |
| } field_info_t; |
| |
| //object info |
| typedef struct { |
| char *name; |
| int fields_count; |
| field_info_t *fields; |
| int collected; |
| } object_info_t; |
| |
| static object_info_t objects_info[TEST_OBJECTS_COUNT]; |
| |
| #define className "nsk/jvmti/IterateThroughHeap/filter_tagged/HeapFilter" |
| #define fieldName "testObjects" |
| #define fieldSig "[Ljava/lang/Object;" |
| #define STRING_SIGNATURE "Ljava/lang/String;" |
| #define INT_ARRAY_SIGNATURE "[I" |
| |
| // Check if the signature is signature of primitive type. |
| jboolean is_primitive_type(const char *signature) { |
| if(!strcmp(signature,"C") |
| || !strcmp(signature, "B") |
| || !strcmp(signature, "S") |
| || !strcmp(signature, "I") |
| || !strcmp(signature, "J") |
| || !strcmp(signature, "F") |
| || !strcmp(signature, "D") |
| || !strcmp(signature, "Z")) |
| return JNI_TRUE; |
| return JNI_FALSE; |
| } |
| |
| //check tag values accoring to heap filter choosed for test |
| jboolean verify_tag(jlong class_tag, jlong object_tag) { |
| switch(filter_type) { |
| case JVMTI_HEAP_FILTER_TAGGED: |
| return object_tag == 0; |
| case JVMTI_HEAP_FILTER_UNTAGGED: |
| return object_tag != 0; |
| case JVMTI_HEAP_FILTER_CLASS_TAGGED: |
| return class_tag == 0; |
| case JVMTI_HEAP_FILTER_CLASS_UNTAGGED: |
| return class_tag != 0; |
| default: |
| return JNI_FALSE; |
| } |
| } |
| |
| //check whether or not field expected to be reported |
| jboolean occurance_expected(int tagged, int is_static, int is_primitive) { |
| switch(filter_type) { |
| case JVMTI_HEAP_FILTER_TAGGED: |
| return !tagged; |
| case JVMTI_HEAP_FILTER_UNTAGGED: |
| return tagged; |
| case JVMTI_HEAP_FILTER_CLASS_TAGGED: |
| return (is_static && is_primitive) || !is_primitive || !tagged; |
| case JVMTI_HEAP_FILTER_CLASS_UNTAGGED: |
| return !is_static && is_primitive && tagged; |
| default: |
| return JNI_ERR; |
| } |
| } |
| |
| jint JNICALL field_callback(jvmtiHeapReferenceKind kind, |
| const jvmtiHeapReferenceInfo* info, |
| jlong object_class_tag, |
| jlong* object_tag_ptr, |
| jvalue value, |
| jvmtiPrimitiveType value_type, |
| void* user_data) { |
| int object; |
| int field; |
| if (!NSK_VERIFY(verify_tag(object_class_tag, *object_tag_ptr))) { |
| nsk_jvmti_setFailStatus(); |
| } |
| |
| //iterate over all fields found during tagging and compare reported value |
| //with their values. |
| if(value_type != JVMTI_PRIMITIVE_TYPE_INT) |
| return 0; |
| for(object = 0; object < TEST_OBJECTS_COUNT; object++) { |
| for(field = 0; field < objects_info[object].fields_count; field++) { |
| if(objects_info[object].fields[field].type == TYPE_FIELD && |
| *(jint*)(objects_info[object].fields[field].value) == value.i) { |
| objects_info[object].fields[field].found++; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| jint JNICALL string_callback(jlong class_tag, |
| jlong size, |
| jlong* tag_ptr, |
| const jchar* value, |
| jint value_length, |
| void* user_data) { |
| int object; |
| int field; |
| if (!NSK_VERIFY(verify_tag(class_tag, *tag_ptr))) { |
| nsk_jvmti_setFailStatus(); |
| } |
| for(object = 0; object < TEST_OBJECTS_COUNT; object++) { |
| for(field = 0; field < objects_info[object].fields_count; field++) { |
| if(objects_info[object].fields[field].type == TYPE_STRING && |
| value_length == objects_info[object].fields[field].size) { |
| int matched = 1; |
| int i; |
| wchar_t *str = (wchar_t*)objects_info[object].fields[field].value; |
| for(i = 0; i < value_length && matched; i++) { |
| matched = (str[i] == value[i]); |
| } |
| if(matched) |
| objects_info[object].fields[field].found++; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| |
| jint JNICALL array_callback(jlong class_tag, |
| jlong size, |
| jlong* tag_ptr, |
| jint element_count, |
| jvmtiPrimitiveType element_type, |
| const void* elements, |
| void* user_data) { |
| int object; |
| int field; |
| if (!NSK_VERIFY(verify_tag(class_tag, *tag_ptr))) { |
| nsk_jvmti_setFailStatus(); |
| } |
| for(object = 0; object < TEST_OBJECTS_COUNT; object++) { |
| for(field = 0; field < objects_info[object].fields_count; field++) { |
| if(objects_info[object].fields[field].type == TYPE_ARRAY && |
| element_count == objects_info[object].fields[field].size) { |
| int matched = 1; |
| int i; |
| for(i = 0; i < element_count && matched; i++) { |
| matched = ((jint*)objects_info[object].fields[field].value)[i]== |
| ((jint*)elements)[i]; |
| } |
| if(matched) |
| objects_info[object].fields[field].found++; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| jint JNICALL heap_callback(jlong class_tag, |
| jlong size, |
| jlong* tag_ptr, |
| jint length, |
| void* user_data) { |
| if (!NSK_VERIFY(verify_tag(class_tag, *tag_ptr))) { |
| NSK_COMPLAIN0("Tag values invalid for selected heap filter were passed " |
| "to jvmtiHeapIterationCallback.\n"); |
| NSK_COMPLAIN2("\tClass tag: 0x%lX;\n\tObject tag: 0x%lX.\n", class_tag, *tag_ptr); |
| nsk_jvmti_setFailStatus(); |
| } |
| reported_objects++; |
| return 0; |
| } |
| |
| JNIEXPORT void JNICALL |
| object_free_callback(jvmtiEnv* jvmti, jlong tag) { |
| if(DECODE_TYPE(tag) == OBJECT_TAG) { |
| objects_info[DECODE_OBJECT(tag)].collected = 1; |
| } else if(DECODE_TYPE(tag) == FIELD_TAG) { |
| objects_info[DECODE_OBJECT(tag)].fields[DECODE_FIELD(tag)].collected = 1; |
| } |
| } |
| |
| //set expected fields value according to it's type |
| void set_expected_value(field_info_t *field, int tagged, int is_static) { |
| if(field->primitive) { |
| field->size = (int) sizeof(jint); |
| if(is_static) { |
| field->value = (void*)(tagged ? &TAGGED_STATIC_INT_VALUE: |
| &UNTAGGED_STATIC_INT_VALUE); |
| } else { |
| field->value = (void*)(tagged ? &TAGGED_INT_VALUE: |
| &UNTAGGED_INT_VALUE); |
| } |
| field->type = TYPE_FIELD; |
| } else if (0==strcmp(field->signature,STRING_SIGNATURE)) { |
| field->value = (void*)(tagged ? TAGGED_STRING_VALUE: |
| UNTAGGED_STRING_VALUE); |
| field->size = (int) wcslen((wchar_t*)field->value); |
| field->type = TYPE_STRING; |
| } else if (0==strcmp(field->signature,INT_ARRAY_SIGNATURE)) { |
| field->size = INT_ARRAY_LENGTH; |
| field->value = (void*)(tagged ? TAGGED_INT_ARRAY_VALUE: |
| UNTAGGED_INT_ARRAY_VALUE); |
| field->type = TYPE_ARRAY; |
| } |
| } |
| |
| /** |
| * Read array of test objects. |
| * Tag each of these objects, their classes, non-primitive fields and non-primitive fields classes. |
| */ |
| int tag_objects(jvmtiEnv *jvmti, JNIEnv *jni) { |
| jclass debugee; |
| jfieldID testObjectsField; |
| jobjectArray testObjects; |
| int object; |
| |
| if(!NSK_VERIFY(NULL != |
| (debugee = NSK_CPP_STUB2(FindClass, jni, className)))) |
| return JNI_ERR; |
| |
| if(!NSK_VERIFY(NULL != |
| (testObjectsField = NSK_CPP_STUB4(GetStaticFieldID, |
| jni, debugee, |
| fieldName, |
| fieldSig)))) |
| return JNI_ERR; |
| |
| if(!NSK_VERIFY(NULL != |
| (testObjects = (jobjectArray)(NSK_CPP_STUB3(GetStaticObjectField, |
| jni, debugee, |
| testObjectsField))))) |
| return JNI_ERR; |
| |
| // For each of test objects tag every field |
| for(object = 0; object<TEST_OBJECTS_COUNT; object++) { |
| jobject target; |
| jclass targetClass; |
| jfieldID *targetFields; |
| jint field; |
| int tagged = object == 0; |
| |
| memset(&objects_info[object],0,sizeof(object_info_t)); |
| if(!NSK_VERIFY(NULL != |
| (target = NSK_CPP_STUB3(GetObjectArrayElement,jni, |
| testObjects,object)))) |
| return JNI_ERR; |
| |
| if(!NSK_VERIFY(NULL != |
| (targetClass = NSK_CPP_STUB2(GetObjectClass, jni, target)))) |
| return JNI_ERR; |
| |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(GetClassSignature, jvmti, targetClass, |
| &(objects_info[object].name), NULL))) |
| return JNI_ERR; |
| |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(GetClassFields, jvmti, targetClass, |
| &(objects_info[object].fields_count), |
| &targetFields))) |
| return JNI_ERR; |
| |
| objects_info[object].fields = (field_info_t*)calloc(objects_info[object].fields_count,sizeof(field_info_t)); |
| |
| // Iterate over fields, collect info about it and tag non primitive fields. |
| for(field = 0; field < objects_info[object].fields_count; field++) { |
| jint modifiers; |
| int is_static = 0; |
| int is_primitive = 0; |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB6(GetFieldName, jvmti, |
| targetClass, targetFields[field], |
| &objects_info[object].fields[field].name, |
| &objects_info[object].fields[field].signature, |
| NULL))) |
| return JNI_ERR; |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(GetFieldModifiers, jvmti, |
| targetClass, targetFields[field], |
| &modifiers))) { |
| return JNI_ERR; |
| } |
| is_static = (modifiers & STATIC_FIELD) == STATIC_FIELD; |
| if(is_primitive_type(objects_info[object].fields[field].signature)) { |
| objects_info[object].fields[field].primitive = 1; |
| is_primitive = 1; |
| } else { |
| jobject value; |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(GetFieldModifiers, jvmti, |
| targetClass, targetFields[field], |
| &modifiers))) { |
| return JNI_ERR; |
| } |
| if(is_static) { |
| if(!NSK_VERIFY(NULL != (value = NSK_CPP_STUB3(GetStaticObjectField, jni, |
| targetClass, |
| targetFields[field])))) { |
| return JNI_ERR; |
| } |
| } else { |
| if(!NSK_VERIFY(NULL != (value = NSK_CPP_STUB3(GetObjectField, jni, |
| target, |
| targetFields[field])))) { |
| return JNI_ERR; |
| } |
| } |
| if(tagged) { |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, value, |
| ENCODE_TAG(FIELD_TAG,object,field)))) { |
| return JNI_ERR; |
| } |
| } |
| NSK_CPP_STUB2(DeleteLocalRef, jni, value); |
| } |
| |
| objects_info[object].fields[field].expected = |
| occurance_expected(tagged,is_static,is_primitive); |
| expected_object_count += |
| objects_info[object].fields[field].expected && !is_primitive; |
| set_expected_value(&objects_info[object].fields[field], tagged, is_static); |
| } |
| |
| // tag class and it's instance to pass this tag into primitive field callback |
| if(tagged) { |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, target, |
| ENCODE_TAG(OBJECT_TAG,object,0)))) |
| return JNI_ERR; |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, targetClass, |
| ENCODE_TAG(CLASS_TAG,object,0)))) |
| return JNI_ERR; |
| } |
| |
| NSK_JVMTI_VERIFY(NSK_CPP_STUB2(Deallocate, jvmti, (unsigned char*)targetFields)); |
| NSK_CPP_STUB2(DeleteLocalRef, jni, target); |
| NSK_CPP_STUB2(DeleteLocalRef, jni, targetClass); |
| } |
| |
| NSK_CPP_STUB2(DeleteLocalRef, jni, testObjects); |
| |
| return JNI_OK; |
| } |
| |
| |
| //release resources allocated in tag_objects |
| void release_object_info(jvmtiEnv *jvmti, JNIEnv *jni) { |
| int object; |
| int field; |
| for(object = 0; object < TEST_OBJECTS_COUNT; object++) { |
| for(field = 0; field < objects_info[object].fields_count; field++) { |
| NSK_CPP_STUB2(Deallocate, jvmti, |
| (unsigned char*)objects_info[object].fields[field].name); |
| NSK_CPP_STUB2(Deallocate, jvmti, |
| (unsigned char*)objects_info[object].fields[field].signature); |
| } |
| NSK_CPP_STUB2(Deallocate, jvmti, (unsigned char*)objects_info[object].name); |
| free(objects_info[object].fields); |
| } |
| } |
| |
| // Check that every field was found expected amount of times |
| void verify_objects(int reachable) { |
| int object; |
| int field; |
| for(object = 0; object < (reachable?TEST_OBJECTS_COUNT:TAGGED_OBJECTS); object++) { |
| for(field = 0; field < objects_info[object].fields_count; field++) { |
| // If primitive field of object that was not collected or |
| // non primitive field that was not collected was not found |
| // expected amount of times, than test failed. |
| if((objects_info[object].fields[field].primitive && |
| !objects_info[object].collected) |
| || |
| (!objects_info[object].fields[field].primitive && |
| !objects_info[object].fields[field].collected)) { |
| if(objects_info[object].fields[field].expected != |
| objects_info[object].fields[field].found) { |
| NSK_COMPLAIN4("Field %s::%s expected to be found %d times, " |
| "but it was found %d times.\n", |
| objects_info[object].name, |
| objects_info[object].fields[field].name, |
| objects_info[object].fields[field].expected, |
| objects_info[object].fields[field].found); |
| nsk_jvmti_setFailStatus(); |
| } |
| } |
| objects_info[object].fields[field].found = 0; |
| } |
| } |
| } |
| |
| static void JNICALL |
| agent(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) { |
| jvmtiEvent event = JVMTI_EVENT_OBJECT_FREE; |
| jvmtiHeapCallbacks primitive_callbacks; |
| jvmtiEventCallbacks event_callbacks; |
| |
| NSK_DISPLAY0("Waiting debugee.\n"); |
| if(!NSK_VERIFY(nsk_jvmti_enableEvents(JVMTI_ENABLE, 1, &event, NULL))) { |
| return; |
| } |
| if(!NSK_VERIFY(nsk_jvmti_waitForSync(timeout))) { |
| return; |
| } |
| |
| NSK_DISPLAY0("Tagging fields.\n"); |
| if(!NSK_VERIFY(JNI_OK==tag_objects(jvmti, jni))) { |
| return; |
| } |
| |
| memset(&primitive_callbacks, 0, sizeof(jvmtiHeapCallbacks)); |
| primitive_callbacks.primitive_field_callback = &field_callback; |
| primitive_callbacks.array_primitive_value_callback = &array_callback; |
| primitive_callbacks.string_primitive_value_callback = &string_callback; |
| primitive_callbacks.heap_iteration_callback = &heap_callback; |
| |
| NSK_DISPLAY0("Iterating over reachable objects.\n"); |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB5(IterateThroughHeap, jvmti, |
| filter_type, NULL, &primitive_callbacks, NULL))) { |
| nsk_jvmti_setFailStatus(); |
| return; |
| } |
| |
| NSK_DISPLAY0("Verifying that all fields were found.\n"); |
| verify_objects(1); |
| |
| if(!NSK_VERIFY(nsk_jvmti_resumeSync())) { |
| return; |
| } |
| |
| if(!NSK_VERIFY(nsk_jvmti_waitForSync(timeout))) { |
| return; |
| } |
| |
| NSK_DISPLAY0("Iterating over unreachable objects.\n"); |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB5(IterateThroughHeap, jvmti, |
| filter_type, NULL, &primitive_callbacks, NULL))) { |
| nsk_jvmti_setFailStatus(); |
| return; |
| } |
| |
| NSK_DISPLAY0("Verifying that all fields were found.\n"); |
| verify_objects(0); |
| |
| /* |
| * This is done to clear event_callbacks.ObjectFree before we call release_object_info(), |
| * since it will free some memory that the callback will access. |
| */ |
| memset(&event_callbacks, 0, sizeof(jvmtiEventCallbacks)); |
| if(!NSK_JVMTI_VERIFY( |
| NSK_CPP_STUB3(SetEventCallbacks, jvmti, |
| &event_callbacks, sizeof(jvmtiEventCallbacks)))) { |
| return; |
| } |
| |
| release_object_info(jvmti, jni); |
| |
| if(!NSK_VERIFY(nsk_jvmti_resumeSync())) |
| return; |
| } |
| |
| #ifdef STATIC_BUILD |
| JNIEXPORT jint JNICALL Agent_OnLoad_HeapFilter(JavaVM *jvm, char *options, void *reserved) { |
| return Agent_Initialize(jvm, options, reserved); |
| } |
| JNIEXPORT jint JNICALL Agent_OnAttach_HeapFilter(JavaVM *jvm, char *options, void *reserved) { |
| return Agent_Initialize(jvm, options, reserved); |
| } |
| JNIEXPORT jint JNI_OnLoad_HeapFilter(JavaVM *jvm, char *options, void *reserved) { |
| return JNI_VERSION_1_8; |
| } |
| #endif |
| jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { |
| jvmtiEnv *jvmti; |
| jvmtiCapabilities caps; |
| jvmtiEventCallbacks event_callbacks; |
| const char *type; |
| |
| if(!NSK_VERIFY((jvmti = nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL)) { |
| return JNI_ERR; |
| } |
| |
| nsk_jvmti_parseOptions(options); |
| |
| type = nsk_jvmti_findOptionValue("filter"); |
| if(type != NULL) { |
| if(0 == strcmp(type, "JVMTI_HEAP_FILTER_TAGGED")) { |
| filter_type = JVMTI_HEAP_FILTER_TAGGED; |
| } else if(0 == strcmp(type, "JVMTI_HEAP_FILTER_UNTAGGED")) { |
| filter_type = JVMTI_HEAP_FILTER_UNTAGGED; |
| } else if(0 == strcmp(type, "JVMTI_HEAP_FILTER_CLASS_TAGGED")) { |
| filter_type = JVMTI_HEAP_FILTER_CLASS_TAGGED; |
| } else if(0 == strcmp(type, "JVMTI_HEAP_FILTER_CLASS_UNTAGGED")) { |
| filter_type = JVMTI_HEAP_FILTER_CLASS_UNTAGGED; |
| } else { |
| NSK_COMPLAIN1("unknown filter value '%s'.\n",type); |
| return JNI_ERR; |
| } |
| } else { |
| NSK_COMPLAIN0("filter option shound be presented.\n"); |
| return JNI_ERR; |
| } |
| |
| timeout = nsk_jvmti_getWaitTime() * 60 * 1000; |
| |
| memset(&caps, 0, sizeof(caps)); |
| caps.can_tag_objects = 1; |
| caps.can_generate_object_free_events = 1; |
| |
| if(!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(AddCapabilities, jvmti, &caps))) { |
| return JNI_ERR; |
| } |
| |
| memset(&event_callbacks, 0, sizeof(jvmtiEventCallbacks)); |
| event_callbacks.ObjectFree = &object_free_callback; |
| if(!NSK_JVMTI_VERIFY( |
| NSK_CPP_STUB3(SetEventCallbacks, jvmti, |
| &event_callbacks, sizeof(jvmtiEventCallbacks)))) { |
| return JNI_ERR; |
| } |
| |
| if (!NSK_VERIFY(nsk_jvmti_setAgentProc(agent, NULL))) { |
| return JNI_ERR; |
| } |
| |
| return JNI_OK; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |