Add mallopt M_PURGE benchmark.

Update the native allocator documentation to include running of this
benchmark.

Move the malloc_benchmark.cpp to malloc_sql_benchmark.cpp and use
malloc_benchmark.cpp for benchmarking functions from malloc.h.

Bug: 137795072

Test: Ran new benchmark.
Change-Id: I76856de833032da324ad0bc0b6bd85a4ea8c253d
diff --git a/benchmarks/malloc_benchmark.cpp b/benchmarks/malloc_benchmark.cpp
index ca54f11..18ba523 100644
--- a/benchmarks/malloc_benchmark.cpp
+++ b/benchmarks/malloc_benchmark.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,102 +27,46 @@
  */
 
 #include <malloc.h>
-#include <stdlib.h>
+#include <unistd.h>
+
+#include <vector>
 
 #include <benchmark/benchmark.h>
 #include "util.h"
 
 #if defined(__BIONIC__)
 
-enum AllocEnum : uint8_t {
-  MALLOC = 0,
-  CALLOC,
-  MEMALIGN,
-  REALLOC,
-  FREE,
-};
-
-struct MallocEntry {
-  AllocEnum type;
-  size_t idx;
-  size_t size;
-  size_t arg2;
-};
-
-void BenchmarkMalloc(MallocEntry entries[], size_t total_entries, size_t max_allocs) {
-  void* ptrs[max_allocs];
-
-  for (size_t i = 0; i < total_entries; i++) {
-    switch (entries[i].type) {
-    case MALLOC:
-      ptrs[entries[i].idx] = malloc(entries[i].size);
-      // Touch at least one byte of the allocation to make sure that
-      // PSS for this allocation is counted.
-      reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 10;
-      break;
-    case CALLOC:
-      ptrs[entries[i].idx] = calloc(entries[i].arg2, entries[i].size);
-      // Touch at least one byte of the allocation to make sure that
-      // PSS for this allocation is counted.
-      reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 20;
-      break;
-    case MEMALIGN:
-      ptrs[entries[i].idx] = memalign(entries[i].arg2, entries[i].size);
-      // Touch at least one byte of the allocation to make sure that
-      // PSS for this allocation is counted.
-      reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 30;
-      break;
-    case REALLOC:
-      if (entries[i].arg2 == 0) {
-        ptrs[entries[i].idx] = realloc(nullptr, entries[i].size);
-      } else {
-        ptrs[entries[i].idx] = realloc(ptrs[entries[i].arg2 - 1], entries[i].size);
-      }
-      // Touch at least one byte of the allocation to make sure that
-      // PSS for this allocation is counted.
-      reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 40;
-      break;
-    case FREE:
-      free(ptrs[entries[i].idx]);
-      break;
-    }
-  }
-}
-
-// This codifies playing back a single threaded trace of the allocations
-// when running the SQLite BenchMark app.
-// Instructions for recreating:
-//   - Enable malloc debug
-//       setprop wrap.com.wtsang02.sqliteutil "LIBC_DEBUG_MALLOC_OPTIONS=record_allocs logwrapper"
-//   - Start the SQLite BenchMark app
-//   - Dump allocs using the signal to get rid of non sql allocs(kill -47 <SQLITE_PID>)
-//   - Run the benchmark.
-//   - Dump allocs using the signal again.
-//   - Find the thread that has the most allocs and run the helper script
-//       bionic/libc/malloc_debug/tools/gen_malloc.pl -i <THREAD_ID> g_sql_entries kMaxSqlAllocSlots < <ALLOC_FILE> > malloc_sql.h
-#include "malloc_sql.h"
-
-static void BM_malloc_sql_trace_default(benchmark::State& state) {
-  // The default is expected to be a zero decay time.
-  mallopt(M_DECAY_TIME, 0);
-
-  for (auto _ : state) {
-    BenchmarkMalloc(g_sql_entries, sizeof(g_sql_entries) / sizeof(MallocEntry),
-                    kMaxSqlAllocSlots);
-  }
-}
-BIONIC_BENCHMARK(BM_malloc_sql_trace_default);
-
-static void BM_malloc_sql_trace_decay1(benchmark::State& state) {
+static void BM_mallopt_purge(benchmark::State& state) {
+  static size_t sizes[] = {8, 16, 32, 64, 128, 1024, 4096, 16384, 65536, 131072, 1048576};
+  static int pagesize = getpagesize();
   mallopt(M_DECAY_TIME, 1);
-
+  mallopt(M_PURGE, 0);
   for (auto _ : state) {
-    BenchmarkMalloc(g_sql_entries, sizeof(g_sql_entries) / sizeof(MallocEntry),
-                    kMaxSqlAllocSlots);
-  }
+    state.PauseTiming();
+    std::vector<void*> ptrs;
+    for (auto size : sizes) {
+      // Allocate at least two pages worth of the allocations.
+      for (size_t allocated = 0; allocated < 2 * static_cast<size_t>(pagesize); allocated += size) {
+        void* ptr = malloc(size);
+        if (ptr == nullptr) {
+          state.SkipWithError("Failed to allocate memory");
+        }
+        MakeAllocationResident(ptr, size, pagesize);
+        ptrs.push_back(ptr);
+      }
+    }
+    // Free the memory, which should leave many of the pages resident until
+    // the purge call.
+    for (auto ptr : ptrs) {
+      free(ptr);
+    }
+    ptrs.clear();
+    state.ResumeTiming();
 
+    mallopt(M_PURGE, 0);
+  }
   mallopt(M_DECAY_TIME, 0);
 }
-BIONIC_BENCHMARK(BM_malloc_sql_trace_decay1);
+BIONIC_BENCHMARK(BM_mallopt_purge);
 
 #endif