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 AGENTS 2
  64 
  65 static JavaVM *vm;
  66 
  67 static jvmtiEnv *jvmti[AGENTS]; /* JVMTI env of an agent */
  68 static void *agentThr[AGENTS];
  69 static volatile int redir[AGENTS]; /* redirection in an agent done */
  70 static volatile int thrstarted[AGENTS]; /* an agent started */
  71 
  72 static volatile int verbose = 0;
  73 
  74 static volatile jint result = PASSED;
  75 
  76 /* the original JNI function table */
  77 static jniNativeInterface *orig_jni_functions[AGENTS];
  78 
  79 /* the redirected JNI function table */
  80 static jniNativeInterface *redir_jni_functions[AGENTS];
  81 
  82 /* number of the redirected JNI function calls */
  83 static volatile int redir_calls[AGENTS];
  84 
  85 static void doRedirect(JNIEnv*, jvmtiEnv*, int);
  86 static void provokeIntercept(JNIEnv*, const char*);
  87 static int checkIntercept(int, int, int);
  88 static int initAgent(int);
  89 static void startAgent(int);
  90 static int agentA(void*);
  91 static int agentB(void*);
  92 static void JNICALL VMInitA(jvmtiEnv*, JNIEnv*, jthread);
  93 static void JNICALL VMInitB(jvmtiEnv*, JNIEnv*, jthread);
  94 
  95 /** redirected JNI functions **/
  96 /* function redirected inside the agent A */
  97 jint JNICALL MyGetVersionA(JNIEnv *env) {
  98     redir_calls[0]++;
  99 
 100     NSK_DISPLAY1("\nMyGetVersionA: the function called successfully: number of calls=%d\n",
 101         redir_calls[0]);
 102 
 103     return orig_jni_functions[0]->GetVersion(
 104         JNI_ENV_ARG1(env));
 105 }
 106 
 107 /* function redirected inside the agent B */
 108 jint JNICALL MyGetVersionB(JNIEnv *env) {
 109     redir_calls[1]++;
 110 
 111     NSK_DISPLAY1("\nMyGetVersionB: the function called successfully: number of calls=%d\n",
 112         redir_calls[1]);
 113 
 114     return (orig_jni_functions[1])->GetVersion(
 115         JNI_ENV_ARG1(env));
 116 }
 117 /*****************************/
 118 
 119 static void doRedirect(JNIEnv *env, jvmtiEnv *jvmti, int indx) {
 120     jvmtiError err;
 121 
 122     NSK_DISPLAY1("\n%s JVMTI env: doRedirect: obtaining the JNI function table ...\n",
 123         (indx==0)?"first":"second");
 124     if ((err = (*jvmti)->GetJNIFunctionTable(jvmti, &orig_jni_functions[indx])) !=
 125             JVMTI_ERROR_NONE) {
 126         result = STATUS_FAILED;
 127         NSK_COMPLAIN2("TEST FAILED: %s JVMTI env: failed to get original JNI function table: %s\n",
 128             (indx==0)?"first":"second", TranslateError(err));
 129         JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env,
 130             "failed to get original JNI function table"));
 131     }
 132     if ((err = (*jvmti)->GetJNIFunctionTable(jvmti, &redir_jni_functions[indx])) !=
 133             JVMTI_ERROR_NONE) {
 134         result = STATUS_FAILED;
 135         NSK_COMPLAIN2("TEST FAILED: %s JVMTI env: failed to get redirected JNI function table: %s\n",
 136             (indx==0)?"first":"second", TranslateError(err));
 137         JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env,
 138             "failed to get redirected JNI function table"));
 139     }
 140 
 141     NSK_DISPLAY1("%s JVMTI env: doRedirect: the JNI function table obtained successfully\n\
 142 \toverwriting the function GetVersion() ...\n",
 143         (indx==0)?"first":"second");
 144     redir_jni_functions[indx]->GetVersion =
 145         (indx==0)?MyGetVersionA:MyGetVersionB;
 146 
 147     if ((err = (*jvmti)->SetJNIFunctionTable(jvmti, redir_jni_functions[indx])) !=
 148             JVMTI_ERROR_NONE) {
 149         result = STATUS_FAILED;
 150         NSK_COMPLAIN2("TEST FAILED: %s JVMTI env: failed to set new JNI function table: %s\n",
 151             (indx==0)?"first":"second", TranslateError(err));
 152         JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG(env,
 153             "failed to set new JNI function table"));
 154     }
 155 
 156     NSK_DISPLAY1("%s JVMTI env: doRedirect: the functions are overwritten successfully\n",
 157         (indx==0)?"first":"second");
 158 }
 159 
 160 static void provokeIntercept(JNIEnv *env, const char *name) {
 161     jint res;
 162 
 163     res = JNI_ENV_PTR(env)->
 164         GetVersion(JNI_ENV_ARG1(env));
 165     NSK_DISPLAY2("\nGetVersion() called by the agent %s returns %d\n",
 166         name, res);
 167 }
 168 
 169 static int checkIntercept(int indx, int env_num, int exCalls) {
 170     if (redir_calls[indx] == exCalls) {
 171         NSK_DISPLAY5("\nCHECK PASSED: GetVersion() interception set in the %s JVMTI env %s properly:\n\
 172 \t%d interception(s) with the%s%s JVMTI env as expected\n",
 173             (indx==0)?"first":"second",
 174             (exCalls==0)?"overwritten by another environment":"works",
 175             redir_calls[indx],
 176             (indx==env_num)?" same ":" ",
 177             (env_num==0)?"first":"second");
 178     }
 179     else {
 180         result = STATUS_FAILED;
 181         NSK_COMPLAIN6("\nTEST FAILED: GetVersion() interception set in the %s JVMTI env doesn't %s properly:\n\
 182 \t%d interception(s) with the%s%s JVMTI env instead of %d as expected\n",
 183             (indx==0)?"first":"second",
 184             (exCalls==0)?"overwritten by another environment":"work",
 185             redir_calls[indx],
 186             (indx==env_num)?" same ":" ",
 187             (env_num==0)?"first":"second",
 188             exCalls);
 189         return STATUS_FAILED;
 190     }
 191 
 192     return PASSED;
 193 }
 194 
 195 static int initAgent(int indx) {
 196     jvmtiEventCallbacks callbacks; /* callback functions */
 197     int exitCode = PASSED;
 198     jvmtiError err;
 199     jint res;
 200 
 201     thrstarted[indx] = redir[indx] = redir_calls[indx] = 0;
 202 
 203     NSK_DISPLAY1("\nagent %s initializer: obtaining the JVMTI env ...\n",
 204         (indx==0)?"A":"B");
 205     res = JNI_ENV_PTR(vm)->
 206         GetEnv(JNI_ENV_ARG(vm, (void **) &jvmti[indx]), JVMTI_VERSION_1_1);
 207     if (res != JNI_OK || jvmti[indx] == NULL) {
 208         NSK_COMPLAIN1("TEST FAILURE: failed to call GetEnv for the agent %s\n",
 209             (indx==0)?"A":"B");
 210         result = STATUS_FAILED;
 211         return STATUS_FAILED;
 212     }
 213 
 214     NSK_DISPLAY1("\nagent %s initializer: the JVMTI env obtained\n\tsetting event callbacks ...\n",
 215         (indx==0)?"A":"B");
 216     (void) memset(&callbacks, 0, sizeof(callbacks));
 217     switch(indx) {
 218     case 0:
 219         callbacks.VMInit = &VMInitA;
 220         break;
 221     case 1:
 222         callbacks.VMInit = &VMInitB;
 223         break;
 224     }
 225     if ((err = (*jvmti[indx])->SetEventCallbacks(jvmti[indx],
 226             &callbacks, sizeof(callbacks))) != JVMTI_ERROR_NONE) {
 227         NSK_COMPLAIN1("TEST FAILURE: failed to set event callbacks: %s\n",
 228             TranslateError(err));
 229         result = STATUS_FAILED;
 230         return STATUS_FAILED;
 231     }
 232 
 233     NSK_DISPLAY1("\nagent %s initializer: setting event callbacks done\n\tenabling events ...\n",
 234         (indx==0)?"A":"B");
 235     if ((err = (*jvmti[indx])->SetEventNotificationMode(jvmti[indx],
 236             JVMTI_ENABLE,
 237             JVMTI_EVENT_VM_INIT, NULL)) != JVMTI_ERROR_NONE) { /* enable event globally */
 238         NSK_COMPLAIN2("TEST FAILURE: failed to enable JVMTI_EVENT_VM_INIT event for the agent %s: %s\n",
 239             (indx==0)?"A":"B", TranslateError(err));
 240         result = STATUS_FAILED;
 241         return STATUS_FAILED;
 242     }
 243     NSK_DISPLAY2("\nagent %s initializer: enabling events done, returning exit code %d\n",
 244         (indx==0)?"A":"B", exitCode);
 245 
 246     return exitCode;
 247 }
 248 
 249 static void startAgent(int indx) {
 250     int tries = 0;
 251 
 252     NSK_DISPLAY1("\nstartAgent: starting agent %s thread ...\n",
 253         (indx==0)?"A":"B");
 254     agentThr[indx] = THREAD_new((indx==0)?agentA:agentB,
 255         (indx==0)?"agent A":"agent B");
 256     if (THREAD_start(agentThr[indx]) == NULL) {
 257         NSK_COMPLAIN1("TEST FAILURE: cannot start the agent %s thread\n",
 258             (indx==0)?"A":"B");
 259         exit(STATUS_FAILED);
 260     }
 261 
 262     NSK_DISPLAY1("\nstartAgent: waiting for the agent %s to be started ...\n",
 263         (indx==0)?"A":"B");
 264     do {
 265         THREAD_sleep(1);
 266         tries++;
 267         if (tries > TRIES) {
 268             NSK_COMPLAIN2("TEST FAILURE: the agent %s thread is still not started after %d attempts\n",
 269                 (indx==0)?"A":"B", TRIES);
 270             exit(STATUS_FAILED);
 271         }
 272     } while(thrstarted[indx] != 1);
 273 
 274     NSK_DISPLAY1("\nstartAgent: the agent %s thread started\n",
 275         (indx==0)?"A":"B");
 276 }
 277 
 278 /* agent thread procedures */
 279 static int agentA(void *context) {
 280     JNIEnv *env;
 281     jint res;
 282     int tries = 0;
 283     int i;
 284     int exitCode = PASSED;
 285 
 286     NSK_DISPLAY0("\nthe agent A started\n\tattaching the thread to the VM ...\n");
 287     if ((res =
 288             JNI_ENV_PTR(vm)->AttachCurrentThread(
 289                 JNI_ENV_ARG(vm, (void **) &env), (void *) 0)) != 0) {
 290         NSK_COMPLAIN1("TEST FAILURE: AttachCurrentThread() returns: %d\n",
 291             res);
 292         exit(STATUS_FAILED);
 293     }
 294 
 295     /* intercept the JNI function table */
 296     /* check the interception set in another JVMTI env */
 297     NSK_DISPLAY0("\n>>> TEST CASE #1) First JVMTI env: checking the redirection set in the same env ...\n\
 298 \nagent A (first JVMTI env): redirecting the function table ...\n");
 299     doRedirect(env, jvmti[0], 0);
 300 
 301     /* check that the interception has been set properly */
 302     NSK_DISPLAY0("\nagent A (first JVMTI env): checking that the interception has been set properly ...\n");
 303     provokeIntercept(env, "A");
 304     checkIntercept(0, 0, 1); /* expected interceptions: 1 */
 305     NSK_DISPLAY0("\n<<< TEST CASE #1) done\n");
 306 
 307     /* the flag set too late in order to make sure that
 308        the agent B will be started _after_ the interception */
 309     thrstarted[0] = 1;
 310 
 311     redir[0] = 1;
 312 
 313     NSK_DISPLAY0("\nagent A: waiting for the redirection in agent B ...\n");
 314     do {
 315         THREAD_sleep(1);
 316         tries++;
 317         if (tries > TRIES) {
 318             NSK_COMPLAIN1("TEST FAILURE: failed to wait for the redirection in agent B after %d attempts\n",
 319                 TRIES);
 320             if ((res = JNI_ENV_PTR(vm)->DetachCurrentThread(JNI_ENV_ARG1(vm))) != 0) {
 321                 NSK_COMPLAIN1("TEST WARNING: agent A: DetachCurrentThread() returns: %d\n", res);
 322             }
 323             exit(STATUS_FAILED);
 324         }
 325     } while(redir[1] != 1);
 326 
 327     /* check the interception set in another JVMTI env */
 328     NSK_DISPLAY0("\n>>> TEST CASE #4) First JVMTI env: checking the redirection set in second JVMTI env ...\n");
 329     for (i=0; i<AGENTS; i++) {
 330         redir_calls[i] = 0;
 331     }
 332     provokeIntercept(env, "A");
 333     /* check that the previous interception has been overwritten */
 334     checkIntercept(0, 0, 1); /* expected interceptions: 1 */
 335     /* check the current interception set in another JVMTI env */
 336     checkIntercept(1, 0, 1); /* expected interceptions: 1 */
 337     NSK_DISPLAY0("\n<<< TEST CASE #4) done\n");
 338 
 339     NSK_DISPLAY1("\nagent A: detaching and returning exit code %d\n",
 340         exitCode);
 341     if ((res = JNI_ENV_PTR(vm)->DetachCurrentThread(JNI_ENV_ARG1(vm))) != 0) {
 342         NSK_COMPLAIN1("TEST WARNING: agent A: DetachCurrentThread() returns: %d\n", res);
 343     }
 344     return exitCode;
 345 }
 346 
 347 static int agentB(void *context) {
 348     JNIEnv *env;
 349     jint res;
 350     int tries = 0;
 351     int i;
 352     int exitCode = PASSED;
 353 
 354     NSK_DISPLAY0("\nthe agent B started\n\tattaching the thread to the VM ...\n");
 355     if ((res =
 356             JNI_ENV_PTR(vm)->AttachCurrentThread(
 357                 JNI_ENV_ARG(vm, (void **) &env), (void *) 0)) != 0) {
 358         NSK_COMPLAIN1("TEST FAILURE: AttachCurrentThread() returns: %d\n",
 359             res);
 360         if ((res = JNI_ENV_PTR(vm)->DetachCurrentThread(JNI_ENV_ARG1(vm))) != 0) {
 361             NSK_COMPLAIN1("TEST WARNING: agent B: DetachCurrentThread() returns: %d\n", res);
 362         }
 363         exit(STATUS_FAILED);
 364     }
 365 
 366     thrstarted[1] = 1;
 367 
 368     NSK_DISPLAY0("\nagent B: waiting for the redirection in agent A ...\n");
 369     do {
 370         THREAD_sleep(1);
 371         tries++;
 372         if (tries > TRIES) {
 373             NSK_COMPLAIN1("TEST FAILURE: failed to wait for the redirection in agent A after %d attempts\n",
 374                 TRIES);
 375             if ((res = JNI_ENV_PTR(vm)->DetachCurrentThread(JNI_ENV_ARG1(vm))) != 0) {
 376                 NSK_COMPLAIN1("TEST WARNING: agent B: DetachCurrentThread() returns: %d\n", res);
 377             }
 378             exit(STATUS_FAILED);
 379         }
 380     } while(redir[0] != 1);
 381 
 382     /* check the interception set in another JVMTI env */
 383     NSK_DISPLAY0("\n>>> TEST CASE #2) Second JVMTI env: checking the redirection set in first JVMTI env ...\n");
 384     for (i=0; i<AGENTS; i++) {
 385         redir_calls[i] = 0;
 386     }
 387     provokeIntercept(env, "B");
 388     checkIntercept(0, 1, 1); /* expected interceptions: 1 */
 389     NSK_DISPLAY0("\n<<< TEST CASE #2) done\n");
 390 
 391     /* intercept the JNI function table */
 392     NSK_DISPLAY0("\n>>> TEST CASE #3) Second JVMTI env: checking the redirection set in the same env ...\n\
 393 \nagent B (second JVMTI env): redirecting the function table ...\n");
 394     doRedirect(env, jvmti[1], 1);
 395 
 396     for (i=0; i<AGENTS; i++) {
 397         redir_calls[i] = 0;
 398     }
 399     provokeIntercept(env, "B");
 400     /* check that the previous interception has been overwritten */
 401     checkIntercept(0, 1, 1); /* expected interceptions: 1 */
 402     /* check that the current interception has been set properly */
 403     checkIntercept(1, 1, 1); /* expected interceptions: 1 */
 404     NSK_DISPLAY0("\n<<< TEST CASE #3) done\n");
 405 
 406     redir[1] = 1;
 407 
 408     NSK_DISPLAY1("\nagent B: detaching and returning exit code %d\n",
 409         exitCode);
 410     if ((res = JNI_ENV_PTR(vm)->DetachCurrentThread(JNI_ENV_ARG1(vm))) != 0) {
 411         NSK_COMPLAIN1("TEST WARNING: agent B: DetachCurrentThread() returns: %d\n", res);
 412     }
 413     return exitCode;
 414 }
 415 /*********************/
 416 
 417 /* callback functions */
 418 void JNICALL
 419 VMInitA(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread) {
 420     NSK_DISPLAY0("\nagent A: VMInit event\n");
 421 
 422     startAgent(0);
 423 }
 424 
 425 void JNICALL
 426 VMInitB(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread) {
 427     NSK_DISPLAY0("\nagent B: VMInit event\n");
 428 
 429     startAgent(1);
 430 }
 431 /*********************/
 432 
 433 JNIEXPORT jint JNICALL
 434 Java_nsk_jvmti_scenarios_jni_1interception_JI05_ji05t001_getResult(JNIEnv *env, jobject obj) {
 435     int i;
 436 
 437     for (i=0; i<AGENTS; i++) {
 438         NSK_DISPLAY1("\ngetResult: waiting for the agent %s thread...\n",
 439             (i==0)?"A":"B");
 440         THREAD_waitFor(agentThr[i]);
 441         if (THREAD_status(agentThr[i]) != PASSED) {
 442             result = STATUS_FAILED;
 443             NSK_COMPLAIN2("TEST FAILED: the agent %s thread done with the error code %d\n",
 444                 (i==0)?"A":"B", THREAD_status(agentThr[i]));
 445         }
 446         else NSK_DISPLAY2("getResult: the agent %s thread done with the code %d\n",
 447                 (i==0)?"A":"B", THREAD_status(agentThr[i]));
 448         free(agentThr[i]);
 449     }
 450 
 451     return result;
 452 }
 453 
 454 #ifdef STATIC_BUILD
 455 JNIEXPORT jint JNICALL Agent_OnLoad_ji05t001(JavaVM *jvm, char *options, void *reserved) {
 456     return Agent_Initialize(jvm, options, reserved);
 457 }
 458 JNIEXPORT jint JNICALL Agent_OnAttach_ji05t001(JavaVM *jvm, char *options, void *reserved) {
 459     return Agent_Initialize(jvm, options, reserved);
 460 }
 461 JNIEXPORT jint JNI_OnLoad_ji05t001(JavaVM *jvm, char *options, void *reserved) {
 462     return JNI_VERSION_1_8;
 463 }
 464 #endif
 465 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 466     int i;
 467 
 468     /* init framework and parse options */
 469     if (!NSK_VERIFY(nsk_jvmti_parseOptions(options)))
 470         return JNI_ERR;
 471 
 472     vm = jvm;
 473 
 474     for (i=0; i<AGENTS; i++) {
 475         NSK_DISPLAY1("initializing agent %s ...\n",
 476                 (i==0)?"A":"B");
 477         if (initAgent(i) != PASSED)
 478             return JNI_ERR;
 479     }
 480 
 481     return JNI_OK;
 482 }
 483 
 484 #ifdef __cplusplus
 485 }
 486 #endif