Introduce base::PageAllocator to wrap mmap

Gets rid of the various posix_memalign hacks accumulated so far.
Also moves the pre-existing IsMmaped function into
base/vm_test_utils.h.

Change-Id: I171dc2dc01c4b69f4f711a4923a1a58c75431df6
diff --git a/Android.bp b/Android.bp
index 9b815dd..75dd1c3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -571,6 +571,7 @@
     ":perfetto_protos_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
     "src/base/android_task_runner.cc",
+    "src/base/page_allocator.cc",
     "src/base/thread_checker.cc",
     "src/base/unix_task_runner.cc",
     "src/ftrace_reader/cpu_reader.cc",
@@ -639,6 +640,7 @@
     ":perfetto_protos_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
     "src/base/android_task_runner.cc",
+    "src/base/page_allocator.cc",
     "src/base/thread_checker.cc",
     "src/base/unix_task_runner.cc",
     "src/ipc/buffered_frame_deserializer.cc",
@@ -707,9 +709,12 @@
     ":perfetto_src_protozero_testing_messages_lite_gen",
     ":perfetto_src_protozero_testing_messages_zero_gen",
     "src/base/android_task_runner.cc",
+    "src/base/page_allocator.cc",
+    "src/base/page_allocator_unittest.cc",
     "src/base/scoped_file_unittest.cc",
     "src/base/task_runner_unittest.cc",
     "src/base/test/test_task_runner.cc",
