Merge "Revert fwalk/sfp locking to fix concurrent reads"
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index bc3a9dc..bf0494d 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -103,6 +103,14 @@
     }
     return 1;
   } else if (param == M_PURGE) {
+    // Only clear the current thread cache since there is no easy way to
+    // clear the caches of other threads.
+    // This must be done first so that cleared allocations get purged
+    // in the next calls.
+    if (je_mallctl("thread.tcache.flush", nullptr, nullptr, nullptr, 0) != 0) {
+      return 0;
+    }
+
     unsigned narenas;
     size_t sz = sizeof(unsigned);
     if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
@@ -120,10 +128,10 @@
 
 __BEGIN_DECLS
 
-size_t __mallinfo_narenas();
-size_t __mallinfo_nbins();
-struct mallinfo __mallinfo_arena_info(size_t);
-struct mallinfo __mallinfo_bin_info(size_t, size_t);
+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
 
@@ -136,8 +144,8 @@
   MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\"");
 
   // Dump all of the large allocations in the arenas.
-  for (size_t i = 0; i < __mallinfo_narenas(); i++) {
-    struct mallinfo mi = __mallinfo_arena_info(i);
+  for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
+    struct mallinfo mi = je_mallinfo_arena_info(i);
     if (mi.hblkhd != 0) {
       MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i);
       {
@@ -146,8 +154,8 @@
         MallocXmlElem(fp, "allocated-bins").Contents("%zu", mi.fsmblks);
 
         size_t total = 0;
-        for (size_t j = 0; j < __mallinfo_nbins(); j++) {
-          struct mallinfo mi = __mallinfo_bin_info(i, j);
+        for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
+          struct mallinfo mi = je_mallinfo_bin_info(i, j);
           if (mi.ordblks != 0) {
             MallocXmlElem bin_elem(fp, "bin", "nr=\"%d\"", j);
             MallocXmlElem(fp, "allocated").Contents("%zu", mi.ordblks);
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index eba18a8..aa046b9 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -125,7 +125,8 @@
 
 /**
  * [mallinfo(3)](http://man7.org/linux/man-pages/man3/mallinfo.3.html) returns
- * information about the current state of the heap.
+ * information about the current state of the heap. Note that mallinfo() is
+ * inherently unreliable and consider using malloc_info() instead.
  */
 struct mallinfo mallinfo(void);
 
diff --git a/linker/Android.bp b/linker/Android.bp
index 73328da..95fd4f7 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -267,7 +267,6 @@
         "libc_nomalloc",
         "libm",
         "libziparchive",
-        "libutils",
         "libbase",
         "libz",
 
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 706de15..983592f 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -395,6 +395,60 @@
 #endif
 }
 
+TEST(malloc, malloc_info_matches_mallinfo) {
+#ifdef __BIONIC__
+  SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
+
+  char* buf;
+  size_t bufsize;
+  FILE* memstream = open_memstream(&buf, &bufsize);
+  ASSERT_NE(nullptr, memstream);
+  size_t mallinfo_before_allocated_bytes = mallinfo().uordblks;
+  ASSERT_EQ(0, malloc_info(0, memstream));
+  size_t mallinfo_after_allocated_bytes = mallinfo().uordblks;
+  ASSERT_EQ(0, fclose(memstream));
+
+  tinyxml2::XMLDocument doc;
+  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+
+  size_t total_allocated_bytes = 0;
+  auto root = doc.FirstChildElement();
+  ASSERT_NE(nullptr, root);
+  ASSERT_STREQ("malloc", root->Name());
+  if (std::string(root->Attribute("version")) == "jemalloc-1") {
+    // Verify jemalloc version of this data.
+    ASSERT_STREQ("jemalloc-1", root->Attribute("version"));
+
+    auto arena = root->FirstChildElement();
+    for (; arena != nullptr; arena = arena->NextSiblingElement()) {
+      int val;
+
+      ASSERT_STREQ("heap", arena->Name());
+      ASSERT_EQ(tinyxml2::XML_SUCCESS, arena->QueryIntAttribute("nr", &val));
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("allocated-large")->QueryIntText(&val));
+      total_allocated_bytes += val;
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("allocated-huge")->QueryIntText(&val));
+      total_allocated_bytes += val;
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("allocated-bins")->QueryIntText(&val));
+      total_allocated_bytes += val;
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("bins-total")->QueryIntText(&val));
+    }
+    // The total needs to be between the mallinfo call before and after
+    // since malloc_info allocates some memory.
+    EXPECT_LE(mallinfo_before_allocated_bytes, total_allocated_bytes);
+    EXPECT_GE(mallinfo_after_allocated_bytes, total_allocated_bytes);
+  } else {
+    // Only verify that this is debug-malloc-1, the malloc debug unit tests
+    // verify the output.
+    ASSERT_STREQ("debug-malloc-1", root->Attribute("version"));
+  }
+#endif
+}
+
 TEST(malloc, calloc_usable_size) {
   for (size_t size = 1; size <= 2048; size++) {
     void* pointer = malloc(size);