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 <pthread.h>
19
20 #include <cstdio>
21 #include <iomanip>
22 #include <iostream>
23 #include <sstream>
24 #include <vector>
25
26 #include "android-base/logging.h"
27 #include "android-base/stringprintf.h"
28
29 #include "jni.h"
30 #include "jvmti.h"
31 #include "scoped_primitive_array.h"
32
33 // Test infrastructure
34 #include "jvmti_helper.h"
35 #include "test_env.h"
36 #include "ti_macros.h"
37 #include "ti_utf.h"
38
39 namespace art {
40 namespace Test906IterateHeap {
41
42 class IterationConfig {
43 public:
IterationConfig()44 IterationConfig() {}
~IterationConfig()45 virtual ~IterationConfig() {}
46
47 virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
48 };
49
HeapIterationCallback(jlong class_tag,jlong size,jlong * tag_ptr,jint length,void * user_data)50 static jint JNICALL HeapIterationCallback(jlong class_tag,
51 jlong size,
52 jlong* tag_ptr,
53 jint length,
54 void* user_data) {
55 IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data);
56 return config->Handle(class_tag, size, tag_ptr, length);
57 }
58
Run(JNIEnv * env,jint heap_filter,jclass klass_filter,IterationConfig * config)59 static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) {
60 jvmtiHeapCallbacks callbacks;
61 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
62 callbacks.heap_iteration_callback = HeapIterationCallback;
63
64 jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter,
65 klass_filter,
66 &callbacks,
67 config);
68 if (JvmtiErrorToException(env, jvmti_env, ret)) {
69 return false;
70 }
71 return true;
72 }
73
Java_art_Test906_iterateThroughHeapCount(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jint heap_filter,jclass klass_filter,jint stop_after)74 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapCount(
75 JNIEnv* env,
76 jclass klass ATTRIBUTE_UNUSED,
77 jint heap_filter,
78 jclass klass_filter,
79 jint stop_after) {
80 class CountIterationConfig : public IterationConfig {
81 public:
82 CountIterationConfig(jint _counter, jint _stop_after)
83 : counter(_counter),
84 stop_after(_stop_after) {
85 }
86
87 jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
88 jlong size ATTRIBUTE_UNUSED,
89 jlong* tag_ptr ATTRIBUTE_UNUSED,
90 jint length ATTRIBUTE_UNUSED) override {
91 counter++;
92 if (counter == stop_after) {
93 return JVMTI_VISIT_ABORT;
94 }
95 return 0;
96 }
97
98 jint counter;
99 const jint stop_after;
100 };
101
102 CountIterationConfig config(0, stop_after);
103 Run(env, heap_filter, klass_filter, &config);
104
105 if (config.counter > config.stop_after) {
106 printf("Error: more objects visited than signaled.");
107 }
108
109 return config.counter;
110 }
111
Java_art_Test906_iterateThroughHeapData(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jint heap_filter,jclass klass_filter,jlongArray class_tags,jlongArray sizes,jlongArray tags,jintArray lengths)112 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapData(
113 JNIEnv* env,
114 jclass klass ATTRIBUTE_UNUSED,
115 jint heap_filter,
116 jclass klass_filter,
117 jlongArray class_tags,
118 jlongArray sizes,
119 jlongArray tags,
120 jintArray lengths) {
121 class DataIterationConfig : public IterationConfig {
122 public:
123 jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) override {
124 class_tags_.push_back(class_tag);
125 sizes_.push_back(size);
126 tags_.push_back(*tag_ptr);
127 lengths_.push_back(length);
128
129 return 0; // Continue.
130 }
131
132 std::vector<jlong> class_tags_;
133 std::vector<jlong> sizes_;
134 std::vector<jlong> tags_;
135 std::vector<jint> lengths_;
136 };
137
138 DataIterationConfig config;
139 if (!Run(env, heap_filter, klass_filter, &config)) {
140 return -1;
141 }
142
143 ScopedLongArrayRW s_class_tags(env, class_tags);
144 ScopedLongArrayRW s_sizes(env, sizes);
145 ScopedLongArrayRW s_tags(env, tags);
146 ScopedIntArrayRW s_lengths(env, lengths);
147
148 for (size_t i = 0; i != config.class_tags_.size(); ++i) {
149 s_class_tags[i] = config.class_tags_[i];
150 s_sizes[i] = config.sizes_[i];
151 s_tags[i] = config.tags_[i];
152 s_lengths[i] = config.lengths_[i];
153 }
154
155 return static_cast<jint>(config.class_tags_.size());
156 }
157
Java_art_Test906_iterateThroughHeapAdd(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jint heap_filter,jclass klass_filter)158 extern "C" JNIEXPORT void JNICALL Java_art_Test906_iterateThroughHeapAdd(
159 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_filter, jclass klass_filter) {
160 class AddIterationConfig : public IterationConfig {
161 public:
162 AddIterationConfig() {}
163
164 jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
165 jlong size ATTRIBUTE_UNUSED,
166 jlong* tag_ptr,
167 jint length ATTRIBUTE_UNUSED) override {
168 jlong current_tag = *tag_ptr;
169 if (current_tag != 0) {
170 *tag_ptr = current_tag + 10;
171 }
172 return 0;
173 }
174 };
175
176 AddIterationConfig config;
177 Run(env, heap_filter, klass_filter, &config);
178 }
179
Java_art_Test906_iterateThroughHeapString(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jlong tag)180 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapString(
181 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
182 struct FindStringCallbacks {
183 explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
184
185 static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
186 jlong size ATTRIBUTE_UNUSED,
187 jlong* tag_ptr ATTRIBUTE_UNUSED,
188 jint length ATTRIBUTE_UNUSED,
189 void* user_data ATTRIBUTE_UNUSED) {
190 return 0;
191 }
192
193 static jint JNICALL StringValueCallback(jlong class_tag,
194 jlong size,
195 jlong* tag_ptr,
196 const jchar* value,
197 jint value_length,
198 void* user_data) {
199 FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
200 if (*tag_ptr == p->tag_to_find) {
201 size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
202 std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
203 memset(mod_utf.get(), 0, utf_byte_count + 1);
204 ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
205 if (!p->data.empty()) {
206 p->data += "\n";
207 }
208 p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
209 *tag_ptr,
210 class_tag,
211 size,
212 mod_utf.get());
213 // Update the tag to test whether that works.
214 *tag_ptr = *tag_ptr + 1;
215 }
216 return 0;
217 }
218
219 std::string data;
220 const jlong tag_to_find;
221 };
222
223 jvmtiHeapCallbacks callbacks;
224 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
225 callbacks.heap_iteration_callback = FindStringCallbacks::HeapIterationCallback;
226 callbacks.string_primitive_value_callback = FindStringCallbacks::StringValueCallback;
227
228 FindStringCallbacks fsc(tag);
229 jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc);
230 if (JvmtiErrorToException(env, jvmti_env, ret)) {
231 return nullptr;
232 }
233 return env->NewStringUTF(fsc.data.c_str());
234 }
235
Java_art_Test906_iterateThroughHeapPrimitiveArray(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jlong tag)236 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveArray(
237 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
238 struct FindArrayCallbacks {
239 explicit FindArrayCallbacks(jlong t) : tag_to_find(t) {}
240
241 static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
242 jlong size ATTRIBUTE_UNUSED,
243 jlong* tag_ptr ATTRIBUTE_UNUSED,
244 jint length ATTRIBUTE_UNUSED,
245 void* user_data ATTRIBUTE_UNUSED) {
246 return 0;
247 }
248
249 static jint JNICALL ArrayValueCallback(jlong class_tag,
250 jlong size,
251 jlong* tag_ptr,
252 jint element_count,
253 jvmtiPrimitiveType element_type,
254 const void* elements,
255 void* user_data) {
256 FindArrayCallbacks* p = reinterpret_cast<FindArrayCallbacks*>(user_data);
257 if (*tag_ptr == p->tag_to_find) {
258 std::ostringstream oss;
259 oss << *tag_ptr
260 << '@'
261 << class_tag
262 << " ("
263 << size
264 << ", "
265 << element_count
266 << "x"
267 << static_cast<char>(element_type)
268 << " '";
269 size_t element_size;
270 switch (element_type) {
271 case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
272 case JVMTI_PRIMITIVE_TYPE_BYTE:
273 element_size = 1;
274 break;
275 case JVMTI_PRIMITIVE_TYPE_CHAR:
276 case JVMTI_PRIMITIVE_TYPE_SHORT:
277 element_size = 2;
278 break;
279 case JVMTI_PRIMITIVE_TYPE_INT:
280 case JVMTI_PRIMITIVE_TYPE_FLOAT:
281 element_size = 4;
282 break;
283 case JVMTI_PRIMITIVE_TYPE_LONG:
284 case JVMTI_PRIMITIVE_TYPE_DOUBLE:
285 element_size = 8;
286 break;
287 default:
288 LOG(FATAL) << "Unknown type " << static_cast<size_t>(element_type);
289 UNREACHABLE();
290 }
291 const uint8_t* data = reinterpret_cast<const uint8_t*>(elements);
292 for (size_t i = 0; i != element_size * element_count; ++i) {
293 oss << android::base::StringPrintf("%02x", data[i]);
294 }
295 oss << "')";
296
297 if (!p->data.empty()) {
298 p->data += "\n";
299 }
300 p->data += oss.str();
301 // Update the tag to test whether that works.
302 *tag_ptr = *tag_ptr + 1;
303 }
304 return 0;
305 }
306
307 std::string data;
308 const jlong tag_to_find;
309 };
310
311 jvmtiHeapCallbacks callbacks;
312 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
313 callbacks.heap_iteration_callback = FindArrayCallbacks::HeapIterationCallback;
314 callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback;
315
316 FindArrayCallbacks fac(tag);
317 jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
318 if (JvmtiErrorToException(env, jvmti_env, ret)) {
319 return nullptr;
320 }
321 return env->NewStringUTF(fac.data.c_str());
322 }
323
GetPrimitiveTypeName(jvmtiPrimitiveType type)324 static constexpr const char* GetPrimitiveTypeName(jvmtiPrimitiveType type) {
325 switch (type) {
326 case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
327 return "boolean";
328 case JVMTI_PRIMITIVE_TYPE_BYTE:
329 return "byte";
330 case JVMTI_PRIMITIVE_TYPE_CHAR:
331 return "char";
332 case JVMTI_PRIMITIVE_TYPE_SHORT:
333 return "short";
334 case JVMTI_PRIMITIVE_TYPE_INT:
335 return "int";
336 case JVMTI_PRIMITIVE_TYPE_FLOAT:
337 return "float";
338 case JVMTI_PRIMITIVE_TYPE_LONG:
339 return "long";
340 case JVMTI_PRIMITIVE_TYPE_DOUBLE:
341 return "double";
342 }
343 LOG(FATAL) << "Unknown type " << static_cast<size_t>(type);
344 UNREACHABLE();
345 }
346
Java_art_Test906_iterateThroughHeapPrimitiveFields(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jlong tag)347 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveFields(
348 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
349 struct FindFieldCallbacks {
350 explicit FindFieldCallbacks(jlong t) : tag_to_find(t) {}
351
352 static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
353 jlong size ATTRIBUTE_UNUSED,
354 jlong* tag_ptr ATTRIBUTE_UNUSED,
355 jint length ATTRIBUTE_UNUSED,
356 void* user_data ATTRIBUTE_UNUSED) {
357 return 0;
358 }
359
360 static jint JNICALL PrimitiveFieldValueCallback(jvmtiHeapReferenceKind kind,
361 const jvmtiHeapReferenceInfo* info,
362 jlong class_tag,
363 jlong* tag_ptr,
364 jvalue value,
365 jvmtiPrimitiveType value_type,
366 void* user_data) {
367 FindFieldCallbacks* p = reinterpret_cast<FindFieldCallbacks*>(user_data);
368 if (*tag_ptr >= p->tag_to_find) {
369 std::ostringstream oss;
370 oss << *tag_ptr
371 << '@'
372 << class_tag
373 << " ("
374 << (kind == JVMTI_HEAP_REFERENCE_FIELD ? "instance, " : "static, ")
375 << GetPrimitiveTypeName(value_type)
376 << ", index="
377 << info->field.index
378 << ") ";
379 // Be lazy, always print eight bytes.
380 static_assert(sizeof(jvalue) == sizeof(uint64_t), "Unexpected jvalue size");
381 uint64_t val;
382 memcpy(&val, &value, sizeof(uint64_t)); // To avoid undefined behavior.
383 oss << android::base::StringPrintf("%016" PRIx64, val);
384
385 if (!p->data.empty()) {
386 p->data += "\n";
387 }
388 p->data += oss.str();
389 *tag_ptr = *tag_ptr + 1;
390 }
391 return 0;
392 }
393
394 std::string data;
395 const jlong tag_to_find;
396 };
397
398 jvmtiHeapCallbacks callbacks;
399 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
400 callbacks.heap_iteration_callback = FindFieldCallbacks::HeapIterationCallback;
401 callbacks.primitive_field_callback = FindFieldCallbacks::PrimitiveFieldValueCallback;
402
403 FindFieldCallbacks ffc(tag);
404 jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
405 if (JvmtiErrorToException(env, jvmti_env, ret)) {
406 return nullptr;
407 }
408 return env->NewStringUTF(ffc.data.c_str());
409 }
410
Java_art_Test906_checkInitialized(JNIEnv * env,jclass,jclass c)411 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized(
412 JNIEnv* env, jclass, jclass c) {
413 jint status;
414 jvmtiError error = jvmti_env->GetClassStatus(c, &status);
415 if (JvmtiErrorToException(env, jvmti_env, error)) {
416 return false;
417 }
418 return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0;
419 }
420
Java_art_Test906_iterateOverInstancesCount(JNIEnv * env,jclass,jclass target)421 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateOverInstancesCount(
422 JNIEnv* env, jclass, jclass target) {
423 jint cnt = 0;
424 auto count_func = [](jlong, jlong, jlong*, void* user_data) -> jvmtiIterationControl {
425 *reinterpret_cast<jint*>(user_data) += 1;
426 return JVMTI_ITERATION_CONTINUE;
427 };
428 JvmtiErrorToException(env,
429 jvmti_env,
430 jvmti_env->IterateOverInstancesOfClass(target,
431 JVMTI_HEAP_OBJECT_EITHER,
432 count_func,
433 &cnt));
434 return cnt;
435 }
436
437 } // namespace Test906IterateHeap
438 } // namespace art
439