| /* |
| * Copyright (c) 2003, 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 <string.h> |
| #include "jvmti.h" |
| #include "agent_common.h" |
| #include "jni_tools.h" |
| #include "jvmti_tools.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /* ============================================================================= */ |
| |
| /* scaffold objects */ |
| static JNIEnv* jni = NULL; |
| static jvmtiEnv *jvmti = NULL; |
| static jlong timeout = 0; |
| |
| /* constant names */ |
| #define DEBUGEE_CLASS_NAME "nsk/jvmti/scenarios/events/EM05/em05t002" |
| #define THREAD_CLASS_NAME "nsk/jvmti/scenarios/events/EM05/em05t002Thread" |
| #define THREAD_FIELD_NAME "thread" |
| #define THREAD_FIELD_SIG "L"THREAD_CLASS_NAME";" |
| |
| /* constants */ |
| #define MAX_NAME_LENGTH 64 |
| #define EVENTS_COUNT 2 |
| #define METHODS_COUNT 2 |
| #define MOMENTS_COUNT 3 |
| |
| /* compilation moments */ |
| #define COMPILATION_MOMENT_BEFORE 0 |
| #define COMPILATION_MOMENT_RUNNING 1 |
| #define COMPILATION_MOMENT_AFTER 2 |
| |
| /* tested events */ |
| static jvmtiEvent eventsList[EVENTS_COUNT] = { |
| JVMTI_EVENT_COMPILED_METHOD_LOAD, |
| JVMTI_EVENT_COMPILED_METHOD_UNLOAD |
| }; |
| |
| /* method description structure */ |
| typedef struct { |
| char methodName[MAX_NAME_LENGTH]; |
| char methodSig[MAX_NAME_LENGTH]; |
| jmethodID method; |
| int compiled; |
| int loadEvents[MOMENTS_COUNT]; |
| int unloadEvents[MOMENTS_COUNT]; |
| } MethodDesc; |
| |
| /* descriptions of tested methods */ |
| static MethodDesc methodsDesc[METHODS_COUNT] = { |
| {"javaMethod", "(I)I", NULL, 0, {0}, {0}}, |
| {"nativeMethod", "(I)I", NULL, 0, {0}, {0}} |
| }; |
| |
| /* current compilation moment */ |
| static volatile int moment = COMPILATION_MOMENT_BEFORE; |
| |
| /* ============================================================================= */ |
| |
| /* testcase(s) */ |
| static int prepare(); |
| static int generateEvents(); |
| static int checkEvents(); |
| static int clean(); |
| |
| /* ============================================================================= */ |
| |
| /** Agent algorithm. */ |
| static void JNICALL |
| agentProc(jvmtiEnv* jvmti, JNIEnv* agentJNI, void* arg) { |
| jni = agentJNI; |
| |
| NSK_DISPLAY0("Wait for debuggee to become ready\n"); |
| if (!nsk_jvmti_waitForSync(timeout)) |
| return; |
| |
| { |
| NSK_DISPLAY0("Prepare data\n"); |
| if (!prepare()) { |
| nsk_jvmti_setFailStatus(); |
| return; |
| } |
| |
| NSK_DISPLAY0("Testcase #1: generate events before running thread\n"); |
| moment = COMPILATION_MOMENT_BEFORE; |
| NSK_DISPLAY0("Call GenerateEvents() before running methods\n"); |
| if (!generateEvents()) |
| return; |
| NSK_DISPLAY0("Check if events received\n"); |
| if (!checkEvents()) |
| return; |
| |
| NSK_DISPLAY0("Testcase #2: run methods to provoke compilation\n"); |
| moment = COMPILATION_MOMENT_RUNNING; |
| NSK_DISPLAY0("Provoke methods compilation\n"); |
| if (!nsk_jvmti_resumeSync()) |
| return; |
| NSK_DISPLAY0("Wait for thread to completed\n"); |
| if (!nsk_jvmti_waitForSync(timeout)) |
| return; |
| NSK_DISPLAY0("Check if events received\n"); |
| if (!checkEvents()) |
| return; |
| |
| NSK_DISPLAY0("Testcase #3: generate events before running thread\n"); |
| moment = COMPILATION_MOMENT_AFTER; |
| NSK_DISPLAY0("Call GenerateEvents() after running methods\n"); |
| if (!generateEvents()) |
| return; |
| NSK_DISPLAY0("Check if events received\n"); |
| if (!checkEvents()) |
| return; |
| |
| NSK_DISPLAY0("Clean data\n"); |
| if (!clean()) { |
| nsk_jvmti_setFailStatus(); |
| return; |
| } |
| } |
| |
| NSK_DISPLAY0("Let debuggee to finish\n"); |
| if (!nsk_jvmti_resumeSync()) |
| return; |
| } |
| |
| /* ============================================================================= */ |
| |
| /** |
| * Generate tested events (COMPILED_METHOD_LOAD only). |
| */ |
| static int generateEvents() { |
| if (!NSK_JVMTI_VERIFY( |
| NSK_CPP_STUB2(GenerateEvents, jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD))) { |
| nsk_jvmti_setFailStatus(); |
| return NSK_FALSE; |
| } |
| return NSK_TRUE; |
| } |
| |
| /** |
| * Prepare data. |
| * - find tested thread |
| * - get tested methodIDs |
| * - enable events |
| */ |
| static int prepare() { |
| jclass debugeeClass = NULL; |
| jclass threadClass = NULL; |
| jfieldID threadFieldID = NULL; |
| jthread thread = NULL; |
| int i; |
| |
| for (i = 0; i < METHODS_COUNT; i++) { |
| int j; |
| methodsDesc[i].method = (jmethodID)NULL; |
| methodsDesc[i].compiled = NSK_FALSE; |
| for (j = 0; j < MOMENTS_COUNT; j++) { |
| methodsDesc[i].loadEvents[j] = 0; |
| methodsDesc[i].unloadEvents[j] = 0; |
| } |
| } |
| |
| if (!NSK_JNI_VERIFY(jni, (debugeeClass = |
| NSK_CPP_STUB2(FindClass, jni, DEBUGEE_CLASS_NAME)) != NULL)) |
| return NSK_FALSE; |
| |
| if (!NSK_JNI_VERIFY(jni, (threadFieldID = |
| NSK_CPP_STUB4(GetStaticFieldID, jni, debugeeClass, |
| THREAD_FIELD_NAME, THREAD_FIELD_SIG)) != NULL)) |
| return NSK_FALSE; |
| |
| if (!NSK_JNI_VERIFY(jni, (thread = (jthread) |
| NSK_CPP_STUB3(GetStaticObjectField, jni, debugeeClass, threadFieldID)) != NULL)) |
| return NSK_FALSE; |
| |
| if (!NSK_JNI_VERIFY(jni, (threadClass = |
| NSK_CPP_STUB2(GetObjectClass, jni, thread)) != NULL)) |
| return NSK_FALSE; |
| |
| NSK_DISPLAY0("Find tested methods:\n"); |
| for (i = 0; i < METHODS_COUNT; i++) { |
| if (!NSK_JNI_VERIFY(jni, (methodsDesc[i].method = |
| NSK_CPP_STUB4(GetMethodID, jni, threadClass, |
| methodsDesc[i].methodName, methodsDesc[i].methodSig)) != NULL)) |
| return NSK_FALSE; |
| NSK_DISPLAY3(" method #%d (%s): 0x%p\n", |
| i, methodsDesc[i].methodName, (void*)methodsDesc[i].method); |
| } |
| |
| NSK_DISPLAY0("Enable events\n"); |
| if (!nsk_jvmti_enableEvents(JVMTI_ENABLE, EVENTS_COUNT, eventsList, NULL)) |
| return NSK_FALSE; |
| |
| return NSK_TRUE; |
| } |
| |
| /** |
| * Testcase: check tested events. |
| * - check if expected events received for each method |
| * |
| * Returns NSK_TRUE if test may continue; or NSK_FALSE for test break. |
| */ |
| static int checkEvents() { |
| int i; |
| |
| for (i = 0; i < METHODS_COUNT; i++) { |
| NSK_DISPLAY2(" method #%d (%s):\n", |
| i, methodsDesc[i].methodName); |
| NSK_DISPLAY2(" COMPILED_METHOD_LOAD: %d, COMPILED_METHOD_UNLOAD: %d\n", |
| methodsDesc[i].loadEvents[moment], |
| methodsDesc[i].unloadEvents[moment]); |
| |
| if (moment == COMPILATION_MOMENT_AFTER) { |
| int loadEventsTotal = methodsDesc[i].loadEvents[COMPILATION_MOMENT_BEFORE] |
| + methodsDesc[i].loadEvents[COMPILATION_MOMENT_RUNNING]; |
| int unloadEventsTotal = methodsDesc[i].unloadEvents[COMPILATION_MOMENT_BEFORE] |
| + methodsDesc[i].unloadEvents[COMPILATION_MOMENT_RUNNING]; |
| |
| /* complain if no COMPILED_METHOD_LOAD events finally generated for compiled events */ |
| if (methodsDesc[i].compiled) { |
| if (methodsDesc[i].loadEvents[COMPILATION_MOMENT_AFTER] > loadEventsTotal) { |
| NSK_COMPLAIN4("No COMPILED_METHOD_LOAD events finally generated for compiled method: %s\n" |
| "# total COMPILED_METHOD_LOAD: %d\n" |
| "# total COMPILED_METHOD_UNLOAD: %d\n" |
| "# final GenerateEvents(): %d\n", |
| methodsDesc[i].methodName, |
| loadEventsTotal, |
| unloadEventsTotal, |
| methodsDesc[i].loadEvents[COMPILATION_MOMENT_AFTER]); |
| nsk_jvmti_setFailStatus(); |
| } |
| } |
| |
| /* complain if too many COMPILED_METHOD_LOAD events finally generated */ |
| if (methodsDesc[i].loadEvents[COMPILATION_MOMENT_AFTER] > loadEventsTotal) { |
| NSK_COMPLAIN5("Too many COMPILED_METHOD_LOAD events finally generated for method: %s\n" |
| "# GenerateEvents() before execution: %d\n" |
| "# generated during execution: %d\n" |
| "# total: %d\n" |
| "# GenerateEvents() after execution: %d\n", |
| methodsDesc[i].methodName, |
| methodsDesc[i].loadEvents[COMPILATION_MOMENT_BEFORE], |
| methodsDesc[i].loadEvents[COMPILATION_MOMENT_RUNNING], |
| loadEventsTotal, |
| methodsDesc[i].loadEvents[COMPILATION_MOMENT_AFTER]); |
| nsk_jvmti_setFailStatus(); |
| } |
| |
| /* warn if too mamy COMPILED_METHOD_UNLOAD events received */ |
| if (unloadEventsTotal > loadEventsTotal) { |
| NSK_DISPLAY1("# WARNING: Too many COMPILED_METHOD_UNLOAD events for method: %s\n", |
| methodsDesc[i].methodName); |
| NSK_DISPLAY2("# COMPILED_METHOD_LOAD: %d, COMPILED_METHOD_UNLOAD: %d\n", |
| loadEventsTotal, unloadEventsTotal); |
| } |
| } |
| } |
| return NSK_TRUE; |
| } |
| |
| /** |
| * Clean data. |
| * - disable events |
| */ |
| static int clean() { |
| NSK_DISPLAY0("Disable events\n"); |
| if (!nsk_jvmti_enableEvents(JVMTI_DISABLE, EVENTS_COUNT, eventsList, NULL)) |
| return NSK_FALSE; |
| |
| return NSK_TRUE; |
| } |
| |
| /* ============================================================================= */ |
| |
| /** |
| * COMPILED_METHOD_LOAD callback. |
| * - turm on flag that method is compiled |
| */ |
| JNIEXPORT void JNICALL |
| callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, |
| jint code_size, const void* code_addr, |
| jint map_length, const jvmtiAddrLocationMap* map, |
| const void* compile_info) { |
| int i; |
| |
| /* check if event is for tested method and count it */ |
| for (i = 0; i < METHODS_COUNT; i++) { |
| if (methodsDesc[i].method == method) { |
| methodsDesc[i].loadEvents[moment]++; |
| methodsDesc[i].compiled = NSK_TRUE; |
| |
| NSK_DISPLAY3(" COMPILED_METHOD_LOAD for method #%d (%s): %d times\n", |
| i, methodsDesc[i].methodName, |
| methodsDesc[i].loadEvents[moment]); |
| NSK_DISPLAY1(" methodID: 0x%p\n", |
| (void*)methodsDesc[i].method); |
| NSK_DISPLAY1(" code_size: %d\n", |
| (int)code_size); |
| NSK_DISPLAY1(" map_length: %d\n", |
| (int)map_length); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * COMPILED_METHOD_UNLOAD callback. |
| * - turn off flag that method is compiled |
| */ |
| JNIEXPORT void JNICALL |
| callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method, |
| const void* code_addr) { |
| int i; |
| |
| /* check if event is for tested method and count it */ |
| for (i = 0; i < METHODS_COUNT; i++) { |
| if (methodsDesc[i].method == method) { |
| methodsDesc[i].unloadEvents[moment]++; |
| methodsDesc[i].compiled = NSK_FALSE; |
| |
| NSK_DISPLAY3(" COMPILED_METHOD_UNLOAD for method #%d (%s): %d times\n", |
| i, methodsDesc[i].methodName, |
| methodsDesc[i].loadEvents[moment]); |
| NSK_DISPLAY1(" methodID: 0x%p\n", |
| (void*)methodsDesc[i].method); |
| break; |
| } |
| } |
| } |
| |
| /* ============================================================================= */ |
| |
| /** Native running method in tested thread. */ |
| JNIEXPORT jint JNICALL |
| Java_nsk_jvmti_scenarios_events_EM05_em05t002Thread_nativeMethod(JNIEnv* jni, |
| jobject obj, jint i) { |
| jint k = 0; |
| jint j; |
| |
| for (j = 0; j < i; j++) { |
| k += (i - j); |
| } |
| return k; |
| } |
| |
| /* ============================================================================= */ |
| |
| /** Agent library initialization. */ |
| #ifdef STATIC_BUILD |
| JNIEXPORT jint JNICALL Agent_OnLoad_em05t002(JavaVM *jvm, char *options, void *reserved) { |
| return Agent_Initialize(jvm, options, reserved); |
| } |
| JNIEXPORT jint JNICALL Agent_OnAttach_em05t002(JavaVM *jvm, char *options, void *reserved) { |
| return Agent_Initialize(jvm, options, reserved); |
| } |
| JNIEXPORT jint JNI_OnLoad_em05t002(JavaVM *jvm, char *options, void *reserved) { |
| return JNI_VERSION_1_8; |
| } |
| #endif |
| jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { |
| |
| if (!NSK_VERIFY(nsk_jvmti_parseOptions(options))) |
| return JNI_ERR; |
| |
| timeout = nsk_jvmti_getWaitTime() * 60 * 1000; |
| |
| if (!NSK_VERIFY((jvmti = |
| nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL)) |
| return JNI_ERR; |
| |
| { |
| jvmtiCapabilities caps; |
| memset(&caps, 0, sizeof(caps)); |
| caps.can_generate_compiled_method_load_events = 1; |
| if (!NSK_JVMTI_VERIFY( |
| NSK_CPP_STUB2(AddCapabilities, jvmti, &caps))) |
| return JNI_ERR; |
| } |
| |
| { |
| jvmtiEventCallbacks eventCallbacks; |
| memset(&eventCallbacks, 0, sizeof(eventCallbacks)); |
| eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad; |
| eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload; |
| if (!NSK_JVMTI_VERIFY( |
| NSK_CPP_STUB3(SetEventCallbacks, jvmti, |
| &eventCallbacks, sizeof(eventCallbacks)))) |
| return JNI_ERR; |
| } |
| |
| if (!NSK_VERIFY(nsk_jvmti_setAgentProc(agentProc, NULL))) |
| return JNI_ERR; |
| |
| return JNI_OK; |
| } |
| |
| /* ============================================================================= */ |
| |
| #ifdef __cplusplus |
| } |
| #endif |