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