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