Replace ashmem in ART with prctl

ART use ashmem just for naming anonymous regions. This is a hack.
Further, we are moving away from ashmem in favor of native Linux based
shared-memory mechanims for Android. We already have a PRCTL for naming
maps in the Android kernels. Lets use that for Android.

This also results in removal of ashmem hacks for host-only builds.

As a side-effect, the naming of regions change from "/dev/ashmem/<name>"
to "[anon:<name>" so we need to update debug code in frameworks. Another
CL does that.

Note: The user pointers passed to the prctl should be available during
maps read time, so we cannot use pointers from the stack. To solve this,
we maintain an std::map of unique debug strings when we name the maps.
This doesn't result in more memory usage than before, since ashmem is
heavier storage-wise due to additional kernel data structure
allocations.

Performance of 'dumpsys meminfo' gone up by 25% (1s to 0.75s). Slab
memory consumption gone down by 3-5MB on boot.

Test: Boot and dumpsys meminfo before/after
Bug: 111903542
Internal gerrit: ag/4827680
Change-Id: Ia658fce62977f6c69dc4ce89ffdcfe2d9ae22738
Signed-off-by: Joel Fernandes <joelaf@google.com>
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index d9760bd..a82da2d 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -23,6 +23,10 @@
 #include <sys/resource.h>
 #endif
 
+#if defined(__linux__)
+#include <sys/prctl.h>
+#endif
+
 #include <map>
 #include <memory>
 #include <sstream>
@@ -30,12 +34,6 @@
 #include "android-base/stringprintf.h"
 #include "android-base/unique_fd.h"
 
-#if !defined(__Fuchsia__)
-#include "cutils/ashmem.h"
-#else
-#include "fuchsia_compat.h"
-#endif
-
 #include "allocator.h"
 #include "bit_utils.h"
 #include "globals.h"
@@ -61,6 +59,9 @@
 // All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()).
 static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr;
 
+// A map containing unique strings used for indentifying anonymous mappings
+static std::map<std::string, int> debugStrMap GUARDED_BY(MemMap::GetMemMapsLock());
+
 // Retrieve iterator to a `gMaps` entry that is known to exist.
 Maps::iterator GetGMapsEntry(const MemMap& map) REQUIRES(MemMap::GetMemMapsLock()) {
   DCHECK(map.IsValid());
@@ -246,6 +247,33 @@
 }
 #endif
 
+void MemMap::SetDebugName(void* map_ptr, const char* name, size_t size) {
+  // Debug naming is only used for Android target builds. For Linux targets,
+  // we'll still call prctl but it wont do anything till we upstream the prctl.
+  if (kIsTargetFuchsia || !kIsTargetBuild) {
+    return;
+  }
+
+  // lock as std::map is not thread-safe
+  std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+
+  std::string debug_friendly_name("dalvik-");
+  debug_friendly_name += name;
+  auto it = debugStrMap.find(debug_friendly_name);
+
+  if (it == debugStrMap.end()) {
+    it = debugStrMap.insert(std::make_pair(std::move(debug_friendly_name), 1)).first;
+  }
+
+  DCHECK(it != debugStrMap.end());
+#if defined(PR_SET_VMA) && defined(__linux__)
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, size, it->first.c_str());
+#else
+  // Prevent variable unused compiler errors.
+  UNUSED(map_ptr, size);
+#endif
+}
+
 MemMap MemMap::MapAnonymous(const char* name,
                             uint8_t* addr,
                             size_t byte_count,
@@ -253,11 +281,10 @@
                             bool low_4gb,
                             bool reuse,
                             std::string* error_msg,
-                            bool use_ashmem) {
+                            bool use_debug_name) {
 #ifndef __LP64__
   UNUSED(low_4gb);
 #endif
-  use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia;
   if (byte_count == 0) {
     *error_msg = "Empty MemMap requested.";
     return Invalid();
@@ -274,41 +301,8 @@
     flags |= MAP_FIXED;
   }
 
-  if (use_ashmem) {
-    if (!kIsTargetBuild) {
-      // When not on Android (either host or assuming a linux target) ashmem is faked using
-      // files in /tmp. Ensure that such files won't fail due to ulimit restrictions. If they
-      // will then use a regular mmap.
-      struct rlimit rlimit_fsize;
-      CHECK_EQ(getrlimit(RLIMIT_FSIZE, &rlimit_fsize), 0);
-      use_ashmem = (rlimit_fsize.rlim_cur == RLIM_INFINITY) ||
-        (page_aligned_byte_count < rlimit_fsize.rlim_cur);
-    }
-  }
-
   unique_fd fd;
 
-
-  if (use_ashmem) {
-    // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
-    // prefixed "dalvik-".
-    std::string debug_friendly_name("dalvik-");
-    debug_friendly_name += name;
-    fd.reset(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count));
-
-    if (fd.get() == -1) {
-      // We failed to create the ashmem region. Print a warning, but continue
-      // anyway by creating a true anonymous mmap with an fd of -1. It is
-      // better to use an unlabelled anonymous map than to fail to create a
-      // map at all.
-      PLOG(WARNING) << "ashmem_create_region failed for '" << name << "'";
-    } else {
-      // We succeeded in creating the ashmem region. Use the created ashmem
-      // region as backing for the mmap.
-      flags &= ~MAP_ANONYMOUS;
-    }
-  }
-
   // We need to store and potentially set an error number for pretty printing of errors
   int saved_errno = 0;
 
@@ -341,6 +335,11 @@
   if (!CheckMapRequest(addr, actual, page_aligned_byte_count, error_msg)) {
     return Invalid();
   }
+
+  if (use_debug_name) {
+    SetDebugName(actual, name, page_aligned_byte_count);
+  }
+
   return MemMap(name,
                 reinterpret_cast<uint8_t*>(actual),
                 byte_count,
@@ -639,8 +638,7 @@
                           const char* tail_name,
                           int tail_prot,
                           std::string* error_msg,
-                          bool use_ashmem) {
-  use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia;
+                          bool use_debug_name) {
   DCHECK_GE(new_end, Begin());
   DCHECK_LE(new_end, End());
   DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
@@ -666,19 +664,6 @@
 
   unique_fd fd;
   int flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
-  if (use_ashmem) {
-    // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
-    // prefixed "dalvik-".
-    std::string debug_friendly_name("dalvik-");
-    debug_friendly_name += tail_name;
-    fd.reset(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
-    flags = MAP_PRIVATE | MAP_FIXED;
-    if (fd.get() == -1) {
-      *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
-                                tail_name, strerror(errno));
-      return Invalid();
-    }
-  }
 
   MEMORY_TOOL_MAKE_UNDEFINED(tail_base_begin, tail_base_size);
   // Note: Do not explicitly unmap the tail region, mmap() with MAP_FIXED automatically
@@ -703,6 +688,11 @@
     auto it = GetGMapsEntry(*this);
     gMaps->erase(it);
   }
+
+  if (use_debug_name) {
+    SetDebugName(actual, tail_name, tail_base_size);
+  }
+
   size_ = new_size;
   base_size_ = new_base_size;
   // Return the new mapping.