1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107

base / memory / madv_free_discardable_memory_allocator_posix_unittest.cc [blame]

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fcntl.h>
#include <stdint.h>

#include <memory>

#include "base/files/scoped_file.h"
#include "base/memory/madv_free_discardable_memory_allocator_posix.h"
#include "base/memory/madv_free_discardable_memory_posix.h"
#include "base/memory/page_size.h"
#include "base/tracing_buildflags.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(ENABLE_BASE_TRACING)
#include "base/trace_event/memory_allocator_dump.h"  // no-presubmit-check
#include "base/trace_event/process_memory_dump.h"    // no-presubmit-check
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)

#define SUCCEED_IF_MADV_FREE_UNSUPPORTED()                                  \
  do {                                                                      \
    if (GetMadvFreeSupport() != base::MadvFreeSupport::kSupported) {        \
      SUCCEED()                                                             \
          << "MADV_FREE is not supported (Linux 4.5+ required), vacuously " \
             "passing test";                                                \
      return;                                                               \
    }                                                                       \
  } while (0)

namespace base {

class MadvFreeDiscardableMemoryAllocatorPosixTest : public ::testing::Test {
 protected:
  MadvFreeDiscardableMemoryAllocatorPosixTest() {
#if BUILDFLAG(ENABLE_BASE_TRACING)
    base::trace_event::MemoryDumpArgs dump_args = {
        base::trace_event::MemoryDumpLevelOfDetail::kDetailed};
    pmd_ = std::make_unique<base::trace_event::ProcessMemoryDump>(dump_args);
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
  }

  std::unique_ptr<MadvFreeDiscardableMemoryPosix>
  AllocateLockedMadvFreeDiscardableMemory(size_t size) {
    return std::unique_ptr<MadvFreeDiscardableMemoryPosix>(
        static_cast<MadvFreeDiscardableMemoryPosix*>(
            allocator_.AllocateLockedDiscardableMemory(size).release()));
  }

#if BUILDFLAG(ENABLE_BASE_TRACING)
  size_t GetDiscardableMemorySizeFromDump(const DiscardableMemory& mem,
                                          const std::string& dump_id) {
    return mem.CreateMemoryAllocatorDump(dump_id.c_str(), pmd_.get())
        ->GetSizeInternal();
  }
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)

  MadvFreeDiscardableMemoryAllocatorPosix allocator_;
  const size_t kPageSize = base::GetPageSize();
#if BUILDFLAG(ENABLE_BASE_TRACING)
  std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd_;
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
};

TEST_F(MadvFreeDiscardableMemoryAllocatorPosixTest, AllocateAndUseMemory) {
  SUCCEED_IF_MADV_FREE_UNSUPPORTED();

  // Allocate 4 pages of discardable memory.
  auto mem1 = AllocateLockedMadvFreeDiscardableMemory(kPageSize * 3 + 1);

  EXPECT_TRUE(mem1->IsLockedForTesting());
#if BUILDFLAG(ENABLE_BASE_TRACING)
  EXPECT_EQ(GetDiscardableMemorySizeFromDump(*mem1, "dummy_dump_1"),
            kPageSize * 3 + 1);
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
  EXPECT_EQ(allocator_.GetBytesAllocated(), kPageSize * 3 + 1);

  // Allocate 3 pages of discardable memory, and free the previously allocated
  // pages.
  auto mem2 = AllocateLockedMadvFreeDiscardableMemory(kPageSize * 3);

  EXPECT_TRUE(mem2->IsLockedForTesting());
#if BUILDFLAG(ENABLE_BASE_TRACING)
  EXPECT_EQ(GetDiscardableMemorySizeFromDump(*mem2, "dummy_dump_2"),
            kPageSize * 3);
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
  EXPECT_EQ(allocator_.GetBytesAllocated(), kPageSize * 6 + 1);

  mem1.reset();

  EXPECT_EQ(allocator_.GetBytesAllocated(), kPageSize * 3);

  // Write to and read from an allocated discardable memory buffer.
  const char test_pattern[] = "ABCDEFGHIJKLMNOP";
  char buffer[sizeof(test_pattern)];

  void* data = mem2->data();
  memcpy(data, test_pattern, sizeof(test_pattern));

  data = mem2->data_as<uint8_t>();
  memcpy(buffer, data, sizeof(test_pattern));

  EXPECT_EQ(memcmp(test_pattern, buffer, sizeof(test_pattern)), 0);
}
}  // namespace base