1 /* 2 * Copyright 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 <cstdio> 18 #include <fstream> 19 #include <iomanip> 20 #include <iostream> 21 #include <memory> 22 #include <sstream> 23 #include <strstream> 24 25 #include <jni.h> 26 27 #include "base/utils.h" 28 #include "exec_utils.h" 29 #include "jvmti.h" 30 31 #pragma clang diagnostic push 32 33 // Slicer's headers have code that triggers these warnings. b/65298177 34 #pragma clang diagnostic ignored "-Wunused-parameter" 35 #pragma clang diagnostic ignored "-Wsign-compare" 36 37 #include "slicer/code_ir.h" 38 #include "slicer/control_flow_graph.h" 39 #include "slicer/dex_ir.h" 40 #include "slicer/dex_ir_builder.h" 41 #include "slicer/instrumentation.h" 42 #include "slicer/reader.h" 43 #include "slicer/writer.h" 44 45 #pragma clang diagnostic pop 46 47 namespace art { 48 49 // Should we do a 'full_rewrite' with this test? 50 static constexpr bool kDoFullRewrite = true; 51 52 struct StressData { 53 bool vm_class_loader_initialized; 54 bool trace_stress; 55 bool redefine_stress; 56 bool field_stress; 57 bool step_stress; 58 }; 59 60 static bool DoExtractClassFromData(jvmtiEnv* env, 61 const std::string& descriptor, 62 jint in_len, 63 const unsigned char* in_data, 64 /*out*/jint* out_len, 65 /*out*/unsigned char** out_data) { 66 dex::Reader reader(in_data, in_len); 67 dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str()); 68 if (class_idx != dex::kNoIndex) { 69 reader.CreateClassIr(class_idx); 70 } else { 71 LOG(ERROR) << "ERROR: Can't find class " << descriptor; 72 return false; 73 } 74 auto dex_ir = reader.GetIr(); 75 76 if (kDoFullRewrite) { 77 for (auto& ir_method : dex_ir->encoded_methods) { 78 if (ir_method->code != nullptr) { 79 lir::CodeIr code_ir(ir_method.get(), dex_ir); 80 lir::ControlFlowGraph cfg_compact(&code_ir, false); 81 lir::ControlFlowGraph cfg_verbose(&code_ir, true); 82 code_ir.Assemble(); 83 } 84 } 85 } 86 dex::Writer writer(dex_ir); 87 88 struct Allocator : public dex::Writer::Allocator { 89 explicit Allocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {} 90 virtual void* Allocate(size_t size) { 91 unsigned char* out = nullptr; 92 if (JVMTI_ERROR_NONE != jvmti_env_->Allocate(size, &out)) { 93 return nullptr; 94 } else { 95 return out; 96 } 97 } 98 virtual void Free(void* ptr) { 99 jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); 100 } 101 private: 102 jvmtiEnv* jvmti_env_; 103 }; 104 Allocator alloc(env); 105 size_t res_len; 106 unsigned char* res = writer.CreateImage(&alloc, &res_len); 107 if (res != nullptr) { 108 *out_data = res; 109 *out_len = res_len; 110 return true; 111 } else { 112 return false; 113 } 114 } 115 116 class ScopedThreadInfo { 117 public: 118 ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread) 119 : jvmtienv_(jvmtienv), env_(env), free_name_(false) { 120 memset(&info_, 0, sizeof(info_)); 121 if (thread == nullptr) { 122 info_.name = const_cast<char*>("<NULLPTR>"); 123 } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) { 124 info_.name = const_cast<char*>("<UNKNOWN THREAD>"); 125 } else { 126 free_name_ = true; 127 } 128 } 129 130 ~ScopedThreadInfo() { 131 if (free_name_) { 132 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name)); 133 } 134 env_->DeleteLocalRef(info_.thread_group); 135 env_->DeleteLocalRef(info_.context_class_loader); 136 } 137 138 const char* GetName() const { 139 return info_.name; 140 } 141 142 private: 143 jvmtiEnv* jvmtienv_; 144 JNIEnv* env_; 145 bool free_name_; 146 jvmtiThreadInfo info_; 147 }; 148 149 class ScopedClassInfo { 150 public: 151 ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c) 152 : jvmtienv_(jvmtienv), 153 class_(c), 154 name_(nullptr), 155 generic_(nullptr), 156 file_(nullptr), 157 debug_ext_(nullptr) {} 158 159 ~ScopedClassInfo() { 160 if (class_ != nullptr) { 161 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_)); 162 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_)); 163 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_)); 164 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_)); 165 } 166 } 167 168 bool Init() { 169 if (class_ == nullptr) { 170 name_ = const_cast<char*>("<NONE>"); 171 generic_ = const_cast<char*>("<NONE>"); 172 return true; 173 } else { 174 jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_); 175 jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_); 176 return jvmtienv_->GetClassSignature(class_, &name_, &generic_) == JVMTI_ERROR_NONE && 177 ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY && 178 ret1 != JVMTI_ERROR_INVALID_CLASS && 179 ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY && 180 ret2 != JVMTI_ERROR_INVALID_CLASS; 181 } 182 } 183 184 jclass GetClass() const { 185 return class_; 186 } 187 const char* GetName() const { 188 return name_; 189 } 190 const char* GetGeneric() const { 191 return generic_; 192 } 193 const char* GetSourceDebugExtension() const { 194 if (debug_ext_ == nullptr) { 195 return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>"; 196 } else { 197 return debug_ext_; 198 } 199 } 200 const char* GetSourceFileName() const { 201 if (file_ == nullptr) { 202 return "<UNKNOWN_FILE>"; 203 } else { 204 return file_; 205 } 206 } 207 208 private: 209 jvmtiEnv* jvmtienv_; 210 jclass class_; 211 char* name_; 212 char* generic_; 213 char* file_; 214 char* debug_ext_; 215 }; 216 217 class ScopedMethodInfo { 218 public: 219 ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m) 220 : jvmtienv_(jvmtienv), 221 env_(env), 222 method_(m), 223 declaring_class_(nullptr), 224 class_info_(nullptr), 225 name_(nullptr), 226 signature_(nullptr), 227 generic_(nullptr), 228 first_line_(-1) {} 229 230 ~ScopedMethodInfo() { 231 env_->DeleteLocalRef(declaring_class_); 232 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_)); 233 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_)); 234 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_)); 235 } 236 237 bool Init() { 238 if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) { 239 return false; 240 } 241 class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_)); 242 jint nlines; 243 jvmtiLineNumberEntry* lines; 244 jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines); 245 if (err == JVMTI_ERROR_NONE) { 246 if (nlines > 0) { 247 first_line_ = lines[0].line_number; 248 } 249 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines)); 250 } else if (err != JVMTI_ERROR_ABSENT_INFORMATION && 251 err != JVMTI_ERROR_NATIVE_METHOD) { 252 return false; 253 } 254 return class_info_->Init() && 255 (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE); 256 } 257 258 const ScopedClassInfo& GetDeclaringClassInfo() const { 259 return *class_info_; 260 } 261 262 jclass GetDeclaringClass() const { 263 return declaring_class_; 264 } 265 266 const char* GetName() const { 267 return name_; 268 } 269 270 const char* GetSignature() const { 271 return signature_; 272 } 273 274 const char* GetGeneric() const { 275 return generic_; 276 } 277 278 jint GetFirstLine() const { 279 return first_line_; 280 } 281 282 private: 283 jvmtiEnv* jvmtienv_; 284 JNIEnv* env_; 285 jmethodID method_; 286 jclass declaring_class_; 287 std::unique_ptr<ScopedClassInfo> class_info_; 288 char* name_; 289 char* signature_; 290 char* generic_; 291 jint first_line_; 292 293 friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m); 294 }; 295 296 class ScopedFieldInfo { 297 public: 298 ScopedFieldInfo(jvmtiEnv* jvmtienv, jclass field_klass, jfieldID field) 299 : jvmtienv_(jvmtienv), 300 declaring_class_(field_klass), 301 field_(field), 302 class_info_(nullptr), 303 name_(nullptr), 304 type_(nullptr), 305 generic_(nullptr) {} 306 307 ~ScopedFieldInfo() { 308 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_)); 309 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(type_)); 310 jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_)); 311 } 312 313 bool Init() { 314 class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_)); 315 return class_info_->Init() && 316 (jvmtienv_->GetFieldName( 317 declaring_class_, field_, &name_, &type_, &generic_) == JVMTI_ERROR_NONE); 318 } 319 320 const ScopedClassInfo& GetDeclaringClassInfo() const { 321 return *class_info_; 322 } 323 324 jclass GetDeclaringClass() const { 325 return declaring_class_; 326 } 327 328 const char* GetName() const { 329 return name_; 330 } 331 332 const char* GetType() const { 333 return type_; 334 } 335 336 const char* GetGeneric() const { 337 return generic_; 338 } 339 340 private: 341 jvmtiEnv* jvmtienv_; 342 jclass declaring_class_; 343 jfieldID field_; 344 std::unique_ptr<ScopedClassInfo> class_info_; 345 char* name_; 346 char* type_; 347 char* generic_; 348 349 friend std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m); 350 }; 351 352 std::ostream& operator<<(std::ostream &os, const ScopedFieldInfo* m) { 353 return os << *m; 354 } 355 356 std::ostream& operator<<(std::ostream &os, ScopedFieldInfo const& m) { 357 return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() 358 << ":" << m.GetType(); 359 } 360 361 std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) { 362 return os << *m; 363 } 364 365 std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) { 366 return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature() 367 << " (source: " << m.GetDeclaringClassInfo().GetSourceFileName() << ":" 368 << m.GetFirstLine() << ")"; 369 } 370 371 static void doJvmtiMethodBind(jvmtiEnv* jvmtienv, 372 JNIEnv* env, 373 jthread thread, 374 jmethodID m, 375 void* address, 376 /*out*/void** out_address) { 377 *out_address = address; 378 ScopedThreadInfo thread_info(jvmtienv, env, thread); 379 ScopedMethodInfo method_info(jvmtienv, env, m); 380 if (!method_info.Init()) { 381 LOG(ERROR) << "Unable to get method info!"; 382 return; 383 } 384 LOG(INFO) << "Loading native method \"" << method_info << "\". Thread is " 385 << thread_info.GetName(); 386 } 387 388 static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) { 389 jclass klass = jnienv->GetObjectClass(obj); 390 char *cname, *cgen; 391 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) { 392 LOG(ERROR) << "Unable to get class name!"; 393 jnienv->DeleteLocalRef(klass); 394 return "<UNKNOWN>"; 395 } 396 std::string name(cname); 397 if (name == "Ljava/lang/String;") { 398 jstring str = reinterpret_cast<jstring>(obj); 399 const char* val = jnienv->GetStringUTFChars(str, nullptr); 400 if (val == nullptr) { 401 name += " (unable to get value)"; 402 } else { 403 std::ostringstream oss; 404 oss << name << " (value: \"" << val << "\")"; 405 name = oss.str(); 406 jnienv->ReleaseStringUTFChars(str, val); 407 } 408 } 409 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname)); 410 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen)); 411 jnienv->DeleteLocalRef(klass); 412 return name; 413 } 414 415 static std::string GetValOf(jvmtiEnv* env, JNIEnv* jnienv, std::string type, jvalue val) { 416 std::ostringstream oss; 417 switch (type[0]) { 418 case '[': 419 case 'L': 420 return val.l != nullptr ? GetName(env, jnienv, val.l) : "null"; 421 case 'Z': 422 return val.z == JNI_TRUE ? "true" : "false"; 423 case 'B': 424 oss << val.b; 425 return oss.str(); 426 case 'C': 427 oss << val.c; 428 return oss.str(); 429 case 'S': 430 oss << val.s; 431 return oss.str(); 432 case 'I': 433 oss << val.i; 434 return oss.str(); 435 case 'J': 436 oss << val.j; 437 return oss.str(); 438 case 'F': 439 oss << val.f; 440 return oss.str(); 441 case 'D': 442 oss << val.d; 443 return oss.str(); 444 case 'V': 445 return "<void>"; 446 default: 447 return "<ERROR Found type " + type + ">"; 448 } 449 } 450 451 void JNICALL FieldAccessHook(jvmtiEnv* jvmtienv, 452 JNIEnv* env, 453 jthread thread, 454 jmethodID m, 455 jlocation location, 456 jclass field_klass, 457 jobject object, 458 jfieldID field) { 459 ScopedThreadInfo info(jvmtienv, env, thread); 460 ScopedMethodInfo method_info(jvmtienv, env, m); 461 ScopedFieldInfo field_info(jvmtienv, field_klass, field); 462 jclass oklass = (object != nullptr) ? env->GetObjectClass(object) : nullptr; 463 ScopedClassInfo obj_class_info(jvmtienv, oklass); 464 if (!method_info.Init() || !field_info.Init() || !obj_class_info.Init()) { 465 LOG(ERROR) << "Unable to get callback info!"; 466 return; 467 } 468 LOG(INFO) << "ACCESS field \"" << field_info << "\" on object of " 469 << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info 470 << "\" at location 0x" << std::hex << location << ". Thread is \"" 471 << info.GetName() << "\"."; 472 env->DeleteLocalRef(oklass); 473 } 474 475 static std::string PrintJValue(jvmtiEnv* jvmtienv, JNIEnv* env, char type, jvalue new_value) { 476 std::ostringstream oss; 477 switch (type) { 478 case 'L': { 479 jobject nv = new_value.l; 480 if (nv == nullptr) { 481 oss << "\"null\""; 482 } else { 483 jclass nv_klass = env->GetObjectClass(nv); 484 ScopedClassInfo nv_class_info(jvmtienv, nv_klass); 485 if (!nv_class_info.Init()) { 486 oss << "with unknown type"; 487 } else { 488 oss << "of type \"" << nv_class_info.GetName() << "\""; 489 } 490 env->DeleteLocalRef(nv_klass); 491 } 492 break; 493 } 494 case 'Z': { 495 if (new_value.z) { 496 oss << "true"; 497 } else { 498 oss << "false"; 499 } 500 break; 501 } 502 #define SEND_VALUE(chr, sym, type) \ 503 case chr: { \ 504 oss << static_cast<type>(new_value.sym); \ 505 break; \ 506 } 507 SEND_VALUE('B', b, int8_t); 508 SEND_VALUE('C', c, uint16_t); 509 SEND_VALUE('S', s, int16_t); 510 SEND_VALUE('I', i, int32_t); 511 SEND_VALUE('J', j, int64_t); 512 SEND_VALUE('F', f, float); 513 SEND_VALUE('D', d, double); 514 #undef SEND_VALUE 515 } 516 return oss.str(); 517 } 518 519 void JNICALL FieldModificationHook(jvmtiEnv* jvmtienv, 520 JNIEnv* env, 521 jthread thread, 522 jmethodID m, 523 jlocation location, 524 jclass field_klass, 525 jobject object, 526 jfieldID field, 527 char type, 528 jvalue new_value) { 529 ScopedThreadInfo info(jvmtienv, env, thread); 530 ScopedMethodInfo method_info(jvmtienv, env, m); 531 ScopedFieldInfo field_info(jvmtienv, field_klass, field); 532 jclass oklass = (object != nullptr) ? env->GetObjectClass(object) : nullptr; 533 ScopedClassInfo obj_class_info(jvmtienv, oklass); 534 if (!method_info.Init() || !field_info.Init() || !obj_class_info.Init()) { 535 LOG(ERROR) << "Unable to get callback info!"; 536 return; 537 } 538 LOG(INFO) << "MODIFY field \"" << field_info << "\" on object of " 539 << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info 540 << "\" at location 0x" << std::hex << location << std::dec << ". New value is " 541 << PrintJValue(jvmtienv, env, type, new_value) << ". Thread is \"" 542 << info.GetName() << "\"."; 543 env->DeleteLocalRef(oklass); 544 } 545 void JNICALL MethodExitHook(jvmtiEnv* jvmtienv, 546 JNIEnv* env, 547 jthread thread, 548 jmethodID m, 549 jboolean was_popped_by_exception, 550 jvalue val) { 551 ScopedThreadInfo info(jvmtienv, env, thread); 552 ScopedMethodInfo method_info(jvmtienv, env, m); 553 if (!method_info.Init()) { 554 LOG(ERROR) << "Unable to get method info!"; 555 return; 556 } 557 std::string type(method_info.GetSignature()); 558 type = type.substr(type.find(')') + 1); 559 std::string out_val(was_popped_by_exception ? "" : GetValOf(jvmtienv, env, type, val)); 560 LOG(INFO) << "Leaving method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"." 561 << std::endl 562 << " Cause: " << (was_popped_by_exception ? "exception" : "return ") 563 << out_val << "."; 564 } 565 566 void JNICALL MethodEntryHook(jvmtiEnv* jvmtienv, 567 JNIEnv* env, 568 jthread thread, 569 jmethodID m) { 570 ScopedThreadInfo info(jvmtienv, env, thread); 571 ScopedMethodInfo method_info(jvmtienv, env, m); 572 if (!method_info.Init()) { 573 LOG(ERROR) << "Unable to get method info!"; 574 return; 575 } 576 LOG(INFO) << "Entering method \"" << method_info << "\". Thread is \"" << info.GetName() << "\""; 577 } 578 579 void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv, 580 JNIEnv* env, 581 jthread thread, 582 jclass klass) { 583 StressData* data = nullptr; 584 CHECK_EQ(jvmtienv->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)), 585 JVMTI_ERROR_NONE); 586 if (data->field_stress) { 587 jint nfields; 588 jfieldID* fields; 589 if (jvmtienv->GetClassFields(klass, &nfields, &fields) != JVMTI_ERROR_NONE) { 590 LOG(ERROR) << "Unable to get a classes fields!"; 591 return; 592 } 593 for (jint i = 0; i < nfields; i++) { 594 jfieldID f = fields[i]; 595 // Ignore errors 596 jvmtienv->SetFieldAccessWatch(klass, f); 597 jvmtienv->SetFieldModificationWatch(klass, f); 598 } 599 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fields)); 600 } 601 if (data->trace_stress) { 602 ScopedThreadInfo info(jvmtienv, env, thread); 603 ScopedClassInfo class_info(jvmtienv, klass); 604 if (!class_info.Init()) { 605 LOG(ERROR) << "Unable to get class info!"; 606 return; 607 } 608 LOG(INFO) << "Prepared class \"" << class_info.GetName() << "\". Thread is \"" 609 << info.GetName() << "\""; 610 } 611 } 612 613 void JNICALL SingleStepHook(jvmtiEnv* jvmtienv, 614 JNIEnv* env, 615 jthread thread, 616 jmethodID method, 617 jlocation location) { 618 ScopedThreadInfo info(jvmtienv, env, thread); 619 ScopedMethodInfo method_info(jvmtienv, env, method); 620 if (!method_info.Init()) { 621 LOG(ERROR) << "Unable to get method info!"; 622 return; 623 } 624 LOG(INFO) << "Single step at location: 0x" << std::setw(8) << std::setfill('0') << std::hex 625 << location << " in method " << method_info << " thread: " << info.GetName(); 626 } 627 628 // The hook we are using. 629 void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti, 630 JNIEnv* jni_env ATTRIBUTE_UNUSED, 631 jclass class_being_redefined ATTRIBUTE_UNUSED, 632 jobject loader ATTRIBUTE_UNUSED, 633 const char* name, 634 jobject protection_domain ATTRIBUTE_UNUSED, 635 jint class_data_len, 636 const unsigned char* class_data, 637 jint* new_class_data_len, 638 unsigned char** new_class_data) { 639 std::vector<unsigned char> out; 640 // Make the jvmti semi-descriptor into the full descriptor. 641 std::string name_str("L"); 642 name_str += name; 643 name_str += ";"; 644 StressData* data = nullptr; 645 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)), 646 JVMTI_ERROR_NONE); 647 if (!data->vm_class_loader_initialized) { 648 LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet " 649 << "initialized. Transforming this class could cause spurious test failures."; 650 return; 651 } else if (DoExtractClassFromData(jvmti, name_str, class_data_len, class_data, 652 /*out*/ new_class_data_len, /*out*/ new_class_data)) { 653 LOG(INFO) << "Extracted class: " << name; 654 } else { 655 std::cerr << "Unable to extract class " << name << std::endl; 656 *new_class_data_len = 0; 657 *new_class_data = nullptr; 658 } 659 } 660 661 static std::string AdvanceOption(const std::string& ops) { 662 return ops.substr(ops.find(',') + 1); 663 } 664 665 static bool HasNextOption(const std::string& ops) { 666 return ops.find(',') != std::string::npos; 667 } 668 669 static std::string GetOption(const std::string& in) { 670 return in.substr(0, in.find(',')); 671 } 672 673 // Options are 674 // jvmti-stress,[redefine,][trace,][field] 675 static void ReadOptions(StressData* data, char* options) { 676 std::string ops(options); 677 CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress"; 678 do { 679 ops = AdvanceOption(ops); 680 std::string cur = GetOption(ops); 681 if (cur == "trace") { 682 data->trace_stress = true; 683 } else if (cur == "step") { 684 data->step_stress = true; 685 } else if (cur == "field") { 686 data->field_stress = true; 687 } else if (cur == "redefine") { 688 data->redefine_stress = true; 689 } else { 690 LOG(FATAL) << "Unknown option: " << GetOption(ops); 691 } 692 } while (HasNextOption(ops)); 693 } 694 695 // Do final setup during the VMInit callback. By this time most things are all setup. 696 static void JNICALL PerformFinalSetupVMInit(jvmtiEnv *jvmti_env, 697 JNIEnv* jni_env, 698 jthread thread ATTRIBUTE_UNUSED) { 699 // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have 700 // visibility but the class will be loaded behind the scenes. 701 LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!"; 702 jclass klass = jni_env->FindClass("java/lang/VMClassLoader"); 703 StressData* data = nullptr; 704 CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)), 705 JVMTI_ERROR_NONE); 706 // We need to make sure that VMClassLoader is initialized before we start redefining anything 707 // since it can give (non-fatal) error messages if it's initialized after we've redefined BCP 708 // classes. These error messages are expected and no problem but they will mess up our testing 709 // infrastructure. 710 if (klass == nullptr) { 711 // Probably on RI. Clear the exception so we can continue but don't mark vmclassloader as 712 // initialized. 713 LOG(WARNING) << "Unable to find VMClassLoader class!"; 714 jni_env->ExceptionClear(); 715 } else { 716 // GetMethodID is spec'd to cause the class to be initialized. 717 jni_env->GetMethodID(klass, "hashCode", "()I"); 718 jni_env->DeleteLocalRef(klass); 719 data->vm_class_loader_initialized = true; 720 } 721 } 722 723 static bool WatchAllFields(JavaVM* vm, jvmtiEnv* jvmti) { 724 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 725 JVMTI_EVENT_CLASS_PREPARE, 726 nullptr) != JVMTI_ERROR_NONE) { 727 LOG(ERROR) << "Couldn't set prepare event!"; 728 return false; 729 } 730 // TODO We really shouldn't need to do this step here. 731 jint nklass; 732 jclass* klasses; 733 if (jvmti->GetLoadedClasses(&nklass, &klasses) != JVMTI_ERROR_NONE) { 734 LOG(WARNING) << "Couldn't get loaded classes! Ignoring."; 735 return true; 736 } 737 JNIEnv* jni = nullptr; 738 if (vm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6)) { 739 LOG(ERROR) << "Unable to get jni env. Ignoring and potentially leaking jobjects."; 740 return false; 741 } 742 for (jint i = 0; i < nklass; i++) { 743 jclass k = klasses[i]; 744 ScopedClassInfo sci(jvmti, k); 745 if (sci.Init()) { 746 LOG(INFO) << "NOTE: class " << sci.GetName() << " already loaded."; 747 } 748 jint nfields; 749 jfieldID* fields; 750 jvmtiError err = jvmti->GetClassFields(k, &nfields, &fields); 751 if (err == JVMTI_ERROR_NONE) { 752 for (jint j = 0; j < nfields; j++) { 753 jfieldID f = fields[j]; 754 if (jvmti->SetFieldModificationWatch(k, f) != JVMTI_ERROR_NONE || 755 jvmti->SetFieldAccessWatch(k, f) != JVMTI_ERROR_NONE) { 756 LOG(ERROR) << "Unable to set watches on a field."; 757 return false; 758 } 759 } 760 } else if (err != JVMTI_ERROR_CLASS_NOT_PREPARED) { 761 LOG(ERROR) << "Unexpected error getting class fields!"; 762 return false; 763 } 764 jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields)); 765 jni->DeleteLocalRef(k); 766 } 767 jvmti->Deallocate(reinterpret_cast<unsigned char*>(klasses)); 768 return true; 769 } 770 771 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, 772 char* options, 773 void* reserved ATTRIBUTE_UNUSED) { 774 jvmtiEnv* jvmti = nullptr; 775 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) { 776 LOG(ERROR) << "Unable to get jvmti env."; 777 return 1; 778 } 779 StressData* data = nullptr; 780 if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData), 781 reinterpret_cast<unsigned char**>(&data))) { 782 LOG(ERROR) << "Unable to allocate data for stress test."; 783 return 1; 784 } 785 memset(data, 0, sizeof(StressData)); 786 // Read the options into the static variables that hold them. 787 ReadOptions(data, options); 788 // Save the data 789 if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) { 790 LOG(ERROR) << "Unable to save stress test data."; 791 return 1; 792 } 793 794 // Just get all capabilities. 795 jvmtiCapabilities caps = { 796 .can_tag_objects = 0, 797 .can_generate_field_modification_events = 1, 798 .can_generate_field_access_events = 1, 799 .can_get_bytecodes = 0, 800 .can_get_synthetic_attribute = 0, 801 .can_get_owned_monitor_info = 0, 802 .can_get_current_contended_monitor = 0, 803 .can_get_monitor_info = 0, 804 .can_pop_frame = 0, 805 .can_redefine_classes = 1, 806 .can_signal_thread = 0, 807 .can_get_source_file_name = 1, 808 .can_get_line_numbers = 1, 809 .can_get_source_debug_extension = 1, 810 .can_access_local_variables = 0, 811 .can_maintain_original_method_order = 0, 812 .can_generate_single_step_events = 1, 813 .can_generate_exception_events = 0, 814 .can_generate_frame_pop_events = 0, 815 .can_generate_breakpoint_events = 0, 816 .can_suspend = 0, 817 .can_redefine_any_class = 0, 818 .can_get_current_thread_cpu_time = 0, 819 .can_get_thread_cpu_time = 0, 820 .can_generate_method_entry_events = 1, 821 .can_generate_method_exit_events = 1, 822 .can_generate_all_class_hook_events = 0, 823 .can_generate_compiled_method_load_events = 0, 824 .can_generate_monitor_events = 0, 825 .can_generate_vm_object_alloc_events = 0, 826 .can_generate_native_method_bind_events = 1, 827 .can_generate_garbage_collection_events = 0, 828 .can_generate_object_free_events = 0, 829 .can_force_early_return = 0, 830 .can_get_owned_monitor_stack_depth_info = 0, 831 .can_get_constant_pool = 0, 832 .can_set_native_method_prefix = 0, 833 .can_retransform_classes = 1, 834 .can_retransform_any_class = 0, 835 .can_generate_resource_exhaustion_heap_events = 0, 836 .can_generate_resource_exhaustion_threads_events = 0, 837 }; 838 jvmti->AddCapabilities(&caps); 839 840 // Set callbacks. 841 jvmtiEventCallbacks cb; 842 memset(&cb, 0, sizeof(cb)); 843 cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp; 844 cb.NativeMethodBind = doJvmtiMethodBind; 845 cb.VMInit = PerformFinalSetupVMInit; 846 cb.MethodEntry = MethodEntryHook; 847 cb.MethodExit = MethodExitHook; 848 cb.FieldAccess = FieldAccessHook; 849 cb.FieldModification = FieldModificationHook; 850 cb.ClassPrepare = ClassPrepareHook; 851 cb.SingleStep = SingleStepHook; 852 if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) { 853 LOG(ERROR) << "Unable to set class file load hook cb!"; 854 return 1; 855 } 856 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 857 JVMTI_EVENT_VM_INIT, 858 nullptr) != JVMTI_ERROR_NONE) { 859 LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!"; 860 return 1; 861 } 862 if (data->redefine_stress) { 863 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 864 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, 865 nullptr) != JVMTI_ERROR_NONE) { 866 LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!"; 867 return 1; 868 } 869 } 870 if (data->trace_stress) { 871 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 872 JVMTI_EVENT_CLASS_PREPARE, 873 nullptr) != JVMTI_ERROR_NONE) { 874 LOG(ERROR) << "Unable to enable CLASS_PREPARE event!"; 875 return 1; 876 } 877 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 878 JVMTI_EVENT_NATIVE_METHOD_BIND, 879 nullptr) != JVMTI_ERROR_NONE) { 880 LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!"; 881 return 1; 882 } 883 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 884 JVMTI_EVENT_METHOD_ENTRY, 885 nullptr) != JVMTI_ERROR_NONE) { 886 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_ENTRY event!"; 887 return 1; 888 } 889 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 890 JVMTI_EVENT_METHOD_EXIT, 891 nullptr) != JVMTI_ERROR_NONE) { 892 LOG(ERROR) << "Unable to enable JVMTI_EVENT_METHOD_EXIT event!"; 893 return 1; 894 } 895 } 896 if (data->field_stress) { 897 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 898 JVMTI_EVENT_FIELD_MODIFICATION, 899 nullptr) != JVMTI_ERROR_NONE) { 900 LOG(ERROR) << "Unable to enable FIELD_MODIFICATION event!"; 901 return 1; 902 } 903 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 904 JVMTI_EVENT_FIELD_ACCESS, 905 nullptr) != JVMTI_ERROR_NONE) { 906 LOG(ERROR) << "Unable to enable FIELD_ACCESS event!"; 907 return 1; 908 } 909 if (!WatchAllFields(vm, jvmti)) { 910 return 1; 911 } 912 } 913 if (data->step_stress) { 914 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE, 915 JVMTI_EVENT_SINGLE_STEP, 916 nullptr) != JVMTI_ERROR_NONE) { 917 return 1; 918 } 919 } 920 return 0; 921 } 922 923 } // namespace art 924