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,jint heap_filter,jclass klass_filter,jint stop_after)74 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapCount(
75 JNIEnv* env,
76 [[maybe_unused]] jclass klass,
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([[maybe_unused]] jlong class_tag,
88 [[maybe_unused]] jlong size,
89 [[maybe_unused]] jlong* tag_ptr,
90 [[maybe_unused]] jint length) 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,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 [[maybe_unused]] jclass klass,
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,jint heap_filter,jclass klass_filter)158 extern "C" JNIEXPORT void JNICALL Java_art_Test906_iterateThroughHeapAdd(
159 JNIEnv* env, [[maybe_unused]] jclass klass, jint heap_filter, jclass klass_filter) {
160 class AddIterationConfig : public IterationConfig {
161 public:
162 AddIterationConfig() {}
163
164 jint Handle([[maybe_unused]] jlong class_tag,
165 [[maybe_unused]] jlong size,
166 jlong* tag_ptr,
167 [[maybe_unused]] jint length) 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,jlong tag)180 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapString(
181 JNIEnv* env, [[maybe_unused]] jclass klass, jlong tag) {
182 struct FindStringCallbacks {
183 explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
184
185 static jint JNICALL HeapIterationCallback([[maybe_unused]] jlong class_tag,
186 [[maybe_unused]] jlong size,
187 [[maybe_unused]] jlong* tag_ptr,
188 [[maybe_unused]] jint length,
189 [[maybe_unused]] void* user_data) {
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::CountModifiedUtf8BytesInUtf16(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,jlong tag)236 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveArray(
237 JNIEnv* env, [[maybe_unused]] jclass klass, jlong tag) {
238 struct FindArrayCallbacks {
239 explicit FindArrayCallbacks(jlong t) : tag_to_find(t) {}
240
241 static jint JNICALL HeapIterationCallback([[maybe_unused]] jlong class_tag,
242 [[maybe_unused]] jlong size,
243 [[maybe_unused]] jlong* tag_ptr,
244 [[maybe_unused]] jint length,
245 [[maybe_unused]] void* user_data) {
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 }
288 const uint8_t* data = reinterpret_cast<const uint8_t*>(elements);
289 for (size_t i = 0; i != element_size * element_count; ++i) {
290 oss << android::base::StringPrintf("%02x", data[i]);
291 }
292 oss << "')";
293
294 if (!p->data.empty()) {
295 p->data += "\n";
296 }
297 p->data += oss.str();
298 // Update the tag to test whether that works.
299 *tag_ptr = *tag_ptr + 1;
300 }
301 return 0;
302 }
303
304 std::string data;
305 const jlong tag_to_find;
306 };
307
308 jvmtiHeapCallbacks callbacks;
309 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
310 callbacks.heap_iteration_callback = FindArrayCallbacks::HeapIterationCallback;
311 callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback;
312
313 FindArrayCallbacks fac(tag);
314 jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
315 if (JvmtiErrorToException(env, jvmti_env, ret)) {
316 return nullptr;
317 }
318 return env->NewStringUTF(fac.data.c_str());
319 }
320
GetPrimitiveTypeName(jvmtiPrimitiveType type)321 static constexpr const char* GetPrimitiveTypeName(jvmtiPrimitiveType type) {
322 switch (type) {
323 case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
324 return "boolean";
325 case JVMTI_PRIMITIVE_TYPE_BYTE:
326 return "byte";
327 case JVMTI_PRIMITIVE_TYPE_CHAR:
328 return "char";
329 case JVMTI_PRIMITIVE_TYPE_SHORT:
330 return "short";
331 case JVMTI_PRIMITIVE_TYPE_INT:
332 return "int";
333 case JVMTI_PRIMITIVE_TYPE_FLOAT:
334 return "float";
335 case JVMTI_PRIMITIVE_TYPE_LONG:
336 return "long";
337 case JVMTI_PRIMITIVE_TYPE_DOUBLE:
338 return "double";
339 }
340 LOG(FATAL) << "Unknown type " << static_cast<size_t>(type);
341 UNREACHABLE();
342 }
343
Java_art_Test906_iterateThroughHeapPrimitiveFields(JNIEnv * env,jclass klass,jlong tag)344 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveFields(
345 JNIEnv* env, [[maybe_unused]] jclass klass, jlong tag) {
346 struct FindFieldCallbacks {
347 explicit FindFieldCallbacks(jlong t) : tag_to_find(t) {}
348
349 static jint JNICALL HeapIterationCallback([[maybe_unused]] jlong class_tag,
350 [[maybe_unused]] jlong size,
351 [[maybe_unused]] jlong* tag_ptr,
352 [[maybe_unused]] jint length,
353 [[maybe_unused]] void* user_data) {
354 return 0;
355 }
356
357 static jint JNICALL PrimitiveFieldValueCallback(jvmtiHeapReferenceKind kind,
358 const jvmtiHeapReferenceInfo* info,
359 jlong class_tag,
360 jlong* tag_ptr,
361 jvalue value,
362 jvmtiPrimitiveType value_type,
363 void* user_data) {
364 FindFieldCallbacks* p = reinterpret_cast<FindFieldCallbacks*>(user_data);
365 if (*tag_ptr >= p->tag_to_find) {
366 std::ostringstream oss;
367 oss << *tag_ptr
368 << '@'
369 << class_tag
370 << " ("
371 << (kind == JVMTI_HEAP_REFERENCE_FIELD ? "instance, " : "static, ")
372 << GetPrimitiveTypeName(value_type)
373 << ", index="
374 << info->field.index
375 << ") ";
376 // Be lazy, always print eight bytes.
377 static_assert(sizeof(jvalue) == sizeof(uint64_t), "Unexpected jvalue size");
378 uint64_t val;
379 memcpy(&val, &value, sizeof(uint64_t)); // To avoid undefined behavior.
380 oss << android::base::StringPrintf("%016" PRIx64, val);
381
382 if (!p->data.empty()) {
383 p->data += "\n";
384 }
385 p->data += oss.str();
386 *tag_ptr = *tag_ptr + 1;
387 }
388 return 0;
389 }
390
391 std::string data;
392 const jlong tag_to_find;
393 };
394
395 jvmtiHeapCallbacks callbacks;
396 memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
397 callbacks.heap_iteration_callback = FindFieldCallbacks::HeapIterationCallback;
398 callbacks.primitive_field_callback = FindFieldCallbacks::PrimitiveFieldValueCallback;
399
400 FindFieldCallbacks ffc(tag);
401 jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
402 if (JvmtiErrorToException(env, jvmti_env, ret)) {
403 return nullptr;
404 }
405 return env->NewStringUTF(ffc.data.c_str());
406 }
407
Java_art_Test906_checkInitialized(JNIEnv * env,jclass,jclass c)408 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized(
409 JNIEnv* env, jclass, jclass c) {
410 jint status;
411 jvmtiError error = jvmti_env->GetClassStatus(c, &status);
412 if (JvmtiErrorToException(env, jvmti_env, error)) {
413 return false;
414 }
415 return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0;
416 }
417
Java_art_Test906_iterateOverInstancesCount(JNIEnv * env,jclass,jclass target)418 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateOverInstancesCount(
419 JNIEnv* env, jclass, jclass target) {
420 jint cnt = 0;
421 auto count_func = [](jlong, jlong, jlong*, void* user_data) -> jvmtiIterationControl {
422 *reinterpret_cast<jint*>(user_data) += 1;
423 return JVMTI_ITERATION_CONTINUE;
424 };
425 JvmtiErrorToException(env,
426 jvmti_env,
427 jvmti_env->IterateOverInstancesOfClass(target,
428 JVMTI_HEAP_OBJECT_EITHER,
429 count_func,
430 &cnt));
431 return cnt;
432 }
433
434 } // namespace Test906IterateHeap
435 } // namespace art
436