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