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 #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