Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  mem_map_test.cc

  Sprache: C
 

/*
 * Copyright (C) 2013 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 "mem_map.h"

#include <memory>
#include <random>

#include "bit_utils.h"
#include "casts.h"
#include "common_art_test.h"
#include "logging.h"
#include "memory_tool.h"
#include "mman.h"
#include "unix_file/fd_file.h"

namespace art {

class MemMapTest : public CommonArtTest {
 public:
  static bool IsAddressMapped(void* addr) {
    bool res = msync(addr, 1, MS_SYNC) == 0;
    if (!res && errno != ENOMEM) {
      PLOG(FATAL) << "Unexpected error occurred on msync";
    }
    return res;
  }

  static std::vector<uint8_t> RandomData(size_t size) {
    std::random_device rd;
    std::uniform_int_distribution<uint8_t> dist;
    std::vector<uint8_t> res;
    res.resize(size);
    for (size_t i = 0; i < size; i++) {
      res[i] = dist(rd);
    }
    return res;
  }

  static uint8_t* GetValidMapAddress(size_t size, bool low_4gb) {
    // Find a valid map address and unmap it before returning.
    std::string error_msg;
    MemMap map = MemMap::MapAnonymous("temp",
                                      size,
                                      PROT_READ,
                                      low_4gb,
                                      &error_msg);
    CHECK(map.IsValid());
    return map.Begin();
  }

  static void RemapAtEndTest(bool low_4gb) {
    std::string error_msg;
    // Cast the page size to size_t.
    const size_t page_size = MemMap::GetPageSize();
    // Map a two-page memory region.
    MemMap m0 = MemMap::MapAnonymous("MemMapTest_RemapAtEndTest_map0",
                                     2 * page_size,
                                     PROT_READ | PROT_WRITE,
                                     low_4gb,
                                     &error_msg);
    // Check its state and write to it.
    ASSERT_TRUE(m0.IsValid());
    uint8_t* base0 = m0.Begin();
    ASSERT_TRUE(base0 != nullptr) << error_msg;
    size_t size0 = m0.Size();
    EXPECT_EQ(m0.Size(), 2 * page_size);
    EXPECT_EQ(m0.BaseBegin(), base0);
    EXPECT_EQ(m0.BaseSize(), size0);
    memset(base0, 422 * page_size);
    // Remap the latter half into a second MemMap.
    MemMap m1 = m0.RemapAtEnd(base0 + page_size,
                              "MemMapTest_RemapAtEndTest_map1",
                              PROT_READ | PROT_WRITE,
                              &error_msg);
    // Check the states of the two maps.
    EXPECT_EQ(m0.Begin(), base0) << error_msg;
    EXPECT_EQ(m0.Size(), page_size);
    EXPECT_EQ(m0.BaseBegin(), base0);
    EXPECT_EQ(m0.BaseSize(), page_size);
    uint8_t* base1 = m1.Begin();
    size_t size1 = m1.Size();
    EXPECT_EQ(base1, base0 + page_size);
    EXPECT_EQ(size1, page_size);
    EXPECT_EQ(m1.BaseBegin(), base1);
    EXPECT_EQ(m1.BaseSize(), size1);
    // Write to the second region.
    memset(base1, 43, page_size);
    // Check the contents of the two regions.
    for (size_t i = 0; i < page_size; ++i) {
      EXPECT_EQ(base0[i], 42);
    }
    for (size_t i = 0; i < page_size; ++i) {
      EXPECT_EQ(base1[i], 43);
    }
    // Unmap the first region.
    m0.Reset();
    // Make sure the second region is still accessible after the first
    // region is unmapped.
    for (size_t i = 0; i < page_size; ++i) {
      EXPECT_EQ(base1[i], 43);
    }
    MemMap m2 = m1.RemapAtEnd(m1.Begin(),
                              "MemMapTest_RemapAtEndTest_map1",
                              PROT_READ | PROT_WRITE,
                              &error_msg);
    ASSERT_TRUE(m2.IsValid()) << error_msg;
    ASSERT_FALSE(m1.IsValid());
  }

  void CommonInit() {
    MemMap::Init();
  }

#if defined(__LP64__) && !defined(__x86_64__)
  static uintptr_t GetLinearScanPos() {
    return MemMap::next_mem_pos_;
  }
#endif
};

#if defined(__LP64__) && !defined(__x86_64__)

#ifdef __BIONIC__
extern uintptr_t CreateStartPos(uint64_t input, uint64_t page_size);
#endif

TEST_F(MemMapTest, PageSize) {
  const size_t page_size = MemMap::GetPageSize();
  EXPECT_EQ(page_size, GetPageSizeSlow());
}

TEST_F(MemMapTest, Start) {
  CommonInit();
  uintptr_t start = GetLinearScanPos();
  EXPECT_LE(64 * KB, start);
  EXPECT_LT(start, static_cast<uintptr_t>(ART_BASE_ADDRESS));
#ifdef __BIONIC__
  const size_t page_size = MemMap::GetPageSize();
  // Test a couple of values. Make sure they are different.
  uintptr_t last = 0;
  for (size_t i = 0; i < 100; ++i) {
    uintptr_t random_start = CreateStartPos(i * page_size, page_size);
    EXPECT_NE(last, random_start);
    last = random_start;
  }

  // Even on max, should be below ART_BASE_ADDRESS.
  EXPECT_LT(CreateStartPos(~0, page_size), static_cast<uintptr_t>(ART_BASE_ADDRESS));
#endif
  // End of test.
}
#endif

// We need mremap to be able to test ReplaceMapping at all
#if HAVE_MREMAP_SYSCALL
TEST_F(MemMapTest, ReplaceMapping_SameSize) {
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap dest = MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
                                     page_size,
                                     PROT_READ,
                                     /*low_4gb=*/ false,
                                     &error_msg);
  ASSERT_TRUE(dest.IsValid());
  MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
                                       page_size,
                                       PROT_WRITE | PROT_READ,
                                       /*low_4gb=*/ false,
                                       &error_msg);
  ASSERT_TRUE(source.IsValid());
  void* source_addr = source.Begin();
  void* dest_addr = dest.Begin();
  ASSERT_TRUE(IsAddressMapped(source_addr));
  ASSERT_TRUE(IsAddressMapped(dest_addr));

  std::vector<uint8_t> data = RandomData(page_size);
  memcpy(source.Begin(), data.data(), data.size());

  ASSERT_TRUE(dest.ReplaceWith(&source, &error_msg)) << error_msg;

  ASSERT_FALSE(IsAddressMapped(source_addr));
  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_FALSE(source.IsValid());

  ASSERT_EQ(dest.Size(), static_cast<size_t>(page_size));

  ASSERT_EQ(memcmp(dest.Begin(), data.data(), dest.Size()), 0);
}

