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