Bug 1011350 - Add mfbt support for PR_SET_VMA_ANON_NAME. r=njn r=froydnj
authorJed Davis <jld@mozilla.com>
Tue, 17 Jun 2014 17:54:00 +0200
changeset 189308 7958c801c56ae9b74aac24d854b6cc37a28376ad
parent 189307 4eb625761ecb8644cd3b1e2831017fe8a4805eeb
child 189309 df4bfca6f533060c5ad4b9176d1768b490a59f19
push id26984
push userryanvm@gmail.com
push dateWed, 18 Jun 2014 19:48:29 +0000
treeherdermozilla-central@b83f5f1d25b4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn, froydnj
bugs1011350
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1011350 - Add mfbt support for PR_SET_VMA_ANON_NAME. r=njn r=froydnj This is a Linux kernel feature present in newer Android and B2G devices which allows attaching an arbitrary name to an anonymous memory regions, thus tagging it for the purposes of procfs mapping lists and causing it to be reported separately from adjacent regions with different names.
mfbt/TaggedAnonymousMemory.cpp
mfbt/TaggedAnonymousMemory.h
mfbt/moz.build
new file mode 100644
--- /dev/null
+++ b/mfbt/TaggedAnonymousMemory.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef ANDROID
+
+#include "mozilla/TaggedAnonymousMemory.h"
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/NullPtr.h"
+
+// These constants are copied from <sys/prctl.h>, because the headers
+// used for building may not have them even though the running kernel
+// supports them.
+#ifndef PR_SET_VMA
+#define PR_SET_VMA		0x53564d41
+#endif
+#ifndef PR_SET_VMA_ANON_NAME
+#define PR_SET_VMA_ANON_NAME		0
+#endif
+
+namespace mozilla {
+
+// Returns 0 for success and -1 (with errno) for error.
+static int
+TagAnonymousMemoryAligned(const void* aPtr, size_t aLength, const char* aTag)
+{
+  return prctl(PR_SET_VMA,
+               PR_SET_VMA_ANON_NAME,
+               reinterpret_cast<unsigned long>(aPtr),
+               aLength,
+               reinterpret_cast<unsigned long>(aTag));
+}
+
+// On some architectures, it's possible for the page size to be larger
+// than the PAGE_SIZE we were compiled with.  This computes the
+// equivalent of PAGE_MASK.
+static uintptr_t
+GetPageMask()
+{
+  static uintptr_t mask = 0;
+
+  if (mask == 0) {
+    uintptr_t pageSize = sysconf(_SC_PAGESIZE);
+    mask = ~(pageSize - 1);
+    MOZ_ASSERT((pageSize & (pageSize - 1)) == 0,
+               "Page size must be a power of 2!");
+  }
+  return mask;
+}
+
+} // namespace mozilla
+
+int
+MozTaggedMemoryIsSupported(void)
+{
+  static int supported = -1;
+
+  if (supported == -1) {
+    // Tagging an empty range always "succeeds" if the feature is supported,
+    // regardless of the start pointer.
+    supported = mozilla::TagAnonymousMemoryAligned(nullptr, 0, nullptr) == 0;
+  }
+  return supported;
+}
+
+void
+MozTagAnonymousMemory(const void* aPtr, size_t aLength, const char* aTag)
+{
+  if (MozTaggedMemoryIsSupported()) {
+    // The kernel will round up the end of the range to the next page
+    // boundary if it's not aligned (comments indicate this behavior
+    // is based on that of madvise), but it will reject the request if
+    // the start is not aligned.  We therefore round down the start
+    // address and adjust the length accordingly.
+    uintptr_t addr = reinterpret_cast<uintptr_t>(aPtr);
+    uintptr_t end = addr + aLength;
+    uintptr_t addrRounded = addr & mozilla::GetPageMask();
+    const void* ptrRounded = reinterpret_cast<const void*>(addrRounded);
+
+    mozilla::TagAnonymousMemoryAligned(ptrRounded, end - addrRounded, aTag);
+  }
+}
+
+void*
+MozTaggedAnonymousMmap(void* aAddr, size_t aLength, int aProt, int aFlags,
+                       int aFd, off_t aOffset, const char* aTag)
+{
+  void* mapped = mmap(aAddr, aLength, aProt, aFlags, aFd, aOffset);
+  if (MozTaggedMemoryIsSupported() &&
+      (aFlags & MAP_ANONYMOUS) == MAP_ANONYMOUS &&
+      mapped != MAP_FAILED) {
+    mozilla::TagAnonymousMemoryAligned(mapped, aLength, aTag);
+  }
+  return mapped;
+}
+
+#endif // ANDROID
new file mode 100644
--- /dev/null
+++ b/mfbt/TaggedAnonymousMemory.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Some Linux kernels -- specifically, newer versions of Android and
+// some B2G devices -- have a feature for assigning names to ranges of
+// anonymous memory (i.e., memory that doesn't have a "name" in the
+// form of an underlying mapped file).  These names are reported in
+// /proc/<pid>/smaps alongside system-level memory usage information
+// such as Proportional Set Size (memory usage adjusted for sharing
+// between processes), which allows reporting this information at a
+// finer granularity than would otherwise be possible (e.g.,
+// separating malloc() heap from JS heap).
+//
+// Existing memory can be tagged with MozTagAnonymousMemory(); it will
+// tag the range of complete pages containing the given interval, so
+// the results may be inexact if the range isn't page-aligned.
+// MozTaggedAnonymousMmap() can be used like mmap() with an extra
+// parameter, and will tag the returned memory if the mapping was
+// successful (and if it was in fact anonymous).
+//
+// NOTE: The pointer given as the "tag" argument MUST remain valid as
+// long as the mapping exists.  The referenced string is read when
+// /proc/<pid>/smaps or /proc/<pid>/maps is read, not when the tag is
+// established, so freeing it or changing its contents will have
+// unexpected results.  Using a static string is probably best.
+//
+// Also note that this header can be used by both C and C++ code.
+
+#ifndef mozilla_TaggedAnonymousMemory_h
+#define mozilla_TaggedAnonymousMemory_h
+
+#ifndef XP_WIN
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "mozilla/Types.h"
+
+#ifdef ANDROID
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MFBT_API void
+MozTagAnonymousMemory(const void* aPtr, size_t aLength, const char* aTag);
+
+MFBT_API void*
+MozTaggedAnonymousMmap(void* aAddr, size_t aLength, int aProt, int aFlags,
+                         int aFd, off_t aOffset, const char* aTag);
+
+MFBT_API int
+MozTaggedMemoryIsSupported(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#else // ANDROID
+
+static inline void
+MozTagAnonymousMemory(const void* aPtr, size_t aLength, const char* aTag)
+{
+}
+
+static inline void*
+MozTaggedAnonymousMmap(void* aAddr, size_t aLength, int aProt, int aFlags,
+                       int aFd, off_t aOffset, const char* aTag)
+{
+  return mmap(aAddr, aLength, aProt, aFlags, aFd, aOffset);
+}
+
+static inline int
+MozTaggedMemoryIsSupported(void)
+{
+  return 0;
+}
+
+#endif // ANDROID
+
+#endif // !XP_WIN
+
+#endif // mozilla_TaggedAnonymousMemory_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -54,16 +54,17 @@ EXPORTS.mozilla = [
     'RangedPtr.h',
     'RefCountType.h',
     'ReentrancyGuard.h',
     'RefPtr.h',
     'RollingMean.h',
     'Scoped.h',
     'SHA1.h',
     'SplayTree.h',
+    'TaggedAnonymousMemory.h',
     'TemplateLib.h',
     'ThreadLocal.h',
     'ToString.h',
     'TypedEnum.h',
     'TypedEnumBits.h',
     'TypedEnumInternal.h',
     'Types.h',
     'TypeTraits.h',
@@ -84,16 +85,17 @@ UNIFIED_SOURCES = [
     'double-conversion/double-conversion.cc',
     'double-conversion/fast-dtoa.cc',
     'double-conversion/fixed-dtoa.cc',
     'double-conversion/strtod.cc',
     'FloatingPoint.cpp',
     'HashFunctions.cpp',
     'Poison.cpp',
     'SHA1.cpp',
+    'TaggedAnonymousMemory.cpp',
 ]
 
 DEFINES['IMPL_MFBT'] = True
 
 # Compression.cpp cannot be built in unified mode because it pulls in Windows system headers.
 # Decimal.cpp doesn't build in unified mode with gcc.
 SOURCES += [
     'Compression.cpp',