+    "src/base/test/vm_test_utils.cc",
     "src/base/thread_checker.cc",
     "src/base/thread_checker_unittest.cc",
     "src/base/unix_task_runner.cc",
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index 14bfc2f..d1d188a 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -16,6 +16,7 @@
   sources = [
     "build_config.h",
     "logging.h",
+    "page_allocator.h",
     "scoped_file.h",
     "task_runner.h",
     "thread_checker.h",
diff --git a/include/perfetto/base/page_allocator.h b/include/perfetto/base/page_allocator.h
new file mode 100644
index 0000000..05ad3ca
--- /dev/null
+++ b/include/perfetto/base/page_allocator.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_BASE_PAGE_ALLOCATOR_H_
+#define INCLUDE_PERFETTO_BASE_PAGE_ALLOCATOR_H_
+
+#include <memory>
+
+namespace perfetto {
+namespace base {
+
+class PageAllocator {
+ public:
+  class Deleter {
+   public:
+    Deleter();
+    explicit Deleter(size_t);
+    void operator()(void*) const;
+
+   private:
+    size_t size_;
+  };
+
+  using UniquePtr = std::unique_ptr<void, Deleter>;
+
+  // Allocates |size| bytes using mmap(MAP_ANONYMOUS). The returned pointer is
+  // guaranteed to be page-aligned and the memory is guaranteed to be zeroed.
+  // |size| must be a multiple of 4KB (a page size). Crashes if the underlying
+  // mmap() fails.
+  static UniquePtr Allocate(size_t size);
+
+  // Like the above, but returns a nullptr if the mmap() fails (e.g., if out
+  // of virtual address space).
+  static UniquePtr AllocateMayFail(size_t size);
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_BASE_PAGE_ALLOCATOR_H_
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index b91fccb..3a8a851 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -22,6 +22,7 @@
     "../../include/perfetto/base",
   ]
   sources = [
+    "page_allocator.cc",
     "thread_checker.cc",
     "unix_task_runner.cc",
   ]
@@ -51,10 +52,13 @@
   deps = [
     ":base",
     "../../gn:default_deps",
+    "../../gn:gtest_deps",
   ]
   sources = [
     "test/test_task_runner.cc",
     "test/test_task_runner.h",
+    "test/vm_test_utils.cc",
+    "test/vm_test_utils.h",
   ]
 }
 
@@ -67,6 +71,7 @@
     "../../gn:gtest_deps",
   ]
   sources = [
+    "page_allocator_unittest.cc",
     "scoped_file_unittest.cc",
     "task_runner_unittest.cc",
     "thread_checker_unittest.cc",
diff --git a/src/base/page_allocator.cc b/src/base/page_allocator.cc
new file mode 100644
index 0000000..a966b04
--- /dev/null
+++ b/src/base/page_allocator.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/base/page_allocator.h"
+
+#include <sys/mman.h>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+static constexpr size_t kPageSize = 4096;
+static constexpr size_t kGuardSize = kPageSize;
+
+// static
+PageAllocator::UniquePtr AllocateInternal(size_t size, bool unchecked) {
+  PERFETTO_DCHECK(size % kPageSize == 0);
+  size_t outer_size = size + kGuardSize * 2;
+  void* ptr = mmap(nullptr, outer_size, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+  if (ptr == MAP_FAILED && unchecked)
+    return nullptr;
+  PERFETTO_CHECK(ptr && ptr != MAP_FAILED);
+  char* usable_region = reinterpret_cast<char*>(ptr) + kGuardSize;
+  int res = mprotect(ptr, kGuardSize, PROT_NONE);
+  res |= mprotect(usable_region + size, kGuardSize, PROT_NONE);
+  PERFETTO_CHECK(res == 0);
+  return PageAllocator::UniquePtr(usable_region, PageAllocator::Deleter(size));
+}
+
+}  // namespace
+
+PageAllocator::Deleter::Deleter() : Deleter(0) {}
+PageAllocator::Deleter::Deleter(size_t size) : size_(size) {}
+
+void PageAllocator::Deleter::operator()(void* ptr) const {
+  if (!ptr)
+    return;
+  PERFETTO_CHECK(size_);
+  char* start = reinterpret_cast<char*>(ptr) - kGuardSize;
+  const size_t outer_size = size_ + kGuardSize * 2;
+  int res = munmap(start, outer_size);
+  PERFETTO_CHECK(res == 0);
+}
+
+// static
+PageAllocator::UniquePtr PageAllocator::Allocate(size_t size) {
+  return AllocateInternal(size, false /*unchecked*/);
+}
+
+// static
+PageAllocator::UniquePtr PageAllocator::AllocateMayFail(size_t size) {
+  return AllocateInternal(size, true /*unchecked*/);
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/page_allocator_unittest.cc b/src/base/page_allocator_unittest.cc
new file mode 100644
index 0000000..8737569
--- /dev/null
+++ b/src/base/page_allocator_unittest.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/base/page_allocator.h"
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include "gtest/gtest.h"
+#include "perfetto/base/build_config.h"
+#include "src/base/test/vm_test_utils.h"
+
+#if !BUILDFLAG(OS_MACOSX)
+#include <sys/resource.h>
+#endif
+
+namespace perfetto {
+namespace base {
+namespace {
+
+TEST(PageAllocatorTest, Basic) {
+  const size_t kNumPages = 10;
+  const size_t kSize = 4096 * kNumPages;
+  void* ptr_raw = nullptr;
+  {
+    PageAllocator::UniquePtr ptr = PageAllocator::Allocate(kSize);
+    ASSERT_TRUE(ptr);
+    ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(ptr.get()) % 4096);
+    ptr_raw = ptr.get();
+    for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
+      ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(ptr.get()) + i));
+
+    ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kSize));
+  }
+
+  ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
+}
+
+TEST(PageAllocatorTest, GuardRegions) {
+  const size_t kSize = 4096;
+  PageAllocator::UniquePtr ptr = PageAllocator::Allocate(kSize);
+  ASSERT_TRUE(ptr);
+  volatile char* raw = reinterpret_cast<char*>(ptr.get());
+  EXPECT_DEATH({ raw[-1] = 'x'; }, ".*");
+  EXPECT_DEATH({ raw[kSize] = 'x'; }, ".*");
+}
+
+// Disable this on:
+// MacOS: because it doesn't seem to have an equivalent rlimit to bound mmap().
+// Sanitizers: they seem to try to shadow mmaped memory and fail due to OOMs.
+#if !BUILDFLAG(OS_MACOSX) && !defined(ADDRESS_SANITIZER) &&   \
+    !defined(LEAK_SANITIZER) && !defined(THREAD_SANITIZER) && \
+    !defined(MEMORY_SANITIZER)
+// Glibc headers hit this on RLIMIT_ macros.
+#pragma GCC diagnostic push
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
+#endif
+TEST(PageAllocatorTest, Unchecked) {
+  const size_t kMemLimit = 256 * 1024 * 1024l;
+  struct rlimit limit {
+    kMemLimit, kMemLimit
+  };
+  // ASSERT_EXIT here is to spawn the test in a sub-process and avoid
+  // propagating the setrlimit() to other test units in case of failure.
+  ASSERT_EXIT(
+      {
+        ASSERT_EQ(0, setrlimit(RLIMIT_AS, &limit));
+        auto ptr = PageAllocator::AllocateMayFail(kMemLimit * 2);
+        ASSERT_FALSE(ptr);
+        exit(0);
+      },
+      ::testing::ExitedWithCode(0), "");
+}
+#pragma GCC diagnostic pop
+#endif
+
+}  // namespace
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/test/vm_test_utils.cc b/src/base/test/vm_test_utils.cc
new file mode 100644
index 0000000..7980b51
--- /dev/null
+++ b/src/base/test/vm_test_utils.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/base/test/vm_test_utils.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "gtest/gtest.h"
+#include "perfetto/base/build_config.h"
+
+namespace perfetto {
+namespace base {
+namespace vm_test_utils {
+
+bool IsMapped(void* start, size_t size) {
+#if BUILDFLAG(OS_MACOSX)
+  using PageState = char;
+#else
+  using PageState = unsigned char;
+#endif
+  EXPECT_EQ(0u, size % 4096);
+  const size_t num_pages = size / 4096;
+  std::unique_ptr<PageState[]> page_states(new PageState[num_pages]);
+  memset(page_states.get(), 0, num_pages * sizeof(PageState));
+  int res = mincore(start, size, page_states.get());
+  // Linux returns ENOMEM when an unmapped memory range is passed.
+  // MacOS instead returns 0 but leaves the page_states empty.
+  if (res == -1 && errno == ENOMEM)
+    return false;
+  EXPECT_EQ(0, res);
+  for (size_t i = 0; i < num_pages; i++) {
+    if (!page_states[i])
+      return false;
+  }
+  return true;
+}
+
+}  // namespace vm_test_utils
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/test/vm_test_utils.h b/src/base/test/vm_test_utils.h
new file mode 100644
index 0000000..3e057cb
--- /dev/null
+++ b/src/base/test/vm_test_utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_BASE_TEST_VM_TEST_UTILS_H_
+#define SRC_BASE_TEST_VM_TEST_UTILS_H_
+
+#include <stddef.h>
+
+namespace perfetto {
+namespace base {
+namespace vm_test_utils {
+
+bool IsMapped(void* start, size_t size);
+
+}  // namespace vm_test_utils
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // SRC_BASE_TEST_VM_TEST_UTILS_H_
diff --git a/src/ipc/buffered_frame_deserializer.cc b/src/ipc/buffered_frame_deserializer.cc
index 0f40186..38a8f45 100644
--- a/src/ipc/buffered_frame_deserializer.cc
+++ b/src/ipc/buffered_frame_deserializer.cc
@@ -17,7 +17,6 @@
 #include "src/ipc/buffered_frame_deserializer.h"
 
 #include <inttypes.h>
-#include <sys/mman.h>
 
 #include <algorithm>
 #include <type_traits>
@@ -25,7 +24,6 @@
 
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/base/utils.h"
 
 #include "src/ipc/wire_protocol.pb.h"
 
@@ -35,10 +33,6 @@
 namespace {
 constexpr size_t kPageSize = 4096;
 
-// Size of the PROT_NONE guard region, adjactent to the end of the buffer.
-// It's a safety net to spot any out-of-bounds writes early.
-constexpr size_t kGuardRegionSize = kPageSize;
-
 // The header is just the number of bytes of the Frame protobuf message.
 constexpr size_t kHeaderSize = sizeof(uint32_t);
 }  // namespace
@@ -49,12 +43,7 @@
   PERFETTO_CHECK(max_capacity > kPageSize);
 }
 
-BufferedFrameDeserializer::~BufferedFrameDeserializer() {
-  if (!buf_)
-    return;
-  int res = munmap(buf_, capacity_ + kGuardRegionSize);
-  PERFETTO_DCHECK(res == 0);
-}
+BufferedFrameDeserializer::~BufferedFrameDeserializer() = default;
 
 BufferedFrameDeserializer::ReceiveBuffer
 BufferedFrameDeserializer::BeginReceive() {
@@ -63,24 +52,17 @@
   // automatically give us physical pages back as soon as we page-fault on them.
   if (!buf_) {
     PERFETTO_DCHECK(size_ == 0);
-    buf_ = reinterpret_cast<char*>(mmap(nullptr, capacity_ + kGuardRegionSize,
-                                        PROT_READ | PROT_WRITE,
-                                        MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
-    PERFETTO_CHECK(buf_ != MAP_FAILED);
+    buf_ = base::PageAllocator::Allocate(capacity_);
 
     // Surely we are going to use at least the first page. There is very little
     // point in madvising that as well and immedately after telling the kernel
     // that we want it back (via recv()).
-    int res = madvise(buf_ + kPageSize,
-                      capacity_ + kGuardRegionSize - kPageSize, MADV_DONTNEED);
-    PERFETTO_DCHECK(res == 0);
-
-    res = mprotect(buf_ + capacity_, kGuardRegionSize, PROT_NONE);
+    int res = madvise(buf() + kPageSize, capacity_ - kPageSize, MADV_DONTNEED);
     PERFETTO_DCHECK(res == 0);
   }
 
   PERFETTO_CHECK(capacity_ > size_);
-  return ReceiveBuffer{buf_ + size_, capacity_ - size_};
+  return ReceiveBuffer{buf() + size_, capacity_ - size_};
 }
 
 bool BufferedFrameDeserializer::EndReceive(size_t recv_size) {
@@ -114,7 +96,7 @@
 
     // Read the header into |payload_size|.
     uint32_t payload_size = 0;
-    const char* rd_ptr = buf_ + consumed_size;
+    const char* rd_ptr = buf() + consumed_size;
     memcpy(base::AssumeLittleEndian(&payload_size), rd_ptr, kHeaderSize);
 
     // Saturate the |payload_size| to prevent overflows. The > capacity_ check
@@ -151,10 +133,10 @@
       // Case D. We consumed some frames but there is a leftover at the end of
       // the buffer. Shift out the consumed bytes, so that on the next round
       // |buf_| starts with the header of the next unconsumed frame.
-      const char* move_begin = buf_ + consumed_size;
-      PERFETTO_CHECK(move_begin > buf_);
-      PERFETTO_CHECK(move_begin + size_ <= buf_ + capacity_);
-      memmove(buf_, move_begin, size_);
+      const char* move_begin = buf() + consumed_size;
+      PERFETTO_CHECK(move_begin > buf());
+      PERFETTO_CHECK(move_begin + size_ <= buf() + capacity_);
+      memmove(buf(), move_begin, size_);
     }
     // If we just finished decoding a large frame that used more than one page,
     // release the extra memory in the buffer. Large frames should be quite
@@ -162,10 +144,10 @@
     if (consumed_size > kPageSize) {
       size_t size_rounded_up = (size_ / kPageSize + 1) * kPageSize;
       if (size_rounded_up < capacity_) {
-        char* madvise_begin = buf_ + size_rounded_up;
+        char* madvise_begin = buf() + size_rounded_up;
         const size_t madvise_size = capacity_ - size_rounded_up;
-        PERFETTO_CHECK(madvise_begin > buf_ + size_);
-        PERFETTO_CHECK(madvise_begin + madvise_size <= buf_ + capacity_);
+        PERFETTO_CHECK(madvise_begin > buf() + size_);
+        PERFETTO_CHECK(madvise_begin + madvise_size <= buf() + capacity_);
         int res = madvise(madvise_begin, madvise_size, MADV_DONTNEED);
         PERFETTO_DCHECK(res == 0);
       }
diff --git a/src/ipc/buffered_frame_deserializer.h b/src/ipc/buffered_frame_deserializer.h
index 874a7a2..40e1a64 100644
--- a/src/ipc/buffered_frame_deserializer.h
+++ b/src/ipc/buffered_frame_deserializer.h
@@ -22,7 +22,9 @@
 #include <list>
 #include <memory>
 
-#include "perfetto/base/utils.h"
+#include <sys/mman.h>
+
+#include "perfetto/base/page_allocator.h"
 
 namespace perfetto {
 namespace ipc {
@@ -113,8 +115,10 @@
   // If a valid frame is decoded it is added to |decoded_frames_|.
   void DecodeFrame(const char*, size_t);
 
-  char* buf_ = nullptr;
-  const size_t capacity_ = 0;  // sizeof(|buf_|), excluding the guard region.
+  char* buf() { return reinterpret_cast<char*>(buf_.get()); }
+
+  base::PageAllocator::UniquePtr buf_;
+  const size_t capacity_ = 0;  // sizeof(|buf_|).
 
   // THe number of bytes in |buf_| that contain valid data (as a result of
   // EndReceive()). This is always <= |capacity_|.
diff --git a/src/tracing/core/service_impl.cc b/src/tracing/core/service_impl.cc
index b0cc33c..f1c92ee 100644
--- a/src/tracing/core/service_impl.cc
+++ b/src/tracing/core/service_impl.cc
@@ -135,7 +135,12 @@
     }
     PERFETTO_DCHECK(ts.trace_buffers.count(id) == 0);
     // TODO(primiano): make TraceBuffer::kBufferPageSize dynamic.
-    ts.trace_buffers.emplace(id, TraceBuffer(buffer_cfg.size_kb() * 1024u));
+    const size_t buf_size = buffer_cfg.size_kb() * 1024u;
+    auto it_and_inserted = ts.trace_buffers.emplace(id, TraceBuffer(buf_size));
+    if (!it_and_inserted.second || !it_and_inserted.first->second.is_valid()) {
+      did_allocate_all_buffers = false;
+      break;
+    }
   }
 
   // This can happen if all the kMaxTraceBuffers slots are taken (i.e. we are
@@ -439,17 +444,13 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 ServiceImpl::TraceBuffer::TraceBuffer(size_t sz) : size(sz) {
-  void* ptr = nullptr;
-  PERFETTO_CHECK(size % kBufferPageSize == 0);
-  // TODO(primiano): introduce base::PageAllocator and use mmap() instead.
-  if (posix_memalign(&ptr, kPageSize, size)) {
+  data = base::PageAllocator::AllocateMayFail(size);
+  if (!data) {
     PERFETTO_ELOG("Trace buffer allocation failed (size: %zu, page_size: %zu)",
                   size, kBufferPageSize);
+    size = 0;
     return;
   }
-  PERFETTO_CHECK(ptr);
-  memset(ptr, 0, size);
-  data.reset(ptr);
   abi.reset(new SharedMemoryABI(get_page(0), size, kBufferPageSize));
 }
 
diff --git a/src/tracing/core/service_impl.h b/src/tracing/core/service_impl.h
index 52b1277..8321b15 100644
--- a/src/tracing/core/service_impl.h
+++ b/src/tracing/core/service_impl.h
@@ -22,7 +22,7 @@
 #include <memory>
 #include <set>
 
-#include "perfetto/base/utils.h"
+#include "perfetto/base/page_allocator.h"
 #include "perfetto/base/weak_ptr.h"
 #include "perfetto/tracing/core/basic_types.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
@@ -151,6 +151,7 @@
     TraceBuffer(TraceBuffer&&) noexcept;
     TraceBuffer& operator=(TraceBuffer&&);
 
+    bool is_valid() const { return !!data; }
     size_t num_pages() const { return size / kBufferPageSize; }
 
     uint8_t* get_page(size_t page) {
@@ -166,7 +167,7 @@
 
     size_t size;
     size_t cur_page = 0;  // Write pointer in the ring buffer.
-    std::unique_ptr<void, base::FreeDeleter> data;
+    base::PageAllocator::UniquePtr data;
 
     // TODO(primiano): The TraceBuffer is not shared and there is no reason to
     // use the SharedMemoryABI. This is just a a temporary workaround to reuse
diff --git a/src/tracing/ipc/posix_shared_memory_unittest.cc b/src/tracing/ipc/posix_shared_memory_unittest.cc
index 5073f5a..c08e484 100644
--- a/src/tracing/ipc/posix_shared_memory_unittest.cc
+++ b/src/tracing/ipc/posix_shared_memory_unittest.cc
@@ -20,7 +20,6 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -29,6 +28,7 @@
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/utils.h"
 #include "src/base/test/test_task_runner.h"
+#include "src/base/test/vm_test_utils.h"
 
 namespace perfetto {
 namespace {
@@ -39,29 +39,6 @@
   return lseek(fd, 0, SEEK_CUR) == -1 && errno == EBADF;
 }
 
-bool IsMapped(void* start, size_t size) {
-#if BUILDFLAG(OS_MACOSX)
-  using PageState = char;
-#else
-  using PageState = unsigned char;
-#endif
-  EXPECT_EQ(0u, size % kPageSize);
-  const size_t num_pages = size / kPageSize;
-  std::unique_ptr<PageState[]> page_states(new PageState[num_pages]);
-  memset(page_states.get(), 0, num_pages * sizeof(PageState));
-  int res = mincore(start, size, page_states.get());
-  // Linux returns ENOMEM when an unmapped memory range is passed.
-  // MacOS instead returns 0 but leaves the page_states empty.
-  if (res == -1 && errno == ENOMEM)
-    return false;
-  EXPECT_EQ(0, res);
-  for (size_t i = 0; i < num_pages; i++) {
-    if (!page_states[i])
-      return false;
-  }
-  return true;
-}
-
 TEST(PosixSharedMemoryTest, DestructorUnmapsMemory) {
   PosixSharedMemory::Factory factory;
   std::unique_ptr<SharedMemory> shm = factory.CreateSharedMemory(kPageSize);
@@ -71,10 +48,10 @@
   ASSERT_EQ(kPageSize, shm_size);
 
   memcpy(shm_start, "test", 5);
-  ASSERT_TRUE(IsMapped(shm_start, shm_size));
+  ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size));
 
   shm.reset();
-  ASSERT_FALSE(IsMapped(shm_start, shm_size));
+  ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
 }
 
 TEST(PosixSharedMemoryTest, DestructorClosesFD) {
@@ -105,7 +82,7 @@
 
   shm.reset();
   ASSERT_TRUE(IsFileDescriptorClosed(fd_num));
-  ASSERT_FALSE(IsMapped(shm_start, shm_size));
+  ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
 }
 
 }  // namespace
diff --git a/src/tracing/test/test_shared_memory.cc b/src/tracing/test/test_shared_memory.cc
index 6c1db1f..83ea281 100644
--- a/src/tracing/test/test_shared_memory.cc
+++ b/src/tracing/test/test_shared_memory.cc
@@ -24,11 +24,7 @@
 namespace perfetto {
 
 TestSharedMemory::TestSharedMemory(size_t size) {
-  void* ptr = nullptr;
-  int res = posix_memalign(&ptr, 4096, size);
-  PERFETTO_CHECK(res == 0 && ptr);
-  mem_.reset(ptr);
-  memset(ptr, 0, size);
+  mem_ = base::PageAllocator::Allocate(size);
   size_ = size;
 }
 
diff --git a/src/tracing/test/test_shared_memory.h b/src/tracing/test/test_shared_memory.h
index e074e85..a4f44f8 100644
--- a/src/tracing/test/test_shared_memory.h
+++ b/src/tracing/test/test_shared_memory.h
@@ -21,7 +21,7 @@
 
 #include <memory>
 
-#include "perfetto/base/utils.h"
+#include "perfetto/base/page_allocator.h"
 #include "perfetto/tracing/core/shared_memory.h"
 
 namespace perfetto {
@@ -42,7 +42,7 @@
   void* start() const override { return mem_.get(); }
   size_t size() const override { return size_; }
 
-  std::unique_ptr<void, base::FreeDeleter> mem_;
+  base::PageAllocator::UniquePtr mem_;
   size_t size_;
 };