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