1 /* 2 * Copyright (C) 2013 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 <inttypes.h> 18 19 #include <cstdio> 20 #include <cstring> 21 #include <iostream> 22 #include <map> 23 #include <sstream> 24 #include <vector> 25 26 #include "android-base/logging.h" 27 #include "android-base/macros.h" 28 #include "android-base/stringprintf.h" 29 30 #include "jni.h" 31 #include "jvmti.h" 32 33 // Test infrastructure 34 #include "jni_helper.h" 35 #include "jvmti_helper.h" 36 #include "test_env.h" 37 #include "ti_utf.h" 38 39 namespace art { 40 namespace Test913Heaps { 41 42 using android::base::StringPrintf; 43 44 #define FINAL final 45 #define OVERRIDE override 46 #define UNREACHABLE __builtin_unreachable 47 48 extern "C" JNIEXPORT void JNICALL Java_art_Test913_forceGarbageCollection( 49 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { 50 jvmtiError ret = jvmti_env->ForceGarbageCollection(); 51 JvmtiErrorToException(env, jvmti_env, ret); 52 } 53 54 // Collect sizes of objects (classes) ahead of time, to be able to normalize. 55 struct ClassData { 56 jlong size; // Size as reported by GetObjectSize. 57 jlong serial; // Computed serial that should be printed instead of the size. 58 }; 59 60 // Stores a map from tags to ClassData. 61 static std::map<jlong, ClassData> sClassData; 62 static size_t sClassDataSerial = 0; 63 // Large enough number that a collision with a test object is unlikely. 64 static constexpr jlong kClassDataSerialBase = 123456780000; 65 66 // Register a class (or general object) in the class-data map. The serial number is determined by 67 // the order of calls to this function (so stable Java code leads to stable numbering). 68 extern "C" JNIEXPORT void JNICALL Java_art_Test913_registerClass( 69 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag, jobject obj) { 70 ClassData data; 71 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetObjectSize(obj, &data.size))) { 72 return; 73 } 74 data.serial = kClassDataSerialBase + sClassDataSerial++; 75 // Remove old element, if it exists. 76 auto old = sClassData.find(tag); 77 if (old != sClassData.end()) { 78 sClassData.erase(old); 79 } 80 // Now insert the new mapping. 81 sClassData.insert(std::pair<jlong, ClassData>(tag, data)); 82 } 83 84 class IterationConfig { 85 public: 86 IterationConfig() {} 87 virtual ~IterationConfig() {} 88 89 virtual jint Handle(jvmtiHeapReferenceKind reference_kind, 90 const jvmtiHeapReferenceInfo* reference_info, 91 jlong class_tag, 92 jlong referrer_class_tag, 93 jlong size, 94 jlong* tag_ptr, 95 jlong* referrer_tag_ptr, 96 jint length, 97 void* user_data) = 0; 98 }; 99 100 static jint JNICALL HeapReferenceCallback(jvmtiHeapReferenceKind reference_kind, 101 const jvmtiHeapReferenceInfo* reference_info, 102 jlong class_tag, 103 jlong referrer_class_tag, 104 jlong size, 105 jlong* tag_ptr, 106 jlong* referrer_tag_ptr, 107 jint length, 108 void* user_data) { 109 IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data); 110 return config->Handle(reference_kind, 111 reference_info, 112 class_tag, 113 referrer_class_tag, 114 size, 115 tag_ptr, 116 referrer_tag_ptr, 117 length, 118 user_data); 119 } 120 121 static bool Run(JNIEnv* env, 122 jint heap_filter, 123 jclass klass_filter, 124 jobject initial_object, 125 IterationConfig* config) { 126 jvmtiHeapCallbacks callbacks; 127 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 128 callbacks.heap_reference_callback = HeapReferenceCallback; 129 130 jvmtiError ret = jvmti_env->FollowReferences(heap_filter, 131 klass_filter, 132 initial_object, 133 &callbacks, 134 config); 135 return !JvmtiErrorToException(env, jvmti_env, ret); 136 } 137 138 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test913_followReferences( 139 JNIEnv* env, 140 jclass klass ATTRIBUTE_UNUSED, 141 jint heap_filter, 142 jclass klass_filter, 143 jobject initial_object, 144 jint stop_after, 145 jint follow_set, 146 jobject jniRef) { 147 class PrintIterationConfig FINAL : public IterationConfig { 148 public: 149 PrintIterationConfig(jint _stop_after, jint _follow_set) 150 : counter_(0), 151 stop_after_(_stop_after), 152 follow_set_(_follow_set) { 153 } 154 155 jint Handle(jvmtiHeapReferenceKind reference_kind, 156 const jvmtiHeapReferenceInfo* reference_info, 157 jlong class_tag, 158 jlong referrer_class_tag, 159 jlong size, 160 jlong* tag_ptr, 161 jlong* referrer_tag_ptr, 162 jint length, 163 void* user_data ATTRIBUTE_UNUSED) OVERRIDE { 164 jlong tag = *tag_ptr; 165 166 // Ignore any jni-global roots with untagged classes. These can be from the environment, 167 // or the JIT. 168 if (reference_kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL && class_tag == 0) { 169 return 0; 170 } 171 // Ignore classes (1000 <= tag < 3000) for thread objects. These can be held by the JIT. 172 if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && class_tag == 0 && 173 (1000 <= *tag_ptr && *tag_ptr < 3000)) { 174 return 0; 175 } 176 // Ignore stack-locals of untagged threads. That is the environment. 177 if (reference_kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL && 178 reference_info->stack_local.thread_tag != 3000) { 179 return 0; 180 } 181 // Ignore array elements with an untagged source. These are from the environment. 182 if (reference_kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT && *referrer_tag_ptr == 0) { 183 return 0; 184 } 185 186 // Only check tagged objects. 187 if (tag == 0) { 188 return JVMTI_VISIT_OBJECTS; 189 } 190 191 Print(reference_kind, 192 reference_info, 193 class_tag, 194 referrer_class_tag, 195 size, 196 tag_ptr, 197 referrer_tag_ptr, 198 length); 199 200 counter_++; 201 if (counter_ == stop_after_) { 202 return JVMTI_VISIT_ABORT; 203 } 204 205 if (tag > 0 && tag < 32) { 206 bool should_visit_references = (follow_set_ & (1 << static_cast<int32_t>(tag))) != 0; 207 return should_visit_references ? JVMTI_VISIT_OBJECTS : 0; 208 } 209 210 return JVMTI_VISIT_OBJECTS; 211 } 212 213 void Print(jvmtiHeapReferenceKind reference_kind, 214 const jvmtiHeapReferenceInfo* reference_info, 215 jlong class_tag, 216 jlong referrer_class_tag, 217 jlong size, 218 jlong* tag_ptr, 219 jlong* referrer_tag_ptr, 220 jint length) { 221 std::string referrer_str; 222 if (referrer_tag_ptr == nullptr) { 223 referrer_str = "root@root"; 224 } else { 225 referrer_str = StringPrintf("%" PRId64 "@%" PRId64, *referrer_tag_ptr, referrer_class_tag); 226 } 227 228 jlong adapted_size = size; 229 if (*tag_ptr != 0) { 230 // This is a class or interface, the size of which will be dependent on the architecture. 231 // Do not print the size, but detect known values and "normalize" for the golden file. 232 auto it = sClassData.find(*tag_ptr); 233 if (it != sClassData.end()) { 234 const ClassData& class_data = it->second; 235 if (class_data.size == size) { 236 adapted_size = class_data.serial; 237 } else { 238 adapted_size = 0xDEADDEAD; 239 } 240 } 241 } 242 243 std::string referree_str = StringPrintf("%" PRId64 "@%" PRId64, *tag_ptr, class_tag); 244 245 lines_.push_back(CreateElem(referrer_str, 246 referree_str, 247 reference_kind, 248 reference_info, 249 adapted_size, 250 length)); 251 } 252 253 std::vector<std::string> GetLines() const { 254 std::vector<std::string> ret; 255 for (const std::unique_ptr<Elem>& e : lines_) { 256 ret.push_back(e->Print()); 257 } 258 return ret; 259 } 260 261 private: 262 // We need to postpone some printing, as required functions are not callback-safe. 263 class Elem { 264 public: 265 Elem(const std::string& referrer, const std::string& referree, jlong size, jint length) 266 : referrer_(referrer), referree_(referree), size_(size), length_(length) {} 267 virtual ~Elem() {} 268 269 std::string Print() const { 270 return StringPrintf("%s --(%s)--> %s [size=%" PRId64 ", length=%d]", 271 referrer_.c_str(), 272 PrintArrowType().c_str(), 273 referree_.c_str(), 274 size_, 275 length_); 276 } 277 278 protected: 279 virtual std::string PrintArrowType() const = 0; 280 281 private: 282 std::string referrer_; 283 std::string referree_; 284 jlong size_; 285 jint length_; 286 }; 287 288 class JNILocalElement : public Elem { 289 public: 290 JNILocalElement(const std::string& referrer, 291 const std::string& referree, 292 jlong size, 293 jint length, 294 const jvmtiHeapReferenceInfo* reference_info) 295 : Elem(referrer, referree, size, length) { 296 memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo)); 297 } 298 299 protected: 300 std::string PrintArrowType() const OVERRIDE { 301 char* name = nullptr; 302 if (info_.jni_local.method != nullptr) { 303 jvmti_env->GetMethodName(info_.jni_local.method, &name, nullptr, nullptr); 304 } 305 // Normalize the thread id, as this depends on the number of other threads 306 // and which thread is running the test. Should be: 307 // jlong thread_id = info_.jni_local.thread_id; 308 // TODO: A pre-pass before the test should be able fetch this number, so it can 309 // be compared explicitly. 310 jlong thread_id = 1; 311 std::string ret = StringPrintf("jni-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d," 312 "method=%s]", 313 thread_id, 314 info_.jni_local.thread_tag, 315 info_.jni_local.depth, 316 name == nullptr ? "<null>" : name); 317 if (name != nullptr) { 318 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name)); 319 } 320 321 return ret; 322 } 323 324 private: 325 const std::string string_; 326 jvmtiHeapReferenceInfo info_; 327 }; 328 329 class StackLocalElement : public Elem { 330 public: 331 StackLocalElement(const std::string& referrer, 332 const std::string& referree, 333 jlong size, 334 jint length, 335 const jvmtiHeapReferenceInfo* reference_info) 336 : Elem(referrer, referree, size, length) { 337 memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo)); 338 339 // Debug code. Try to figure out where bad depth is coming from. 340 if (reference_info->stack_local.depth == 6) { 341 LOG(FATAL) << "Unexpected depth of 6"; 342 } 343 } 344 345 protected: 346 std::string PrintArrowType() const OVERRIDE { 347 char* name = nullptr; 348 if (info_.stack_local.method != nullptr) { 349 jvmti_env->GetMethodName(info_.stack_local.method, &name, nullptr, nullptr); 350 } 351 // Normalize the thread id, as this depends on the number of other threads 352 // and which thread is running the test. Should be: 353 // jlong thread_id = info_.stack_local.thread_id; 354 // TODO: A pre-pass before the test should be able fetch this number, so it can 355 // be compared explicitly. 356 jlong thread_id = 1; 357 std::string ret = StringPrintf("stack-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d," 358 "method=%s,vreg=%d,location=% " PRId64 "]", 359 thread_id, 360 info_.stack_local.thread_tag, 361 info_.stack_local.depth, 362 name == nullptr ? "<null>" : name, 363 info_.stack_local.slot, 364 info_.stack_local.location); 365 if (name != nullptr) { 366 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name)); 367 } 368 369 return ret; 370 } 371 372 private: 373 const std::string string_; 374 jvmtiHeapReferenceInfo info_; 375 }; 376 377 // For simple or unimplemented cases. 378 class StringElement : public Elem { 379 public: 380 StringElement(const std::string& referrer, 381 const std::string& referree, 382 jlong size, 383 jint length, 384 const std::string& string) 385 : Elem(referrer, referree, size, length), string_(string) {} 386 387 protected: 388 std::string PrintArrowType() const OVERRIDE { 389 return string_; 390 } 391 392 private: 393 const std::string string_; 394 }; 395 396 static std::unique_ptr<Elem> CreateElem(const std::string& referrer, 397 const std::string& referree, 398 jvmtiHeapReferenceKind reference_kind, 399 const jvmtiHeapReferenceInfo* reference_info, 400 jlong size, 401 jint length) { 402 switch (reference_kind) { 403 case JVMTI_HEAP_REFERENCE_CLASS: 404 return std::unique_ptr<Elem>(new StringElement(referrer, 405 referree, 406 size, 407 length, 408 "class")); 409 case JVMTI_HEAP_REFERENCE_FIELD: { 410 std::string tmp = StringPrintf("field@%d", reference_info->field.index); 411 return std::unique_ptr<Elem>(new StringElement(referrer, 412 referree, 413 size, 414 length, 415 tmp)); 416 } 417 case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: { 418 jint index = reference_info->array.index; 419 // Normalize if it's "0@0" -> "3000@1". 420 // TODO: A pre-pass could probably give us this index to check explicitly. 421 if (referrer == "0@0" && referree == "3000@0") { 422 index = 0; 423 } 424 std::string tmp = StringPrintf("array-element@%d", index); 425 return std::unique_ptr<Elem>(new StringElement(referrer, 426 referree, 427 size, 428 length, 429 tmp)); 430 } 431 case JVMTI_HEAP_REFERENCE_CLASS_LOADER: 432 return std::unique_ptr<Elem>(new StringElement(referrer, 433 referree, 434 size, 435 length, 436 "classloader")); 437 case JVMTI_HEAP_REFERENCE_SIGNERS: 438 return std::unique_ptr<Elem>(new StringElement(referrer, 439 referree, 440 size, 441 length, 442 "signers")); 443 case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN: 444 return std::unique_ptr<Elem>(new StringElement(referrer, 445 referree, 446 size, 447 length, 448 "protection-domain")); 449 case JVMTI_HEAP_REFERENCE_INTERFACE: 450 return std::unique_ptr<Elem>(new StringElement(referrer, 451 referree, 452 size, 453 length, 454 "interface")); 455 case JVMTI_HEAP_REFERENCE_STATIC_FIELD: { 456 std::string tmp = StringPrintf("array-element@%d", reference_info->array.index); 457 return std::unique_ptr<Elem>(new StringElement(referrer, 458 referree, 459 size, 460 length, 461 tmp));; 462 } 463 case JVMTI_HEAP_REFERENCE_CONSTANT_POOL: 464 return std::unique_ptr<Elem>(new StringElement(referrer, 465 referree, 466 size, 467 length, 468 "constant-pool")); 469 case JVMTI_HEAP_REFERENCE_SUPERCLASS: 470 return std::unique_ptr<Elem>(new StringElement(referrer, 471 referree, 472 size, 473 length, 474 "superclass")); 475 case JVMTI_HEAP_REFERENCE_JNI_GLOBAL: 476 return std::unique_ptr<Elem>(new StringElement(referrer, 477 referree, 478 size, 479 length, 480 "jni-global")); 481 case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS: 482 return std::unique_ptr<Elem>(new StringElement(referrer, 483 referree, 484 size, 485 length, 486 "system-class")); 487 case JVMTI_HEAP_REFERENCE_MONITOR: 488 return std::unique_ptr<Elem>(new StringElement(referrer, 489 referree, 490 size, 491 length, 492 "monitor")); 493 case JVMTI_HEAP_REFERENCE_STACK_LOCAL: 494 return std::unique_ptr<Elem>(new StackLocalElement(referrer, 495 referree, 496 size, 497 length, 498 reference_info)); 499 case JVMTI_HEAP_REFERENCE_JNI_LOCAL: 500 return std::unique_ptr<Elem>(new JNILocalElement(referrer, 501 referree, 502 size, 503 length, 504 reference_info)); 505 case JVMTI_HEAP_REFERENCE_THREAD: 506 return std::unique_ptr<Elem>(new StringElement(referrer, 507 referree, 508 size, 509 length, 510 "thread")); 511 case JVMTI_HEAP_REFERENCE_OTHER: 512 return std::unique_ptr<Elem>(new StringElement(referrer, 513 referree, 514 size, 515 length, 516 "other")); 517 } 518 LOG(FATAL) << "Unknown kind"; 519 UNREACHABLE(); 520 } 521 522 jint counter_; 523 const jint stop_after_; 524 const jint follow_set_; 525 526 std::vector<std::unique_ptr<Elem>> lines_; 527 }; 528 529 // If jniRef isn't null, add a local and a global ref. 530 ScopedLocalRef<jobject> jni_local_ref(env, nullptr); 531 jobject jni_global_ref = nullptr; 532 if (jniRef != nullptr) { 533 jni_local_ref.reset(env->NewLocalRef(jniRef)); 534 jni_global_ref = env->NewGlobalRef(jniRef); 535 } 536 537 PrintIterationConfig config(stop_after, follow_set); 538 if (!Run(env, heap_filter, klass_filter, initial_object, &config)) { 539 return nullptr; 540 } 541 542 std::vector<std::string> lines = config.GetLines(); 543 jobjectArray ret = CreateObjectArray(env, 544 static_cast<jint>(lines.size()), 545 "java/lang/String", 546 [&](jint i) { 547 return env->NewStringUTF(lines[i].c_str()); 548 }); 549 550 if (jni_global_ref != nullptr) { 551 env->DeleteGlobalRef(jni_global_ref); 552 } 553 554 return ret; 555 } 556 557 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test913_followReferencesString( 558 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject initial_object) { 559 struct FindStringCallbacks { 560 static jint JNICALL FollowReferencesCallback( 561 jvmtiHeapReferenceKind reference_kind ATTRIBUTE_UNUSED, 562 const jvmtiHeapReferenceInfo* reference_info ATTRIBUTE_UNUSED, 563 jlong class_tag ATTRIBUTE_UNUSED, 564 jlong referrer_class_tag ATTRIBUTE_UNUSED, 565 jlong size ATTRIBUTE_UNUSED, 566 jlong* tag_ptr ATTRIBUTE_UNUSED, 567 jlong* referrer_tag_ptr ATTRIBUTE_UNUSED, 568 jint length ATTRIBUTE_UNUSED, 569 void* user_data ATTRIBUTE_UNUSED) { 570 return JVMTI_VISIT_OBJECTS; // Continue visiting. 571 } 572 573 static jint JNICALL StringValueCallback(jlong class_tag, 574 jlong size, 575 jlong* tag_ptr, 576 const jchar* value, 577 jint value_length, 578 void* user_data) { 579 FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data); 580 if (*tag_ptr != 0) { 581 size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length); 582 std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]); 583 memset(mod_utf.get(), 0, utf_byte_count + 1); 584 ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length); 585 p->data.push_back(android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')", 586 *tag_ptr, 587 class_tag, 588 size, 589 mod_utf.get())); 590 // Update the tag to test whether that works. 591 *tag_ptr = *tag_ptr + 1; 592 } 593 return 0; 594 } 595 596 std::vector<std::string> data; 597 }; 598 599 jvmtiHeapCallbacks callbacks; 600 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 601 callbacks.heap_reference_callback = FindStringCallbacks::FollowReferencesCallback; 602 callbacks.string_primitive_value_callback = FindStringCallbacks::StringValueCallback; 603 604 FindStringCallbacks fsc; 605 jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fsc); 606 if (JvmtiErrorToException(env, jvmti_env, ret)) { 607 return nullptr; 608 } 609 610 jobjectArray retArray = CreateObjectArray(env, 611 static_cast<jint>(fsc.data.size()), 612 "java/lang/String", 613 [&](jint i) { 614 return env->NewStringUTF(fsc.data[i].c_str()); 615 }); 616 return retArray; 617 } 618 619 620 extern "C" JNIEXPORT jstring JNICALL Java_art_Test913_followReferencesPrimitiveArray( 621 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject initial_object) { 622 struct FindArrayCallbacks { 623 static jint JNICALL FollowReferencesCallback( 624 jvmtiHeapReferenceKind reference_kind ATTRIBUTE_UNUSED, 625 const jvmtiHeapReferenceInfo* reference_info ATTRIBUTE_UNUSED, 626 jlong class_tag ATTRIBUTE_UNUSED, 627 jlong referrer_class_tag ATTRIBUTE_UNUSED, 628 jlong size ATTRIBUTE_UNUSED, 629 jlong* tag_ptr ATTRIBUTE_UNUSED, 630 jlong* referrer_tag_ptr ATTRIBUTE_UNUSED, 631 jint length ATTRIBUTE_UNUSED, 632 void* user_data ATTRIBUTE_UNUSED) { 633 return JVMTI_VISIT_OBJECTS; // Continue visiting. 634 } 635 636 static jint JNICALL ArrayValueCallback(jlong class_tag, 637 jlong size, 638 jlong* tag_ptr, 639 jint element_count, 640 jvmtiPrimitiveType element_type, 641 const void* elements, 642 void* user_data) { 643 FindArrayCallbacks* p = reinterpret_cast<FindArrayCallbacks*>(user_data); 644 if (*tag_ptr != 0) { 645 std::ostringstream oss; 646 oss << *tag_ptr 647 << '@' 648 << class_tag 649 << " (" 650 << size 651 << ", " 652 << element_count 653 << "x" 654 << static_cast<char>(element_type) 655 << " '"; 656 size_t element_size; 657 switch (element_type) { 658 case JVMTI_PRIMITIVE_TYPE_BOOLEAN: 659 case JVMTI_PRIMITIVE_TYPE_BYTE: 660 element_size = 1; 661 break; 662 case JVMTI_PRIMITIVE_TYPE_CHAR: 663 case JVMTI_PRIMITIVE_TYPE_SHORT: 664 element_size = 2; 665 break; 666 case JVMTI_PRIMITIVE_TYPE_INT: 667 case JVMTI_PRIMITIVE_TYPE_FLOAT: 668 element_size = 4; 669 break; 670 case JVMTI_PRIMITIVE_TYPE_LONG: 671 case JVMTI_PRIMITIVE_TYPE_DOUBLE: 672 element_size = 8; 673 break; 674 default: 675 LOG(FATAL) << "Unknown type " << static_cast<size_t>(element_type); 676 UNREACHABLE(); 677 } 678 const uint8_t* data = reinterpret_cast<const uint8_t*>(elements); 679 for (size_t i = 0; i != element_size * element_count; ++i) { 680 oss << android::base::StringPrintf("%02x", data[i]); 681 } 682 oss << "')"; 683 684 if (!p->data.empty()) { 685 p->data += "\n"; 686 } 687 p->data += oss.str(); 688 // Update the tag to test whether that works. 689 *tag_ptr = *tag_ptr + 1; 690 } 691 return 0; 692 } 693 694 std::string data; 695 }; 696 697 jvmtiHeapCallbacks callbacks; 698 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 699 callbacks.heap_reference_callback = FindArrayCallbacks::FollowReferencesCallback; 700 callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback; 701 702 FindArrayCallbacks fac; 703 jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fac); 704 if (JvmtiErrorToException(env, jvmti_env, ret)) { 705 return nullptr; 706 } 707 return env->NewStringUTF(fac.data.c_str()); 708 } 709 710 static constexpr const char* GetPrimitiveTypeName(jvmtiPrimitiveType type) { 711 switch (type) { 712 case JVMTI_PRIMITIVE_TYPE_BOOLEAN: 713 return "boolean"; 714 case JVMTI_PRIMITIVE_TYPE_BYTE: 715 return "byte"; 716 case JVMTI_PRIMITIVE_TYPE_CHAR: 717 return "char"; 718 case JVMTI_PRIMITIVE_TYPE_SHORT: 719 return "short"; 720 case JVMTI_PRIMITIVE_TYPE_INT: 721 return "int"; 722 case JVMTI_PRIMITIVE_TYPE_FLOAT: 723 return "float"; 724 case JVMTI_PRIMITIVE_TYPE_LONG: 725 return "long"; 726 case JVMTI_PRIMITIVE_TYPE_DOUBLE: 727 return "double"; 728 } 729 LOG(FATAL) << "Unknown type " << static_cast<size_t>(type); 730 UNREACHABLE(); 731 } 732 733 extern "C" JNIEXPORT jstring JNICALL Java_art_Test913_followReferencesPrimitiveFields( 734 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject initial_object) { 735 struct FindFieldCallbacks { 736 static jint JNICALL FollowReferencesCallback( 737 jvmtiHeapReferenceKind reference_kind ATTRIBUTE_UNUSED, 738 const jvmtiHeapReferenceInfo* reference_info ATTRIBUTE_UNUSED, 739 jlong class_tag ATTRIBUTE_UNUSED, 740 jlong referrer_class_tag ATTRIBUTE_UNUSED, 741 jlong size ATTRIBUTE_UNUSED, 742 jlong* tag_ptr ATTRIBUTE_UNUSED, 743 jlong* referrer_tag_ptr ATTRIBUTE_UNUSED, 744 jint length ATTRIBUTE_UNUSED, 745 void* user_data ATTRIBUTE_UNUSED) { 746 return JVMTI_VISIT_OBJECTS; // Continue visiting. 747 } 748 749 static jint JNICALL PrimitiveFieldValueCallback(jvmtiHeapReferenceKind kind, 750 const jvmtiHeapReferenceInfo* info, 751 jlong class_tag, 752 jlong* tag_ptr, 753 jvalue value, 754 jvmtiPrimitiveType value_type, 755 void* user_data) { 756 FindFieldCallbacks* p = reinterpret_cast<FindFieldCallbacks*>(user_data); 757 if (*tag_ptr != 0) { 758 std::ostringstream oss; 759 oss << *tag_ptr 760 << '@' 761 << class_tag 762 << " (" 763 << (kind == JVMTI_HEAP_REFERENCE_FIELD ? "instance, " : "static, ") 764 << GetPrimitiveTypeName(value_type) 765 << ", index=" 766 << info->field.index 767 << ") "; 768 // Be lazy, always print eight bytes. 769 static_assert(sizeof(jvalue) == sizeof(uint64_t), "Unexpected jvalue size"); 770 uint64_t val; 771 memcpy(&val, &value, sizeof(uint64_t)); // To avoid undefined behavior. 772 oss << android::base::StringPrintf("%016" PRIx64, val); 773 774 if (!p->data.empty()) { 775 p->data += "\n"; 776 } 777 p->data += oss.str(); 778 // Update the tag to test whether that works. 779 *tag_ptr = *tag_ptr + 1; 780 } 781 return 0; 782 } 783 784 std::string data; 785 }; 786 787 jvmtiHeapCallbacks callbacks; 788 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 789 callbacks.heap_reference_callback = FindFieldCallbacks::FollowReferencesCallback; 790 callbacks.primitive_field_callback = FindFieldCallbacks::PrimitiveFieldValueCallback; 791 792 FindFieldCallbacks ffc; 793 jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &ffc); 794 if (JvmtiErrorToException(env, jvmti_env, ret)) { 795 return nullptr; 796 } 797 return env->NewStringUTF(ffc.data.c_str()); 798 } 799 800 // This is copied from test 908. Consider moving this to the main shim. 801 802 static size_t starts = 0; 803 static size_t finishes = 0; 804 805 static void JNICALL GarbageCollectionFinish(jvmtiEnv* ti_env ATTRIBUTE_UNUSED) { 806 finishes++; 807 } 808 809 static void JNICALL GarbageCollectionStart(jvmtiEnv* ti_env ATTRIBUTE_UNUSED) { 810 starts++; 811 } 812 813 extern "C" JNIEXPORT void JNICALL Java_art_Test913_setupGcCallback( 814 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { 815 jvmtiEventCallbacks callbacks; 816 memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); 817 callbacks.GarbageCollectionFinish = GarbageCollectionFinish; 818 callbacks.GarbageCollectionStart = GarbageCollectionStart; 819 820 jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); 821 JvmtiErrorToException(env, jvmti_env, ret); 822 } 823 824 extern "C" JNIEXPORT void JNICALL Java_art_Test913_enableGcTracking(JNIEnv* env, 825 jclass klass ATTRIBUTE_UNUSED, 826 jboolean enable) { 827 jvmtiError ret = jvmti_env->SetEventNotificationMode( 828 enable ? JVMTI_ENABLE : JVMTI_DISABLE, 829 JVMTI_EVENT_GARBAGE_COLLECTION_START, 830 nullptr); 831 if (JvmtiErrorToException(env, jvmti_env, ret)) { 832 return; 833 } 834 ret = jvmti_env->SetEventNotificationMode( 835 enable ? JVMTI_ENABLE : JVMTI_DISABLE, 836 JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, 837 nullptr); 838 if (JvmtiErrorToException(env, jvmti_env, ret)) { 839 return; 840 } 841 } 842 843 extern "C" JNIEXPORT jint JNICALL Java_art_Test913_getGcStarts(JNIEnv* env ATTRIBUTE_UNUSED, 844 jclass klass ATTRIBUTE_UNUSED) { 845 jint result = static_cast<jint>(starts); 846 starts = 0; 847 return result; 848 } 849 850 extern "C" JNIEXPORT jint JNICALL Java_art_Test913_getGcFinishes(JNIEnv* env ATTRIBUTE_UNUSED, 851 jclass klass ATTRIBUTE_UNUSED) { 852 jint result = static_cast<jint>(finishes); 853 finishes = 0; 854 return result; 855 } 856 857 using GetObjectHeapId = jvmtiError(*)(jvmtiEnv*, jlong, jint*, ...); 858 static GetObjectHeapId gGetObjectHeapIdFn = nullptr; 859 860 using GetHeapName = jvmtiError(*)(jvmtiEnv*, jint, char**, ...); 861 static GetHeapName gGetHeapNameFn = nullptr; 862 863 using IterateThroughHeapExt = jvmtiError(*)(jvmtiEnv*, 864 jint, 865 jclass, 866 const jvmtiHeapCallbacks*, 867 const void*); 868 static IterateThroughHeapExt gIterateThroughHeapExt = nullptr; 869 870 871 static void FreeExtensionFunctionInfo(jvmtiExtensionFunctionInfo* extensions, jint count) { 872 for (size_t i = 0; i != static_cast<size_t>(count); ++i) { 873 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].id)); 874 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].short_description)); 875 for (size_t j = 0; j != static_cast<size_t>(extensions[i].param_count); ++j) { 876 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].params[j].name)); 877 } 878 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].params)); 879 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].errors)); 880 } 881 } 882 883 extern "C" JNIEXPORT void JNICALL Java_art_Test913_checkForExtensionApis( 884 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { 885 jint extension_count; 886 jvmtiExtensionFunctionInfo* extensions; 887 jvmtiError result = jvmti_env->GetExtensionFunctions(&extension_count, &extensions); 888 if (JvmtiErrorToException(env, jvmti_env, result)) { 889 return; 890 } 891 892 for (size_t i = 0; i != static_cast<size_t>(extension_count); ++i) { 893 if (strcmp("com.android.art.heap.get_object_heap_id", extensions[i].id) == 0) { 894 CHECK(gGetObjectHeapIdFn == nullptr); 895 gGetObjectHeapIdFn = reinterpret_cast<GetObjectHeapId>(extensions[i].func); 896 897 CHECK_EQ(extensions[i].param_count, 2); 898 899 CHECK_EQ(strcmp("tag", extensions[i].params[0].name), 0); 900 CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JLONG); 901 CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN); 902 903 CHECK_EQ(strcmp("heap_id", extensions[i].params[1].name), 0); 904 CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_JINT); 905 CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_OUT); 906 CHECK_EQ(extensions[i].params[1].null_ok, false); 907 908 CHECK_EQ(extensions[i].error_count, 1); 909 CHECK(extensions[i].errors != nullptr); 910 CHECK(extensions[i].errors[0] == JVMTI_ERROR_NOT_FOUND); 911 912 continue; 913 } 914 915 if (strcmp("com.android.art.heap.get_heap_name", extensions[i].id) == 0) { 916 CHECK(gGetHeapNameFn == nullptr); 917 gGetHeapNameFn = reinterpret_cast<GetHeapName>(extensions[i].func); 918 919 CHECK_EQ(extensions[i].param_count, 2); 920 921 CHECK_EQ(strcmp("heap_id", extensions[i].params[0].name), 0); 922 CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JINT); 923 CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN); 924 925 CHECK_EQ(strcmp("heap_name", extensions[i].params[1].name), 0); 926 CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_CCHAR); 927 CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_ALLOC_BUF); 928 CHECK_EQ(extensions[i].params[1].null_ok, false); 929 930 CHECK_EQ(extensions[i].error_count, 1); 931 CHECK(extensions[i].errors != nullptr); 932 CHECK(extensions[i].errors[0] == JVMTI_ERROR_ILLEGAL_ARGUMENT); 933 } 934 935 if (strcmp("com.android.art.heap.iterate_through_heap_ext", extensions[i].id) == 0) { 936 CHECK(gIterateThroughHeapExt == nullptr); 937 gIterateThroughHeapExt = reinterpret_cast<IterateThroughHeapExt>(extensions[i].func); 938 939 CHECK_EQ(extensions[i].param_count, 4); 940 941 CHECK_EQ(strcmp("heap_filter", extensions[i].params[0].name), 0); 942 CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JINT); 943 CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN); 944 945 CHECK_EQ(strcmp("klass", extensions[i].params[1].name), 0); 946 CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_JCLASS); 947 CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_IN); 948 CHECK_EQ(extensions[i].params[1].null_ok, true); 949 950 CHECK_EQ(strcmp("callbacks", extensions[i].params[2].name), 0); 951 CHECK_EQ(extensions[i].params[2].base_type, JVMTI_TYPE_CVOID); 952 CHECK_EQ(extensions[i].params[2].kind, JVMTI_KIND_IN_PTR); 953 CHECK_EQ(extensions[i].params[2].null_ok, false); 954 955 CHECK_EQ(strcmp("user_data", extensions[i].params[3].name), 0); 956 CHECK_EQ(extensions[i].params[3].base_type, JVMTI_TYPE_CVOID); 957 CHECK_EQ(extensions[i].params[3].kind, JVMTI_KIND_IN_PTR); 958 CHECK_EQ(extensions[i].params[3].null_ok, true); 959 960 CHECK_EQ(extensions[i].error_count, 3); 961 CHECK(extensions[i].errors != nullptr); 962 CHECK(extensions[i].errors[0] == JVMTI_ERROR_MUST_POSSESS_CAPABILITY); 963 CHECK(extensions[i].errors[1] == JVMTI_ERROR_INVALID_CLASS); 964 CHECK(extensions[i].errors[2] == JVMTI_ERROR_NULL_POINTER); 965 } 966 } 967 968 CHECK(gGetObjectHeapIdFn != nullptr); 969 CHECK(gGetHeapNameFn != nullptr); 970 971 FreeExtensionFunctionInfo(extensions, extension_count); 972 } 973 974 extern "C" JNIEXPORT jint JNICALL Java_art_Test913_getObjectHeapId( 975 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) { 976 CHECK(gGetObjectHeapIdFn != nullptr); 977 jint heap_id; 978 jvmtiError result = gGetObjectHeapIdFn(jvmti_env, tag, &heap_id); 979 JvmtiErrorToException(env, jvmti_env, result); 980 return heap_id; 981 } 982 983 extern "C" JNIEXPORT jstring JNICALL Java_art_Test913_getHeapName( 984 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_id) { 985 CHECK(gGetHeapNameFn != nullptr); 986 char* heap_name; 987 jvmtiError result = gGetHeapNameFn(jvmti_env, heap_id, &heap_name); 988 if (JvmtiErrorToException(env, jvmti_env, result)) { 989 return nullptr; 990 } 991 jstring ret = env->NewStringUTF(heap_name); 992 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(heap_name)); 993 return ret; 994 } 995 996 extern "C" JNIEXPORT void JNICALL Java_art_Test913_checkGetObjectHeapIdInCallback( 997 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag, jint heap_id) { 998 CHECK(gGetObjectHeapIdFn != nullptr); 999 1000 { 1001 struct GetObjectHeapIdCallbacks { 1002 static jint JNICALL FollowReferencesCallback( 1003 jvmtiHeapReferenceKind reference_kind ATTRIBUTE_UNUSED, 1004 const jvmtiHeapReferenceInfo* reference_info ATTRIBUTE_UNUSED, 1005 jlong class_tag ATTRIBUTE_UNUSED, 1006 jlong referrer_class_tag ATTRIBUTE_UNUSED, 1007 jlong size ATTRIBUTE_UNUSED, 1008 jlong* tag_ptr, 1009 jlong* referrer_tag_ptr ATTRIBUTE_UNUSED, 1010 jint length ATTRIBUTE_UNUSED, 1011 void* user_data) { 1012 if (*tag_ptr != 0) { 1013 GetObjectHeapIdCallbacks* p = reinterpret_cast<GetObjectHeapIdCallbacks*>(user_data); 1014 if (*tag_ptr == p->check_callback_tag) { 1015 jint tag_heap_id; 1016 jvmtiError result = gGetObjectHeapIdFn(jvmti_env, *tag_ptr, &tag_heap_id); 1017 CHECK_EQ(result, JVMTI_ERROR_NONE); 1018 CHECK_EQ(tag_heap_id, p->check_callback_id); 1019 return JVMTI_VISIT_ABORT; 1020 } 1021 } 1022 1023 return JVMTI_VISIT_OBJECTS; // Continue visiting. 1024 } 1025 1026 jlong check_callback_tag; 1027 jint check_callback_id; 1028 }; 1029 1030 jvmtiHeapCallbacks callbacks; 1031 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 1032 callbacks.heap_reference_callback = GetObjectHeapIdCallbacks::FollowReferencesCallback; 1033 1034 GetObjectHeapIdCallbacks ffc; 1035 ffc.check_callback_tag = tag; 1036 ffc.check_callback_id = heap_id; 1037 1038 jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, nullptr, &callbacks, &ffc); 1039 if (JvmtiErrorToException(env, jvmti_env, ret)) { 1040 return; 1041 } 1042 } 1043 1044 { 1045 struct GetObjectHeapIdCallbacks { 1046 static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED, 1047 jlong size ATTRIBUTE_UNUSED, 1048 jlong* tag_ptr, 1049 jint length ATTRIBUTE_UNUSED, 1050 void* user_data) { 1051 if (*tag_ptr != 0) { 1052 GetObjectHeapIdCallbacks* p = reinterpret_cast<GetObjectHeapIdCallbacks*>(user_data); 1053 if (*tag_ptr == p->check_callback_tag) { 1054 jint tag_heap_id; 1055 jvmtiError result = gGetObjectHeapIdFn(jvmti_env, *tag_ptr, &tag_heap_id); 1056 CHECK_EQ(result, JVMTI_ERROR_NONE); 1057 CHECK_EQ(tag_heap_id, p->check_callback_id); 1058 return JVMTI_VISIT_ABORT; 1059 } 1060 } 1061 1062 return 0; // Continue visiting. 1063 } 1064 1065 jlong check_callback_tag; 1066 jint check_callback_id; 1067 }; 1068 1069 jvmtiHeapCallbacks callbacks; 1070 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 1071 callbacks.heap_iteration_callback = GetObjectHeapIdCallbacks::HeapIterationCallback; 1072 1073 GetObjectHeapIdCallbacks ffc; 1074 ffc.check_callback_tag = tag; 1075 ffc.check_callback_id = heap_id; 1076 1077 jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc); 1078 if (JvmtiErrorToException(env, jvmti_env, ret)) { 1079 return; 1080 } 1081 } 1082 } 1083 1084 static bool gFoundExt = false; 1085 1086 static jint JNICALL HeapIterationExtCallback(jlong class_tag ATTRIBUTE_UNUSED, 1087 jlong size ATTRIBUTE_UNUSED, 1088 jlong* tag_ptr, 1089 jint length ATTRIBUTE_UNUSED, 1090 void* user_data ATTRIBUTE_UNUSED, 1091 jint heap_id) { 1092 // We expect some tagged objects at or above the threshold, where the expected heap id is 1093 // encoded into lowest byte. 1094 constexpr jlong kThreshold = 30000000; 1095 jlong tag = *tag_ptr; 1096 if (tag >= kThreshold) { 1097 jint expected_heap_id = static_cast<jint>(tag - kThreshold); 1098 CHECK_EQ(expected_heap_id, heap_id); 1099 gFoundExt = true; 1100 } 1101 return 0; 1102 } 1103 1104 extern "C" JNIEXPORT void JNICALL Java_art_Test913_iterateThroughHeapExt( 1105 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { 1106 CHECK(gIterateThroughHeapExt != nullptr); 1107 1108 jvmtiHeapCallbacks callbacks; 1109 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); 1110 callbacks.heap_iteration_callback = 1111 reinterpret_cast<decltype(callbacks.heap_iteration_callback)>(HeapIterationExtCallback); 1112 1113 jvmtiError ret = gIterateThroughHeapExt(jvmti_env, 0, nullptr, &callbacks, nullptr); 1114 JvmtiErrorToException(env, jvmti_env, ret); 1115 CHECK(gFoundExt); 1116 } 1117 1118 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test913_checkInitialized(JNIEnv* env, jclass, jclass c) { 1119 jint status; 1120 jvmtiError error = jvmti_env->GetClassStatus(c, &status); 1121 if (JvmtiErrorToException(env, jvmti_env, error)) { 1122 return false; 1123 } 1124 return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0; 1125 } 1126 1127 } // namespace Test913Heaps 1128 } // namespace art 1129