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