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 }