1 /*
   2  * Copyright (c) 2003, 2019, 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 "jni_tools.h"
  28 #include "agent_common.h"
  29 #include "JVMTITools.h"
  30 
  31 extern "C" {
  32 
  33 
  34 #define STATUS_PASSED 0
  35 #define STATUS_FAILED 2
  36 
  37 static jvmtiEnv *jvmti = NULL;
  38 static jvmtiEventCallbacks callbacks;
  39 static jint result = STATUS_PASSED;
  40 static jboolean printdump = JNI_FALSE;
  41 static jmethodID mid = NULL;
  42 static jvmtiLocalVariableEntry *table = NULL;
  43 static jint entryCount = 0;
  44 static jint methodExitCnt = -1;
  45 
  46 void print_LocalVariableEntry(jvmtiLocalVariableEntry *lvt_elem) {
  47   printf("\n Var name: %s, slot: %d", lvt_elem->name, lvt_elem->slot);
  48   printf(", start_bci: %" LL "d", lvt_elem->start_location);
  49   printf(", end_bci: %" LL "d",   lvt_elem->start_location + lvt_elem->length);
  50   printf(", signature: %s\n", lvt_elem->signature);
  51 }
  52 
  53 static void
  54 test_locals(jvmtiEnv *jvmti, jthread thr, jlocation location) {
  55     jvmtiError err;
  56     jint intVal;
  57     jlong longVal;
  58     jfloat floatVal;
  59     jdouble doubleVal;
  60     jobject obj;
  61     jint i;
  62 
  63     for (i = 0; i < entryCount; i++) {
  64         if (table[i].start_location > location ||
  65             table[i].start_location + table[i].length < location) {
  66             continue;  /* The local variable is not visible */
  67         }
  68         print_LocalVariableEntry(&table[i]);
  69         char sig = table[i].signature[0];
  70         int slot = table[i].slot;
  71 
  72         if (sig == 'Z' || sig == 'B' || sig == 'C' || sig == 'S') {
  73             sig = 'I'; // covered by GetLocalInt
  74         }
  75         err = jvmti->GetLocalInt(thr, 0, slot, &intVal);
  76         printf(" GetLocalInt:     %s (%d)\n", TranslateError(err), err);
  77         if (err != JVMTI_ERROR_NONE && sig == 'I') {
  78             printf("FAIL: GetLocalInt failed to get value of int\n");
  79             result = STATUS_FAILED;
  80         } else if (err != JVMTI_ERROR_TYPE_MISMATCH && sig != 'I') {
  81             printf("FAIL: GetLocalInt did not return JVMTI_ERROR_TYPE_MISMATCH for non-int\n");
  82             result = STATUS_FAILED;
  83         }
  84 
  85         err = jvmti->GetLocalLong(thr, 0, slot, &longVal);
  86         printf(" GetLocalLong:    %s (%d)\n", TranslateError(err), err);
  87         if (err != JVMTI_ERROR_NONE && sig == 'J') {
  88             printf("FAIL: GetLocalLong failed to get value of long\n");
  89             result = STATUS_FAILED;
  90         } else if (err != JVMTI_ERROR_INVALID_SLOT &&
  91                    err != JVMTI_ERROR_TYPE_MISMATCH &&
  92                    sig != 'J') {
  93             printf("FAIL: GetLocalLong did not return JVMTI_ERROR_INVALID_SLOT"
  94                    " nor JVMTI_ERROR_TYPE_MISMATCH for non-long\n");
  95             result = STATUS_FAILED;
  96         }
  97 
  98         err = jvmti->GetLocalFloat(thr, 0, slot, &floatVal);
  99         printf(" GetLocalFloat:   %s (%d)\n", TranslateError(err), err);
 100         if (err != JVMTI_ERROR_NONE && sig == 'F') {
 101             printf("FAIL: GetLocalFloat failed to get value of float\n");
 102             result = STATUS_FAILED;
 103         } else if (err != JVMTI_ERROR_TYPE_MISMATCH && sig != 'F') {
 104             printf("FAIL: GetLocalFloat did not return JVMTI_ERROR_TYPE_MISMATCH for non-float\n");
 105             result = STATUS_FAILED;
 106         }
 107 
 108         err = jvmti->GetLocalDouble(thr, 0, slot, &doubleVal);
 109         printf(" GetLocalDouble:  %s (%d)\n", TranslateError(err), err);
 110         if (err != JVMTI_ERROR_NONE && sig == 'D') {
 111             printf("FAIL: GetLocalDouble failed to get value of double\n");
 112             result = STATUS_FAILED;
 113         } else if (err != JVMTI_ERROR_INVALID_SLOT &&
 114                    err != JVMTI_ERROR_TYPE_MISMATCH &&
 115                    sig != 'D') {
 116             printf("FAIL: GetLocalDouble did not return JVMTI_ERROR_INVALID_SLOT"
 117                    " nor JVMTI_ERROR_TYPE_MISMATCH for non-double\n");
 118             result = STATUS_FAILED;
 119         }
 120 
 121         err = jvmti->GetLocalObject(thr, 0, slot, &obj);
 122         printf(" GetLocalObject:  %s (%d)\n", TranslateError(err), err);
 123         if (err != JVMTI_ERROR_NONE && sig == 'L') {
 124             printf("FAIL: GetLocalObject failed to get value of object\n");
 125             result = STATUS_FAILED;
 126         } else if (err != JVMTI_ERROR_TYPE_MISMATCH && sig != 'L') {
 127             printf("FAIL: GetLocalObject did not return JVMTI_ERROR_TYPE_MISMATCH for non-object\n");
 128             result = STATUS_FAILED;
 129         }
 130     }
 131 }
 132 
 133 static void JNICALL
 134 MethodExit(jvmtiEnv *jvmti_env,
 135            JNIEnv *env,
 136            jthread thr,
 137            jmethodID method,
 138            jboolean was_poped_by_exception,
 139            jvalue return_value) {
 140 
 141     jvmtiError err;
 142     jlocation location;
 143     jmethodID frame_method = NULL;
 144 
 145     if (mid != method) {
 146         return;
 147     }
 148     err = jvmti->GetFrameLocation(thr, 0, &frame_method, &location);
 149     if (err != JVMTI_ERROR_NONE) {
 150         printf("\t failure: %s (%d)\n", TranslateError(err), err);
 151         result = STATUS_FAILED;
 152         return;
 153     }
 154     if (frame_method != method) {
 155         printf("\t failure: GetFrameLocation returned wrong jmethodID\n");
 156         result = STATUS_FAILED;
 157         return;
 158     }
 159 
 160     printf("\n MethodExit: BEGIN %d\n",  ++methodExitCnt);
 161 
 162     test_locals(jvmti, thr, location);
 163 
 164     printf("\n MethodExit: END %d\n\n", methodExitCnt);
 165     fflush(stdout);
 166 }
 167 
 168 #ifdef STATIC_BUILD
 169 JNIEXPORT jint JNICALL Agent_OnLoad_getlocal003(JavaVM *jvm, char *options, void *reserved) {
 170     return Agent_Initialize(jvm, options, reserved);
 171 }
 172 JNIEXPORT jint JNICALL Agent_OnAttach_getlocal003(JavaVM *jvm, char *options, void *reserved) {
 173     return Agent_Initialize(jvm, options, reserved);
 174 }
 175 JNIEXPORT jint JNI_OnLoad_getlocal003(JavaVM *jvm, char *options, void *reserved) {
 176     return JNI_VERSION_1_8;
 177 }
 178 #endif
 179 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 180     jint res;
 181     jvmtiError err;
 182     static jvmtiCapabilities caps;
 183 
 184     if (options != NULL && strcmp(options, "printdump") == 0) {
 185         printdump = JNI_TRUE;
 186     }
 187 
 188     res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
 189     if (res != JNI_OK || jvmti == NULL) {
 190         printf("Wrong result of a valid call to GetEnv!\n");
 191         return JNI_ERR;
 192     }
 193 
 194     err = jvmti->GetPotentialCapabilities(&caps);
 195     if (err != JVMTI_ERROR_NONE) {
 196         printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
 197                TranslateError(err), err);
 198         return JNI_ERR;
 199     }
 200 
 201     err = jvmti->AddCapabilities(&caps);
 202     if (err != JVMTI_ERROR_NONE) {
 203         printf("(AddCapabilities) unexpected error: %s (%d)\n",
 204                TranslateError(err), err);
 205         return JNI_ERR;
 206     }
 207 
 208     err = jvmti->GetCapabilities(&caps);
 209     if (err != JVMTI_ERROR_NONE) {
 210         printf("(GetCapabilities) unexpected error: %s (%d)\n",
 211                TranslateError(err), err);
 212         return JNI_ERR;
 213     }
 214 
 215     if (!caps.can_access_local_variables) {
 216         printf("Warning: Access to local variables is not implemented\n");
 217         return JNI_ERR;
 218     }
 219     if (!caps.can_generate_method_exit_events) {
 220         printf("Warning: MethodExit event is not implemented\n");
 221         return JNI_ERR;
 222     }
 223     callbacks.MethodExit = &MethodExit;
 224     err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
 225     if (err != JVMTI_ERROR_NONE) {
 226         printf("(SetEventCallbacks) unexpected error: %s (%d)\n",
 227                TranslateError(err), err);
 228         return JNI_ERR;
 229     }
 230     return JNI_OK;
 231 }
 232 
 233 JNIEXPORT void JNICALL
 234 Java_nsk_jvmti_unit_GetLocalVariable_getlocal003_getMeth(JNIEnv *env, jclass cls) {
 235     jvmtiError err;
 236 
 237     if (jvmti == NULL) {
 238         printf("JVMTI client was not properly loaded!\n");
 239         result = STATUS_FAILED;
 240         return;
 241     }
 242 
 243     mid = env->GetStaticMethodID(cls, "staticMeth", "(I)I");
 244     if (mid == NULL) {
 245         printf("Cannot find Method ID for staticMeth\n");
 246         result = STATUS_FAILED;
 247         return;
 248     }
 249 
 250     err = jvmti->GetLocalVariableTable(mid, &entryCount, &table);
 251     if (err != JVMTI_ERROR_NONE) {
 252         printf("(GetLocalVariableTable) unexpected error: %s (%d)\n",
 253                TranslateError(err), err);
 254         result = STATUS_FAILED;
 255         return;
 256     }
 257 
 258     err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, NULL);
 259     if (err != JVMTI_ERROR_NONE) {
 260         printf("Failed to enable metod exit event: %s (%d)\n",
 261                TranslateError(err), err);
 262         result = STATUS_FAILED;
 263     }
 264     fflush(stdout);
 265 }
 266 
 267 JNIEXPORT void JNICALL
 268 Java_nsk_jvmti_unit_GetLocalVariable_getlocal003_checkLoc(JNIEnv *env,
 269         jclass cls, jthread thr) {
 270     jvmtiError err;
 271     jvmtiLocalVariableEntry *table;
 272     jint entryCount;
 273     jmethodID mid;
 274     jint locVar;
 275     jint i, j;
 276     int  overlap = 0;
 277 
 278     if (jvmti == NULL) {
 279         printf("JVMTI client was not properly loaded!\n");
 280         result = STATUS_FAILED;
 281         return;
 282     }
 283     printf("\n checkLoc: START\n");
 284 
 285     mid = env->GetStaticMethodID(cls, "staticMeth", "(I)I");
 286     if (mid == NULL) {
 287         printf("Cannot find Method ID for staticMeth\n");
 288         result = STATUS_FAILED;
 289         return;
 290     }
 291 
 292     err = jvmti->GetLocalVariableTable(mid, &entryCount, &table);
 293     if (err != JVMTI_ERROR_NONE) {
 294         printf("(GetLocalVariableTable) unexpected error: %s (%d)\n",
 295                TranslateError(err), err);
 296         result = STATUS_FAILED;
 297         return;
 298     }
 299 
 300     for (i = 0; i < entryCount; i++) {
 301         print_LocalVariableEntry(&table[i]);
 302 
 303         err = jvmti->GetLocalInt(thr, 1, table[i].slot, &locVar);
 304 
 305         if (strcmp(table[i].name, "intArg") == 0) {
 306             if (err != JVMTI_ERROR_NONE) {
 307                 printf(" GetLocalInt: %s (%d)\n", TranslateError(err), err);
 308                 printf(" failure: JVMTI_ERROR_NONE is expected\n");
 309                 result = STATUS_FAILED;
 310             }
 311         }
 312         else if (strcmp(table[i].name, "pi") == 0) {
 313             if (err != JVMTI_ERROR_TYPE_MISMATCH) {
 314                 printf(" GetLocalInt: %s (%d)\n", TranslateError(err), err);
 315                 printf(" failure: JVMTI_ERROR_TYPE_MISMATCH is expected\n");
 316                 result = STATUS_FAILED;
 317             }
 318         } else {
 319             if (err != JVMTI_ERROR_INVALID_SLOT) {
 320                 printf(" GetLocalInt: %s (%d)\n", TranslateError(err), err);
 321                 printf(" failure: JVMTI_ERROR_INVALID_SLOT is expected\n");
 322                 result = STATUS_FAILED;
 323             }
 324         }
 325         if (table[i].slot != 2) {
 326            continue;
 327         }
 328 
 329         for (j = 0; j < entryCount; j++) {
 330            /* We do cross checks between all variables having slot #2.
 331             * No overlapping between location ranges are allowed.
 332             */
 333            if (table[j].slot != 2 || i == j) {
 334               continue;
 335            }
 336            if (table[i].start_location > table[j].start_location + table[j].length ||
 337                table[j].start_location > table[i].start_location + table[i].length
 338            ) {
 339                continue; /* Everything is Ok */
 340            }
 341 
 342            printf(" failure: locations of vars with slot #2 are overlapped:\n");
 343            print_LocalVariableEntry(&table[i]);
 344            print_LocalVariableEntry(&table[j]);
 345            overlap++;
 346            result = STATUS_FAILED;
 347         }
 348     }
 349     if (!overlap) {
 350         printf("\n Success: locations of vars with slot #2 are NOT overlapped\n");
 351     }
 352     printf("\n checkLoc: END\n\n");
 353     fflush(stdout);
 354 }
 355 
 356 JNIEXPORT jint JNICALL
 357 Java_nsk_jvmti_unit_GetLocalVariable_getlocal003_getRes(JNIEnv *env, jclass cls) {
 358     return result;
 359 }
 360 
 361 }