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