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