Add new mallopt M_LOG_STATS.

This new mallopt cause statistics of the allocator to be printed in
the log.

Add a stats print for jemalloc.

This is designed to be used as part of a dumpsys meminfo --XXXX
option so that it's easier to get information about apps that
have an unusual memory footprint.

Test: Unit tests pass.
Test: Ran on a device using jemalloc and verified log data.
Test: Ran on a device using scudo and verified log data.
Change-Id: I6fa44ce619c064b2596fbbb478c231994af94f4c
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index ce3f314..a2bb1db 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -19,10 +19,20 @@
 #include <sys/param.h>
 #include <unistd.h>
 
+#include <async_safe/log.h>
 #include <private/MallocXmlElem.h>
 
 #include "jemalloc.h"
 
+__BEGIN_DECLS
+
+size_t je_mallinfo_narenas();
+size_t je_mallinfo_nbins();
+struct mallinfo je_mallinfo_arena_info(size_t);
+struct mallinfo je_mallinfo_bin_info(size_t, size_t);
+
+__END_DECLS
+
 void* je_pvalloc(size_t bytes) {
   size_t pagesize = getpagesize();
   size_t size = __BIONIC_ALIGN(bytes, pagesize);
@@ -121,19 +131,36 @@
       return 0;
     }
     return 1;
+  } else if (param == M_LOG_STATS) {
+    for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
+      struct mallinfo mi = je_mallinfo_arena_info(i);
+      if (mi.hblkhd != 0) {
+        async_safe_format_log(ANDROID_LOG_INFO, "jemalloc",
+                              "Arena %zu: large bytes %zu huge bytes %zu bin bytes %zu", i,
+                              mi.ordblks, mi.uordblks, mi.fsmblks);
+
+        for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
+          struct mallinfo mi = je_mallinfo_bin_info(i, j);
+          if (mi.ordblks != 0) {
+            size_t total_allocs = 1;
+            if (mi.uordblks > mi.fordblks) {
+              total_allocs = mi.uordblks - mi.fordblks;
+            }
+            size_t bin_size = mi.ordblks / total_allocs;
+            async_safe_format_log(
+                ANDROID_LOG_INFO, "jemalloc",
+                "  Bin %zu (%zu bytes): allocated bytes %zu nmalloc %zu ndalloc %zu", j, bin_size,
+                mi.ordblks, mi.uordblks, mi.fordblks);
+          }
+        }
+      }
+    }
+    return 1;
   }
+
   return 0;
 }
 
-__BEGIN_DECLS
-
-size_t je_mallinfo_narenas();
-size_t je_mallinfo_nbins();
-struct mallinfo je_mallinfo_arena_info(size_t);
-struct mallinfo je_mallinfo_bin_info(size_t, size_t);
-
-__END_DECLS
-
 int je_malloc_info(int options, FILE* fp) {
   if (options != 0) {
     errno = EINVAL;
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index 91d63b3..4158c6e 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -320,6 +320,16 @@
 };
 
 /**
+ * mallopt() option to print human readable statistics about the memory
+ * allocator to the log. There is no format for this data, each allocator
+ * can use a different format, and the data that is printed can
+ * change at any time. This is expected to be used as a debugging aid.
+ *
+ * Available since API level 35.
+ */
+#define M_LOG_STATS (-205)
+
+/**
  * [mallopt(3)](http://man7.org/linux/man-pages/man3/mallopt.3.html) modifies
  * heap behavior. Values of `__option` are the `M_` constants from this header.
  *
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 22905f4..5df694c 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -708,6 +708,15 @@
 #endif
 }
 
+TEST(malloc, mallopt_log_stats) {
+#if defined(__BIONIC__)
+  SKIP_WITH_HWASAN << "hwasan does not implement mallopt";
+  ASSERT_EQ(1, mallopt(M_LOG_STATS, 0));
+#else
+  GTEST_SKIP() << "bionic-only test";
+#endif
+}
+
 // Verify that all of the mallopt values are unique.
 TEST(malloc, mallopt_unique_params) {
 #if defined(__BIONIC__)
@@ -722,6 +731,7 @@
       std::make_pair(M_TSDS_COUNT_MAX, "M_TSDS_COUNT_MAX"),
       std::make_pair(M_BIONIC_ZERO_INIT, "M_BIONIC_ZERO_INIT"),
       std::make_pair(M_BIONIC_SET_HEAP_TAGGING_LEVEL, "M_BIONIC_SET_HEAP_TAGGING_LEVEL"),
+      std::make_pair(M_LOG_STATS, "M_LOG_STATS"),
   };
 
   std::unordered_map<int, std::string> all_params;