TEST_F(MemMapTest, ReplaceMapping_MakeLarger) {
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap dest = MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
                                     5 * page_size,  // Need to make it larger
                                                     // initially so we know
                                                     // there won't be mappings
                                                     // in the way when we move
                                                     // source.
                                     PROT_READ,
                                     /*low_4gb=*/ false,
                                     &error_msg);
  ASSERT_TRUE(dest.IsValid());
  MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
                                       3 * page_size,
                                       PROT_WRITE | PROT_READ,
                                       /*low_4gb=*/ false,
                                       &error_msg);
  ASSERT_TRUE(source.IsValid());
  uint8_t* source_addr = source.Begin();
  uint8_t* dest_addr = dest.Begin();
  ASSERT_TRUE(IsAddressMapped(source_addr));

  // Fill the source with random data.
  std::vector<uint8_t> data = RandomData(3 * page_size);
  memcpy(source.Begin(), data.data(), data.size());

  // Make the dest smaller so that we know we'll have space.
  dest.SetSize(page_size);

  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_FALSE(IsAddressMapped(dest_addr + 2 * page_size));
  ASSERT_EQ(dest.Size(), static_cast<size_t>(page_size));

  ASSERT_TRUE(dest.ReplaceWith(&source, &error_msg)) << error_msg;

  ASSERT_FALSE(IsAddressMapped(source_addr));
  ASSERT_EQ(dest.Size(), static_cast<size_t>(3 * page_size));
  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_TRUE(IsAddressMapped(dest_addr + 2 * page_size));
  ASSERT_FALSE(source.IsValid());

  ASSERT_EQ(memcmp(dest.Begin(), data.data(), dest.Size()), 0);
}

TEST_F(MemMapTest, ReplaceMapping_MakeSmaller) {
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap dest = MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
                                     3 * page_size,
                                     PROT_READ,
                                     /*low_4gb=*/ false,
                                     &error_msg);
  ASSERT_TRUE(dest.IsValid());
  MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
                                       page_size,
                                       PROT_WRITE | PROT_READ,
                                       /*low_4gb=*/ false,
                                       &error_msg);
  ASSERT_TRUE(source.IsValid());
  uint8_t* source_addr = source.Begin();
  uint8_t* dest_addr = dest.Begin();
  ASSERT_TRUE(IsAddressMapped(source_addr));
  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_TRUE(IsAddressMapped(dest_addr + 2 * page_size));
  ASSERT_EQ(dest.Size(), static_cast<size_t>(3 * page_size));

  std::vector<uint8_t> data = RandomData(page_size);
  memcpy(source.Begin(), data.data(), page_size);

  ASSERT_TRUE(dest.ReplaceWith(&source, &error_msg)) << error_msg;

  ASSERT_FALSE(IsAddressMapped(source_addr));
  ASSERT_EQ(dest.Size(), static_cast<size_t>(page_size));
  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_FALSE(IsAddressMapped(dest_addr + 2 * page_size));
  ASSERT_FALSE(source.IsValid());

  ASSERT_EQ(memcmp(dest.Begin(), data.data(), dest.Size()), 0);
}

