brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.4 KiB · 8741c82 Raw
589 lines · cpp
1//===-- secondary_test.cpp --------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "memtag.h"10#include "tests/scudo_unit_test.h"11 12#include "allocator_config.h"13#include "allocator_config_wrapper.h"14#include "secondary.h"15 16#include <string.h>17 18#include <algorithm>19#include <condition_variable>20#include <memory>21#include <mutex>22#include <random>23#include <thread>24#include <vector>25 26// Get this once to use through-out the tests.27const scudo::uptr PageSize = scudo::getPageSizeCached();28 29template <typename Config> static scudo::Options getOptionsForConfig() {30  if (!scudo::systemSupportsMemoryTagging())31    return {};32  scudo::AtomicOptions AO;33  AO.set(scudo::OptionBit::UseMemoryTagging);34  return AO.load();35}36 37template <class Config> struct AllocatorInfoType {38  std::unique_ptr<scudo::MapAllocator<scudo::SecondaryConfig<Config>>>39      Allocator;40  scudo::GlobalStats GlobalStats;41  scudo::Options Options;42 43  AllocatorInfoType(scudo::s32 ReleaseToOsInterval) {44    using SecondaryT = scudo::MapAllocator<scudo::SecondaryConfig<Config>>;45    Options = getOptionsForConfig<scudo::SecondaryConfig<Config>>();46    GlobalStats.init();47    Allocator.reset(new SecondaryT);48    Allocator->init(&GlobalStats, ReleaseToOsInterval);49  }50 51  AllocatorInfoType() : AllocatorInfoType(-1) {}52 53  ~AllocatorInfoType() {54    if (Allocator == nullptr) {55      return;56    }57 58    if (TEST_HAS_FAILURE) {59      // Print all of the stats if the test fails.60      scudo::ScopedString Str;61      Allocator->getStats(&Str);62      Str.output();63    }64 65    Allocator->unmapTestOnly();66  }67};68 69struct TestNoCacheConfig {70  static const bool MaySupportMemoryTagging = false;71  template <typename> using TSDRegistryT = void;72  template <typename> using PrimaryT = void;73  template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;74 75  struct Secondary {76    template <typename Config>77    using CacheT = scudo::MapAllocatorNoCache<Config>;78  };79};80 81struct TestNoCacheNoGuardPageConfig {82  static const bool MaySupportMemoryTagging = false;83  template <typename> using TSDRegistryT = void;84  template <typename> using PrimaryT = void;85  template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;86 87  struct Secondary {88    template <typename Config>89    using CacheT = scudo::MapAllocatorNoCache<Config>;90    static const bool EnableGuardPages = false;91  };92};93 94struct TestCacheConfig {95  static const bool MaySupportMemoryTagging = false;96  template <typename> using TSDRegistryT = void;97  template <typename> using PrimaryT = void;98  template <typename> using SecondaryT = void;99 100  struct Secondary {101    struct Cache {102      static const scudo::u32 EntriesArraySize = 128U;103      static const scudo::u32 QuarantineSize = 0U;104      static const scudo::u32 DefaultMaxEntriesCount = 64U;105      static const scudo::uptr DefaultMaxEntrySize = 1UL << 20;106      static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;107      static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;108    };109 110    template <typename Config> using CacheT = scudo::MapAllocatorCache<Config>;111  };112};113 114struct TestCacheNoGuardPageConfig {115  static const bool MaySupportMemoryTagging = false;116  template <typename> using TSDRegistryT = void;117  template <typename> using PrimaryT = void;118  template <typename> using SecondaryT = void;119 120  struct Secondary {121    struct Cache {122      static const scudo::u32 EntriesArraySize = 128U;123      static const scudo::u32 QuarantineSize = 0U;124      static const scudo::u32 DefaultMaxEntriesCount = 64U;125      static const scudo::uptr DefaultMaxEntrySize = 1UL << 20;126      static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;127      static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;128    };129 130    template <typename Config> using CacheT = scudo::MapAllocatorCache<Config>;131    static const bool EnableGuardPages = false;132  };133};134 135template <typename Config> static void testBasic() {136  using SecondaryT = scudo::MapAllocator<scudo::SecondaryConfig<Config>>;137  AllocatorInfoType<Config> Info;138 139  const scudo::uptr Size = 1U << 16;140  void *P = Info.Allocator->allocate(Info.Options, Size);141  EXPECT_NE(P, nullptr);142  memset(P, 'A', Size);143  EXPECT_GE(SecondaryT::getBlockSize(P), Size);144  Info.Allocator->deallocate(Info.Options, P);145 146  // If the Secondary can't cache that pointer, it will be unmapped.147  if (!Info.Allocator->canCache(Size)) {148    EXPECT_DEATH(149        {150          // Repeat few time to avoid missing crash if it's mmaped by unrelated151          // code.152          for (int i = 0; i < 10; ++i) {153            P = Info.Allocator->allocate(Info.Options, Size);154            Info.Allocator->deallocate(Info.Options, P);155            memset(P, 'A', Size);156          }157        },158        "");159  }160 161  const scudo::uptr Align = 1U << 16;162  P = Info.Allocator->allocate(Info.Options, Size + Align, Align);163  EXPECT_NE(P, nullptr);164  void *AlignedP = reinterpret_cast<void *>(165      scudo::roundUp(reinterpret_cast<scudo::uptr>(P), Align));166  memset(AlignedP, 'A', Size);167  Info.Allocator->deallocate(Info.Options, P);168 169  std::vector<void *> V;170  for (scudo::uptr I = 0; I < 32U; I++)171    V.push_back(Info.Allocator->allocate(Info.Options, Size));172  std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));173  while (!V.empty()) {174    Info.Allocator->deallocate(Info.Options, V.back());175    V.pop_back();176  }177}178 179TEST(ScudoSecondaryTest, Basic) {180  testBasic<TestNoCacheConfig>();181  testBasic<TestNoCacheNoGuardPageConfig>();182  testBasic<TestCacheConfig>();183  testBasic<TestCacheNoGuardPageConfig>();184  testBasic<scudo::DefaultConfig>();185}186 187// This exercises a variety of combinations of size and alignment for the188// MapAllocator. The size computation done here mimic the ones done by the189// combined allocator.190template <typename Config> void testAllocatorCombinations() {191  AllocatorInfoType<Config> Info;192 193  constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);194  constexpr scudo::uptr HeaderSize = scudo::roundUp(8, MinAlign);195  for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {196    for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;197         AlignLog++) {198      const scudo::uptr Align = 1U << AlignLog;199      for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {200        if ((1LL << SizeLog) + Delta <= 0)201          continue;202        const scudo::uptr UserSize = scudo::roundUp(203            static_cast<scudo::uptr>((1LL << SizeLog) + Delta), MinAlign);204        const scudo::uptr Size =205            HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);206        void *P = Info.Allocator->allocate(Info.Options, Size, Align);207        EXPECT_NE(P, nullptr);208        void *AlignedP = reinterpret_cast<void *>(209            scudo::roundUp(reinterpret_cast<scudo::uptr>(P), Align));210        memset(AlignedP, 0xff, UserSize);211        Info.Allocator->deallocate(Info.Options, P);212      }213    }214  }215}216 217TEST(ScudoSecondaryTest, AllocatorCombinations) {218  testAllocatorCombinations<TestNoCacheConfig>();219  testAllocatorCombinations<TestNoCacheNoGuardPageConfig>();220}221 222template <typename Config> void testAllocatorIterate() {223  AllocatorInfoType<Config> Info;224 225  std::vector<void *> V;226  for (scudo::uptr I = 0; I < 32U; I++)227    V.push_back(Info.Allocator->allocate(228        Info.Options,229        (static_cast<scudo::uptr>(std::rand()) % 16U) * PageSize));230  auto Lambda = [&V](scudo::uptr Block) {231    EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),232              V.end());233  };234  Info.Allocator->disable();235  Info.Allocator->iterateOverBlocks(Lambda);236  Info.Allocator->enable();237  while (!V.empty()) {238    Info.Allocator->deallocate(Info.Options, V.back());239    V.pop_back();240  }241}242 243TEST(ScudoSecondaryTest, AllocatorIterate) {244  testAllocatorIterate<TestNoCacheConfig>();245  testAllocatorIterate<TestNoCacheNoGuardPageConfig>();246}247 248template <typename Config> void testAllocatorWithReleaseThreadsRace() {249  AllocatorInfoType<Config> Info(/*ReleaseToOsInterval=*/0);250 251  std::mutex Mutex;252  std::condition_variable Cv;253  bool Ready = false;254 255  std::thread Threads[16];256  for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++)257    Threads[I] = std::thread([&Mutex, &Cv, &Ready, &Info]() {258      std::vector<void *> V;259      {260        std::unique_lock<std::mutex> Lock(Mutex);261        while (!Ready)262          Cv.wait(Lock);263      }264      for (scudo::uptr I = 0; I < 128U; I++) {265        // Deallocate 75% of the blocks.266        const bool Deallocate = (std::rand() & 3) != 0;267        void *P = Info.Allocator->allocate(268            Info.Options,269            (static_cast<scudo::uptr>(std::rand()) % 16U) * PageSize);270        if (Deallocate)271          Info.Allocator->deallocate(Info.Options, P);272        else273          V.push_back(P);274      }275      while (!V.empty()) {276        Info.Allocator->deallocate(Info.Options, V.back());277        V.pop_back();278      }279    });280 281  {282    std::unique_lock<std::mutex> Lock(Mutex);283    Ready = true;284    Cv.notify_all();285  }286  for (auto &T : Threads)287    T.join();288}289 290TEST(ScudoSecondaryTest, AllocatorWithReleaseThreadsRace) {291  testAllocatorWithReleaseThreadsRace<TestNoCacheConfig>();292  testAllocatorWithReleaseThreadsRace<TestNoCacheNoGuardPageConfig>();293}294 295template <typename Config>296void testGetMappedSize(scudo::uptr Size, scudo::uptr *mapped,297                       scudo::uptr *guard_page_size) {298  AllocatorInfoType<Config> Info;299 300  scudo::uptr Stats[scudo::StatCount] = {};301  Info.GlobalStats.get(Stats);302  *mapped = Stats[scudo::StatMapped];303  Stats[scudo::StatMapped] = 0;304 305  // Make sure the allocation is aligned to a page boundary so that the checks306  // in the tests can avoid problems due to allocations having different307  // alignments.308  void *Ptr = Info.Allocator->allocate(Info.Options, Size, PageSize);309  EXPECT_NE(Ptr, nullptr);310 311  Info.GlobalStats.get(Stats);312  EXPECT_GE(Stats[scudo::StatMapped], *mapped);313  *mapped = Stats[scudo::StatMapped] - *mapped;314 315  Info.Allocator->deallocate(Info.Options, Ptr);316 317  *guard_page_size = Info.Allocator->getGuardPageSize();318}319 320TEST(ScudoSecondaryTest, VerifyGuardPageOption) {321  static scudo::uptr AllocSize = 1000 * PageSize;322 323  // Verify that a config with guard pages enabled:324  //  - Non-zero sized guard page325  //  - Mapped in at least the size of the allocation plus 2 * guard page size326  scudo::uptr guard_mapped = 0;327  scudo::uptr guard_page_size = 0;328  testGetMappedSize<TestNoCacheConfig>(AllocSize, &guard_mapped,329                                       &guard_page_size);330  EXPECT_GT(guard_page_size, 0U);331  EXPECT_GE(guard_mapped, AllocSize + 2 * guard_page_size);332 333  // Verify that a config with guard pages disabled:334  //  - Zero sized guard page335  //  - The total mapped in is greater than the allocation size336  scudo::uptr no_guard_mapped = 0;337  scudo::uptr no_guard_page_size = 0;338  testGetMappedSize<TestNoCacheNoGuardPageConfig>(AllocSize, &no_guard_mapped,339                                                  &no_guard_page_size);340  EXPECT_EQ(no_guard_page_size, 0U);341  EXPECT_GE(no_guard_mapped, AllocSize);342 343  // Verify that a guard page config mapped in at least twice the size of344  // their guard page when compared to a no guard page config.345  EXPECT_GE(guard_mapped, no_guard_mapped + guard_page_size * 2);346}347 348// Value written to cache entries that are unmapped.349static scudo::u32 UnmappedMarker = 0xDEADBEEF;350 351template <class Config> struct CacheInfoType {352  static void addMarkerToMapCallback(scudo::MemMapT &MemMap) {353    // When a cache entry is unmaped, don't unmap it write a special marker354    // to indicate the cache entry was released. The real unmap will happen355    // in the destructor. It is assumed that all of these maps will be in356    // the MemMaps vector.357    scudo::u32 *Ptr = reinterpret_cast<scudo::u32 *>(MemMap.getBase());358    *Ptr = UnmappedMarker;359  }360 361  using SecondaryConfig = scudo::SecondaryConfig<TestCacheConfig>;362  using CacheConfig = SecondaryConfig::CacheConfig;363  using CacheT = scudo::MapAllocatorCache<CacheConfig, addMarkerToMapCallback>;364  scudo::Options Options = getOptionsForConfig<SecondaryConfig>();365  std::unique_ptr<CacheT> Cache = std::make_unique<CacheT>();366  std::vector<scudo::MemMapT> MemMaps;367  // The current test allocation size is set to the maximum368  // cache entry size369  static constexpr scudo::uptr TestAllocSize =370      CacheConfig::getDefaultMaxEntrySize();371 372  CacheInfoType() { Cache->init(/*ReleaseToOsInterval=*/-1); }373 374  ~CacheInfoType() {375    if (Cache == nullptr) {376      return;377    }378 379    // Clean up MemMaps380    for (auto &MemMap : MemMaps)381      MemMap.unmap();382  }383 384  scudo::MemMapT allocate(scudo::uptr Size) {385    scudo::uptr MapSize = scudo::roundUp(Size, PageSize);386    scudo::ReservedMemoryT ReservedMemory;387    CHECK(ReservedMemory.create(0U, MapSize, nullptr, MAP_ALLOWNOMEM));388 389    scudo::MemMapT MemMap = ReservedMemory.dispatch(390        ReservedMemory.getBase(), ReservedMemory.getCapacity());391    MemMap.remap(MemMap.getBase(), MemMap.getCapacity(), "scudo:test",392                 MAP_RESIZABLE | MAP_ALLOWNOMEM);393    return MemMap;394  }395 396  void fillCacheWithSameSizeBlocks(scudo::uptr NumEntries, scudo::uptr Size) {397    for (scudo::uptr I = 0; I < NumEntries; I++) {398      MemMaps.emplace_back(allocate(Size));399      auto &MemMap = MemMaps[I];400      Cache->store(Options, MemMap.getBase(), MemMap.getCapacity(),401                   MemMap.getBase(), MemMap);402    }403  }404 405  void storeMemMap(scudo::MemMapT &MemMap) {406    Cache->store(Options, MemMap.getBase(), MemMap.getCapacity(),407                 MemMap.getBase(), MemMap);408  }409};410 411TEST(ScudoSecondaryTest, AllocatorCacheEntryOrder) {412  CacheInfoType<TestCacheConfig> Info;413  using CacheConfig = CacheInfoType<TestCacheConfig>::CacheConfig;414 415  Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount,416                        CacheConfig::getEntriesArraySize());417 418  Info.fillCacheWithSameSizeBlocks(CacheConfig::getEntriesArraySize(),419                                   Info.TestAllocSize);420 421  // Retrieval order should be the inverse of insertion order422  for (scudo::uptr I = CacheConfig::getEntriesArraySize(); I > 0; I--) {423    scudo::uptr EntryHeaderPos;424    scudo::CachedBlock Entry = Info.Cache->retrieve(425        0, Info.TestAllocSize, PageSize, 0, EntryHeaderPos);426    EXPECT_EQ(Entry.MemMap.getBase(), Info.MemMaps[I - 1].getBase());427  }428}429 430TEST(ScudoSecondaryTest, AllocatorCachePartialChunkHeuristicRetrievalTest) {431  CacheInfoType<TestCacheConfig> Info;432 433  const scudo::uptr FragmentedPages =434      1 + scudo::CachedBlock::MaxReleasedCachePages;435  scudo::uptr EntryHeaderPos;436  scudo::CachedBlock Entry;437  scudo::MemMapT MemMap = Info.allocate(PageSize + FragmentedPages * PageSize);438  Info.Cache->store(Info.Options, MemMap.getBase(), MemMap.getCapacity(),439                    MemMap.getBase(), MemMap);440 441  // FragmentedPages > MaxAllowedFragmentedPages so PageSize442  // cannot be retrieved from the cache443  Entry = Info.Cache->retrieve(/*MaxAllowedFragmentedPages=*/0, PageSize,444                               PageSize, 0, EntryHeaderPos);445  EXPECT_FALSE(Entry.isValid());446 447  // FragmentedPages == MaxAllowedFragmentedPages so PageSize448  // can be retrieved from the cache449  Entry = Info.Cache->retrieve(FragmentedPages, PageSize, PageSize, 0,450                               EntryHeaderPos);451  EXPECT_TRUE(Entry.isValid());452 453  MemMap.unmap();454}455 456TEST(ScudoSecondaryTest, AllocatorCacheMemoryLeakTest) {457  CacheInfoType<TestCacheConfig> Info;458  using CacheConfig = CacheInfoType<TestCacheConfig>::CacheConfig;459 460  // Fill the cache above MaxEntriesCount to force an eviction461  // The first cache entry should be evicted (because it is the oldest)462  // due to the maximum number of entries being reached463  Info.fillCacheWithSameSizeBlocks(CacheConfig::getDefaultMaxEntriesCount() + 1,464                                   Info.TestAllocSize);465 466  std::vector<scudo::CachedBlock> RetrievedEntries;467 468  // First MemMap should be evicted from cache because it was the first469  // inserted into the cache470  for (scudo::uptr I = CacheConfig::getDefaultMaxEntriesCount(); I > 0; I--) {471    scudo::uptr EntryHeaderPos;472    RetrievedEntries.push_back(Info.Cache->retrieve(473        0, Info.TestAllocSize, PageSize, 0, EntryHeaderPos));474    EXPECT_EQ(Info.MemMaps[I].getBase(),475              RetrievedEntries.back().MemMap.getBase());476  }477 478  // Evicted entry should be marked due to unmap callback479  EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[0].getBase()),480            UnmappedMarker);481}482 483TEST(ScudoSecondaryTest, AllocatorCacheOptions) {484  CacheInfoType<TestCacheConfig> Info;485 486  // Attempt to set a maximum number of entries higher than the array size.487  EXPECT_TRUE(488      Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount, 4096U));489 490  // Attempt to set an invalid (negative) number of entries491  EXPECT_FALSE(Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount, -1));492 493  // Various valid combinations.494  EXPECT_TRUE(Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount, 4U));495  EXPECT_TRUE(496      Info.Cache->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));497  EXPECT_TRUE(Info.Cache->canCache(1UL << 18));498  EXPECT_TRUE(499      Info.Cache->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17));500  EXPECT_FALSE(Info.Cache->canCache(1UL << 18));501  EXPECT_TRUE(Info.Cache->canCache(1UL << 16));502  EXPECT_TRUE(Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount, 0U));503  EXPECT_FALSE(Info.Cache->canCache(1UL << 16));504  EXPECT_TRUE(Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount, 4U));505  EXPECT_TRUE(506      Info.Cache->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));507  EXPECT_TRUE(Info.Cache->canCache(1UL << 16));508}509 510TEST(ScudoSecondaryTest, ReleaseOlderThanAllEntries) {511  CacheInfoType<TestCacheConfig> Info;512  using CacheConfig = CacheInfoType<TestCacheConfig>::CacheConfig;513 514  Info.Cache->releaseOlderThanTestOnly(UINT64_MAX);515 516  Info.fillCacheWithSameSizeBlocks(CacheConfig::getDefaultMaxEntriesCount(),517                                   1024);518  for (size_t I = 0; I < Info.MemMaps.size(); I++) {519    // Set the first u32 value to a non-zero value.520    *reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()) = 10;521  }522 523  Info.Cache->releaseOlderThanTestOnly(UINT64_MAX);524 525  EXPECT_EQ(Info.MemMaps.size(), CacheConfig::getDefaultMaxEntriesCount());526  for (size_t I = 0; I < Info.MemMaps.size(); I++) {527    // All released maps will now be zero.528    EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 0U);529  }530}531 532// This test assumes that the timestamp comes from getMonotonicFast.533TEST(ScudoSecondaryTest, ReleaseOlderThanGroups) {534  CacheInfoType<TestCacheConfig> Info;535 536  // Disable the release interval so we can do tests the releaseOlderThan537  // function.538  Info.Cache->setOption(scudo::Option::ReleaseInterval, -1);539 540  // Create all of the maps we are going to use.541  for (size_t I = 0; I < 6; I++) {542    Info.MemMaps.emplace_back(Info.allocate(1024));543    // Set the first u32 value to a non-zero value.544    *reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()) = 10;545  }546 547  // Create three groups of entries at three different intervals.548  Info.storeMemMap(Info.MemMaps[0]);549  Info.storeMemMap(Info.MemMaps[1]);550  scudo::u64 FirstTime = scudo::getMonotonicTimeFast();551 552  // Need to make sure the next set of entries are stamped with a newer time.553  while (scudo::getMonotonicTimeFast() <= FirstTime)554    ;555 556  Info.storeMemMap(Info.MemMaps[2]);557  Info.storeMemMap(Info.MemMaps[3]);558  scudo::u64 SecondTime = scudo::getMonotonicTimeFast();559 560  // Need to make sure the next set of entries are stamped with a newer time.561  while (scudo::getMonotonicTimeFast() <= SecondTime)562    ;563 564  Info.storeMemMap(Info.MemMaps[4]);565  Info.storeMemMap(Info.MemMaps[5]);566  scudo::u64 ThirdTime = scudo::getMonotonicTimeFast();567 568  Info.Cache->releaseOlderThanTestOnly(FirstTime);569  for (size_t I = 0; I < 2; I++) {570    EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 0U);571  }572  for (size_t I = 2; I < 6; I++) {573    EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 10U);574  }575 576  Info.Cache->releaseOlderThanTestOnly(SecondTime);577  for (size_t I = 0; I < 4; I++) {578    EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 0U);579  }580  for (size_t I = 4; I < 6; I++) {581    EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 10U);582  }583 584  Info.Cache->releaseOlderThanTestOnly(ThirdTime);585  for (size_t I = 0; I < 6; I++) {586    EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 0U);587  }588}589