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: 44 IterationConfig() {} 45 virtual ~IterationConfig() {} 46 47 virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0; 48 }; 49 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 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 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 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 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 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 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 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 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 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 421 } // namespace Test906IterateHeap 422 } // namespace art 423