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 <stdarg.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 #include <jvmti.h> 30 #include "agent_common.h" 31 32 #include "nsk_tools.h" 33 #include "JVMTITools.h" 34 #include "jvmti_tools.h" 35 #include "native_thread.h" 36 37 #ifdef __cplusplus 38 extern "C" { 39 #endif 40 41 #ifndef JNI_ENV_ARG 42 #ifdef __cplusplus 43 #define JNI_ENV_ARG(x, y) y 44 #define JNI_ENV_PTR(x) x 45 #else 46 #define JNI_ENV_ARG(x, y) x, y 47 #define JNI_ENV_PTR(x) (*x) 48 #endif 49 #endif 50 51 #ifndef JNI_ENV_ARG1 52 #ifdef __cplusplus 53 #define JNI_ENV_ARG1(x) 54 #else 55 #define JNI_ENV_ARG1(x) x 56 #endif 57 #endif 58 59 #define PASSED 0 60 #define STATUS_FAILED 2 61 62 #define TRIES 30 63 #define MAX_THREADS 5 64 65 // Helper for thread detach and terminate 66 #define THREAD_return(status) \ 67 do { \ 68 int res = JNI_ENV_PTR(vm)->DetachCurrentThread(JNI_ENV_ARG1(vm)); \ 69 if (res != 0) \ 70 NSK_COMPLAIN1("TEST WARNING: DetachCurrentThread() returns: %d\n", res); \ 71 else \ 72 NSK_DISPLAY0("Detaching thread ...\n"); \ 73 return status; \ 74 } while (0) 75 76 77 static const char *javaField = "_ji06t001a"; 78 static const char *classSig = 79 "Lnsk/jvmti/scenarios/jni_interception/JI06/ji06t001a;"; 80 81 static JavaVM *vm; 82 static jvmtiEnv *jvmti = NULL; 83 84 static volatile int verbose = 0; 85 86 static volatile jint result = PASSED; 87 static volatile int monEntered = 0; /* the monitor entered */ 88 static volatile int thrStarted[MAX_THREADS]; /* a thread started */ 89 static volatile int releaseMon = 0; /* flag to release the monitor */ 90 91 static volatile jobject clsObj; 92 static jrawMonitorID countLock; 93 94 /* the original JNI function table */ 95 static jniNativeInterface *orig_jni_functions = NULL; 96 97 /* the redirected JNI function table */ 98 static jniNativeInterface *redir_jni_functions = NULL; 99 100 /* number of the redirected JNI function calls */ 101 static volatile int monent_calls = 0; 102 103 static void lock() { 104 if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorEnter, 105 jvmti, countLock))) 106 exit(STATUS_FAILED); 107 } 108 109 static void unlock() { 110 if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(RawMonitorExit, 111 jvmti, countLock))) 112 exit(STATUS_FAILED); 113 } 114 115 /** redirected JNI functions **/ 116 jint JNICALL MyMonitorEnter(JNIEnv *env, jobject obj) { 117 lock(); 118 monent_calls++; 119 unlock(); 120 121 NSK_DISPLAY1("MyMonitorEnter: the function called successfully: number of calls=%d\n", 122 monent_calls); 123 124 return orig_jni_functions->MonitorEnter( 125 JNI_ENV_ARG(env, obj)); 126 } 127 /*****************************/ 128 129 static jint enterMonitor(JNIEnv *env, const char *thr) { 130 jint result; 131 132 if ((result = JNI_ENV_PTR(env)-> 133 MonitorEnter(JNI_ENV_ARG(env, clsObj))) != 0) { 134 NSK_COMPLAIN2("TEST FAILURE: %s: MonitorEnter() returns: %d\n", 135 thr, result); 136 return STATUS_FAILED; 137 } 138 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG1(env))) { 139 NSK_COMPLAIN1("TEST FAILURE: %s: exception occured\n", 140 thr); 141 JNI_ENV_PTR(env)->ExceptionDescribe(JNI_ENV_ARG1(env)); 142 JNI_ENV_PTR(env)->ExceptionClear(JNI_ENV_ARG1(env)); 143 return STATUS_FAILED; 144 } 145 146 return PASSED; 147 } 148 149 static jint exitMonitor(JNIEnv *env, const char *thr) { 150 jint result; 151 152 if ((result = JNI_ENV_PTR(env)-> 153 MonitorExit(JNI_ENV_ARG(env, clsObj))) != 0) { 154 NSK_COMPLAIN2("TEST FAILURE: %s: MonitorExit() returns: %d\n", 155 thr, result); 156 return STATUS_FAILED; 157 } 158 159 return PASSED; 160 } 161 162 static void doRedirect(JNIEnv *env) { 163 jvmtiError err; 164 165 NSK_DISPLAY0("doRedirect: obtaining the JNI function table ...\n"); 166 if ((err = (*jvmti)->GetJNIFunctionTable(jvmti, &orig_jni_functions)) != 167 JVMTI_ERROR_NONE) { 168 result = STATUS_FAILED; 169 NSK_COMPLAIN1("TEST FAILED: failed to get original JNI function table: %s\n", 170 TranslateError(err)); 171 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 172 "failed to get original JNI function table")); 173 } 174 if ((err = (*jvmti)->GetJNIFunctionTable(jvmti, &redir_jni_functions)) != 175 JVMTI_ERROR_NONE) { 176 result = STATUS_FAILED; 177 NSK_COMPLAIN1("TEST FAILED: failed to get redirected JNI function table: %s\n", 178 TranslateError(err)); 179 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 180 "failed to get redirected JNI function table")); 181 } 182 183 NSK_DISPLAY0("doRedirect: the JNI function table obtained successfully\n\ 184 \toverwriting the function MonitorEnter ...\n"); 185 186 redir_jni_functions->MonitorEnter = MyMonitorEnter; 187 188 if ((err = (*jvmti)->SetJNIFunctionTable(jvmti, redir_jni_functions)) != 189 JVMTI_ERROR_NONE) { 190 result = STATUS_FAILED; 191 NSK_COMPLAIN1("TEST FAILED: failed to set new JNI function table: %s\n", 192 TranslateError(err)); 193 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 194 "failed to set new JNI function table")); 195 } 196 197 NSK_DISPLAY0("doRedirect: the functions are overwritten successfully\n"); 198 } 199 200 static void checkCall(int exMonEntCalls) { 201 if (monent_calls >= exMonEntCalls) { 202 NSK_DISPLAY1("CHECK PASSED: the tested JNI function MonitorEnter() has been redirected:\n\ 203 \tat least %d intercepted call(s) as expected", 204 monent_calls); 205 } 206 else { 207 result = STATUS_FAILED; 208 NSK_COMPLAIN2("TEST FAILED: the tested JNI function MonitorEnter() has not been redirected properly:\n\ 209 \tonly %d intercepted call(s) instead of at least %d as expected\n", 210 monent_calls, exMonEntCalls); 211 } 212 } 213 214 /* thread procedures */ 215 static int waitingThread(void *context) { 216 JNIEnv *env; 217 int exitCode = PASSED; 218 jint res; 219 int tries = 0; 220 /* 4932877 fix in accordance with ANSI C: thread context of type void* -> int* -> int */ 221 int indx = *((int *) context); 222 223 NSK_DISPLAY1("waitingThread: thread #%d started\n\ 224 \tattaching the thread to the VM ...\n", 225 indx); 226 if ((res = 227 JNI_ENV_PTR(vm)->AttachCurrentThread( 228 JNI_ENV_ARG(vm, (void **) &env), (void *) 0)) != 0) { 229 NSK_COMPLAIN1("TEST FAILURE: waitingThread: AttachCurrentThread() returns: %d\n", 230 res); 231 return STATUS_FAILED; 232 } 233 234 NSK_DISPLAY1("waitingThread: thread #%d is trying to enter the monitor ...\n", 235 indx); 236 237 thrStarted[indx-1] = 1; /* the thread is started */ 238 239 if (enterMonitor(env, "waitingThread") == STATUS_FAILED) 240 THREAD_return(STATUS_FAILED); 241 if (verbose) 242 printf("waitingThread: thread #%d entered the monitor\n", 243 indx); 244 if (exitMonitor(env, "waitingThread") == STATUS_FAILED) 245 THREAD_return(STATUS_FAILED); 246 247 NSK_DISPLAY2("waitingThread: thread #%d exits the monitor\n\treturning %d\n", 248 indx, exitCode); 249 THREAD_return(exitCode); 250 } 251 252 static int ownerThread(void *context) { 253 JNIEnv *env; 254 int exitCode = PASSED; 255 jint res; 256 int tries = 0; 257 258 NSK_DISPLAY0("ownerThread: thread started\n\tattaching the thread to the VM ...\n"); 259 if ((res = 260 JNI_ENV_PTR(vm)->AttachCurrentThread( 261 JNI_ENV_ARG(vm, (void **) &env), (void *) 0)) != 0) { 262 NSK_COMPLAIN1("TEST FAILURE: ownerThread: AttachCurrentThread() returns: %d\n", 263 res); 264 return STATUS_FAILED; 265 } 266 267 NSK_DISPLAY0("ownerThread: trying to enter the monitor ...\n"); 268 if (enterMonitor(env, "ownerThread") == STATUS_FAILED) 269 THREAD_return(STATUS_FAILED); 270 271 monEntered = 1; /* the monitor has been entered */ 272 NSK_DISPLAY1("ownerThread: entered the monitor: monEntered=%d\n\ 273 \twaiting ...\n", 274 monEntered); 275 do { 276 THREAD_sleep(1); 277 tries++; 278 if (tries > TRIES) { 279 NSK_COMPLAIN1("TEST FAILED: ownerThread: time exceed after %d attempts\n", 280 TRIES); 281 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 282 "ownerThread: time exceed")); 283 } 284 } while(releaseMon != 1); 285 286 if (exitMonitor(env, "ownerThread") == STATUS_FAILED) 287 THREAD_return(STATUS_FAILED); 288 289 NSK_DISPLAY1("ownerThread: exits the monitor\n\treturning %d\n", 290 exitCode); 291 292 THREAD_return(exitCode); 293 } 294 295 static int redirectorThread(void *context) { 296 JNIEnv *env; 297 int exitCode = PASSED; 298 jint res; 299 int tries = 0; 300 301 NSK_DISPLAY0("redirectorThread: thread started\n\tattaching the thread to the VM ...\n"); 302 if ((res = 303 JNI_ENV_PTR(vm)->AttachCurrentThread( 304 JNI_ENV_ARG(vm, (void **) &env), (void *) 0)) != 0) { 305 NSK_COMPLAIN1("TEST FAILURE: redirectorThread: AttachCurrentThread() returns: %d\n", 306 res); 307 return STATUS_FAILED; 308 } 309 310 NSK_DISPLAY0("redirectorThread: trying to redirect the MonitorEnter() ...\n"); 311 doRedirect(env); 312 313 NSK_DISPLAY1("redirectorThread: the MonitorEnter() redirected\n\treturning %d\n", 314 exitCode); 315 316 THREAD_return(exitCode); 317 } 318 /*********************/ 319 320 static jobject getObjectFromField(JNIEnv *env, jobject obj) { 321 jfieldID fid; 322 jclass _objCls; 323 324 _objCls = JNI_ENV_PTR(env)->GetObjectClass(JNI_ENV_ARG(env, obj)); 325 326 NSK_DISPLAY2("getObjectFromField: obtaining field ID for name=\"%s\" signature=\"%s\"...\n", 327 javaField, classSig); 328 if ((fid = JNI_ENV_PTR(env)->GetFieldID( 329 JNI_ENV_ARG(env, _objCls), javaField, classSig)) == 0) { 330 result = STATUS_FAILED; 331 NSK_COMPLAIN1("TEST FAILURE: failed to get ID for the field \"%s\"\n", 332 javaField); 333 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 334 "failed to get ID for the java field")); 335 } 336 337 return JNI_ENV_PTR(env)->GetObjectField( 338 JNI_ENV_ARG(env, obj), fid); 339 } 340 341 JNIEXPORT jint JNICALL 342 Java_nsk_jvmti_scenarios_jni_1interception_JI06_ji06t001_check(JNIEnv *env, jobject obj) { 343 char *ownContext = "ownerThr"; 344 char *redirContext = "redirectorThr"; 345 int exitCode = PASSED; 346 void *ownThr = NULL; 347 void *redirThr = NULL; 348 void *waitThr[MAX_THREADS]; 349 int waitContElem[MAX_THREADS]; /* context of a particular waiting thread */ 350 int i; 351 int tries = 0; 352 353 if (jvmti == NULL) { 354 NSK_COMPLAIN0("TEST FAILURE: JVMTI client was not properly loaded\n"); 355 return STATUS_FAILED; 356 } 357 358 /* prepare the testing */ 359 if ((clsObj = JNI_ENV_PTR(env)->NewGlobalRef( 360 JNI_ENV_ARG(env, getObjectFromField(env, obj)))) == NULL) { 361 NSK_COMPLAIN1("TEST FAILURE: cannot create a new global reference of class \"%s\"\n", 362 classSig); 363 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 364 "failed to create a new global reference")); 365 } 366 367 NSK_DISPLAY0("starting monitor owner thread ...\n"); 368 ownThr = THREAD_new(ownerThread, ownContext); 369 if (THREAD_start(ownThr) == NULL) { 370 NSK_COMPLAIN0("TEST FAILURE: cannot start monitor owner thread\n"); 371 exit(STATUS_FAILED); 372 } 373 374 NSK_DISPLAY0("waiting for the monitor to be entered ...\n"); 375 do { 376 THREAD_sleep(1); 377 tries++; 378 if (tries > TRIES) { 379 NSK_COMPLAIN1("TEST FAILURE: the monitor is still not entered by the owner thread after %d attempts\n", 380 TRIES); 381 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env, 382 " the monitor is still not entered by the owner thread")); 383 } 384 } while(monEntered != 1); 385 386 for (i=0; i<MAX_THREADS-1; i++) { 387 NSK_DISPLAY1("starting waiting thread #%d ...\n", 388 i+1); 389 thrStarted[i] = 0; 390 waitContElem[i] = i+1; 391 /* 4932877 fix in accordance with ANSI C: thread context of type int -> int* -> void* */ 392 waitThr[i] = THREAD_new(waitingThread, (void *) &(waitContElem[i])); 393 if (THREAD_start(waitThr[i]) == NULL) { 394 NSK_COMPLAIN1("TEST FAILURE: cannot start waiting thread #%d\n", 395 i+1); 396 exit(STATUS_FAILED); 397 } 398 399 tries = 0; 400 do { 401 THREAD_sleep(1); 402 tries++; 403 if (tries > TRIES) { 404 NSK_COMPLAIN1("TEST FAILURE: waiting thread #%d is still not started\n", 405 i+1); 406 exit(STATUS_FAILED); 407 } 408 } while(thrStarted[i] != 1); 409 NSK_DISPLAY1("the waiting thread #%d started\n", 410 i+1); 411 } 412 413 /* begin the testing */ 414 NSK_DISPLAY0(">>> TEST CASE a) Trying to redirect the JNI function ...\n\ 415 \nstarting redirector thread ...\n"); 416 redirThr = THREAD_new(redirectorThread, redirContext); 417 if (THREAD_start(redirThr) == NULL) { 418 NSK_COMPLAIN0("TEST FAILURE: cannot start redirector thread\n"); 419 exit(STATUS_FAILED); 420 } 421 422 NSK_DISPLAY0("waiting for the redirector thread ...\n"); 423 THREAD_waitFor(redirThr); 424 if (THREAD_status(redirThr) != PASSED) 425 exitCode = result = STATUS_FAILED; 426 if (exitCode == STATUS_FAILED) 427 NSK_COMPLAIN1("the redirector thread done with the code %d\n", 428 THREAD_status(redirThr)); 429 else 430 NSK_DISPLAY1("the redirector thread done with the code %d\n", 431 THREAD_status(redirThr)); 432 free(redirThr); 433 434 releaseMon = 1; 435 436 NSK_DISPLAY0("waiting for the monitor owner thread ...\n"); 437 THREAD_waitFor(ownThr); 438 if (THREAD_status(ownThr) != PASSED) 439 exitCode = result = STATUS_FAILED; 440 if (exitCode == STATUS_FAILED) 441 NSK_COMPLAIN1("the monitor owner thread done with the code %d\n", 442 THREAD_status(ownThr)); 443 else 444 NSK_DISPLAY1("the monitor owner thread done with the code %d\n", 445 THREAD_status(ownThr)); 446 free(ownThr); 447 NSK_DISPLAY0("<<<\n\n"); 448 449 /* verification of the interception */ 450 NSK_DISPLAY0(">>> TEST CASE b) Exercising the interception ...\n\ 451 \nmain thread: trying to enter the monitor ...\n"); 452 if (enterMonitor(env, "mainThread") == STATUS_FAILED) 453 exitCode = STATUS_FAILED; 454 NSK_DISPLAY0("main thread: entered the monitor\n"); 455 if (exitMonitor(env, "mainThread") == STATUS_FAILED) 456 exitCode = STATUS_FAILED; 457 NSK_DISPLAY0("main thread: exited the monitor\n"); 458 459 NSK_DISPLAY0("starting a separate verification thread ...\n"); 460 waitContElem[MAX_THREADS-1] = MAX_THREADS; 461 /* 4932877 fix in accordance with ANSI C: thread context of type int -> int* -> void* */ 462 waitThr[MAX_THREADS-1] = THREAD_new(waitingThread, 463 (void *) &(waitContElem[MAX_THREADS-1])); 464 if (THREAD_start(waitThr[MAX_THREADS-1]) == NULL) { 465 NSK_COMPLAIN0("TEST FAILURE: cannot start verification thread\n"); 466 exit(STATUS_FAILED); 467 } 468 NSK_DISPLAY0("the verification thread started\n"); 469 470 /* finish the testing */ 471 for (i=0; i<MAX_THREADS; i++) { 472 NSK_DISPLAY1("waiting for the thread #%d...\n", 473 i+1); 474 THREAD_waitFor(waitThr[i]); 475 if (THREAD_status(waitThr[i]) != PASSED) { 476 result = STATUS_FAILED; 477 NSK_COMPLAIN2("TEST FAILED: the waiting thread #%d done with the error code %d\n", 478 i+1, THREAD_status(waitThr[i])); 479 } 480 else 481 NSK_DISPLAY2("the thread #%d done with the code %d\n", 482 i+1, THREAD_status(waitThr[i])); 483 484 free(waitThr[i]); 485 } 486 487 JNI_ENV_PTR(env)->DeleteGlobalRef(JNI_ENV_ARG(env, clsObj)); 488 NSK_DISPLAY0("<<<\n\n"); 489 490 NSK_DISPLAY0(">>> TEST CASE c) Checking number of the intercepted calls ...\n"); 491 checkCall(2); 492 NSK_DISPLAY0("<<<\n\n"); 493 494 return result; 495 } 496 497 #ifdef STATIC_BUILD 498 JNIEXPORT jint JNICALL Agent_OnLoad_ji06t001(JavaVM *jvm, char *options, void *reserved) { 499 return Agent_Initialize(jvm, options, reserved); 500 } 501 JNIEXPORT jint JNICALL Agent_OnAttach_ji06t001(JavaVM *jvm, char *options, void *reserved) { 502 return Agent_Initialize(jvm, options, reserved); 503 } 504 JNIEXPORT jint JNI_OnLoad_ji06t001(JavaVM *jvm, char *options, void *reserved) { 505 return JNI_VERSION_1_8; 506 } 507 #endif 508 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 509 /* init framework and parse options */ 510 if (!NSK_VERIFY(nsk_jvmti_parseOptions(options))) 511 return JNI_ERR; 512 513 /* create JVMTI environment */ 514 if (!NSK_VERIFY((jvmti = 515 nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL)) 516 return JNI_ERR; 517 518 vm = jvm; 519 520 if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(CreateRawMonitor, 521 jvmti, "_counter_lock", &countLock))) 522 return JNI_ERR; 523 524 return JNI_OK; 525 } 526 527 #ifdef __cplusplus 528 } 529 #endif