1 /*
   2  * Copyright (c) 2007, 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 <string.h>
  26 #include "jvmti.h"
  27 #include "agent_common.h"
  28 #include "JVMTITools.h"
  29 
  30 #ifdef __cplusplus
  31 extern "C" {
  32 #endif
  33 
  34 #ifndef JNI_ENV_ARG
  35 
  36 #ifdef __cplusplus
  37 #define JNI_ENV_ARG(x, y) y
  38 #define JNI_ENV_PTR(x) x
  39 #else
  40 #define JNI_ENV_ARG(x,y) x, y
  41 #define JNI_ENV_PTR(x) (*x)
  42 #endif
  43 
  44 #endif
  45 
  46 #define PASSED 0
  47 #define STATUS_FAILED 2
  48 
  49 #define RETURN_FAILED errCode = STATUS_FAILED; fflush(0); return
  50 
  51 #define METHCNT 2
  52 static jvmtiEnv *jvmti = NULL;
  53 static jvmtiCapabilities caps;
  54 static jvmtiEventCallbacks callbacks;
  55 static jint errCode = PASSED;
  56 static jboolean printdump = JNI_TRUE;
  57 static jmethodID midCheckPoint      = NULL;
  58 static jmethodID midRun             = NULL;
  59 static jmethodID midCountDownFloat  = NULL;
  60 static jmethodID midCountDownDouble = NULL;
  61 
  62 /* There is no synchronization for the counters because
  63  * they are incremented in single threaded mode.
  64  */
  65 static jint methodExitEventCount = 0;
  66 static jint framesExpected = 0;
  67 static jint framesCount = 0;
  68 
  69 static jint methidx = 0;
  70 static const char *cls_exp = "Lnsk/jvmti/unit/ForceEarlyReturn/earlyretfp$earlyretThread;";
  71 static const char *sig_exp [METHCNT] = { "(I)D", "(I)F" };
  72 static const char *name_exp[METHCNT] = { "countDownDouble", "countDownFloat" };
  73 
  74 /* These are really initialized in getReady() */
  75 static float  ret_val_f = 0.0;
  76 static double ret_val_d = 0.0;
  77 
  78 static const char *argName = "nestingCount";
  79 
  80 void check(jvmtiEnv *jvmti_env, jthread thr, jmethodID mid,
  81            jlocation loc, jint i) {
  82     jvmtiError err;
  83     jclass cls;
  84     jlocation loc_exp = (i == 0) ? 0x16 : 0xd;
  85     char *sigClass, *name, *sig, *generic;
  86     jvmtiLocalVariableEntry *table = NULL;
  87     jint entryCount = 0;
  88     jint argValue;
  89     jint j;
  90 
  91     err = jvmti_env->GetMethodDeclaringClass(mid, &cls);
  92     if (err != JVMTI_ERROR_NONE) {
  93         printf("(GetMethodDeclaringClass#%d) unexpected error: %s (%d)\n",
  94                i, TranslateError(err), err);
  95         RETURN_FAILED;
  96     }
  97 
  98     err = jvmti_env->GetClassSignature(cls, &sigClass, &generic);
  99     if (err != JVMTI_ERROR_NONE) {
 100         printf("(GetClassSignature#%d) unexpected error: %s (%d)\n",
 101                i, TranslateError(err), err);
 102         RETURN_FAILED;
 103     }
 104 
 105     err = jvmti_env->GetMethodName(mid, &name, &sig, &generic);
 106     if (err != JVMTI_ERROR_NONE) {
 107         printf("(GetMethodName#%d) unexpected error: %s (%d)\n",
 108                i, TranslateError(err), err);
 109         RETURN_FAILED;
 110     }
 111 
 112     /* Get Local Variable Table to be able to get the argument value
 113      * from current method frame and compare it with the expected value
 114      */
 115     err = jvmti_env->GetLocalVariableTable(mid, &entryCount, &table);
 116     if (err != JVMTI_ERROR_NONE) {
 117         printf("(GetLocalVariableTable#%d) unexpected error: %s (%d)\n",
 118                i, TranslateError(err), err);
 119         RETURN_FAILED;
 120     }
 121     if (table != NULL) {
 122         for (j = 0; j < entryCount; j++) {
 123             if (strcmp(table[j].name, argName) == 0) {
 124                 err = jvmti_env->GetLocalInt(thr, 0,
 125                     table[j].slot, &argValue);
 126                 if (err != JVMTI_ERROR_NONE) {
 127                     printf("(GetLocalInt#%d) unexpected error: %s (%d)\n",
 128                            i, TranslateError(err), err);
 129                     RETURN_FAILED;
 130                 }
 131             }
 132         }
 133     }
 134 
 135     if (printdump == JNI_TRUE) {
 136         printf("\n>>> step %d: \"%s.%s%s\"\n", i, sigClass, name, sig);
 137         printf(">>>   location: 0x%x%08x", (jint)(loc >> 32), (jint)loc);
 138         printf(", arg value: %d\n", argValue);
 139     }
 140 
 141     if (sigClass == NULL || strcmp(sigClass, cls_exp) != 0) {
 142         printf("(step %d) wrong class sig: \"%s\",\n", i, sigClass);
 143         printf(" expected: \"%s\"\n", cls_exp);
 144         RETURN_FAILED;
 145     }
 146     if (name == NULL || strcmp(name, name_exp[methidx]) != 0) {
 147         printf("(step %d) wrong method name: \"%s\",", i, name);
 148         printf(" expected: \"%s\"\n", name_exp[methidx]);
 149         RETURN_FAILED;
 150     }
 151     if (sig == NULL || strcmp(sig, sig_exp[methidx]) != 0) {
 152         printf("(step %d) wrong method sig: \"%s\",", i, sig);
 153         printf(" expected: \"%s\"\n", sig_exp[methidx]);
 154         RETURN_FAILED;
 155     }
 156     methidx = (methidx + 1) % METHCNT;
 157     if (loc != loc_exp) {
 158         printf("(step %d) wrong location: 0x%x%08x,",
 159                i, (jint)(loc >> 32), (jint)loc);
 160         printf(" expected: 0x%x\n", (jint)loc_exp);
 161         RETURN_FAILED;
 162     }
 163     if (argValue != i) {
 164         printf("(step %d) wrong argument value: %d,", i, argValue);
 165         printf(" expected: %d\n", i);
 166         RETURN_FAILED;
 167     }
 168 
 169     if (sigClass != NULL) {
 170         jvmti_env->Deallocate((unsigned char*)sigClass);
 171     }
 172     if (name != NULL) {
 173         jvmti_env->Deallocate((unsigned char*)name);
 174     }
 175     if (sig != NULL) {
 176         jvmti_env->Deallocate((unsigned char*)sig);
 177     }
 178     if (table != NULL) {
 179         for (j = 0; j < entryCount; j++) {
 180             jvmti_env->Deallocate((unsigned char*)(table[j].name));
 181             jvmti_env->Deallocate((unsigned char*)(table[j].signature));
 182         }
 183         jvmti_env->Deallocate((unsigned char*)table);
 184     }
 185     if (methodExitEventCount != framesCount + 1) {
 186         printf("(step %d) Wrong methodExitEventCount: %d,",
 187                i, methodExitEventCount);
 188         printf(" expected: %d\n", framesCount + 1);
 189         RETURN_FAILED;
 190     }
 191     fflush(0);
 192 }
 193 
 194 void JNICALL Breakpoint(jvmtiEnv *jvmti_env, JNIEnv *env,
 195         jthread thread, jmethodID method, jlocation location) {
 196     jvmtiError err;
 197 
 198     if (midCheckPoint != method) {
 199         printf("bp: don't know where we get called from");
 200         RETURN_FAILED;
 201     }
 202 
 203     if (printdump == JNI_TRUE) {
 204         printf(">>> breakpoint in checkPoint\n");
 205     }
 206 
 207     err = jvmti_env->ClearBreakpoint(midCheckPoint, 0);
 208     if (err != JVMTI_ERROR_NONE) {
 209         printf("(ClearBreakpoint) unexpected error: %s (%d)\n",
 210                TranslateError(err), err);
 211         RETURN_FAILED;
 212     }
 213 
 214     err = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
 215         JVMTI_EVENT_SINGLE_STEP, thread);
 216     if (err != JVMTI_ERROR_NONE) {
 217         printf("Cannot enable single step: %s (%d)\n",
 218                TranslateError(err), err);
 219         RETURN_FAILED;
 220     }
 221 
 222     err = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
 223         JVMTI_EVENT_METHOD_EXIT, thread);
 224     if (err != JVMTI_ERROR_NONE) {
 225         printf("Cannot enable method exit events: %s (%d)\n",
 226                TranslateError(err), err);
 227         RETURN_FAILED;
 228     }
 229 
 230     err = jvmti_env->ForceEarlyReturnVoid(thread);
 231     if (err != JVMTI_ERROR_NONE) {
 232         printf("(ForceEarlyReturn) unexpected error: %s (%d)\n",
 233                TranslateError(err), err);
 234         RETURN_FAILED;
 235     }
 236     fflush(0);
 237 }
 238 
 239 void JNICALL SingleStep(jvmtiEnv *jvmti_env, JNIEnv *env,
 240         jthread thread, jmethodID method, jlocation location) {
 241     jvmtiError err = JVMTI_ERROR_NONE;
 242 
 243     if (method == midRun) {
 244         if (printdump == JNI_TRUE) {
 245             printf(">>> returned early %d frames till method \"run()\"\n",
 246                    framesCount);
 247         }
 248 
 249         err = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
 250             JVMTI_EVENT_SINGLE_STEP, thread);
 251         if (err != JVMTI_ERROR_NONE) {
 252             printf("Cannot disable single step: %s (%d)\n",
 253                    TranslateError(err), err);
 254             RETURN_FAILED;
 255         }
 256     } else {
 257         check(jvmti_env, thread, method, location, framesCount);
 258         framesCount++;
 259 
 260         if (method == midCountDownFloat) {
 261           err = jvmti_env->ForceEarlyReturnFloat(thread, ret_val_f);
 262         }
 263         if (method == midCountDownDouble) {
 264           err = jvmti_env->ForceEarlyReturnDouble(thread, ret_val_d);
 265         }
 266         if (err != JVMTI_ERROR_NONE) {
 267             printf("(ForceEarlyReturn) unexpected error: %s (%d)\n",
 268                    TranslateError(err), err);
 269             RETURN_FAILED;
 270         }
 271     }
 272     fflush(0);
 273 }
 274 
 275 void JNICALL MethodExit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread,
 276         jmethodID method, jboolean was_popped_by_exception, jvalue value) {
 277 
 278     jboolean bad_exception = was_popped_by_exception;
 279     methodExitEventCount++;
 280     if (method == midRun || method == midCheckPoint) {
 281         // ignore
 282     } else if (method == midCountDownFloat) {
 283         printf(">>> ForceEarlyReturnFloat value: %8.4f, expected %8.4f\n",
 284                 value.f, ret_val_f);
 285         if (value.f != ret_val_f) {
 286             printf("Wrong ForceEarlyReturnFloat return value: %8.4f\n", value.f);
 287             errCode = STATUS_FAILED;
 288         }
 289     } else if (method == midCountDownDouble) {
 290         printf(">>> ForceEarlyReturnDouble value: %8.4f, expected %8.4f\n",
 291                 value.d, ret_val_d);
 292         if (value.d != ret_val_d) {
 293             printf("Wrong ForceEarlyReturnDouble return value: %8.4f\n", value.d);
 294             errCode = STATUS_FAILED;
 295         }
 296     } else {
 297         // Exceptions are ok for non test methods. Sometimes the VM invokes java
 298         // and throws exceptions, and we don't want to complain about these.
 299         bad_exception = JNI_FALSE;
 300     }
 301     if (bad_exception) {
 302         printf("Method was_popped_by_exception unexpectedly\n");
 303         errCode = STATUS_FAILED;
 304     }
 305     fflush(0);
 306 }
 307 
 308 #ifdef STATIC_BUILD
 309 JNIEXPORT jint JNICALL Agent_OnLoad_earlyretfp(JavaVM *jvm, char *options, void *reserved) {
 310     return Agent_Initialize(jvm, options, reserved);
 311 }
 312 JNIEXPORT jint JNICALL Agent_OnAttach_earlyretfp(JavaVM *jvm, char *options, void *reserved) {
 313     return Agent_Initialize(jvm, options, reserved);
 314 }
 315 JNIEXPORT jint JNI_OnLoad_earlyretfp(JavaVM *jvm, char *options, void *reserved) {
 316     return JNI_VERSION_1_8;
 317 }
 318 #endif
 319 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 320     jvmtiError err;
 321     jint res;
 322 
 323     if (options != NULL && strcmp(options, "printdump") == 0) {
 324         printf("Printdump is turned on!\n");
 325 
 326         printdump = JNI_TRUE;
 327     }
 328 
 329     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
 330         JVMTI_VERSION_1_1);
 331     if (res != JNI_OK || jvmti == NULL) {
 332         printf("Wrong error code from a valid call to GetEnv!\n");
 333         return JNI_ERR;
 334     }
 335 
 336     err = jvmti->GetPotentialCapabilities(&caps);
 337     if (err != JVMTI_ERROR_NONE) {
 338         printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
 339                TranslateError(err), err);
 340         return JNI_ERR;
 341     }
 342 
 343     err = jvmti->AddCapabilities(&caps);
 344     if (err != JVMTI_ERROR_NONE) {
 345         printf("(AddCapabilities) unexpected error: %s (%d)\n",
 346                TranslateError(err), err);
 347         return JNI_ERR;
 348     }
 349 
 350     err = jvmti->GetCapabilities(&caps);
 351     if (err != JVMTI_ERROR_NONE) {
 352         printf("(GetCapabilities) unexpected error: %s (%d)\n",
 353                TranslateError(err), err);
 354         return JNI_ERR;
 355     }
 356 
 357     if (!caps.can_force_early_return) {
 358         printf("Warning: ForceEarlyReturn is not implemented\n");
 359     }
 360 
 361     if (caps.can_generate_breakpoint_events &&
 362         caps.can_generate_method_exit_events &&
 363         caps.can_generate_single_step_events)
 364     {
 365         callbacks.Breakpoint = &Breakpoint;
 366         callbacks.SingleStep = &SingleStep;
 367         callbacks.MethodExit = &MethodExit;
 368         err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
 369         if (err != JVMTI_ERROR_NONE) {
 370             printf("(SetEventCallbacks) unexpected error: %s (%d)\n",
 371                    TranslateError(err), err);
 372             return JNI_ERR;
 373         }
 374     } else {
 375         printf("Warning: Breakpoint or SingleStep event are not implemented\n");
 376     }
 377 
 378     fflush(0);
 379     return JNI_OK;
 380 }
 381 
 382 JNIEXPORT void JNICALL
 383 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretfp_getReady(
 384     JNIEnv *env, jclass c, jclass cls, jint depth, jdouble retval) {
 385     jvmtiError err;
 386 
 387     if (jvmti == NULL) {
 388         printf("JVMTI client was not properly loaded!\n");
 389         RETURN_FAILED;
 390     }
 391 
 392     if (!caps.can_force_early_return ||
 393         !caps.can_generate_breakpoint_events ||
 394         !caps.can_generate_method_exit_events ||
 395         !caps.can_generate_single_step_events) {
 396         return;
 397     }
 398 
 399     midRun = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, cls),
 400          "run", "()V");
 401     if (midRun == NULL) {
 402         printf("Cannot find Method ID for method run\n");
 403         RETURN_FAILED;
 404     }
 405 
 406     midCheckPoint = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, cls),
 407          "checkPoint", "()V");
 408     if (midCheckPoint == NULL) {
 409         printf("Cannot find Method ID for method checkPoint\n");
 410         RETURN_FAILED;
 411     }
 412 
 413     midCountDownFloat = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, cls),
 414          "countDownFloat", "(I)F");
 415     if (midCountDownFloat == NULL) {
 416         printf("Cannot find Method ID for method countDownFloat\n");
 417         RETURN_FAILED;
 418     }
 419 
 420     midCountDownDouble = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, cls),
 421          "countDownDouble", "(I)D");
 422     if (midCountDownDouble == NULL) {
 423         printf("Cannot find Method ID for method countDownDouble\n");
 424         RETURN_FAILED;
 425     }
 426 
 427     err = jvmti->SetBreakpoint(midCheckPoint, 0);
 428     if (err != JVMTI_ERROR_NONE) {
 429         printf("(SetBreakpoint) unexpected error: %s (%d)\n",
 430                TranslateError(err), err);
 431         RETURN_FAILED;
 432     }
 433 
 434     err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
 435         JVMTI_EVENT_BREAKPOINT, NULL);
 436     if (err != JVMTI_ERROR_NONE) {
 437         printf("Failed to enable BREAKPOINT event: %s (%d)\n",
 438                TranslateError(err), err);
 439         RETURN_FAILED;
 440     } else {
 441         framesExpected = depth;
 442         ret_val_d      = retval;
 443         ret_val_f      = (float) (retval - 1.0);
 444     }
 445 }
 446 
 447 JNIEXPORT void JNICALL
 448 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretfp_printFloat(
 449       JNIEnv *env, jclass cls, jfloat val) {
 450 
 451     printf("\n>>> Returned value is %8.4f, hex: %#a\n", val, val);
 452     fflush(0);
 453     return;
 454 }
 455 
 456 JNIEXPORT void JNICALL
 457 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretfp_printDouble(
 458       JNIEnv *env, jclass cls, jdouble val) {
 459 
 460     printf("\n>>> Returned value is %8.4f, hex: %#a\n", val, val);
 461     fflush(0);
 462     return;
 463 }
 464 
 465 JNIEXPORT jint JNICALL
 466 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretfp_check(JNIEnv *env, jclass cls) {
 467     if (framesCount != framesExpected) {
 468         printf("Wrong number of returned early frames: %d, expected: %d\n",
 469             framesCount, framesExpected);
 470         errCode = STATUS_FAILED;
 471     }
 472     fflush(0);
 473     return errCode;
 474 }
 475 
 476 #ifdef __cplusplus
 477 }
 478 #endif