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