TEST_F(MemMapTest, ReplaceMapping_FailureOverlap) {
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap dest =
      MemMap::MapAnonymous(
          "MapAnonymousEmpty-atomic-replace-dest",
          3 * page_size,  // Need to make it larger initially so we know there won't be mappings in
                          // the way when we move source.
          PROT_READ | PROT_WRITE,
          /*low_4gb=*/ false,
          &error_msg);
  ASSERT_TRUE(dest.IsValid());
  // Resize down to 1 page so we can remap the rest.
  dest.SetSize(page_size);
  // Create source from the last 2 pages
  MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
                                       dest.Begin() + page_size,
                                       2 * page_size,
                                       PROT_WRITE | PROT_READ,
                                       /*low_4gb=*/ false,
                                       /*reuse=*/ false,
                                       /*reservation=*/ nullptr,
                                       &error_msg);
  ASSERT_TRUE(source.IsValid());
  ASSERT_EQ(dest.Begin() + page_size, source.Begin());
  uint8_t* source_addr = source.Begin();
  uint8_t* dest_addr = dest.Begin();
  ASSERT_TRUE(IsAddressMapped(source_addr));

  // Fill the source and dest with random data.
  std::vector<uint8_t> data = RandomData(2 * page_size);
  memcpy(source.Begin(), data.data(), data.size());
  std::vector<uint8_t> dest_data = RandomData(page_size);
  memcpy(dest.Begin(), dest_data.data(), dest_data.size());

  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_EQ(dest.Size(), static_cast<size_t>(page_size));

  ASSERT_FALSE(dest.ReplaceWith(&source, &error_msg)) << error_msg;

  ASSERT_TRUE(IsAddressMapped(source_addr));
  ASSERT_TRUE(IsAddressMapped(dest_addr));
  ASSERT_EQ(source.Size(), data.size());
  ASSERT_EQ(dest.Size(), dest_data.size());

  ASSERT_EQ(memcmp(source.Begin(), data.data(), data.size()), 0);
  ASSERT_EQ(memcmp(dest.Begin(), dest_data.data(), dest_data.size()), 0);
}
#endif  // HAVE_MREMAP_SYSCALL

TEST_F(MemMapTest, FormatDebugName) {
  // Test short/empty names. They should all succeed with "dalvik-" prefixes.
  EXPECT_EQ("dalvik-", MemMap::FormatDebugName(""));
  EXPECT_EQ("dalvik-", MemMap::FormatDebugName(nullptr));
  EXPECT_EQ("dalvik-short", MemMap::FormatDebugName("short"));

  // Test name exactly at the limit (79 non-null chars total).
  // "dalvik-" is 7 chars. We get 72 more chars.
  std::string name_72(72'a');
  EXPECT_EQ("dalvik-" + name_72, MemMap::FormatDebugName(name_72.c_str()));

  // Test name just over the limit (80 chars total).
  std::string name_73(73'a');
  constexpr std::string_view expected_debug_name_73 =
      "dalvik-aaaaaaaaaaaaaaaaaaaa...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  static_assert(expected_debug_name_73.size() == 79);
  EXPECT_EQ(expected_debug_name_73, MemMap::FormatDebugName(name_73.c_str()));

  // Test name exceeding the limit.
  // This should ellipsize the interior of the requested name, preferring a
  // lengthier suffix (49 char).
  std::string name_long =
      "a_very_long_name_that_is_over_72_and_should_be_ellipsized_to_preserve_the_end.dex";
  constexpr std::string_view expected_debug_name_long =
      "dalvik-a_very_long_name_tha..._and_should_be_ellipsized_to_preserve_the_end.dex";
  static_assert(expected_debug_name_long.size() == 79);
  EXPECT_EQ(expected_debug_name_long, MemMap::FormatDebugName(name_long.c_str()));
}

TEST_F(MemMapTest, MapAnonymousEmpty) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap map = MemMap::MapAnonymous("MapAnonymousEmpty",
                                    /*byte_count=*/ 0,
                                    PROT_READ,
                                    /*low_4gb=*/ false,
                                    &error_msg);
  ASSERT_FALSE(map.IsValid()) << error_msg;
  ASSERT_FALSE(error_msg.empty());

  error_msg.clear();
  map = MemMap::MapAnonymous("MapAnonymousNonEmpty",
                             page_size,
                             PROT_READ | PROT_WRITE,
                             /*low_4gb=*/ false,
                             &error_msg);
  ASSERT_TRUE(map.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
}

