Add VMDebug.countInstancesOfClasses

countInstancesOfClasses allows passing multiple classes unlike
countInstanceOfClass instead of needing to do one at a time.

This is going to be used to speed up string mode checking.

Also changed the logic to not do a GC, this was the old Dalvik
behavior. It is the job of the caller to do this.

Added test.

https://code.google.com/p/android/issues/detail?id=177552

Change-Id: Ia85684f40cf59a52aa71a8479c711a994651209b
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 26a45d3..0ae9cdf 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1704,11 +1704,12 @@
     mirror::Class* instance_class = obj->GetClass();
     CHECK(instance_class != nullptr);
     for (size_t i = 0; i < instance_counter->classes_.size(); ++i) {
+      mirror::Class* klass = instance_counter->classes_[i];
       if (instance_counter->use_is_assignable_from_) {
-        if (instance_counter->classes_[i]->IsAssignableFrom(instance_class)) {
+        if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
           ++instance_counter->counts_[i];
         }
-      } else if (instance_class == instance_counter->classes_[i]) {
+      } else if (instance_class == klass) {
         ++instance_counter->counts_[i];
       }
     }
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 1078492..8febb62 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -257,21 +257,45 @@
 static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass,
                                            jboolean countAssignable) {
   ScopedObjectAccess soa(env);
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  // We only want reachable instances, so do a GC. Heap::VisitObjects visits all of the heap
-  // objects in the all spaces and the allocation stack.
-  heap->CollectGarbage(false);
+  gc::Heap* const heap = Runtime::Current()->GetHeap();
+  // Caller's responsibility to do GC if desired.
   mirror::Class* c = soa.Decode<mirror::Class*>(javaClass);
   if (c == nullptr) {
     return 0;
   }
-  std::vector<mirror::Class*> classes;
-  classes.push_back(c);
+  std::vector<mirror::Class*> classes {c};
   uint64_t count = 0;
   heap->CountInstances(classes, countAssignable, &count);
   return count;
 }
 
+static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses,
+                                                  jboolean countAssignable) {
+  ScopedObjectAccess soa(env);
+  gc::Heap* const heap = Runtime::Current()->GetHeap();
+  // Caller's responsibility to do GC if desired.
+  auto* decoded_classes = soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaClasses);
+  if (decoded_classes == nullptr) {
+    return nullptr;
+  }
+  std::vector<mirror::Class*> classes;
+  for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) {
+    classes.push_back(decoded_classes->Get(i));
+  }
+  std::vector<uint64_t> counts(classes.size(), 0u);
+  // Heap::CountInstances can handle null and will put 0 for these classes.
+  heap->CountInstances(classes, countAssignable, &counts[0]);
+  auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
+  if (long_counts == nullptr) {
+    soa.Self()->AssertPendingOOMException();
+    return nullptr;
+  }
+  for (size_t i = 0; i < counts.size(); ++i) {
+    long_counts->Set(i, counts[i]);
+  }
+  return soa.AddLocalReference<jlongArray>(long_counts);
+}
+
 // 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
@@ -452,6 +476,7 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
+  NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
   NATIVE_METHOD(VMDebug, crash, "()V"),
   NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"),
   NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt
index 579f98f..b8d72f6 100644
--- a/test/099-vmdebug/expected.txt
+++ b/test/099-vmdebug/expected.txt
@@ -17,3 +17,9 @@
 Got expected exception
 Test sampling with bogus (<= 0) interval
 Got expected exception
+Instances of ClassA 2
+Instances of ClassB 1
+Instances of null 0
+Instances of ClassA assignable 3
+Array counts [2, 1, 0]
+Array counts assignable [3, 1, 0]
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index add2ff6..1be5765 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -17,6 +17,8 @@
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Map;
 
 public class Main {
@@ -30,7 +32,9 @@
             return;
         }
         testMethodTracing();
+        testCountInstances();
         testRuntimeStat();
+        testRuntimeStats();
     }
 
     private static File createTempFile() throws Exception {
@@ -220,12 +224,39 @@
         checkHistogram(blocking_gc_count_rate_histogram);
     }
 
+    static class ClassA { }
+    static class ClassB { }
+    static class ClassC extends ClassA { }
+
+    private static void testCountInstances() throws Exception {
+        ArrayList<Object> l = new ArrayList<Object>();
+        l.add(new ClassA());
+        l.add(new ClassB());
+        l.add(new ClassA());
+        l.add(new ClassC());
+        Runtime.getRuntime().gc();
+        System.out.println("Instances of ClassA " +
+                VMDebug.countInstancesofClass(ClassA.class, false));
+        System.out.println("Instances of ClassB " +
+                VMDebug.countInstancesofClass(ClassB.class, false));
+        System.out.println("Instances of null " + VMDebug.countInstancesofClass(null, false));
+        System.out.println("Instances of ClassA assignable " +
+                VMDebug.countInstancesofClass(ClassA.class, true));
+        Class[] classes = new Class[]{ClassA.class, ClassB.class, null};
+        long[] counts = VMDebug.countInstancesofClasses(classes, false);
+        System.out.println("Array counts " + Arrays.toString(counts));
+        counts = VMDebug.countInstancesofClasses(classes, true);
+        System.out.println("Array counts assignable " + Arrays.toString(counts));
+    }
+
     private static class VMDebug {
         private static final Method startMethodTracingMethod;
         private static final Method stopMethodTracingMethod;
         private static final Method getMethodTracingModeMethod;
         private static final Method getRuntimeStatMethod;
         private static final Method getRuntimeStatsMethod;
+        private static final Method countInstancesOfClassMethod;
+        private static final Method countInstancesOfClassesMethod;
         static {
             try {
                 Class c = Class.forName("dalvik.system.VMDebug");
@@ -235,6 +266,10 @@
                 getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
                 getRuntimeStatMethod = c.getDeclaredMethod("getRuntimeStat", String.class);
                 getRuntimeStatsMethod = c.getDeclaredMethod("getRuntimeStats");
+                countInstancesOfClassMethod = c.getDeclaredMethod("countInstancesOfClass",
+                        Class.class, Boolean.TYPE);
+                countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses",
+                        Class[].class, Boolean.TYPE);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
@@ -257,5 +292,13 @@
         public static Map<String, String> getRuntimeStats() throws Exception {
             return (Map<String, String>) getRuntimeStatsMethod.invoke(null);
         }
+        public static long countInstancesofClass(Class c, boolean assignable) throws Exception {
+            return (long) countInstancesOfClassMethod.invoke(null, new Object[]{c, assignable});
+        }
+        public static long[] countInstancesofClasses(Class[] classes, boolean assignable)
+                throws Exception {
+            return (long[]) countInstancesOfClassesMethod.invoke(
+                    null, new Object[]{classes, assignable});
+        }
     }
 }