class MemMapTest : public CommonArtTest { public: staticbool 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();
}
staticvoid 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, 42, 2 * 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());
}
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());
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);
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());
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, 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());
}
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, MapAnonymousExactAddr32bitHighAddr) { // This test does not work under AddressSanitizer. // Historical note: This test did not work under Valgrind either.
TEST_DISABLED_FOR_MEMORY_TOOL();
// 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
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet am 2026-06-29)
¤
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.