TEST_F(MemMapTest, MapAnonymousFailNullError) {
  // Host system's mmap_min_addr configuration could allow for arbitrarily low addresses to be
  // successfully mapped, breaking the expectation that the MapAnonymous call should fail.
  TEST_DISABLED_FOR_HOST();

  CommonInit();
  uint8_t* invalid_page[16];  // Use this address as mmap hint address.
  const size_t page_size = MemMap::GetPageSize();
  // Test that we don't crash with a null error_str when mapping at an invalid location.
  MemMap map = MemMap::MapAnonymous("MapAnonymousInvalid",
                                    reinterpret_cast<uint8_t*>(AlignDown(invalid_page, page_size)),
                                    0x20000,
                                    PROT_READ | PROT_WRITE,
                                    /*low_4gb=*/false,
                                    /*reuse=*/false,
                                    /*reservation=*/nullptr,
                                    nullptr);
  ASSERT_FALSE(map.IsValid());
}

#ifdef __LP64__
TEST_F(MemMapTest, MapAnonymousEmpty32bit) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap map = MemMap::MapAnonymous("MapAnonymousEmpty",
                                    /*byte_count=*/ 0,
                                    PROT_READ,
                                    /*low_4gb=*/ true,
                                    &error_msg);
  ASSERT_FALSE(map.IsValid()) << error_msg;
  ASSERT_FALSE(error_msg.empty());

  error_msg.clear();
  map = MemMap::MapAnonymous("MapAnonymousNonEmpty",
                             page_size,
                             PROT_READ | PROT_WRITE,
                             /*low_4gb=*/ true,
                             &error_msg);
  ASSERT_TRUE(map.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_LT(reinterpret_cast<uintptr_t>(map.BaseBegin()), 1ULL << 32);
}
TEST_F(MemMapTest, MapFile32Bit) {
  CommonInit();
  std::string error_msg;
  ScratchFile scratch_file;
  const size_t map_size = MemMap::GetPageSize();
  std::unique_ptr<uint8_t[]> data(new uint8_t[map_size]());
  ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], map_size));
  MemMap map = MemMap::MapFile(/*byte_count=*/map_size,
                               PROT_READ,
                               MAP_PRIVATE,
                               scratch_file.GetFd(),
                               /*start=*/0,
                               /*low_4gb=*/true,
                               scratch_file.GetFilename().c_str(),
                               &error_msg);
  ASSERT_TRUE(map.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map.Size(), map_size);
  ASSERT_LT(reinterpret_cast<uintptr_t>(map.BaseBegin()), 1ULL << 32);
}
#endif

TEST_F(MemMapTest, MapAnonymousExactAddr) {
  // TODO: The semantics of the MemMap::MapAnonymous() with a given address but without
  // `reuse == true` or `reservation != nullptr` is weird. We should either drop support
  // for it, or take it only as a hint and allow the result to be mapped elsewhere.
  // Currently we're seeing failures with ASAN. b/118408378
  TEST_DISABLED_FOR_MEMORY_TOOL();

  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  // Find a valid address.
  uint8_t* valid_address = GetValidMapAddress(page_size, /*low_4gb=*/false);
  // Map at an address that should work, which should succeed.
  MemMap map0 = MemMap::MapAnonymous("MapAnonymous0",
                                     valid_address,
                                     page_size,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     /*reuse=*/ false,
                                     /*reservation=*/ nullptr,
                                     &error_msg);
  ASSERT_TRUE(map0.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_TRUE(map0.BaseBegin() == valid_address);
  // Map at an unspecified address, which should succeed.
  MemMap map1 = MemMap::MapAnonymous("MapAnonymous1",
                                     page_size,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     &error_msg);
  ASSERT_TRUE(map1.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_TRUE(map1.BaseBegin() != nullptr);
  // Attempt to map at the same address, which should fail.
  MemMap map2 = MemMap::MapAnonymous("MapAnonymous2",
                                     reinterpret_cast<uint8_t*>(map1.BaseBegin()),
                                     page_size,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     /*reuse=*/ false,
                                     /*reservation=*/ nullptr,
                                     &error_msg);
  ASSERT_FALSE(map2.IsValid()) << error_msg;
  ASSERT_TRUE(!error_msg.empty());
}

TEST_F(MemMapTest, RemapAtEnd) {
  RemapAtEndTest(false);
}

#ifdef __LP64__
TEST_F(MemMapTest, RemapAtEnd32bit) {
  RemapAtEndTest(true);
}
#endif

TEST_F(MemMapTest, RemapFileViewAtEnd) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  ScratchFile scratch_file;

  // Create a scratch file 3 pages large.
  const size_t map_size = 3 * page_size;
  std::unique_ptr<uint8_t[]> data(new uint8_t[map_size]());
  memset(data.get(), 1, page_size);
  memset(&data[0], 0x55, page_size);
  memset(&data[page_size], 0x5a, page_size);
  memset(&data[2 * page_size], 0xaa, page_size);
  ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], map_size));

  MemMap map = MemMap::MapFile(/*byte_count=*/map_size,
                               PROT_READ,
                               MAP_PRIVATE,
                               scratch_file.GetFd(),
                               /*start=*/0,
                               /*low_4gb=*/true,
                               scratch_file.GetFilename().c_str(),
                               &error_msg);
  ASSERT_TRUE(map.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map.Size(), map_size);
  ASSERT_LT(reinterpret_cast<uintptr_t>(map.BaseBegin()), 1ULL << 32);
  ASSERT_EQ(data[0], *map.Begin());
  ASSERT_EQ(data[page_size], *(map.Begin() + page_size));
  ASSERT_EQ(data[2 * page_size], *(map.Begin() + 2 * page_size));

  for (size_t offset = 2 * page_size; offset > 0; offset -= page_size) {
    MemMap tail = map.RemapAtEnd(map.Begin() + offset,
                                 "bad_offset_map",
                                 PROT_READ,
                                 MAP_PRIVATE | MAP_FIXED,
                                 scratch_file.GetFd(),
                                 offset,
                                 &error_msg);
    ASSERT_TRUE(tail.IsValid()) << error_msg;
    ASSERT_TRUE(error_msg.empty());
    ASSERT_EQ(offset, map.Size());
    ASSERT_EQ(static_cast<size_t>(page_size), tail.Size());
    ASSERT_EQ(tail.Begin(), map.Begin() + map.Size());
    ASSERT_EQ(data[offset], *tail.Begin());
  }
}

TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) {
  // This test does not work under AddressSanitizer.
  // Historical note: This test did not work under Valgrind either.
  TEST_DISABLED_FOR_MEMORY_TOOL();

  CommonInit();
  constexpr size_t size = 0x100000;
  // Try all addresses starting from 2GB to 4GB.
  size_t start_addr = 2 * GB;
  std::string error_msg;
  MemMap map;
  for (; start_addr <= std::numeric_limits<uint32_t>::max() - size; start_addr += size) {
    map = MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
                               reinterpret_cast<uint8_t*>(start_addr),
                               size,
                               PROT_READ | PROT_WRITE,
                               /*low_4gb=*/ true,
                               /*reuse=*/ false,
                               /*reservation=*/ nullptr,
                               &error_msg);
    if (map.IsValid()) {
      break;
    }
  }
  ASSERT_TRUE(map.IsValid()) << error_msg;
  ASSERT_GE(reinterpret_cast<uintptr_t>(map.End()), 2u * GB);
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map.BaseBegin(), reinterpret_cast<void*>(start_addr));
}

TEST_F(MemMapTest, MapAnonymousOverflow) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  uintptr_t ptr = 0;
  ptr -= page_size;  // Now it's close to the top.
  MemMap map = MemMap::MapAnonymous("MapAnonymousOverflow",
                                    reinterpret_cast<uint8_t*>(ptr),
                                    2 * page_size,  // brings it over the top.
                                    PROT_READ | PROT_WRITE,
                                    /*low_4gb=*/ false,
                                    /*reuse=*/ false,
                                    /*reservation=*/ nullptr,
                                    &error_msg);
  ASSERT_FALSE(map.IsValid());
  ASSERT_FALSE(error_msg.empty());
}

#ifdef __LP64__
TEST_F(MemMapTest, MapAnonymousLow4GBExpectedTooHigh) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  MemMap map =
      MemMap::MapAnonymous("MapAnonymousLow4GBExpectedTooHigh",
                           reinterpret_cast<uint8_t*>(UINT64_C(0x100000000)),
                           page_size,
                           PROT_READ | PROT_WRITE,
                           /*low_4gb=*/ true,
                           /*reuse=*/ false,
                           /*reservation=*/ nullptr,
                           &error_msg);
  ASSERT_FALSE(map.IsValid());
  ASSERT_FALSE(error_msg.empty());
}

TEST_F(MemMapTest, MapAnonymousLow4GBRangeTooHigh) {
  CommonInit();
  std::string error_msg;
  MemMap map = MemMap::MapAnonymous("MapAnonymousLow4GBRangeTooHigh",
                                    /*addr=*/ reinterpret_cast<uint8_t*>(0xF0000000),
                                    /*byte_count=*/ 0x20000000,
                                    PROT_READ | PROT_WRITE,
                                    /*low_4gb=*/ true,
                                    /*reuse=*/ false,
                                    /*reservation=*/ nullptr,
                                    &error_msg);
  ASSERT_FALSE(map.IsValid());
  ASSERT_FALSE(error_msg.empty());
}
#endif

