/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "jni.h" #include "jvmti.h" #include "scoped_primitive_array.h" // Test infrastructure #include "jvmti_helper.h" #include "test_env.h" #include "ti_macros.h" #include "ti_utf.h" namespace art { namespace Test906IterateHeap { class IterationConfig { public: IterationConfig() {} virtual ~IterationConfig() {} virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0; }; static jint JNICALL HeapIterationCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) { IterationConfig* config = reinterpret_cast(user_data); return config->Handle(class_tag, size, tag_ptr, length); } static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) { jvmtiHeapCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); callbacks.heap_iteration_callback = HeapIterationCallback; jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter, klass_filter, &callbacks, config); if (JvmtiErrorToException(env, jvmti_env, ret)) { return false; } return true; } extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapCount( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_filter, jclass klass_filter, jint stop_after) { class CountIterationConfig : public IterationConfig { public: CountIterationConfig(jint _counter, jint _stop_after) : counter(_counter), stop_after(_stop_after) { } jint Handle(jlong class_tag ATTRIBUTE_UNUSED, jlong size ATTRIBUTE_UNUSED, jlong* tag_ptr ATTRIBUTE_UNUSED, jint length ATTRIBUTE_UNUSED) override { counter++; if (counter == stop_after) { return JVMTI_VISIT_ABORT; } return 0; } jint counter; const jint stop_after; }; CountIterationConfig config(0, stop_after); Run(env, heap_filter, klass_filter, &config); if (config.counter > config.stop_after) { printf("Error: more objects visited than signaled."); } return config.counter; } extern "C" JNIEXPORT jint JNICALL 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) { class DataIterationConfig : public IterationConfig { public: jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) override { class_tags_.push_back(class_tag); sizes_.push_back(size); tags_.push_back(*tag_ptr); lengths_.push_back(length); return 0; // Continue. } std::vector class_tags_; std::vector sizes_; std::vector tags_; std::vector lengths_; }; DataIterationConfig config; if (!Run(env, heap_filter, klass_filter, &config)) { return -1; } ScopedLongArrayRW s_class_tags(env, class_tags); ScopedLongArrayRW s_sizes(env, sizes); ScopedLongArrayRW s_tags(env, tags); ScopedIntArrayRW s_lengths(env, lengths); for (size_t i = 0; i != config.class_tags_.size(); ++i) { s_class_tags[i] = config.class_tags_[i]; s_sizes[i] = config.sizes_[i]; s_tags[i] = config.tags_[i]; s_lengths[i] = config.lengths_[i]; } return static_cast(config.class_tags_.size()); } extern "C" JNIEXPORT void JNICALL Java_art_Test906_iterateThroughHeapAdd( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_filter, jclass klass_filter) { class AddIterationConfig : public IterationConfig { public: AddIterationConfig() {} jint Handle(jlong class_tag ATTRIBUTE_UNUSED, jlong size ATTRIBUTE_UNUSED, jlong* tag_ptr, jint length ATTRIBUTE_UNUSED) override { jlong current_tag = *tag_ptr; if (current_tag != 0) { *tag_ptr = current_tag + 10; } return 0; } }; AddIterationConfig config; Run(env, heap_filter, klass_filter, &config); } extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapString( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) { struct FindStringCallbacks { explicit FindStringCallbacks(jlong t) : tag_to_find(t) {} static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED, jlong size ATTRIBUTE_UNUSED, jlong* tag_ptr ATTRIBUTE_UNUSED, jint length ATTRIBUTE_UNUSED, void* user_data ATTRIBUTE_UNUSED) { return 0; } static jint JNICALL StringValueCallback(jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data) { FindStringCallbacks* p = reinterpret_cast(user_data); if (*tag_ptr == p->tag_to_find) { size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length); std::unique_ptr mod_utf(new char[utf_byte_count + 1]); memset(mod_utf.get(), 0, utf_byte_count + 1); ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length); if (!p->data.empty()) { p->data += "\n"; } p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')", *tag_ptr, class_tag, size, mod_utf.get()); // Update the tag to test whether that works. *tag_ptr = *tag_ptr + 1; } return 0; } std::string data; const jlong tag_to_find; }; jvmtiHeapCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); callbacks.heap_iteration_callback = FindStringCallbacks::HeapIterationCallback; callbacks.string_primitive_value_callback = FindStringCallbacks::StringValueCallback; FindStringCallbacks fsc(tag); jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc); if (JvmtiErrorToException(env, jvmti_env, ret)) { return nullptr; } return env->NewStringUTF(fsc.data.c_str()); } extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveArray( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) { struct FindArrayCallbacks { explicit FindArrayCallbacks(jlong t) : tag_to_find(t) {} static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED, jlong size ATTRIBUTE_UNUSED, jlong* tag_ptr ATTRIBUTE_UNUSED, jint length ATTRIBUTE_UNUSED, void* user_data ATTRIBUTE_UNUSED) { return 0; } static jint JNICALL ArrayValueCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data) { FindArrayCallbacks* p = reinterpret_cast(user_data); if (*tag_ptr == p->tag_to_find) { std::ostringstream oss; oss << *tag_ptr << '@' << class_tag << " (" << size << ", " << element_count << "x" << static_cast(element_type) << " '"; size_t element_size; switch (element_type) { case JVMTI_PRIMITIVE_TYPE_BOOLEAN: case JVMTI_PRIMITIVE_TYPE_BYTE: element_size = 1; break; case JVMTI_PRIMITIVE_TYPE_CHAR: case JVMTI_PRIMITIVE_TYPE_SHORT: element_size = 2; break; case JVMTI_PRIMITIVE_TYPE_INT: case JVMTI_PRIMITIVE_TYPE_FLOAT: element_size = 4; break; case JVMTI_PRIMITIVE_TYPE_LONG: case JVMTI_PRIMITIVE_TYPE_DOUBLE: element_size = 8; break; default: LOG(FATAL) << "Unknown type " << static_cast(element_type); UNREACHABLE(); } const uint8_t* data = reinterpret_cast(elements); for (size_t i = 0; i != element_size * element_count; ++i) { oss << android::base::StringPrintf("%02x", data[i]); } oss << "')"; if (!p->data.empty()) { p->data += "\n"; } p->data += oss.str(); // Update the tag to test whether that works. *tag_ptr = *tag_ptr + 1; } return 0; } std::string data; const jlong tag_to_find; }; jvmtiHeapCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); callbacks.heap_iteration_callback = FindArrayCallbacks::HeapIterationCallback; callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback; FindArrayCallbacks fac(tag); jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac); if (JvmtiErrorToException(env, jvmti_env, ret)) { return nullptr; } return env->NewStringUTF(fac.data.c_str()); } static constexpr const char* GetPrimitiveTypeName(jvmtiPrimitiveType type) { switch (type) { case JVMTI_PRIMITIVE_TYPE_BOOLEAN: return "boolean"; case JVMTI_PRIMITIVE_TYPE_BYTE: return "byte"; case JVMTI_PRIMITIVE_TYPE_CHAR: return "char"; case JVMTI_PRIMITIVE_TYPE_SHORT: return "short"; case JVMTI_PRIMITIVE_TYPE_INT: return "int"; case JVMTI_PRIMITIVE_TYPE_FLOAT: return "float"; case JVMTI_PRIMITIVE_TYPE_LONG: return "long"; case JVMTI_PRIMITIVE_TYPE_DOUBLE: return "double"; } LOG(FATAL) << "Unknown type " << static_cast(type); UNREACHABLE(); } extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveFields( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) { struct FindFieldCallbacks { explicit FindFieldCallbacks(jlong t) : tag_to_find(t) {} static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED, jlong size ATTRIBUTE_UNUSED, jlong* tag_ptr ATTRIBUTE_UNUSED, jint length ATTRIBUTE_UNUSED, void* user_data ATTRIBUTE_UNUSED) { return 0; } static jint JNICALL PrimitiveFieldValueCallback(jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong class_tag, jlong* tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data) { FindFieldCallbacks* p = reinterpret_cast(user_data); if (*tag_ptr >= p->tag_to_find) { std::ostringstream oss; oss << *tag_ptr << '@' << class_tag << " (" << (kind == JVMTI_HEAP_REFERENCE_FIELD ? "instance, " : "static, ") << GetPrimitiveTypeName(value_type) << ", index=" << info->field.index << ") "; // Be lazy, always print eight bytes. static_assert(sizeof(jvalue) == sizeof(uint64_t), "Unexpected jvalue size"); uint64_t val; memcpy(&val, &value, sizeof(uint64_t)); // To avoid undefined behavior. oss << android::base::StringPrintf("%016" PRIx64, val); if (!p->data.empty()) { p->data += "\n"; } p->data += oss.str(); *tag_ptr = *tag_ptr + 1; } return 0; } std::string data; const jlong tag_to_find; }; jvmtiHeapCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); callbacks.heap_iteration_callback = FindFieldCallbacks::HeapIterationCallback; callbacks.primitive_field_callback = FindFieldCallbacks::PrimitiveFieldValueCallback; FindFieldCallbacks ffc(tag); jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc); if (JvmtiErrorToException(env, jvmti_env, ret)) { return nullptr; } return env->NewStringUTF(ffc.data.c_str()); } extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized( JNIEnv* env, jclass, jclass c) { jint status; jvmtiError error = jvmti_env->GetClassStatus(c, &status); if (JvmtiErrorToException(env, jvmti_env, error)) { return false; } return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0; } extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateOverInstancesCount( JNIEnv* env, jclass, jclass target) { jint cnt = 0; auto count_func = [](jlong, jlong, jlong*, void* user_data) -> jvmtiIterationControl { *reinterpret_cast(user_data) += 1; return JVMTI_ITERATION_CONTINUE; }; JvmtiErrorToException(env, jvmti_env, jvmti_env->IterateOverInstancesOfClass(target, JVMTI_HEAP_OBJECT_EITHER, count_func, &cnt)); return cnt; } } // namespace Test906IterateHeap } // namespace art