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 <inttypes.h> 27 #include "jvmti.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 midCountDownObject = 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/earlyretobj$earlyretThread;"; 54 55 static jobject val_exp = NULL; 56 static const char *sig_exp = "(I)Lnsk/jvmti/unit/ForceEarlyReturn/earlyretobj$RetObj;"; 57 static const char *name_exp = "countDownObject"; 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("(ForceEarlyReturnVoid) 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 { 244 check(jvmti_env, thread, method, location, framesCount); 245 framesCount++; 246 err = jvmti_env->ForceEarlyReturnObject(thread, 247 val_exp); 248 if (err != JVMTI_ERROR_NONE) { 249 printf("(ForceEarlyReturnObject) 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 jobject ret_val = value.l; 260 261 methodExitEventCount++; 262 printf("MethodExit event: methodExitEventCount=%d\n", methodExitEventCount); 263 if (method == midRun || method == midCheckPoint) { 264 return; 265 } 266 if (method == midCountDownObject) { 267 if (!env->IsSameObject(ret_val, val_exp)) { 268 printf("Wrong ForceEarlyReturnObject return value: 0x%p\n", ret_val); 269 printf("expected: 0x%p\n", val_exp); 270 errCode = STATUS_FAILED; 271 } 272 if (was_popped_by_exception) { 273 printf("Method was_popped_by_exception unexpectedly\n"); 274 errCode = STATUS_FAILED; 275 } 276 } 277 fflush(0); 278 } 279 280 #ifdef STATIC_BUILD 281 JNIEXPORT jint JNICALL Agent_OnLoad_earlyretobj(JavaVM *jvm, char *options, void *reserved) { 282 return Agent_Initialize(jvm, options, reserved); 283 } 284 JNIEXPORT jint JNICALL Agent_OnAttach_earlyretobj(JavaVM *jvm, char *options, void *reserved) { 285 return Agent_Initialize(jvm, options, reserved); 286 } 287 JNIEXPORT jint JNI_OnLoad_earlyretobj(JavaVM *jvm, char *options, void *reserved) { 288 return JNI_VERSION_1_8; 289 } 290 #endif 291 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 292 jvmtiError err; 293 jint res; 294 295 if (options != NULL && strcmp(options, "printdump") == 0) { 296 printf("Printdump is turned on!\n"); 297 298 printdump = JNI_TRUE; 299 } 300 301 res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1); 302 if (res != JNI_OK || jvmti == NULL) { 303 printf("Wrong error code from a valid call to GetEnv!\n"); 304 return JNI_ERR; 305 } 306 307 err = jvmti->GetPotentialCapabilities(&caps); 308 if (err != JVMTI_ERROR_NONE) { 309 printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n", 310 TranslateError(err), err); 311 return JNI_ERR; 312 } 313 314 err = jvmti->AddCapabilities(&caps); 315 if (err != JVMTI_ERROR_NONE) { 316 printf("(AddCapabilities) unexpected error: %s (%d)\n", 317 TranslateError(err), err); 318 return JNI_ERR; 319 } 320 321 err = jvmti->GetCapabilities(&caps); 322 if (err != JVMTI_ERROR_NONE) { 323 printf("(GetCapabilities) unexpected error: %s (%d)\n", 324 TranslateError(err), err); 325 return JNI_ERR; 326 } 327 328 if (!caps.can_force_early_return) { 329 printf("Warning: ForceEarlyReturn is not implemented\n"); 330 } 331 332 if (caps.can_generate_breakpoint_events && 333 caps.can_generate_method_exit_events && 334 caps.can_generate_single_step_events) 335 { 336 callbacks.Breakpoint = &Breakpoint; 337 callbacks.SingleStep = &SingleStep; 338 callbacks.MethodExit = &MethodExit; 339 err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); 340 if (err != JVMTI_ERROR_NONE) { 341 printf("(SetEventCallbacks) unexpected error: %s (%d)\n", 342 TranslateError(err), err); 343 return JNI_ERR; 344 } 345 } else { 346 printf("Warning: Breakpoint or SingleStep event are not implemented\n"); 347 } 348 349 return JNI_OK; 350 } 351 352 JNIEXPORT void JNICALL 353 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretobj_getReady( 354 JNIEnv *env, jclass c, jclass cls, jint depth, jobject ret_obj) { 355 jvmtiError err; 356 357 if (jvmti == NULL) { 358 printf("JVMTI client was not properly loaded!\n"); 359 RETURN_FAILED; 360 } 361 362 if (!caps.can_force_early_return || 363 !caps.can_generate_breakpoint_events || 364 !caps.can_generate_method_exit_events || 365 !caps.can_generate_single_step_events) { 366 return; 367 } 368 369 midRun = env->GetMethodID(cls, "run", "()V"); 370 if (midRun == NULL) { 371 printf("Cannot find Method ID for method run\n"); 372 RETURN_FAILED; 373 } 374 375 midCheckPoint = env->GetMethodID(cls, "checkPoint", "()V"); 376 if (midCheckPoint == NULL) { 377 printf("Cannot find Method ID for method checkPoint\n"); 378 RETURN_FAILED; 379 } 380 381 midCountDownObject = env->GetMethodID(cls, "countDownObject", sig_exp); 382 if (midCountDownObject == NULL) { 383 printf("Cannot find Method ID for method countDownObject\n"); 384 RETURN_FAILED; 385 } 386 387 err = jvmti->SetBreakpoint(midCheckPoint, 0); 388 if (err != JVMTI_ERROR_NONE) { 389 printf("(SetBreakpoint) unexpected error: %s (%d)\n", 390 TranslateError(err), err); 391 RETURN_FAILED; 392 } 393 394 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, 395 JVMTI_EVENT_BREAKPOINT, NULL); 396 if (err != JVMTI_ERROR_NONE) { 397 printf("Failed to enable BREAKPOINT event: %s (%d)\n", 398 TranslateError(err), err); 399 RETURN_FAILED; 400 } else { 401 val_exp = env->NewGlobalRef(ret_obj); 402 framesExpected = depth; 403 } 404 } 405 406 JNIEXPORT jint JNICALL 407 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretobj_check(JNIEnv *env, jclass cls) { 408 if (framesCount != framesExpected) { 409 printf("Wrong number of returned early frames: %d, expected: %d\n", 410 framesCount, framesExpected); 411 errCode = STATUS_FAILED; 412 } 413 fflush(0); 414 return errCode; 415 } 416 417 JNIEXPORT void JNICALL 418 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretobj_printObject( 419 JNIEnv *env, jclass cls, jobject obj) { 420 421 printf("\nReturned jobject: %#" PRIxPTR "\n", (uintptr_t)obj); 422 fflush(0); 423 return; 424 } 425 426 }