mfbt/TaggedAnonymousMemory.cpp
author Ryan VanderMeulen <ryanvm@gmail.com>
Thu, 24 May 2018 14:12:20 -0400
changeset 473436 0f95c023462e4de617ebdd77cbe90ec523efad10
parent 250665 3928ee1b0381453833c00fbe1e1b72a26143f13a
child 508163 6f3709b3878117466168c40affa7bca0b60cf75b
permissions -rw-r--r--
Backed out 5 changesets (bug 1462497) for causing widespread test failures. Backed out changeset f89eb2186154 (bug 1462497) Backed out changeset e8836b424826 (bug 1462497) Backed out changeset 3dcce8353e13 (bug 1462497) Backed out changeset 21f67c8e36fb (bug 1462497) Backed out changeset b14f835411db (bug 1462497)

/* -*- 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"

// 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