TEST_F(MemMapTest, MapAnonymousReuse) {
  CommonInit();
  std::string error_msg;
  MemMap map = MemMap::MapAnonymous("MapAnonymousReserve",
                                    /*byte_count=*/ 0x20000,
                                    PROT_READ | PROT_WRITE,
                                    /*low_4gb=*/ false,
                                    &error_msg);
  ASSERT_TRUE(map.IsValid());
  ASSERT_TRUE(error_msg.empty());
  MemMap map2 = MemMap::MapAnonymous("MapAnonymousReused",
                                     /*addr=*/ reinterpret_cast<uint8_t*>(map.BaseBegin()),
                                     /*byte_count=*/ 0x10000,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     /*reuse=*/ true,
                                     /*reservation=*/ nullptr,
                                     &error_msg);
  ASSERT_TRUE(map2.IsValid());
  ASSERT_TRUE(error_msg.empty());
}

TEST_F(MemMapTest, CheckNoGaps) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  constexpr size_t kNumPages = 3;
  // Map a 3-page mem map.
  MemMap reservation = MemMap::MapAnonymous("MapAnonymous0",
                                            page_size * kNumPages,
                                            PROT_READ | PROT_WRITE,
                                            /*low_4gb=*/ false,
                                            &error_msg);
  ASSERT_TRUE(reservation.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  // Record the base address.
  uint8_t* map_base = reinterpret_cast<uint8_t*>(reservation.BaseBegin());

  // Map at the same address, taking from the `map` reservation.
  MemMap map0 = MemMap::MapAnonymous("MapAnonymous0",
                                     page_size,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     &reservation,
                                     &error_msg);
  ASSERT_TRUE(map0.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map_base, map0.Begin());
  MemMap map1 = MemMap::MapAnonymous("MapAnonymous1",
                                     page_size,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     &reservation,
                                     &error_msg);
  ASSERT_TRUE(map1.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map_base + page_size, map1.Begin());
  MemMap map2 = MemMap::MapAnonymous("MapAnonymous2",
                                     page_size,
                                     PROT_READ | PROT_WRITE,
                                     /*low_4gb=*/ false,
                                     &reservation,
                                     &error_msg);
  ASSERT_TRUE(map2.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map_base + 2 * page_size, map2.Begin());
  ASSERT_FALSE(reservation.IsValid());  // The entire reservation was used.

  // One-map cases.
  ASSERT_TRUE(MemMap::CheckNoGaps(map0, map0));
  ASSERT_TRUE(MemMap::CheckNoGaps(map1, map1));
  ASSERT_TRUE(MemMap::CheckNoGaps(map2, map2));

  // Two or three-map cases.
  ASSERT_TRUE(MemMap::CheckNoGaps(map0, map1));
  ASSERT_TRUE(MemMap::CheckNoGaps(map1, map2));
  ASSERT_TRUE(MemMap::CheckNoGaps(map0, map2));

  // Unmap the middle one.
  map1.Reset();

  // Should return false now that there's a gap in the middle.
  ASSERT_FALSE(MemMap::CheckNoGaps(map0, map2));
}

