blob: 521f9a6c72ca26328551125351b0d3a46d87f445 [file] [log] [blame]
Andreas Gampee54d9922016-10-11 19:55:37 -07001/*
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
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070017#include <inttypes.h>
18#include <pthread.h>
Andreas Gampe3ec8e402017-02-21 15:49:53 -080019
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070020#include <cstdio>
Andreas Gampebecd6ad2017-02-22 19:20:37 -080021#include <iomanip>
Andreas Gampee54d9922016-10-11 19:55:37 -070022#include <iostream>
Andreas Gampebecd6ad2017-02-22 19:20:37 -080023#include <sstream>
Andreas Gampee54d9922016-10-11 19:55:37 -070024#include <vector>
25
Andreas Gampe027444b2017-03-31 12:49:07 -070026#include "android-base/logging.h"
Andreas Gampe3ec8e402017-02-21 15:49:53 -080027#include "android-base/stringprintf.h"
Andreas Gampe027444b2017-03-31 12:49:07 -070028
Andreas Gampee54d9922016-10-11 19:55:37 -070029#include "jni.h"
Andreas Gampe5e03a302017-03-13 13:10:00 -070030#include "jvmti.h"
Andreas Gampe027444b2017-03-31 12:49:07 -070031#include "scoped_primitive_array.h"
Andreas Gampee54d9922016-10-11 19:55:37 -070032
Andreas Gampe3f46c962017-03-30 10:26:59 -070033// Test infrastructure
34#include "jvmti_helper.h"
35#include "test_env.h"
Andreas Gampe027444b2017-03-31 12:49:07 -070036#include "ti_macros.h"
37#include "ti_utf.h"
Andreas Gampe3f46c962017-03-30 10:26:59 -070038
Andreas Gampee54d9922016-10-11 19:55:37 -070039namespace art {
40namespace Test906IterateHeap {
41
42class IterationConfig {
43 public:
44 IterationConfig() {}
45 virtual ~IterationConfig() {}
46
47 virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
48};
49
50static 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
Andreas Gampe3f46c962017-03-30 10:26:59 -070059static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) {
Andreas Gampee54d9922016-10-11 19:55:37 -070060 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);
Andreas Gampe3f46c962017-03-30 10:26:59 -070068 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampee54d9922016-10-11 19:55:37 -070069 return false;
70 }
71 return true;
72}
73
Andreas Gampe46651672017-04-07 09:00:04 -070074extern "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) {
Andreas Gampee54d9922016-10-11 19:55:37 -070080 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,
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010090 jint length ATTRIBUTE_UNUSED) override {
Andreas Gampee54d9922016-10-11 19:55:37 -070091 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);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700103 Run(env, heap_filter, klass_filter, &config);
Andreas Gampee54d9922016-10-11 19:55:37 -0700104
105 if (config.counter > config.stop_after) {
106 printf("Error: more objects visited than signaled.");
107 }
108
109 return config.counter;
110}
111
Andreas Gampe46651672017-04-07 09:00:04 -0700112extern "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) {
Andreas Gampee54d9922016-10-11 19:55:37 -0700121 class DataIterationConfig : public IterationConfig {
122 public:
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100123 jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) override {
Andreas Gampee54d9922016-10-11 19:55:37 -0700124 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;
Andreas Gampe3f46c962017-03-30 10:26:59 -0700139 if (!Run(env, heap_filter, klass_filter, &config)) {
Andreas Gampee54d9922016-10-11 19:55:37 -0700140 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
Andreas Gampe46651672017-04-07 09:00:04 -0700158extern "C" JNIEXPORT void JNICALL Java_art_Test906_iterateThroughHeapAdd(
159 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_filter, jclass klass_filter) {
Andreas Gampee54d9922016-10-11 19:55:37 -0700160 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,
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100167 jint length ATTRIBUTE_UNUSED) override {
Andreas Gampee54d9922016-10-11 19:55:37 -0700168 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;
Andreas Gampe3f46c962017-03-30 10:26:59 -0700177 Run(env, heap_filter, klass_filter, &config);
Andreas Gampee54d9922016-10-11 19:55:37 -0700178}
179
Andreas Gampe46651672017-04-07 09:00:04 -0700180extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapString(
Andreas Gampe3ec8e402017-02-21 15:49:53 -0800181 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
182 struct FindStringCallbacks {
183 explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
184
Andreas Gampebecd6ad2017-02-22 19:20:37 -0800185 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) {
Andreas Gampe3ec8e402017-02-21 15:49:53 -0800190 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) {
Andreas Gampe027444b2017-03-31 12:49:07 -0700201 size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
Andreas Gampe3ec8e402017-02-21 15:49:53 -0800202 std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
203 memset(mod_utf.get(), 0, utf_byte_count + 1);
Andreas Gampe027444b2017-03-31 12:49:07 -0700204 ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
Andreas Gampe3ec8e402017-02-21 15:49:53 -0800205 if (!p->data.empty()) {
206 p->data += "\n";
207 }
Andreas Gampebecd6ad2017-02-22 19:20:37 -0800208 p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
Andreas Gampe3ec8e402017-02-21 15:49:53 -0800209 *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);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700230 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampe3ec8e402017-02-21 15:49:53 -0800231 return nullptr;
232 }
233 return env->NewStringUTF(fsc.data.c_str());
234}
235
Andreas Gampe46651672017-04-07 09:00:04 -0700236extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveArray(
Andreas Gampebecd6ad2017-02-22 19:20:37 -0800237 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);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700318 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampebecd6ad2017-02-22 19:20:37 -0800319 return nullptr;
320 }
321 return env->NewStringUTF(fac.data.c_str());
322}
323
Andreas Gampee7316932017-02-25 09:15:05 -0800324static 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
Andreas Gampe46651672017-04-07 09:00:04 -0700347extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveFields(
Andreas Gampee7316932017-02-25 09:15:05 -0800348 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);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700405 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampee7316932017-02-25 09:15:05 -0800406 return nullptr;
407 }
408 return env->NewStringUTF(ffc.data.c_str());
409}
410
Mathieu Chartierf1dd69a2017-06-08 23:30:15 +0000411extern "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
Alex Lightbbbcb532018-08-30 12:50:27 -0700421extern "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
Andreas Gampee54d9922016-10-11 19:55:37 -0700437} // namespace Test906IterateHeap
438} // namespace art