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