TEST_F(MemMapTest, AlignBy) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  // Map a region.
  MemMap m0 = MemMap::MapAnonymous("MemMapTest_AlignByTest_map0",
                                   14 * page_size,
                                   PROT_READ | PROT_WRITE,
                                   /*low_4gb=*/ false,
                                   &error_msg);
  ASSERT_TRUE(m0.IsValid());
  uint8_t* base0 = m0.Begin();
  ASSERT_TRUE(base0 != nullptr) << error_msg;
  ASSERT_EQ(m0.Size(), 14 * page_size);
  ASSERT_EQ(m0.BaseBegin(), base0);
  ASSERT_EQ(m0.BaseSize(), m0.Size());

  // Break it into several regions by using RemapAtEnd.
  MemMap m1 = m0.RemapAtEnd(base0 + 3 * page_size,
                            "MemMapTest_AlignByTest_map1",
                            PROT_READ | PROT_WRITE,
                            &error_msg);
  uint8_t* base1 = m1.Begin();
  ASSERT_TRUE(base1 != nullptr) << error_msg;
  ASSERT_EQ(base1, base0 + 3 * page_size);
  ASSERT_EQ(m0.Size(), 3 * page_size);

  MemMap m2 = m1.RemapAtEnd(base1 + 4 * page_size,
                            "MemMapTest_AlignByTest_map2",
                            PROT_READ | PROT_WRITE,
                            &error_msg);
  uint8_t* base2 = m2.Begin();
  ASSERT_TRUE(base2 != nullptr) << error_msg;
  ASSERT_EQ(base2, base1 + 4 * page_size);
  ASSERT_EQ(m1.Size(), 4 * page_size);

  MemMap m3 = m2.RemapAtEnd(base2 + 3 * page_size,
                            "MemMapTest_AlignByTest_map1",
                            PROT_READ | PROT_WRITE,
                            &error_msg);
  uint8_t* base3 = m3.Begin();
  ASSERT_TRUE(base3 != nullptr) << error_msg;
  ASSERT_EQ(base3, base2 + 3 * page_size);
  ASSERT_EQ(m2.Size(), 3 * page_size);
  ASSERT_EQ(m3.Size(), 4 * page_size);

  uint8_t* end0 = base0 + m0.Size();
  uint8_t* end1 = base1 + m1.Size();
  uint8_t* end2 = base2 + m2.Size();
  uint8_t* end3 = base3 + m3.Size();

  ASSERT_EQ(static_cast<size_t>(end3 - base0), 14 * page_size);

  if (IsAlignedParam(base0, 2 * page_size)) {
    ASSERT_FALSE(IsAlignedParam(base1, 2 * page_size));
    ASSERT_FALSE(IsAlignedParam(base2, 2 * page_size));
    ASSERT_TRUE(IsAlignedParam(base3, 2 * page_size));
    ASSERT_TRUE(IsAlignedParam(end3, 2 * page_size));
  } else {
    ASSERT_TRUE(IsAlignedParam(base1, 2 * page_size));
    ASSERT_TRUE(IsAlignedParam(base2, 2 * page_size));
    ASSERT_FALSE(IsAlignedParam(base3, 2 * page_size));
    ASSERT_FALSE(IsAlignedParam(end3, 2 * page_size));
  }

  // Align by 2 * page_size;
  m0.AlignBy(2 * page_size);
  m1.AlignBy(2 * page_size);
  m2.AlignBy(2 * page_size);
  m3.AlignBy(2 * page_size);

  EXPECT_TRUE(IsAlignedParam(m0.Begin(), 2 * page_size));
  EXPECT_TRUE(IsAlignedParam(m1.Begin(), 2 * page_size));
  EXPECT_TRUE(IsAlignedParam(m2.Begin(), 2 * page_size));
  EXPECT_TRUE(IsAlignedParam(m3.Begin(), 2 * page_size));

  EXPECT_TRUE(IsAlignedParam(m0.Begin() + m0.Size(), 2 * page_size));
  EXPECT_TRUE(IsAlignedParam(m1.Begin() + m1.Size(), 2 * page_size));
  EXPECT_TRUE(IsAlignedParam(m2.Begin() + m2.Size(), 2 * page_size));
  EXPECT_TRUE(IsAlignedParam(m3.Begin() + m3.Size(), 2 * page_size));

  if (IsAlignedParam(base0, 2 * page_size)) {
    EXPECT_EQ(m0.Begin(), base0);
    EXPECT_EQ(m0.Begin() + m0.Size(), end0 - page_size);
    EXPECT_EQ(m1.Begin(), base1 + page_size);
    EXPECT_EQ(m1.Begin() + m1.Size(), end1 - page_size);
    EXPECT_EQ(m2.Begin(), base2 + page_size);
    EXPECT_EQ(m2.Begin() + m2.Size(), end2);
    EXPECT_EQ(m3.Begin(), base3);
    EXPECT_EQ(m3.Begin() + m3.Size(), end3);
  } else {
    EXPECT_EQ(m0.Begin(), base0 + page_size);
    EXPECT_EQ(m0.Begin() + m0.Size(), end0);
    EXPECT_EQ(m1.Begin(), base1);
    EXPECT_EQ(m1.Begin() + m1.Size(), end1);
    EXPECT_EQ(m2.Begin(), base2);
    EXPECT_EQ(m2.Begin() + m2.Size(), end2 - page_size);
    EXPECT_EQ(m3.Begin(), base3 + page_size);
    EXPECT_EQ(m3.Begin() + m3.Size(), end3 - page_size);
  }
}

