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