1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "common_helper.h" 18 19 #include "jni.h" 20 #include "jvmti.h" 21 22 #include "jvmti_helper.h" 23 #include "scoped_local_ref.h" 24 #include "test_env.h" 25 26 namespace art { 27 28 namespace common_trace { 29 30 struct TraceData { 31 jclass test_klass; 32 jmethodID enter_method; 33 jmethodID exit_method; 34 jmethodID field_access; 35 jmethodID field_modify; 36 jmethodID single_step; 37 bool in_callback; 38 bool access_watch_on_load; 39 bool modify_watch_on_load; 40 }; 41 42 static void singleStepCB(jvmtiEnv* jvmti, 43 JNIEnv* jnienv, 44 jthread thread, 45 jmethodID method, 46 jlocation location) { 47 TraceData* data = nullptr; 48 if (JvmtiErrorToException(jnienv, jvmti, 49 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 50 return; 51 } 52 if (data->in_callback) { 53 return; 54 } 55 CHECK(data->single_step != nullptr); 56 data->in_callback = true; 57 jobject method_arg = GetJavaMethod(jvmti, jnienv, method); 58 jnienv->CallStaticVoidMethod(data->test_klass, 59 data->single_step, 60 thread, 61 method_arg, 62 static_cast<jlong>(location)); 63 jnienv->DeleteLocalRef(method_arg); 64 data->in_callback = false; 65 } 66 67 static void fieldAccessCB(jvmtiEnv* jvmti, 68 JNIEnv* jnienv, 69 jthread thr ATTRIBUTE_UNUSED, 70 jmethodID method, 71 jlocation location, 72 jclass field_klass, 73 jobject object, 74 jfieldID field) { 75 TraceData* data = nullptr; 76 if (JvmtiErrorToException(jnienv, jvmti, 77 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 78 return; 79 } 80 if (data->in_callback) { 81 // Don't do callback for either of these to prevent an infinite loop. 82 return; 83 } 84 CHECK(data->field_access != nullptr); 85 data->in_callback = true; 86 jobject method_arg = GetJavaMethod(jvmti, jnienv, method); 87 jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); 88 jnienv->CallStaticVoidMethod(data->test_klass, 89 data->field_access, 90 method_arg, 91 static_cast<jlong>(location), 92 field_klass, 93 object, 94 field_arg); 95 jnienv->DeleteLocalRef(method_arg); 96 jnienv->DeleteLocalRef(field_arg); 97 data->in_callback = false; 98 } 99 100 static void fieldModificationCB(jvmtiEnv* jvmti, 101 JNIEnv* jnienv, 102 jthread thr ATTRIBUTE_UNUSED, 103 jmethodID method, 104 jlocation location, 105 jclass field_klass, 106 jobject object, 107 jfieldID field, 108 char type_char, 109 jvalue new_value) { 110 TraceData* data = nullptr; 111 if (JvmtiErrorToException(jnienv, jvmti, 112 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 113 return; 114 } 115 if (data->in_callback) { 116 // Don't do callback recursively to prevent an infinite loop. 117 return; 118 } 119 CHECK(data->field_modify != nullptr); 120 data->in_callback = true; 121 jobject method_arg = GetJavaMethod(jvmti, jnienv, method); 122 jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); 123 jobject value = GetJavaValueByType(jnienv, type_char, new_value); 124 if (jnienv->ExceptionCheck()) { 125 data->in_callback = false; 126 jnienv->DeleteLocalRef(method_arg); 127 jnienv->DeleteLocalRef(field_arg); 128 return; 129 } 130 jnienv->CallStaticVoidMethod(data->test_klass, 131 data->field_modify, 132 method_arg, 133 static_cast<jlong>(location), 134 field_klass, 135 object, 136 field_arg, 137 value); 138 jnienv->DeleteLocalRef(method_arg); 139 jnienv->DeleteLocalRef(field_arg); 140 data->in_callback = false; 141 } 142 143 static void methodExitCB(jvmtiEnv* jvmti, 144 JNIEnv* jnienv, 145 jthread thr ATTRIBUTE_UNUSED, 146 jmethodID method, 147 jboolean was_popped_by_exception, 148 jvalue return_value) { 149 TraceData* data = nullptr; 150 if (JvmtiErrorToException(jnienv, jvmti, 151 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 152 return; 153 } 154 if (method == data->exit_method || method == data->enter_method || data->in_callback) { 155 // Don't do callback for either of these to prevent an infinite loop. 156 return; 157 } 158 CHECK(data->exit_method != nullptr); 159 data->in_callback = true; 160 jobject method_arg = GetJavaMethod(jvmti, jnienv, method); 161 jobject result = 162 was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value); 163 if (jnienv->ExceptionCheck()) { 164 data->in_callback = false; 165 return; 166 } 167 jnienv->CallStaticVoidMethod(data->test_klass, 168 data->exit_method, 169 method_arg, 170 was_popped_by_exception, 171 result); 172 jnienv->DeleteLocalRef(method_arg); 173 data->in_callback = false; 174 } 175 176 static void methodEntryCB(jvmtiEnv* jvmti, 177 JNIEnv* jnienv, 178 jthread thr ATTRIBUTE_UNUSED, 179 jmethodID method) { 180 TraceData* data = nullptr; 181 if (JvmtiErrorToException(jnienv, jvmti, 182 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 183 return; 184 } 185 CHECK(data->enter_method != nullptr); 186 if (method == data->exit_method || method == data->enter_method || data->in_callback) { 187 // Don't do callback for either of these to prevent an infinite loop. 188 return; 189 } 190 data->in_callback = true; 191 jobject method_arg = GetJavaMethod(jvmti, jnienv, method); 192 if (jnienv->ExceptionCheck()) { 193 return; 194 } 195 jnienv->CallStaticVoidMethod(data->test_klass, data->enter_method, method_arg); 196 jnienv->DeleteLocalRef(method_arg); 197 data->in_callback = false; 198 } 199 200 static void classPrepareCB(jvmtiEnv* jvmti, 201 JNIEnv* jnienv, 202 jthread thr ATTRIBUTE_UNUSED, 203 jclass klass) { 204 TraceData* data = nullptr; 205 if (JvmtiErrorToException(jnienv, jvmti, 206 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 207 return; 208 } 209 if (data->access_watch_on_load || data->modify_watch_on_load) { 210 jint nfields; 211 jfieldID* fields; 212 if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) { 213 return; 214 } 215 for (jint i = 0; i < nfields; i++) { 216 jfieldID f = fields[i]; 217 // Ignore errors 218 if (data->access_watch_on_load) { 219 jvmti->SetFieldAccessWatch(klass, f); 220 } 221 222 if (data->modify_watch_on_load) { 223 jvmti->SetFieldModificationWatch(klass, f); 224 } 225 } 226 jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields)); 227 } 228 } 229 230 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) { 231 TraceData* data = nullptr; 232 if (JvmtiErrorToException( 233 env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 234 return; 235 } 236 data->access_watch_on_load = true; 237 // We need the classPrepareCB to watch new fields as the classes are loaded/prepared. 238 if (JvmtiErrorToException(env, 239 jvmti_env, 240 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 241 JVMTI_EVENT_CLASS_PREPARE, 242 nullptr))) { 243 return; 244 } 245 jint nklasses; 246 jclass* klasses; 247 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) { 248 return; 249 } 250 for (jint i = 0; i < nklasses; i++) { 251 jclass k = klasses[i]; 252 253 jint nfields; 254 jfieldID* fields; 255 jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields); 256 if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) { 257 continue; 258 } else if (JvmtiErrorToException(env, jvmti_env, err)) { 259 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); 260 return; 261 } 262 for (jint j = 0; j < nfields; j++) { 263 jvmti_env->SetFieldAccessWatch(k, fields[j]); 264 } 265 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields)); 266 } 267 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); 268 } 269 270 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) { 271 TraceData* data = nullptr; 272 if (JvmtiErrorToException( 273 env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 274 return; 275 } 276 data->modify_watch_on_load = true; 277 // We need the classPrepareCB to watch new fields as the classes are loaded/prepared. 278 if (JvmtiErrorToException(env, 279 jvmti_env, 280 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 281 JVMTI_EVENT_CLASS_PREPARE, 282 nullptr))) { 283 return; 284 } 285 jint nklasses; 286 jclass* klasses; 287 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) { 288 return; 289 } 290 for (jint i = 0; i < nklasses; i++) { 291 jclass k = klasses[i]; 292 293 jint nfields; 294 jfieldID* fields; 295 jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields); 296 if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) { 297 continue; 298 } else if (JvmtiErrorToException(env, jvmti_env, err)) { 299 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); 300 return; 301 } 302 for (jint j = 0; j < nfields; j++) { 303 jvmti_env->SetFieldModificationWatch(k, fields[j]); 304 } 305 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields)); 306 } 307 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); 308 } 309 310 static bool GetFieldAndClass(JNIEnv* env, 311 jobject ref_field, 312 jclass* out_klass, 313 jfieldID* out_field) { 314 *out_field = env->FromReflectedField(ref_field); 315 if (env->ExceptionCheck()) { 316 return false; 317 } 318 jclass field_klass = env->FindClass("java/lang/reflect/Field"); 319 if (env->ExceptionCheck()) { 320 return false; 321 } 322 jmethodID get_declaring_class_method = 323 env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;"); 324 if (env->ExceptionCheck()) { 325 env->DeleteLocalRef(field_klass); 326 return false; 327 } 328 *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method)); 329 if (env->ExceptionCheck()) { 330 *out_klass = nullptr; 331 env->DeleteLocalRef(field_klass); 332 return false; 333 } 334 env->DeleteLocalRef(field_klass); 335 return true; 336 } 337 338 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification( 339 JNIEnv* env, 340 jclass trace ATTRIBUTE_UNUSED, 341 jobject field_obj) { 342 jfieldID field; 343 jclass klass; 344 if (!GetFieldAndClass(env, field_obj, &klass, &field)) { 345 return; 346 } 347 348 JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field)); 349 env->DeleteLocalRef(klass); 350 } 351 352 extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess( 353 JNIEnv* env, 354 jclass trace ATTRIBUTE_UNUSED, 355 jobject field_obj) { 356 jfieldID field; 357 jclass klass; 358 if (!GetFieldAndClass(env, field_obj, &klass, &field)) { 359 return; 360 } 361 JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field)); 362 env->DeleteLocalRef(klass); 363 } 364 365 extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing( 366 JNIEnv* env, 367 jclass trace ATTRIBUTE_UNUSED, 368 jclass klass, 369 jobject enter, 370 jobject exit, 371 jobject field_access, 372 jobject field_modify, 373 jobject single_step, 374 jthread thr) { 375 TraceData* data = nullptr; 376 if (JvmtiErrorToException(env, 377 jvmti_env, 378 jvmti_env->Allocate(sizeof(TraceData), 379 reinterpret_cast<unsigned char**>(&data)))) { 380 return; 381 } 382 memset(data, 0, sizeof(TraceData)); 383 data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass)); 384 data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr; 385 data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr; 386 data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr; 387 data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr; 388 data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr; 389 data->in_callback = false; 390 391 void* old_data = nullptr; 392 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { 393 return; 394 } else if (old_data != nullptr) { 395 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); 396 env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); 397 return; 398 } 399 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { 400 return; 401 } 402 403 jvmtiEventCallbacks cb; 404 memset(&cb, 0, sizeof(cb)); 405 cb.MethodEntry = methodEntryCB; 406 cb.MethodExit = methodExitCB; 407 cb.FieldAccess = fieldAccessCB; 408 cb.FieldModification = fieldModificationCB; 409 cb.ClassPrepare = classPrepareCB; 410 cb.SingleStep = singleStepCB; 411 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { 412 return; 413 } 414 if (enter != nullptr && 415 JvmtiErrorToException(env, 416 jvmti_env, 417 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 418 JVMTI_EVENT_METHOD_ENTRY, 419 thr))) { 420 return; 421 } 422 if (exit != nullptr && 423 JvmtiErrorToException(env, 424 jvmti_env, 425 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 426 JVMTI_EVENT_METHOD_EXIT, 427 thr))) { 428 return; 429 } 430 if (field_access != nullptr && 431 JvmtiErrorToException(env, 432 jvmti_env, 433 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 434 JVMTI_EVENT_FIELD_ACCESS, 435 thr))) { 436 return; 437 } 438 if (field_modify != nullptr && 439 JvmtiErrorToException(env, 440 jvmti_env, 441 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 442 JVMTI_EVENT_FIELD_MODIFICATION, 443 thr))) { 444 return; 445 } 446 if (single_step != nullptr && 447 JvmtiErrorToException(env, 448 jvmti_env, 449 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 450 JVMTI_EVENT_SINGLE_STEP, 451 thr))) { 452 return; 453 } 454 } 455 456 extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing( 457 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { 458 if (JvmtiErrorToException(env, jvmti_env, 459 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 460 JVMTI_EVENT_FIELD_ACCESS, 461 thr))) { 462 return; 463 } 464 if (JvmtiErrorToException(env, jvmti_env, 465 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 466 JVMTI_EVENT_FIELD_MODIFICATION, 467 thr))) { 468 return; 469 } 470 if (JvmtiErrorToException(env, jvmti_env, 471 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 472 JVMTI_EVENT_METHOD_ENTRY, 473 thr))) { 474 return; 475 } 476 if (JvmtiErrorToException(env, jvmti_env, 477 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 478 JVMTI_EVENT_METHOD_EXIT, 479 thr))) { 480 return; 481 } 482 if (JvmtiErrorToException(env, jvmti_env, 483 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 484 JVMTI_EVENT_SINGLE_STEP, 485 thr))) { 486 return; 487 } 488 } 489 490 } // namespace common_trace 491 492 493 } // namespace art 494