Home | History | Annotate | Download | only in 913-heaps
      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