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 }