Re-land "Add 2040-huge-native-alloc test"

This reverts commit c256028e1fb92b47c741db67b6bc8ca6995e6c1b.

Reason for revert: The underlying problem is fixed, and we need
better testing.

PS2 and later also contains the following change:

More aggressively notify the collector of native allocation, to account
for the fact that on host, only one notification in 384 is pass through,
since mallinfo is SLOW.

This version once again assumes that we sleep if necessary to allow the
triggered collection to both get started and complete.

Test: Treehugger
Bug: 189150802
Bug: 189955496

Change-Id: I2ec4ca9a37fa9dbd9c7d351208d3d5ca2d4ee5d4
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..0366e5e
--- /dev/null
+++ b/test/2040-huge-native-alloc/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * 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();
+    // Iteration count must be >= Heap::kNotifyNativeInterval.
+    for (int i = 0; i < 400; ++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 e710509..e393f06 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -730,6 +730,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 d9b089d..10da9ab 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."]
     },