TEST_F(MemMapTest, Reservation) {
  CommonInit();
  const size_t page_size = MemMap::GetPageSize();
  std::string error_msg;
  ScratchFile scratch_file;
  const size_t map_size = 5 * page_size;
  std::unique_ptr<uint8_t[]> data(new uint8_t[map_size]());
  ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], map_size));

  MemMap reservation = MemMap::MapAnonymous("Test reservation",
                                            map_size,
                                            PROT_NONE,
                                            /*low_4gb=*/ false,
                                            &error_msg);
  ASSERT_TRUE(reservation.IsValid());
  ASSERT_TRUE(error_msg.empty());

  // Map first part of the reservation.
  const size_t chunk1_size = page_size - 1u;
  ASSERT_LT(chunk1_size, map_size) << "We want to split the reservation.";
  uint8_t* addr1 = reservation.Begin();
  MemMap map1 = MemMap::MapFileAtAddress(addr1,
                                         /*byte_count=*/ chunk1_size,
                                         PROT_READ,
                                         MAP_PRIVATE,
                                         scratch_file.GetFd(),
                                         /*start=*/ 0,
                                         /*low_4gb=*/ false,
                                         scratch_file.GetFilename().c_str(),
                                         /*reuse=*/ false,
                                         &reservation,
                                         &error_msg);
  ASSERT_TRUE(map1.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map1.Size(), chunk1_size);
  ASSERT_EQ(addr1, map1.Begin());
  ASSERT_TRUE(reservation.IsValid());
  // Entire pages are taken from the `reservation`.
  ASSERT_LT(map1.End(), map1.BaseEnd());
  ASSERT_EQ(map1.BaseEnd(), reservation.Begin());

  // Map second part as an anonymous mapping.
  const size_t chunk2_size = 2 * page_size;
  DCHECK_LT(chunk2_size, reservation.Size());  // We want to split the reservation.
  uint8_t* addr2 = reservation.Begin();
  MemMap map2 = MemMap::MapAnonymous("MiddleReservation",
                                     addr2,
                                     /*byte_count=*/ chunk2_size,
                                     PROT_READ,
                                     /*low_4gb=*/ false,
                                     /*reuse=*/ false,
                                     &reservation,
                                     &error_msg);
  ASSERT_TRUE(map2.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map2.Size(), chunk2_size);
  ASSERT_EQ(addr2, map2.Begin());
  ASSERT_EQ(map2.End(), map2.BaseEnd());  // chunk2_size is page aligned.
  ASSERT_EQ(map2.BaseEnd(), reservation.Begin());

  // Map the rest of the reservation except the last byte.
  const size_t chunk3_size = reservation.Size() - 1u;
  uint8_t* addr3 = reservation.Begin();
  MemMap map3 = MemMap::MapFileAtAddress(addr3,
                                         /*byte_count=*/ chunk3_size,
                                         PROT_READ,
                                         MAP_PRIVATE,
                                         scratch_file.GetFd(),
                                         /*start=*/ dchecked_integral_cast<size_t>(addr3 - addr1),
                                         /*low_4gb=*/ false,
                                         scratch_file.GetFilename().c_str(),
                                         /*reuse=*/ false,
                                         &reservation,
                                         &error_msg);
  ASSERT_TRUE(map3.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map3.Size(), chunk3_size);
  ASSERT_EQ(addr3, map3.Begin());
  // Entire pages are taken from the `reservation`, so it's now exhausted.
  ASSERT_FALSE(reservation.IsValid());

  // Now split the MiddleReservation.
  const size_t chunk2a_size = page_size - 1u;
  DCHECK_LT(chunk2a_size, map2.Size());  // We want to split the reservation.
  MemMap map2a = map2.TakeReservedMemory(chunk2a_size);
  ASSERT_TRUE(map2a.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map2a.Size(), chunk2a_size);
  ASSERT_EQ(addr2, map2a.Begin());
  ASSERT_TRUE(map2.IsValid());
  ASSERT_LT(map2a.End(), map2a.BaseEnd());
  ASSERT_EQ(map2a.BaseEnd(), map2.Begin());

  // And take the rest of the middle reservation.
  const size_t chunk2b_size = map2.Size() - 1u;
  uint8_t* addr2b = map2.Begin();
  MemMap map2b = map2.TakeReservedMemory(chunk2b_size);
  ASSERT_TRUE(map2b.IsValid()) << error_msg;
  ASSERT_TRUE(error_msg.empty());
  ASSERT_EQ(map2b.Size(), chunk2a_size);
  ASSERT_EQ(addr2b, map2b.Begin());
  ASSERT_FALSE(map2.IsValid());
}

}  // namespace art

namespace {

class DumpMapsOnFailListener : public ::testing::EmptyTestEventListener {
  void OnTestPartResult(const ::testing::TestPartResult& result) override {
    switch (result.type()) {
      case ::testing::TestPartResult::kFatalFailure:
        art::PrintFileToLog("/proc/self/maps", android::base::LogSeverity::ERROR);
        break;

      // TODO: Could consider logging on EXPECT failures.
      case ::testing::TestPartResult::kNonFatalFailure:
      case ::testing::TestPartResult::kSkip:
      case ::testing::TestPartResult::kSuccess:
        break;
    }
  }
};

}  // namespace

// Inject our listener into the test runner.
extern "C"
__attribute__((visibility("default"))) __attribute__((used))
void ArtTestGlobalInit() {
  ::testing::UnitTest::GetInstance()->listeners().Append(new DumpMapsOnFailListener());
}

Messung V0.5 in Prozent
C=92 H=95 G=93

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik