Add 2040-huge-native-alloc test
Check that if we allocate more than a gigabyte of native memory and then
repeatedly notify the collector of native allocation, we temporarily
block the allocation for a reasonable amount of time to allow the GC to
catch up.
We previously had some coverage of this, but lost it when BigInteger
moved away from using native allocation.
Test: Ran locally on host; TreeHugger.
Bug: 186592536
Change-Id: Id7d120e00630e26bcdf396a80069c9cca7c00804
diff --git a/test/2040-huge-native-alloc/Android.bp b/test/2040-huge-native-alloc/Android.bp
new file mode 100644
index 0000000..8a5501d
--- /dev/null
+++ b/test/2040-huge-native-alloc/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2040-huge-native-alloc`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2040-huge-native-alloc",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2040-huge-native-alloc-expected-stdout",
+ ":art-run-test-2040-huge-native-alloc-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2040-huge-native-alloc-expected-stdout",
+ out: ["art-run-test-2040-huge-native-alloc-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2040-huge-native-alloc-expected-stderr",
+ out: ["art-run-test-2040-huge-native-alloc-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/2040-huge-native-alloc/expected-stderr.txt b/test/2040-huge-native-alloc/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2040-huge-native-alloc/expected-stderr.txt
diff --git a/test/2040-huge-native-alloc/expected-stdout.txt b/test/2040-huge-native-alloc/expected-stdout.txt
new file mode 100644
index 0000000..f2fc51c
--- /dev/null
+++ b/test/2040-huge-native-alloc/expected-stdout.txt
@@ -0,0 +1,3 @@
+JNI_OnLoad called
+Main Started
+Main Finished
diff --git a/test/2040-huge-native-alloc/huge_native_buf.cc b/test/2040-huge-native-alloc/huge_native_buf.cc
new file mode 100644
index 0000000..06186c9
--- /dev/null
+++ b/test/2040-huge-native-alloc/huge_native_buf.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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 "base/utils.h"
+#include "jni.h"
+#include <stddef.h>
+
+namespace art {
+
+static constexpr size_t HUGE_SIZE = 10'000'000;
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getHugeNativeBuffer(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ char* buffer = new char[HUGE_SIZE];
+ return env->NewDirectByteBuffer(buffer, HUGE_SIZE);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_deleteHugeNativeBuffer(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject jbuffer) {
+ delete [] static_cast<char*>(env->GetDirectBufferAddress(jbuffer));
+}
+
+} // namespace art
+
+
diff --git a/test/2040-huge-native-alloc/info.txt b/test/2040-huge-native-alloc/info.txt
new file mode 100644
index 0000000..41c5ef6
--- /dev/null
+++ b/test/2040-huge-native-alloc/info.txt
@@ -0,0 +1,2 @@
+Check that we properly trigger world stop collections after a lot of native
+allocation.
diff --git a/test/2040-huge-native-alloc/src/Main.java b/test/2040-huge-native-alloc/src/Main.java
new file mode 100644
index 0000000..b865347
--- /dev/null
+++ b/test/2040-huge-native-alloc/src/Main.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+import dalvik.system.VMRuntime;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+
+public class Main {
+
+ static final int HOW_MANY_HUGE = 110; // > 1GB to trigger blocking in default config.
+ int allocated = 0;
+ int deallocated = 0;
+ static Object lock = new Object();
+ WeakReference<BufferHolder>[] references = new WeakReference[HOW_MANY_HUGE];
+
+ class BufferHolder {
+ private ByteBuffer buffer;
+ BufferHolder() {
+ ++allocated;
+ buffer = getHugeNativeBuffer();
+ }
+ protected void finalize() {
+ synchronized(lock) {
+ ++deallocated;
+ }
+ deleteHugeNativeBuffer(buffer);
+ buffer = null;
+ }
+ }
+
+ // Repeatedly inform the GC of native allocations. Return the time (in nsecs) this takes.
+ private static long timeNotifications() {
+ VMRuntime vmr = VMRuntime.getRuntime();
+ long startNanos = System.nanoTime();
+ for (int i = 0; i < 200; ++i) {
+ vmr.notifyNativeAllocation();
+ }
+ return System.nanoTime() - startNanos;
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ System.out.println("Main Started");
+ new Main().run();
+ System.out.println("Main Finished");
+ }
+
+ void run() {
+ timeNotifications(); // warm up.
+ long referenceTime1 = timeNotifications();
+ long referenceTime2 = timeNotifications();
+ long referenceTime = Math.min(referenceTime1, referenceTime2);
+
+ // Allocate half a GB of native memory without informing the GC.
+ for (int i = 0; i < HOW_MANY_HUGE; ++i) {
+ new BufferHolder();
+ }
+
+ // One of the notifications should block for GC to catch up.
+ long actualTime = timeNotifications();
+
+ if (actualTime > 500_000_000) {
+ System.out.println("Notifications ran too slowly; excessive blocking? msec = "
+ + (actualTime / 1_000_000));
+ } else if (actualTime < 3 * referenceTime + 2_000_000) {
+ System.out.println("Notifications ran too quickly; no blocking GC? msec = "
+ + (actualTime / 1_000_000));
+ }
+
+ // Let finalizers run.
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected interrupt");
+ }
+
+ if (deallocated > allocated || deallocated < allocated - 5 /* slop for register references */) {
+ System.out.println("Unexpected number of deallocated objects:");
+ System.out.println("Allocated = " + allocated + " deallocated = " + deallocated);
+ }
+ }
+
+ private static native ByteBuffer getHugeNativeBuffer();
+ private static native void deleteHugeNativeBuffer(ByteBuffer buf);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 1a14eff..e375ad9 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -726,6 +726,7 @@
"2033-shutdown-mechanics/native_shutdown.cc",
"2036-jni-filechannel/jni_filechannel.cc",
"2037-thread-name-inherit/thread_name_inherit.cc",
+ "2040-huge-native-alloc/huge_native_buf.cc",
"common/runtime_state.cc",
"common/stack_inspect.cc",
],
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 6034b94..b2480eb 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1190,7 +1190,8 @@
"2033-shutdown-mechanics",
"2035-structural-native-method",
"2036-structural-subclass-shadow",
- "2038-hiddenapi-jvmti-ext"],
+ "2038-hiddenapi-jvmti-ext",
+ "2040-huge-native-alloc"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},