diff options
-rw-r--r-- | runtime/debugger.cc | 6 | ||||
-rw-r--r-- | runtime/gc/heap.cc | 25 | ||||
-rw-r--r-- | runtime/gc/heap.h | 3 | ||||
-rw-r--r-- | runtime/native/dalvik_system_VMDebug.cc | 48 | ||||
-rw-r--r-- | test/099-vmdebug/expected.txt | 6 | ||||
-rw-r--r-- | test/099-vmdebug/info.txt | 2 | ||||
-rw-r--r-- | test/099-vmdebug/src/Main.java | 61 |
7 files changed, 139 insertions, 12 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 1dcd935eea..13029fb958 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -963,7 +963,11 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, } VariableSizedHandleScope hs(Thread::Current()); std::vector<Handle<mirror::Object>> raw_instances; - Runtime::Current()->GetHeap()->GetInstances(hs, hs.NewHandle(c), max_count, raw_instances); + Runtime::Current()->GetHeap()->GetInstances(hs, + hs.NewHandle(c), + /* use_is_assignable_from */ false, + max_count, + raw_instances); for (size_t i = 0; i < raw_instances.size(); ++i) { instances->push_back(gRegistry->Add(raw_instances[i].Get())); } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 9f6266612a..f29ae92e2d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1796,19 +1796,25 @@ uint64_t Heap::GetBytesAllocatedEver() const { return GetBytesFreedEver() + GetBytesAllocated(); } +// Check whether the given object is an instance of the given class. +static bool MatchesClass(mirror::Object* obj, + Handle<mirror::Class> h_class, + bool use_is_assignable_from) REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* instance_class = obj->GetClass(); + CHECK(instance_class != nullptr); + ObjPtr<mirror::Class> klass = h_class.Get(); + if (use_is_assignable_from) { + return klass != nullptr && klass->IsAssignableFrom(instance_class); + } + return instance_class == klass; +} + void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes, bool use_is_assignable_from, uint64_t* counts) { auto instance_counter = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* instance_class = obj->GetClass(); - CHECK(instance_class != nullptr); for (size_t i = 0; i < classes.size(); ++i) { - ObjPtr<mirror::Class> klass = classes[i].Get(); - if (use_is_assignable_from) { - if (klass != nullptr && klass->IsAssignableFrom(instance_class)) { - ++counts[i]; - } - } else if (instance_class == klass) { + if (MatchesClass(obj, classes[i], use_is_assignable_from)) { ++counts[i]; } } @@ -1818,11 +1824,12 @@ void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes, void Heap::GetInstances(VariableSizedHandleScope& scope, Handle<mirror::Class> h_class, + bool use_is_assignable_from, int32_t max_count, std::vector<Handle<mirror::Object>>& instances) { DCHECK_GE(max_count, 0); auto instance_collector = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj->GetClass() == h_class.Get()) { + if (MatchesClass(obj, h_class, use_is_assignable_from)) { if (max_count == 0 || instances.size() < static_cast<size_t>(max_count)) { instances.push_back(scope.NewHandle(obj)); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 4d7424c7ef..ac0d82e12a 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -346,9 +346,10 @@ class Heap { REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Implements JDWP RT_Instances. + // Implements VMDebug.getInstancesOfClasses and JDWP RT_Instances. void GetInstances(VariableSizedHandleScope& scope, Handle<mirror::Class> c, + bool use_is_assignable_from, int32_t max_count, std::vector<Handle<mirror::Object>>& instances) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 2663bea344..88a78ab4be 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -319,6 +319,53 @@ static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, return soa.AddLocalReference<jlongArray>(long_counts); } +static jobjectArray VMDebug_getInstancesOfClasses(JNIEnv* env, + jclass, + jobjectArray javaClasses, + jboolean includeAssignable) { + ScopedObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ObjectArray<mirror::Class>> classes = hs.NewHandle( + soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses)); + if (classes == nullptr) { + return nullptr; + } + + jclass object_array_class = env->FindClass("[Ljava/lang/Object;"); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + CHECK(object_array_class != nullptr); + + size_t num_classes = classes->GetLength(); + jobjectArray result = env->NewObjectArray(num_classes, object_array_class, nullptr); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + gc::Heap* const heap = Runtime::Current()->GetHeap(); + MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr)); + for (size_t i = 0; i < num_classes; ++i) { + h_class.Assign(classes->Get(i)); + + VariableSizedHandleScope hs2(soa.Self()); + std::vector<Handle<mirror::Object>> raw_instances; + heap->GetInstances(hs2, h_class, includeAssignable, /* max_count */ 0, raw_instances); + jobjectArray array = env->NewObjectArray(raw_instances.size(), + WellKnownClasses::java_lang_Object, + nullptr); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + for (size_t j = 0; j < raw_instances.size(); ++j) { + env->SetObjectArrayElement(array, j, raw_instances[j].ToJObject()); + } + env->SetObjectArrayElement(result, i, array); + } + return result; +} + // We export the VM internal per-heap-space size/alloc/free metrics // for the zygote space, alloc space (application heap), and the large // object space for dumpsys meminfo. The other memory region data such @@ -534,6 +581,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), + NATIVE_METHOD(VMDebug, getInstancesOfClasses, "([Ljava/lang/Class;Z)[[Ljava/lang/Object;"), NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt index b8d72f66f8..f7801de62f 100644 --- a/test/099-vmdebug/expected.txt +++ b/test/099-vmdebug/expected.txt @@ -23,3 +23,9 @@ Instances of null 0 Instances of ClassA assignable 3 Array counts [2, 1, 0] Array counts assignable [3, 1, 0] +ClassD got 3, combined mask: 13 +ClassE got 2, combined mask: 18 +null got 0 +ClassD assignable got 5, combined mask: 31 +ClassE assignable got 2, combined mask: 18 +null assignable got 0 diff --git a/test/099-vmdebug/info.txt b/test/099-vmdebug/info.txt index 7f88086986..873429e076 100644 --- a/test/099-vmdebug/info.txt +++ b/test/099-vmdebug/info.txt @@ -1 +1 @@ -Tests of private dalvik.system.VMDebug support for method tracing. +Tests of dalvik.system.VMDebug APIs. diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java index 90ad3155ca..e0d829a0d6 100644 --- a/test/099-vmdebug/src/Main.java +++ b/test/099-vmdebug/src/Main.java @@ -33,6 +33,7 @@ public class Main { } testMethodTracing(); testCountInstances(); + testGetInstances(); testRuntimeStat(); testRuntimeStats(); } @@ -249,6 +250,59 @@ public class Main { System.out.println("Array counts assignable " + Arrays.toString(counts)); } + static class ClassD { + public int mask; + + public ClassD(int mask) { + this.mask = mask; + } + } + + static class ClassE extends ClassD { + public ClassE(int mask) { + super(mask); + } + } + + private static void testGetInstances() throws Exception { + ArrayList<Object> l = new ArrayList<Object>(); + l.add(new ClassD(0x01)); + l.add(new ClassE(0x02)); + l.add(new ClassD(0x04)); + l.add(new ClassD(0x08)); + l.add(new ClassE(0x10)); + Runtime.getRuntime().gc(); + Class<?>[] classes = new Class<?>[] {ClassD.class, ClassE.class, null}; + Object[][] instances = VMDebug.getInstancesOfClasses(classes, false); + + int mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null got " + instances[2].length); + + instances = VMDebug.getInstancesOfClasses(classes, true); + mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD assignable got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE assignable got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null assignable got " + instances[2].length); + } + private static class VMDebug { private static final Method startMethodTracingMethod; private static final Method stopMethodTracingMethod; @@ -257,6 +311,7 @@ public class Main { private static final Method getRuntimeStatsMethod; private static final Method countInstancesOfClassMethod; private static final Method countInstancesOfClassesMethod; + private static final Method getInstancesOfClassesMethod; static { try { Class<?> c = Class.forName("dalvik.system.VMDebug"); @@ -270,6 +325,8 @@ public class Main { Class.class, Boolean.TYPE); countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses", Class[].class, Boolean.TYPE); + getInstancesOfClassesMethod = c.getDeclaredMethod("getInstancesOfClasses", + Class[].class, Boolean.TYPE); } catch (Exception e) { throw new RuntimeException(e); } @@ -300,5 +357,9 @@ public class Main { return (long[]) countInstancesOfClassesMethod.invoke( null, new Object[]{classes, assignable}); } + public static Object[][] getInstancesOfClasses(Class<?>[] classes, boolean assignable) throws Exception { + return (Object[][]) getInstancesOfClassesMethod.invoke( + null, new Object[]{classes, assignable}); + } } } |