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