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