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