| /* |
| * 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 <stdio.h> |
| #include <string.h> |
| |
| #include "nsk_tools.h" |
| #include "JVMTITools.h" |
| #include "jvmti_tools.h" |
| #include "agent_common.h" |
| #include "jni_tools.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #define PASSED 0 |
| #define STATUS_FAILED 2 |
| #define WAIT_TIME 2000 |
| |
| static jvmtiEnv *jvmti = NULL; |
| static jvmtiCapabilities caps; |
| static jvmtiEventCallbacks callbacks; |
| /* volatile variables */ |
| static jrawMonitorID agent_start_lock, thr_start_lock, thr_resume_lock, thr_event_lock; |
| static volatile jthread agent_thread = NULL; |
| static volatile jboolean terminate_debug_agent = JNI_FALSE; |
| static volatile jboolean debug_agent_timed_out = JNI_FALSE; |
| static volatile jboolean debug_agent_started = JNI_FALSE; |
| static volatile jthread next_thread = NULL; |
| static jvmtiThreadInfo inf; |
| static volatile int eventsCount = 0; |
| static volatile jint result = PASSED; |
| |
| /* |
| The agent runs special debugger agent (debug_agent) in a separate thread |
| that operates on behalf of other threads. |
| Upon receiving ThreadStart event, the debugger agent: |
| - suspends the new thread |
| - calls jni_DeleteGlobalRef with a jnienv * for that new thread |
| - resumes the new thread |
| Then the thread suspend status is checked in ThreadStart callback. |
| |
| The following monitors are used to synchronize debugger thread with other |
| threads: |
| 1. agent_start_lock |
| used to notify VMInit callback as well as ThreadStart callback |
| that agent thread has been started. |
| 2. thr_event_lock |
| used to guarantee that only one ThreadStart event is proceeded at |
| the time. |
| 3. thr_start_lock |
| used to notify agent thread that new thread has been started. |
| 4. thr_resume_lock |
| used to notify ThreadStart callback that agent thread finished |
| suspending and resuming the thread. |
| |
| So, the threads behaves as following: |
| |
| VMInit | debug_agent | ThreadStart |
| ------------------------------------------------------------------------- |
| | | |
| agent_start_lock.enter | | agent_start_lock.enter |
| | | |
| ... create debug_agent | ... start | while (!debug_agent) |
| agent_start_lock.wait | | agent_start_lock.wait |
| | agent_start_lock.enter | |
| | agent_start_lock.notifyAll | |
| | agent_start_lock.exit | |
| agent_start_lock.exit | | agent_start_lock.exit |
| | | |
| | | thr_event_lock.enter |
| | | |
| | thr_start_lock.enter | thr_start_lock.enter |
| | if (!next_thread) | thr_resume_lock.enter |
| | thr_start_lock.wait | |
| | | ... next_thread = ... |
| | | thr_start_lock.notify |
| | | thr_start_lock.exit |
| | | |
| | ... suspend new thread | thr_resume_lock.wait |
| | ... resume new thread | |
| | | |
| | thr_resume_lock.enter | |
| | thr_resume_lock.notify | |
| | thr_resume_lock.exit | |
| | | ... check next_thread state |
| | | thr_resume_lock.exit |
| | thr_start_lock.exit | |
| | thr_event_lock.exit |
| |
| |
| */ |
| |
| static void JNICALL |
| debug_agent(jvmtiEnv* jvmti, JNIEnv* jni, void *p) { |
| JNIEnv *env = jni; |
| jint thrStat; |
| jobject temp; |
| |
| /* Notify VMInit callback as well as ThreadStart callback (if any) |
| * that agent thread has been started |
| */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti, agent_start_lock))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("[agent] failed to acquire agent_start_lock\n"); |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorNotifyAll, jvmti, agent_start_lock))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("[agent] failed to notify about agent_start_lock\n"); |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti, agent_start_lock))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("[agent] failed to release agent_start_lock\n"); |
| } |
| |
| NSK_DISPLAY0(">>> [agent] agent created\n"); |
| |
| debug_agent_started = JNI_TRUE; |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti, thr_start_lock))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("[agent] failed to enter thr_start_lock\n"); |
| } |
| |
| while (terminate_debug_agent != JNI_TRUE) { |
| |
| if (next_thread == NULL ) { |
| /* wait till new thread will be created and started */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(RawMonitorWait, jvmti, thr_start_lock, (jlong)0))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("[agent] Failed while waiting thr_start_lock\n"); |
| } |
| } |
| |
| if (next_thread != NULL) { |
| /* hmm, why NewGlobalRef is called one more time??? |
| * next_thread = NSK_CPP_STUB2(NewGlobalRef, env, next_thread); |
| */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(SuspendThread, jvmti, next_thread))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN1("[agent] Failed to suspend thread#%d\n", eventsCount); |
| } |
| |
| NSK_DISPLAY2(">>> [agent] thread#%d %s suspended ...\n", eventsCount, inf.name); |
| |
| /* these dummy calls provoke VM to hang */ |
| temp = NSK_CPP_STUB2(NewGlobalRef, env, next_thread); |
| NSK_CPP_STUB2(DeleteGlobalRef, env, temp); |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(ResumeThread, jvmti, next_thread))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN1("[agent] Failed to resume thread#%d\n", eventsCount); |
| } |
| |
| NSK_DISPLAY2(">>> [agent] thread#%d %s resumed ...\n", eventsCount, inf.name); |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(GetThreadState, jvmti, next_thread, &thrStat))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN1("[agent] Failed to get thread state for thread#%d\n", eventsCount); |
| } |
| |
| NSK_DISPLAY3(">>> [agent] %s threadState=%s (%x)\n", |
| inf.name, TranslateState(thrStat), thrStat); |
| |
| if (thrStat & JVMTI_THREAD_STATE_SUSPENDED) { |
| NSK_COMPLAIN1("[agent] \"%s\" was not resumed\n", inf.name); |
| NSK_CPP_STUB2(FatalError, env, "[agent] could not recover"); |
| } |
| |
| NSK_CPP_STUB2(DeleteGlobalRef, env, next_thread); |
| next_thread = NULL; |
| |
| /* Notify ThreadStart callback that thread has been resumed */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti, thr_resume_lock))) { |
| NSK_COMPLAIN0("[agent] Failed to acquire thr_resume_lock\n"); |
| result = STATUS_FAILED; |
| } |
| |
| debug_agent_timed_out = JNI_FALSE; |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorNotify, jvmti, thr_resume_lock))) { |
| NSK_COMPLAIN0("[agent] Failed to notifing about thr_resume_lock\n"); |
| result = STATUS_FAILED; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti, thr_resume_lock))) { |
| NSK_COMPLAIN0("[agent] Failed to release thr_resume_lock\n"); |
| result = STATUS_FAILED; |
| } |
| } |
| } |
| |
| /* |
| * We don't call RawMonitorExit(thr_start_lock) in the loop so we don't |
| * lose any notify calls. |
| */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti, thr_start_lock))) { |
| NSK_COMPLAIN0("[agent] Failed to release thr_start_lock\n"); |
| result = STATUS_FAILED; |
| } |
| |
| NSK_DISPLAY0(">>> [agent] done.\n"); |
| } |
| |
| void JNICALL ThreadStart(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread) { |
| jint thrStat; |
| jvmtiPhase phase; |
| |
| NSK_DISPLAY0(">>> [ThreadStart hook] start\n"); |
| |
| /* skip if thread is 'agent thread' */ |
| if (NSK_CPP_STUB3(IsSameObject, env, agent_thread, thread) == JNI_TRUE) { |
| NSK_DISPLAY0(">>> [ThreadStart hook] skip agent thread\n"); |
| NSK_DISPLAY0(">>> [ThreadStart hook] end\n"); |
| return; |
| } |
| |
| /* wait till agent thread is started |
| * (otherwise can fail while waiting on thr_resume_thread due to timeout) |
| */ |
| if (debug_agent_started != JNI_TRUE) { |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti_env, agent_start_lock))) { |
| NSK_COMPLAIN0("[ThreadStart hook] Failed to acquire agent_start_lock\n"); |
| result = STATUS_FAILED; |
| } |
| |
| while (debug_agent_started != JNI_TRUE) { |
| NSK_DISPLAY1(">>> [ThreadStart hook] waiting %dms for agent thread to start\n", WAIT_TIME); |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(RawMonitorWait, |
| jvmti_env, agent_start_lock, (jlong)WAIT_TIME))) { |
| NSK_COMPLAIN0("[ThreadStart hook] Failed to wait for agent_start_lock\n"); |
| result = STATUS_FAILED; |
| } |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti_env, agent_start_lock))) { |
| NSK_COMPLAIN0("[ThreadStart hook] Failed to release agent_start_lock\n"); |
| result = STATUS_FAILED; |
| } |
| } |
| |
| |
| /* get JVMTI phase */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(GetPhase, jvmti_env, &phase))) { |
| NSK_COMPLAIN0("[ThreadStart hook] Failed to get JVMTI phase\n"); |
| result = STATUS_FAILED; |
| } |
| |
| /* Acquire event lock, |
| * so only one StartThread callback could be proceeded at the time |
| */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti_env, thr_event_lock))) { |
| NSK_COMPLAIN0("[ThreadStart hook] Failed to acquire thr_event_lock\n"); |
| result = STATUS_FAILED; |
| } |
| |
| { |
| /* Get thread name */ |
| inf.name = "UNKNOWN"; |
| if (phase == JVMTI_PHASE_LIVE) { |
| /* GetThreadInfo may only be called during the live phase */ |
| if (!NSK_JVMTI_VERIFY( |
| NSK_CPP_STUB3(GetThreadInfo, jvmti_env, thread, &inf))) { |
| NSK_COMPLAIN1("[ThreadStart hook] Failed to get thread infor for thread#%d\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| } |
| |
| NSK_DISPLAY2(">>> [ThreadStart hook] thread#%d: %s\n", eventsCount, inf.name); |
| |
| /* Acquire thr_start_lock */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti_env, thr_start_lock))) { |
| NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to acquire thr_start_lock\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| /* Acquire thr_resume_lock before we release thr_start_lock to prevent |
| * debug agent from notifying us before we are ready. |
| */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti_env, thr_resume_lock))) { |
| NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to acquire thr_resume_lock\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| /* Store thread */ |
| next_thread = NSK_CPP_STUB2(NewGlobalRef, env, thread); |
| debug_agent_timed_out = JNI_TRUE; |
| |
| /* Notify agent thread about new started thread and let agent thread to work with it */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorNotify, jvmti_env, thr_start_lock))) { |
| NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to notify about thr_start_lock\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti_env, thr_start_lock))) { |
| NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to release thr_start_lock\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| /* Wait till this started thread will be resumed by agent thread */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(RawMonitorWait, |
| jvmti_env, thr_resume_lock, (jlong)WAIT_TIME ))) { |
| NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed while waiting for thr_resume_lock\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| if (debug_agent_timed_out == JNI_TRUE) { |
| NSK_COMPLAIN1("[ThreadStart hook] \"%s\": debug agent timed out\n", inf.name); |
| NSK_CPP_STUB2(FatalError, env, "[ThreadStart hook] could not recover"); |
| } |
| |
| /* Release thr_resume_lock lock */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti_env, thr_resume_lock))) { |
| NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to release thr_resume_lock\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| /* check that thread is not in SUSPENDED state */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(GetThreadState, jvmti_env, thread, &thrStat))) { |
| NSK_COMPLAIN1("[ThreadStart hook] Failed to get thread state for thread#%d\n", eventsCount); |
| result = STATUS_FAILED; |
| } |
| |
| NSK_DISPLAY2(">>> [ThreadStart hook] threadState=%s (%x)\n", |
| TranslateState(thrStat), thrStat); |
| |
| if (thrStat & JVMTI_THREAD_STATE_SUSPENDED) { |
| NSK_COMPLAIN1("[ThreadStart hook] \"%s\" was self-suspended\n", inf.name); |
| NSK_CPP_STUB2(FatalError, env, "[ThreadStart hook] could not recover"); |
| } |
| |
| eventsCount++; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti_env, thr_event_lock))) { |
| NSK_COMPLAIN0("[ThreadStart hook] Failed to release thr_event_lock\n"); |
| result = STATUS_FAILED; |
| } |
| |
| NSK_DISPLAY0(">>> [ThreadStart hook] end\n"); |
| } |
| |
| void JNICALL VMInit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thr) { |
| jclass cls = NULL; |
| jmethodID mid = NULL; |
| |
| NSK_DISPLAY0(">>> VMInit event: start\n"); |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(SetEventNotificationMode, |
| jvmti_env, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, NULL))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to enable JVMTI_EVENT_THREAD_START\n"); |
| return; |
| } |
| |
| /* Start agent thread */ |
| if (!NSK_VERIFY((cls = |
| NSK_CPP_STUB2(FindClass, env, "java/lang/Thread")) != NULL)) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("TEST FAILED: Cannot start agent thread: FindClass() failed\n"); |
| return; |
| } |
| |
| |
| if (!NSK_VERIFY((mid = |
| NSK_CPP_STUB4(GetMethodID, env, cls, "<init>", "()V")) != NULL)) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("TEST FAILED: Cannot start agent thread: GetMethodID() failed\n"); |
| return; |
| } |
| |
| |
| if (!NSK_VERIFY((agent_thread = |
| NSK_CPP_STUB3(NewObject, env, cls, mid)) != NULL)) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("Cannot start agent thread: NewObject() failed\n"); |
| return; |
| } |
| |
| agent_thread = (jthread) NSK_CPP_STUB2(NewGlobalRef, env, agent_thread); |
| if (agent_thread == NULL) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("Cannot create global reference for agent_thread\n"); |
| return; |
| } |
| |
| /* |
| * Grab agent_start_lock before launching debug_agent to prevent |
| * debug_agent from notifying us before we are ready. |
| */ |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, jvmti_env, agent_start_lock))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("TEST FAILED: failed to enter agent_start_lock\n"); |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB5(RunAgentThread, |
| jvmti_env, agent_thread, debug_agent, NULL, JVMTI_THREAD_NORM_PRIORITY))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("TEST FAILED: failed to create agent thread\n"); |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(RawMonitorWait, jvmti_env, agent_start_lock, (jlong)0))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("TEST FAILED: failed to wait agent_start_lock\n"); |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, jvmti_env, agent_start_lock))) { |
| result = STATUS_FAILED; |
| NSK_COMPLAIN0("TEST FAILED: failed to exit agent_start_lock\n"); |
| } |
| |
| NSK_DISPLAY0(">>> VMInit event: end\n"); |
| } |
| |
| void JNICALL VMDeath(jvmtiEnv *jvmti_env, JNIEnv *env) { |
| NSK_DISPLAY0(">>> VMDeath event\n"); |
| |
| terminate_debug_agent = JNI_TRUE; |
| } |
| |
| #ifdef STATIC_BUILD |
| JNIEXPORT jint JNICALL Agent_OnLoad_threadstart002(JavaVM *jvm, char *options, void *reserved) { |
| return Agent_Initialize(jvm, options, reserved); |
| } |
| JNIEXPORT jint JNICALL Agent_OnAttach_threadstart002(JavaVM *jvm, char *options, void *reserved) { |
| return Agent_Initialize(jvm, options, reserved); |
| } |
| JNIEXPORT jint JNI_OnLoad_threadstart002(JavaVM *jvm, char *options, void *reserved) { |
| return JNI_VERSION_1_8; |
| } |
| #endif |
| jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { |
| |
| /* init framework and parse options */ |
| if (!NSK_VERIFY(nsk_jvmti_parseOptions(options))) |
| return JNI_ERR; |
| |
| /* create JVMTI environment */ |
| if (!NSK_VERIFY((jvmti = |
| nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL)) { |
| NSK_COMPLAIN0("TEST FAILED: failed to create JVMTIEnv\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(GetPotentialCapabilities, jvmti, &caps))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to get potential capabilities\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(AddCapabilities, jvmti, &caps))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to add capabilities during agent load\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(GetCapabilities, jvmti, &caps))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to get capabilities\n"); |
| return JNI_ERR; |
| } |
| |
| if (!caps.can_suspend) { |
| NSK_DISPLAY0("WARNING: suspend/resume is not implemented\n"); |
| } |
| |
| /* create raw monitors */ |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(CreateRawMonitor, jvmti, "_agent_start_lock", &agent_start_lock))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to create agent_start_lock\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(CreateRawMonitor, jvmti, "_thr_event_lock", &thr_event_lock))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to create thr_event_lock\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(CreateRawMonitor, jvmti, "_thr_start_lock", &thr_start_lock))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to create thr_start_lock\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(CreateRawMonitor, jvmti, "_thr_resume_lock", &thr_resume_lock))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to create thr_resume_lock\n"); |
| return JNI_ERR; |
| } |
| |
| callbacks.VMInit = &VMInit; |
| callbacks.VMDeath = &VMDeath; |
| callbacks.ThreadStart = &ThreadStart; |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetEventCallbacks, jvmti, &callbacks, sizeof(callbacks)))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to set event callbacks\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(SetEventNotificationMode, |
| jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to enable JVMTI_EVENT_VM_INIT\n"); |
| return JNI_ERR; |
| } |
| |
| if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB4(SetEventNotificationMode, |
| jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL))) { |
| NSK_COMPLAIN0("TEST FAILED: failed to enable JVMTI_EVENT_VM_DEATH\n"); |
| return JNI_ERR; |
| } |
| |
| return JNI_OK; |
| } |
| |
| JNIEXPORT jint JNICALL |
| Java_nsk_jvmti_ThreadStart_threadstart002_check(JNIEnv *env, jclass cls) { |
| if (eventsCount == 0) { |
| NSK_COMPLAIN0("None of thread start events!\n"); |
| result = STATUS_FAILED; |
| } |
| |
| NSK_DISPLAY1(">>> total of thread start events: %d\n", eventsCount); |
| |
| return result; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |