Add bionic-stress-tests.

I plan to add tests to this test binary that take a long time to run.

The first is a malloc test that demonstrates that jemalloc appears
to be leaking memory slowly over time.

Test: Ran the bionic-stress-tests on host and verified it runs forever,
Test: but doesn't display RSS/VSS data.
Test: Ran the bionic-stress-tests on a taimen and verified it runs forever,
Test: and does display RSS/VSS data.
Change-Id: Ic612e4181ffea898d4d83af097939cd517a180ee
diff --git a/tests/Android.bp b/tests/Android.bp
index f0fe5e6..1755053 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -751,6 +751,33 @@
     ],
 }
 
+cc_test {
+    name: "bionic-stress-tests",
+    defaults: [
+        "bionic_tests_defaults",
+    ],
+
+    // For now, these tests run forever, so do not use the isolation framework.
+    isolated: false,
+
+    srcs: [
+        "malloc_stress_test.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+    ],
+
+    target: {
+        android: {
+            static_libs: [
+                "libmeminfo",
+                "libprocinfo",
+            ],
+        },
+    },
+}
+
 // -----------------------------------------------------------------------------
 // Tests for the device linked against bionic's static library. Run with:
 //   adb shell /data/nativetest/bionic-unit-tests-static/bionic-unit-tests-static
diff --git a/tests/malloc_stress_test.cpp b/tests/malloc_stress_test.cpp
new file mode 100644
index 0000000..6c5cc06
--- /dev/null
+++ b/tests/malloc_stress_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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 <gtest/gtest.h>
+
+#include <inttypes.h>
+#include <malloc.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <thread>
+#include <vector>
+
+#include <android-base/strings.h>
+#if defined(__BIONIC__)
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+#endif
+
+TEST(malloc_stress, multiple_threads_forever) {
+  constexpr size_t kMaxThreads = 256;
+  constexpr size_t kAllocSize = 4096;
+#if defined(__BIONIC__)
+  uint64_t rss_min = UINT64_MAX;
+  uint64_t rss_max = 0;
+  uint64_t vss_min = UINT64_MAX;
+  uint64_t vss_max = 0;
+  ASSERT_EQ(1, mallopt(M_DECAY_TIME, 1));
+#endif
+  uint64_t mallinfo_min = UINT64_MAX;
+  uint64_t mallinfo_max = 0;
+  for (size_t i = 0; ; i++) {
+    printf("Pass %zu\n", i);
+
+    std::vector<std::thread*> threads;
+    for (size_t i = 0; i < kMaxThreads; i++) {
+      threads.push_back(new std::thread([]() {
+        void* buf = malloc(4096);
+        if (buf == nullptr) {
+          printf("Failed to allocate memory\n");
+          _exit(1);
+        }
+        memset(buf, 0, kAllocSize);
+        sleep(1);
+        free(buf);
+      }));
+    }
+
+    for (auto thread : threads) {
+      thread->join();
+      delete thread;
+    }
+    threads.clear();
+
+#if defined(__BIONIC__)
+    android::meminfo::ProcMemInfo proc_mem(getpid());
+    const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
+    uint64_t rss_bytes = 0;
+    uint64_t vss_bytes = 0;
+    for (auto& vma : maps) {
+      if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:")) {
+        android::meminfo::Vma update_vma(vma);
+        ASSERT_TRUE(proc_mem.FillInVmaStats(update_vma));
+        rss_bytes += update_vma.usage.rss;
+        vss_bytes += update_vma.usage.vss;
+      }
+    }
+    if (rss_bytes < rss_min) {
+      rss_min = rss_bytes;
+    }
+    if (rss_bytes > rss_max) {
+      rss_max = rss_bytes;
+    }
+    if (vss_bytes < vss_min) {
+      vss_min = vss_bytes;
+    }
+    if (vss_bytes > vss_max) {
+      vss_max = vss_bytes;
+    }
+    printf("RSS %" PRIu64 " %0.2fMB\n", rss_bytes, rss_bytes / (1024.0 * 1024.0));
+    printf("  Min %" PRIu64 " %0.2fMB\n", rss_min, rss_min / (1024.0 * 1024.0));
+    printf("  Max %" PRIu64 " %0.2fMB\n", rss_max, rss_max / (1024.0 * 1024.0));
+    printf("VSS %" PRIu64 " %0.2f MB\n", vss_bytes, vss_bytes / (1024.0 * 1024.0));
+    printf("  Min %" PRIu64 " %0.2fMB\n", vss_min, vss_min / (1024.0 * 1024.0));
+    printf("  Max %" PRIu64 " %0.2fMB\n", vss_max, vss_max / (1024.0 * 1024.0));
+#endif
+
+    size_t mallinfo_bytes = mallinfo().uordblks;
+    if (mallinfo_bytes < mallinfo_min) {
+      mallinfo_min = mallinfo_bytes;
+    }
+    if (mallinfo_bytes > mallinfo_max) {
+      mallinfo_max = mallinfo_bytes;
+    }
+    printf("Allocated memory %zu %0.2fMB\n", mallinfo_bytes, mallinfo_bytes / (1024.0 * 1024.0));
+    printf("  Min %" PRIu64 " %0.2fMB\n", mallinfo_min, mallinfo_min / (1024.0 * 1024.0));
+    printf("  Max %" PRIu64 " %0.2fMB\n", mallinfo_max, mallinfo_max / (1024.0 * 1024.0));
+  }
+}