1 /*
   2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include <stdio.h>
  25 #include <string.h>
  26 
  27 #include "nsk_tools.h"
  28 #include "JVMTITools.h"
  29 #include "jvmti_tools.h"
  30 #include "agent_common.h"
  31 #include "jni_tools.h"
  32 
  33 extern "C" {
  34 
  35 #define PASSED 0
  36 #define STATUS_FAILED 2
  37 #define WAIT_TIME 2000
  38 
  39 static jvmtiEnv *jvmti = NULL;
  40 static jvmtiCapabilities caps;
  41 static jvmtiEventCallbacks callbacks;
  42 /* volatile variables */
  43 static jrawMonitorID agent_start_lock, thr_start_lock, thr_resume_lock, thr_event_lock;
  44 static volatile jthread agent_thread = NULL;
  45 static volatile jboolean terminate_debug_agent = JNI_FALSE;
  46 static volatile jboolean debug_agent_timed_out = JNI_FALSE;
  47 static volatile jboolean debug_agent_started = JNI_FALSE;
  48 static volatile jthread next_thread = NULL;
  49 static jvmtiThreadInfo inf;
  50 static volatile int eventsCount = 0;
  51 static volatile jint result = PASSED;
  52 
  53 /*
  54     The agent runs special debugger agent (debug_agent) in a separate thread
  55     that operates on behalf of other threads.
  56     Upon receiving ThreadStart event, the debugger agent:
  57     - suspends the new thread
  58     - calls jni_DeleteGlobalRef with a jnienv * for that new thread
  59     - resumes the new thread
  60     Then the thread suspend status is checked in ThreadStart callback.
  61 
  62     The following monitors are used to synchronize debugger thread with other
  63     threads:
  64     1. agent_start_lock
  65        used to notify VMInit callback as well as ThreadStart callback
  66        that agent thread has been started.
  67     2. thr_event_lock
  68        used to guarantee that only one ThreadStart event is proceeded at
  69        the time.
  70     3. thr_start_lock
  71        used to notify agent thread that new thread has been started.
  72     4. thr_resume_lock
  73        used to notify ThreadStart callback that agent thread finished
  74        suspending and resuming the thread.
  75 
  76     So, the threads behaves as following:
  77 
  78 VMInit                  | debug_agent                 |   ThreadStart
  79 -------------------------------------------------------------------------
  80                         |                             |
  81  agent_start_lock.enter |                             | agent_start_lock.enter
  82                         |                             |
  83  ... create debug_agent | ... start                   |  while (!debug_agent)
  84  agent_start_lock.wait  |                             |    agent_start_lock.wait
  85                         | agent_start_lock.enter      |
  86                         | agent_start_lock.notifyAll  |
  87                         | agent_start_lock.exit       |
  88  agent_start_lock.exit  |                             |  agent_start_lock.exit
  89                         |                             |
  90                         |                             |  thr_event_lock.enter
  91                         |                             |
  92                         | thr_start_lock.enter        |  thr_start_lock.enter
  93                         | if (!next_thread)           |  thr_resume_lock.enter
  94                         |     thr_start_lock.wait     |
  95                         |                             |  ... next_thread = ...
  96                         |                             |  thr_start_lock.notify
  97                         |                             |  thr_start_lock.exit
  98                         |                             |
  99                         | ... suspend new thread      |  thr_resume_lock.wait
 100                         | ... resume new thread       |
 101                         |                             |
 102                         | thr_resume_lock.enter       |
 103                         | thr_resume_lock.notify      |
 104                         | thr_resume_lock.exit        |
 105                         |                             |  ... check next_thread state
 106                         |                             |  thr_resume_lock.exit
 107                         | thr_start_lock.exit         |
 108                                                       | thr_event_lock.exit
 109 
 110 
 111 */
 112 
 113 static void JNICALL
 114 debug_agent(jvmtiEnv* jvmti, JNIEnv* jni, void *p) {
 115     JNIEnv *env = jni;
 116     jint thrStat;
 117     jobject temp;
 118 
 119     /* Notify VMInit callback as well as ThreadStart callback (if any)
 120      * that agent thread has been started
 121      */
 122     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorEnter(agent_start_lock))) {
 123         result = STATUS_FAILED;
 124         NSK_COMPLAIN0("[agent] failed to acquire agent_start_lock\n");
 125     }
 126 
 127     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorNotifyAll(agent_start_lock))) {
 128         result = STATUS_FAILED;
 129         NSK_COMPLAIN0("[agent] failed to notify about agent_start_lock\n");
 130     }
 131 
 132     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorExit(agent_start_lock))) {
 133         result = STATUS_FAILED;
 134         NSK_COMPLAIN0("[agent] failed to release agent_start_lock\n");
 135     }
 136 
 137     NSK_DISPLAY0(">>> [agent] agent created\n");
 138 
 139     debug_agent_started = JNI_TRUE;
 140 
 141     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorEnter(thr_start_lock))) {
 142         result = STATUS_FAILED;
 143         NSK_COMPLAIN0("[agent] failed to enter thr_start_lock\n");
 144     }
 145 
 146     while (terminate_debug_agent != JNI_TRUE) {
 147 
 148         if (next_thread == NULL ) {
 149             /* wait till new thread will be created and started */
 150             if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorWait(thr_start_lock, (jlong)0))) {
 151                 result = STATUS_FAILED;
 152                 NSK_COMPLAIN0("[agent] Failed while waiting thr_start_lock\n");
 153             }
 154         }
 155 
 156         if (next_thread != NULL) {
 157             /* hmm, why NewGlobalRef is called one more time???
 158              * next_thread = env->NewGlobalRef(next_thread);
 159              */
 160             if (!NSK_JVMTI_VERIFY(jvmti->SuspendThread(next_thread))) {
 161                 result = STATUS_FAILED;
 162                 NSK_COMPLAIN1("[agent] Failed to suspend thread#%d\n", eventsCount);
 163             }
 164 
 165             NSK_DISPLAY2(">>> [agent] thread#%d %s suspended ...\n", eventsCount, inf.name);
 166 
 167             /* these dummy calls provoke VM to hang */
 168             temp = env->NewGlobalRef(next_thread);
 169             env->DeleteGlobalRef(temp);
 170 
 171             if (!NSK_JVMTI_VERIFY(jvmti->ResumeThread(next_thread))) {
 172                 result = STATUS_FAILED;
 173                 NSK_COMPLAIN1("[agent] Failed to resume thread#%d\n", eventsCount);
 174             }
 175 
 176             NSK_DISPLAY2(">>> [agent] thread#%d %s resumed ...\n", eventsCount, inf.name);
 177 
 178             if (!NSK_JVMTI_VERIFY(jvmti->GetThreadState(next_thread, &thrStat))) {
 179                 result = STATUS_FAILED;
 180                 NSK_COMPLAIN1("[agent] Failed to get thread state for thread#%d\n", eventsCount);
 181             }
 182 
 183             NSK_DISPLAY3(">>> [agent] %s threadState=%s (%x)\n",
 184                     inf.name, TranslateState(thrStat), thrStat);
 185 
 186             if (thrStat & JVMTI_THREAD_STATE_SUSPENDED) {
 187                 NSK_COMPLAIN1("[agent] \"%s\" was not resumed\n", inf.name);
 188                 env->FatalError("[agent] could not recover");
 189             }
 190 
 191             env->DeleteGlobalRef(next_thread);
 192             next_thread = NULL;
 193 
 194             /* Notify ThreadStart callback that thread has been resumed */
 195             if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorEnter(thr_resume_lock))) {
 196                 NSK_COMPLAIN0("[agent] Failed to acquire thr_resume_lock\n");
 197                 result = STATUS_FAILED;
 198             }
 199 
 200             debug_agent_timed_out = JNI_FALSE;
 201 
 202             if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorNotify(thr_resume_lock))) {
 203                 NSK_COMPLAIN0("[agent] Failed to notifing about thr_resume_lock\n");
 204                 result = STATUS_FAILED;
 205             }
 206 
 207             if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorExit(thr_resume_lock))) {
 208                 NSK_COMPLAIN0("[agent] Failed to release thr_resume_lock\n");
 209                 result = STATUS_FAILED;
 210             }
 211         }
 212     }
 213 
 214     /*
 215      * We don't call RawMonitorExit(thr_start_lock) in the loop so we don't
 216      * lose any notify calls.
 217      */
 218     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorExit(thr_start_lock))) {
 219         NSK_COMPLAIN0("[agent] Failed to release thr_start_lock\n");
 220         result = STATUS_FAILED;
 221     }
 222 
 223     NSK_DISPLAY0(">>> [agent] done.\n");
 224 }
 225 
 226 void JNICALL ThreadStart(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread) {
 227     jint thrStat;
 228     jvmtiPhase phase;
 229 
 230     NSK_DISPLAY0(">>> [ThreadStart hook] start\n");
 231 
 232     /* skip if thread is 'agent thread' */
 233     if (env->IsSameObject(agent_thread, thread) == JNI_TRUE) {
 234         NSK_DISPLAY0(">>> [ThreadStart hook] skip agent thread\n");
 235         NSK_DISPLAY0(">>> [ThreadStart hook] end\n");
 236         return;
 237     }
 238 
 239     /* wait till agent thread is started
 240      * (otherwise can fail while waiting on thr_resume_thread due to timeout)
 241      */
 242     if (debug_agent_started != JNI_TRUE) {
 243         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorEnter(agent_start_lock))) {
 244             NSK_COMPLAIN0("[ThreadStart hook] Failed to acquire agent_start_lock\n");
 245             result = STATUS_FAILED;
 246         }
 247 
 248         while (debug_agent_started != JNI_TRUE) {
 249             NSK_DISPLAY1(">>> [ThreadStart hook] waiting %dms for agent thread to start\n", WAIT_TIME);
 250 
 251             if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorWait(agent_start_lock, (jlong)WAIT_TIME))) {
 252                 NSK_COMPLAIN0("[ThreadStart hook] Failed to wait for agent_start_lock\n");
 253                 result = STATUS_FAILED;
 254             }
 255         }
 256 
 257         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorExit(agent_start_lock))) {
 258             NSK_COMPLAIN0("[ThreadStart hook] Failed to release agent_start_lock\n");
 259             result = STATUS_FAILED;
 260         }
 261     }
 262 
 263 
 264     /* get JVMTI phase */
 265     if (!NSK_JVMTI_VERIFY(jvmti_env->GetPhase(&phase))) {
 266         NSK_COMPLAIN0("[ThreadStart hook] Failed to get JVMTI phase\n");
 267         result = STATUS_FAILED;
 268     }
 269 
 270     /* Acquire event lock,
 271      * so only one StartThread callback could be proceeded at the time
 272      */
 273     if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorEnter(thr_event_lock))) {
 274         NSK_COMPLAIN0("[ThreadStart hook] Failed to acquire thr_event_lock\n");
 275         result = STATUS_FAILED;
 276     }
 277 
 278     {
 279         /* Get thread name */
 280         inf.name = (char*) "UNKNOWN";
 281         if (phase == JVMTI_PHASE_LIVE) {
 282             /* GetThreadInfo may only be called during the live phase */
 283             if (!NSK_JVMTI_VERIFY(jvmti_env->GetThreadInfo(thread, &inf))) {
 284                 NSK_COMPLAIN1("[ThreadStart hook] Failed to get thread infor for thread#%d\n", eventsCount);
 285                 result = STATUS_FAILED;
 286             }
 287         }
 288 
 289         NSK_DISPLAY2(">>> [ThreadStart hook] thread#%d: %s\n", eventsCount, inf.name);
 290 
 291         /* Acquire thr_start_lock */
 292         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorEnter(thr_start_lock))) {
 293             NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to acquire thr_start_lock\n", eventsCount);
 294             result = STATUS_FAILED;
 295         }
 296 
 297             /* Acquire thr_resume_lock before we release thr_start_lock to prevent
 298              * debug agent from notifying us before we are ready.
 299          */
 300         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorEnter(thr_resume_lock))) {
 301             NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to acquire thr_resume_lock\n", eventsCount);
 302             result = STATUS_FAILED;
 303         }
 304 
 305         /* Store thread */
 306         next_thread = env->NewGlobalRef(thread);
 307         debug_agent_timed_out = JNI_TRUE;
 308 
 309         /* Notify agent thread about new started thread and let agent thread to work with it */
 310         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorNotify(thr_start_lock))) {
 311             NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to notify about thr_start_lock\n", eventsCount);
 312             result = STATUS_FAILED;
 313         }
 314 
 315         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorExit(thr_start_lock))) {
 316             NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to release thr_start_lock\n", eventsCount);
 317             result = STATUS_FAILED;
 318         }
 319 
 320         /* Wait till this started thread will be resumed by agent thread */
 321         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorWait(thr_resume_lock, (jlong)WAIT_TIME ))) {
 322             NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed while waiting for thr_resume_lock\n", eventsCount);
 323             result = STATUS_FAILED;
 324         }
 325 
 326         if (debug_agent_timed_out == JNI_TRUE) {
 327             NSK_COMPLAIN1("[ThreadStart hook] \"%s\": debug agent timed out\n", inf.name);
 328             env->FatalError("[ThreadStart hook] could not recover");
 329         }
 330 
 331         /* Release thr_resume_lock lock */
 332         if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorExit(thr_resume_lock))) {
 333             NSK_COMPLAIN1("[ThreadStart hook] thread#%d failed to release thr_resume_lock\n", eventsCount);
 334             result = STATUS_FAILED;
 335         }
 336 
 337         /* check that thread is not in SUSPENDED state */
 338         if (!NSK_JVMTI_VERIFY(jvmti_env->GetThreadState(thread, &thrStat))) {
 339             NSK_COMPLAIN1("[ThreadStart hook] Failed to get thread state for thread#%d\n", eventsCount);
 340             result = STATUS_FAILED;
 341         }
 342 
 343         NSK_DISPLAY2(">>> [ThreadStart hook] threadState=%s (%x)\n",
 344                 TranslateState(thrStat), thrStat);
 345 
 346         if (thrStat & JVMTI_THREAD_STATE_SUSPENDED) {
 347             NSK_COMPLAIN1("[ThreadStart hook] \"%s\" was self-suspended\n", inf.name);
 348             env->FatalError("[ThreadStart hook] could not recover");
 349         }
 350 
 351         eventsCount++;
 352     }
 353 
 354     if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorExit(thr_event_lock))) {
 355         NSK_COMPLAIN0("[ThreadStart hook] Failed to release thr_event_lock\n");
 356         result = STATUS_FAILED;
 357     }
 358 
 359     NSK_DISPLAY0(">>> [ThreadStart hook] end\n");
 360 }
 361 
 362 void JNICALL VMInit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thr) {
 363     jclass cls = NULL;
 364     jmethodID mid = NULL;
 365 
 366     NSK_DISPLAY0(">>> VMInit event: start\n");
 367 
 368     if (!NSK_JVMTI_VERIFY(jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, NULL))) {
 369         NSK_COMPLAIN0("TEST FAILED: failed to enable JVMTI_EVENT_THREAD_START\n");
 370         return;
 371     }
 372 
 373     /* Start agent thread */
 374     if (!NSK_VERIFY((cls = env->FindClass("java/lang/Thread")) != NULL)) {
 375         result = STATUS_FAILED;
 376             NSK_COMPLAIN0("TEST FAILED: Cannot start agent thread: FindClass() failed\n");
 377         return;
 378     }
 379 
 380 
 381     if (!NSK_VERIFY((mid = env->GetMethodID(cls, "<init>", "()V")) != NULL)) {
 382         result = STATUS_FAILED;
 383             NSK_COMPLAIN0("TEST FAILED: Cannot start agent thread: GetMethodID() failed\n");
 384         return;
 385     }
 386 
 387 
 388     if (!NSK_VERIFY((agent_thread = env->NewObject(cls, mid)) != NULL)) {
 389         result = STATUS_FAILED;
 390             NSK_COMPLAIN0("Cannot start agent thread: NewObject() failed\n");
 391         return;
 392     }
 393 
 394     agent_thread = (jthread) env->NewGlobalRef(agent_thread);
 395     if (agent_thread == NULL) {
 396         result = STATUS_FAILED;
 397         NSK_COMPLAIN0("Cannot create global reference for agent_thread\n");
 398         return;
 399     }
 400 
 401     /*
 402      * Grab agent_start_lock before launching debug_agent to prevent
 403      * debug_agent from notifying us before we are ready.
 404      */
 405 
 406     if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorEnter(agent_start_lock))) {
 407         result = STATUS_FAILED;
 408         NSK_COMPLAIN0("TEST FAILED: failed to enter agent_start_lock\n");
 409     }
 410 
 411     if (!NSK_JVMTI_VERIFY(jvmti_env->RunAgentThread(agent_thread, debug_agent, NULL, JVMTI_THREAD_NORM_PRIORITY))) {
 412         result = STATUS_FAILED;
 413         NSK_COMPLAIN0("TEST FAILED: failed to create agent thread\n");
 414     }
 415 
 416     if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorWait(agent_start_lock, (jlong)0))) {
 417         result = STATUS_FAILED;
 418         NSK_COMPLAIN0("TEST FAILED: failed to wait agent_start_lock\n");
 419     }
 420 
 421     if (!NSK_JVMTI_VERIFY(jvmti_env->RawMonitorExit(agent_start_lock))) {
 422         result = STATUS_FAILED;
 423         NSK_COMPLAIN0("TEST FAILED: failed to exit agent_start_lock\n");
 424     }
 425 
 426     NSK_DISPLAY0(">>> VMInit event: end\n");
 427 }
 428 
 429 void JNICALL VMDeath(jvmtiEnv *jvmti_env, JNIEnv *env) {
 430     NSK_DISPLAY0(">>> VMDeath event\n");
 431 
 432     terminate_debug_agent = JNI_TRUE;
 433 }
 434 
 435 #ifdef STATIC_BUILD
 436 JNIEXPORT jint JNICALL Agent_OnLoad_threadstart002(JavaVM *jvm, char *options, void *reserved) {
 437     return Agent_Initialize(jvm, options, reserved);
 438 }
 439 JNIEXPORT jint JNICALL Agent_OnAttach_threadstart002(JavaVM *jvm, char *options, void *reserved) {
 440     return Agent_Initialize(jvm, options, reserved);
 441 }
 442 JNIEXPORT jint JNI_OnLoad_threadstart002(JavaVM *jvm, char *options, void *reserved) {
 443     return JNI_VERSION_1_8;
 444 }
 445 #endif
 446 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 447 
 448     /* init framework and parse options */
 449     if (!NSK_VERIFY(nsk_jvmti_parseOptions(options)))
 450         return JNI_ERR;
 451 
 452     /* create JVMTI environment */
 453     if (!NSK_VERIFY((jvmti =
 454             nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL)) {
 455         NSK_COMPLAIN0("TEST FAILED: failed to create JVMTIEnv\n");
 456         return JNI_ERR;
 457     }
 458 
 459     if (!NSK_JVMTI_VERIFY(jvmti->GetPotentialCapabilities(&caps))) {
 460         NSK_COMPLAIN0("TEST FAILED: failed to get potential capabilities\n");
 461         return JNI_ERR;
 462     }
 463 
 464     if (!NSK_JVMTI_VERIFY(jvmti->AddCapabilities(&caps))) {
 465         NSK_COMPLAIN0("TEST FAILED: failed to add capabilities during agent load\n");
 466         return JNI_ERR;
 467     }
 468 
 469     if (!NSK_JVMTI_VERIFY(jvmti->GetCapabilities(&caps))) {
 470         NSK_COMPLAIN0("TEST FAILED: failed to get capabilities\n");
 471         return JNI_ERR;
 472     }
 473 
 474     if (!caps.can_suspend) {
 475         NSK_DISPLAY0("WARNING: suspend/resume is not implemented\n");
 476     }
 477 
 478     /* create raw monitors */
 479     if (!NSK_JVMTI_VERIFY(jvmti->CreateRawMonitor("_agent_start_lock", &agent_start_lock))) {
 480         NSK_COMPLAIN0("TEST FAILED: failed to create agent_start_lock\n");
 481         return JNI_ERR;
 482     }
 483 
 484     if (!NSK_JVMTI_VERIFY(jvmti->CreateRawMonitor("_thr_event_lock", &thr_event_lock))) {
 485         NSK_COMPLAIN0("TEST FAILED: failed to create thr_event_lock\n");
 486         return JNI_ERR;
 487     }
 488 
 489     if (!NSK_JVMTI_VERIFY(jvmti->CreateRawMonitor("_thr_start_lock", &thr_start_lock))) {
 490         NSK_COMPLAIN0("TEST FAILED: failed to create thr_start_lock\n");
 491         return JNI_ERR;
 492     }
 493 
 494     if (!NSK_JVMTI_VERIFY(jvmti->CreateRawMonitor("_thr_resume_lock", &thr_resume_lock))) {
 495         NSK_COMPLAIN0("TEST FAILED: failed to create thr_resume_lock\n");
 496         return JNI_ERR;
 497     }
 498 
 499     callbacks.VMInit = &VMInit;
 500     callbacks.VMDeath = &VMDeath;
 501     callbacks.ThreadStart = &ThreadStart;
 502 
 503     if (!NSK_JVMTI_VERIFY(jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)))) {
 504         NSK_COMPLAIN0("TEST FAILED: failed to set event callbacks\n");
 505         return JNI_ERR;
 506     }
 507 
 508     if (!NSK_JVMTI_VERIFY(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL))) {
 509         NSK_COMPLAIN0("TEST FAILED: failed to enable JVMTI_EVENT_VM_INIT\n");
 510         return JNI_ERR;
 511     }
 512 
 513     if (!NSK_JVMTI_VERIFY(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL))) {
 514         NSK_COMPLAIN0("TEST FAILED: failed to enable JVMTI_EVENT_VM_DEATH\n");
 515         return JNI_ERR;
 516     }
 517 
 518     return JNI_OK;
 519 }
 520 
 521 JNIEXPORT jint JNICALL
 522 Java_nsk_jvmti_ThreadStart_threadstart002_check(JNIEnv *env, jclass cls) {
 523     if (eventsCount == 0) {
 524         NSK_COMPLAIN0("None of thread start events!\n");
 525         result = STATUS_FAILED;
 526     }
 527 
 528     NSK_DISPLAY1(">>> total of thread start events: %d\n", eventsCount);
 529 
 530     return result;
 531 }
 532 
 533 }