Add support for tracking jvmti allocations
Adds a jvmti extension for getting the amount of memory allocated and
by jvmtiEnvs and currently live.
Bug: 62065509
Test: ./test.py --host -j40
Change-Id: I5d05b171260d91f9c415f7a5cb40cc01b48d7d07
diff --git a/test/1900-track-alloc/src/Main.java b/test/1900-track-alloc/src/Main.java
new file mode 100644
index 0000000..0dab4ef
--- /dev/null
+++ b/test/1900-track-alloc/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1900.run();
+ }
+}
diff --git a/test/1900-track-alloc/src/art/Test1900.java b/test/1900-track-alloc/src/art/Test1900.java
new file mode 100644
index 0000000..717999b
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Test1900.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+public class Test1900 {
+ public static void checkLE(long exp, long o) {
+ if (exp > o) {
+ throw new Error("Expected: " + exp + " Got: " + o);
+ }
+ }
+ public static void checkEq(long exp, long o) {
+ if (exp != o) {
+ throw new Error("Expected: " + exp + " Got: " + o);
+ }
+ }
+
+ public static void runConcurrent(Runnable... rs) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(rs.length);
+ Thread[] thrs = new Thread[rs.length];
+ for (int i = 0; i < rs.length; i++) {
+ final Runnable r = rs[i];
+ thrs[i] = new Thread(() -> {
+ latch.countDown();
+ r.run();
+ });
+ thrs[i].start();
+ }
+ for (Thread thr : thrs) {
+ thr.join();
+ }
+ }
+ static class Holder {
+ public long val;
+ }
+
+ public static void run() throws Exception {
+ initializeTest();
+ // Get the overhead for the native part of this test.
+ final long base_state = getAmountAllocated();
+
+ // Basic alloc-dealloc
+ checkEq(base_state + 0, getAmountAllocated());
+ long abc = doAllocate(10);
+ checkLE(base_state + 10, getAmountAllocated());
+ long def = doAllocate(10);
+ checkLE(base_state + 20, getAmountAllocated());
+ doDeallocate(abc);
+ checkLE(base_state + 10, getAmountAllocated());
+
+ doDeallocate(def);
+
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it concurrently.
+ Runnable add10 = () -> { long x = doAllocate(10); doDeallocate(x); };
+ Runnable[] rs = new Runnable[100];
+ Arrays.fill(rs, add10);
+ runConcurrent(rs);
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it concurrently with different threads to allocate and deallocate.
+ final Semaphore sem = new Semaphore(0);
+ final Holder h = new Holder();
+ runConcurrent(
+ () -> {
+ try {
+ h.val = doAllocate(100);
+ checkLE(base_state + 100, getAmountAllocated());
+ sem.release();
+ } catch (Exception e) { throw new Error("exception!", e); }
+ },
+ () -> {
+ try {
+ sem.acquire();
+ long after_acq = getAmountAllocated();
+ doDeallocate(h.val);
+ checkLE(base_state + 100, after_acq);
+ } catch (Exception e) { throw new Error("exception!", e); }
+ }
+ );
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it with multiple jvmtienvs.
+ long env1 = newJvmtiEnv();
+ long env2 = newJvmtiEnv();
+
+ final long new_base_state = getAmountAllocated();
+ // new jvmtienvs shouldn't save us memory.
+ checkLE(base_state, new_base_state);
+ // Make sure we track both.
+ abc = doAllocate(env1, 10);
+ checkLE(new_base_state + 10, getAmountAllocated());
+ def = doAllocate(env2, 10);
+ checkLE(new_base_state + 20, getAmountAllocated());
+ doDeallocate(env1, abc);
+ checkLE(new_base_state + 10, getAmountAllocated());
+
+ doDeallocate(env2, def);
+
+ checkEq(new_base_state + 0, getAmountAllocated());
+
+ destroyJvmtiEnv(env1);
+ destroyJvmtiEnv(env2);
+
+ // Back to normal after getting rid of the envs.
+ checkEq(base_state + 0, getAmountAllocated());
+ }
+
+ private static native long doAllocate(long jvmtienv, long size);
+ private static long doAllocate(long size) {
+ return doAllocate(getDefaultJvmtiEnv(), size);
+ }
+
+ private static native void doDeallocate(long jvmtienv, long ptr);
+ private static void doDeallocate(long size) {
+ doDeallocate(getDefaultJvmtiEnv(), size);
+ }
+
+ private static native long getDefaultJvmtiEnv();
+ private static native long newJvmtiEnv();
+ private static native void destroyJvmtiEnv(long jvmtienv);
+ private static native long getAmountAllocated();
+ private static native void initializeTest();
+}