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 }