1*810390e3Srobert //===-- sanitizer_flat_map.h ------------------------------------*- C++ -*-===// 2*810390e3Srobert // 3*810390e3Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*810390e3Srobert // See https://llvm.org/LICENSE.txt for license information. 5*810390e3Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*810390e3Srobert // 7*810390e3Srobert //===----------------------------------------------------------------------===// 8*810390e3Srobert // 9*810390e3Srobert // Part of the Sanitizer Allocator. 10*810390e3Srobert // 11*810390e3Srobert //===----------------------------------------------------------------------===// 12*810390e3Srobert 13*810390e3Srobert #ifndef SANITIZER_FLAT_MAP_H 14*810390e3Srobert #define SANITIZER_FLAT_MAP_H 15*810390e3Srobert 16*810390e3Srobert #include "sanitizer_atomic.h" 17*810390e3Srobert #include "sanitizer_common.h" 18*810390e3Srobert #include "sanitizer_internal_defs.h" 19*810390e3Srobert #include "sanitizer_local_address_space_view.h" 20*810390e3Srobert #include "sanitizer_mutex.h" 21*810390e3Srobert 22*810390e3Srobert namespace __sanitizer { 23*810390e3Srobert 24*810390e3Srobert // Call these callbacks on mmap/munmap. 25*810390e3Srobert struct NoOpMapUnmapCallback { OnMapNoOpMapUnmapCallback26*810390e3Srobert void OnMap(uptr p, uptr size) const {} OnUnmapNoOpMapUnmapCallback27*810390e3Srobert void OnUnmap(uptr p, uptr size) const {} 28*810390e3Srobert }; 29*810390e3Srobert 30*810390e3Srobert // Maps integers in rage [0, kSize) to values. 31*810390e3Srobert template <typename T, u64 kSize, 32*810390e3Srobert typename AddressSpaceViewTy = LocalAddressSpaceView> 33*810390e3Srobert class FlatMap { 34*810390e3Srobert public: 35*810390e3Srobert using AddressSpaceView = AddressSpaceViewTy; Init()36*810390e3Srobert void Init() { internal_memset(map_, 0, sizeof(map_)); } 37*810390e3Srobert size()38*810390e3Srobert constexpr uptr size() const { return kSize; } 39*810390e3Srobert contains(uptr idx)40*810390e3Srobert bool contains(uptr idx) const { 41*810390e3Srobert CHECK_LT(idx, kSize); 42*810390e3Srobert return true; 43*810390e3Srobert } 44*810390e3Srobert 45*810390e3Srobert T &operator[](uptr idx) { 46*810390e3Srobert DCHECK_LT(idx, kSize); 47*810390e3Srobert return map_[idx]; 48*810390e3Srobert } 49*810390e3Srobert 50*810390e3Srobert const T &operator[](uptr idx) const { 51*810390e3Srobert DCHECK_LT(idx, kSize); 52*810390e3Srobert return map_[idx]; 53*810390e3Srobert } 54*810390e3Srobert 55*810390e3Srobert private: 56*810390e3Srobert T map_[kSize]; 57*810390e3Srobert }; 58*810390e3Srobert 59*810390e3Srobert // TwoLevelMap maps integers in range [0, kSize1*kSize2) to values. 60*810390e3Srobert // It is implemented as a two-dimensional array: array of kSize1 pointers 61*810390e3Srobert // to kSize2-byte arrays. The secondary arrays are mmaped on demand. 62*810390e3Srobert // Each value is initially zero and can be set to something else only once. 63*810390e3Srobert // Setting and getting values from multiple threads is safe w/o extra locking. 64*810390e3Srobert template <typename T, u64 kSize1, u64 kSize2, 65*810390e3Srobert typename AddressSpaceViewTy = LocalAddressSpaceView, 66*810390e3Srobert class MapUnmapCallback = NoOpMapUnmapCallback> 67*810390e3Srobert class TwoLevelMap { 68*810390e3Srobert static_assert(IsPowerOfTwo(kSize2), "Use a power of two for performance."); 69*810390e3Srobert 70*810390e3Srobert public: 71*810390e3Srobert using AddressSpaceView = AddressSpaceViewTy; Init()72*810390e3Srobert void Init() { 73*810390e3Srobert mu_.Init(); 74*810390e3Srobert internal_memset(map1_, 0, sizeof(map1_)); 75*810390e3Srobert } 76*810390e3Srobert TestOnlyUnmap()77*810390e3Srobert void TestOnlyUnmap() { 78*810390e3Srobert for (uptr i = 0; i < kSize1; i++) { 79*810390e3Srobert T *p = Get(i); 80*810390e3Srobert if (!p) 81*810390e3Srobert continue; 82*810390e3Srobert MapUnmapCallback().OnUnmap(reinterpret_cast<uptr>(p), MmapSize()); 83*810390e3Srobert UnmapOrDie(p, kSize2); 84*810390e3Srobert } 85*810390e3Srobert Init(); 86*810390e3Srobert } 87*810390e3Srobert MemoryUsage()88*810390e3Srobert uptr MemoryUsage() const { 89*810390e3Srobert uptr res = 0; 90*810390e3Srobert for (uptr i = 0; i < kSize1; i++) { 91*810390e3Srobert T *p = Get(i); 92*810390e3Srobert if (!p) 93*810390e3Srobert continue; 94*810390e3Srobert res += MmapSize(); 95*810390e3Srobert } 96*810390e3Srobert return res; 97*810390e3Srobert } 98*810390e3Srobert size()99*810390e3Srobert constexpr uptr size() const { return kSize1 * kSize2; } size1()100*810390e3Srobert constexpr uptr size1() const { return kSize1; } size2()101*810390e3Srobert constexpr uptr size2() const { return kSize2; } 102*810390e3Srobert contains(uptr idx)103*810390e3Srobert bool contains(uptr idx) const { 104*810390e3Srobert CHECK_LT(idx, kSize1 * kSize2); 105*810390e3Srobert return Get(idx / kSize2); 106*810390e3Srobert } 107*810390e3Srobert 108*810390e3Srobert const T &operator[](uptr idx) const { 109*810390e3Srobert DCHECK_LT(idx, kSize1 * kSize2); 110*810390e3Srobert T *map2 = GetOrCreate(idx / kSize2); 111*810390e3Srobert return *AddressSpaceView::Load(&map2[idx % kSize2]); 112*810390e3Srobert } 113*810390e3Srobert 114*810390e3Srobert T &operator[](uptr idx) { 115*810390e3Srobert DCHECK_LT(idx, kSize1 * kSize2); 116*810390e3Srobert T *map2 = GetOrCreate(idx / kSize2); 117*810390e3Srobert return *AddressSpaceView::LoadWritable(&map2[idx % kSize2]); 118*810390e3Srobert } 119*810390e3Srobert 120*810390e3Srobert private: MmapSize()121*810390e3Srobert constexpr uptr MmapSize() const { 122*810390e3Srobert return RoundUpTo(kSize2 * sizeof(T), GetPageSizeCached()); 123*810390e3Srobert } 124*810390e3Srobert Get(uptr idx)125*810390e3Srobert T *Get(uptr idx) const { 126*810390e3Srobert DCHECK_LT(idx, kSize1); 127*810390e3Srobert return reinterpret_cast<T *>( 128*810390e3Srobert atomic_load(&map1_[idx], memory_order_acquire)); 129*810390e3Srobert } 130*810390e3Srobert GetOrCreate(uptr idx)131*810390e3Srobert T *GetOrCreate(uptr idx) const { 132*810390e3Srobert DCHECK_LT(idx, kSize1); 133*810390e3Srobert // This code needs to use memory_order_acquire/consume, but we use 134*810390e3Srobert // memory_order_relaxed for performance reasons (matters for arm64). We 135*810390e3Srobert // expect memory_order_relaxed to be effectively equivalent to 136*810390e3Srobert // memory_order_consume in this case for all relevant architectures: all 137*810390e3Srobert // dependent data is reachable only by dereferencing the resulting pointer. 138*810390e3Srobert // If relaxed load fails to see stored ptr, the code will fall back to 139*810390e3Srobert // Create() and reload the value again with locked mutex as a memory 140*810390e3Srobert // barrier. 141*810390e3Srobert T *res = reinterpret_cast<T *>(atomic_load_relaxed(&map1_[idx])); 142*810390e3Srobert if (LIKELY(res)) 143*810390e3Srobert return res; 144*810390e3Srobert return Create(idx); 145*810390e3Srobert } 146*810390e3Srobert Create(uptr idx)147*810390e3Srobert NOINLINE T *Create(uptr idx) const { 148*810390e3Srobert SpinMutexLock l(&mu_); 149*810390e3Srobert T *res = Get(idx); 150*810390e3Srobert if (!res) { 151*810390e3Srobert res = reinterpret_cast<T *>(MmapOrDie(MmapSize(), "TwoLevelMap")); 152*810390e3Srobert MapUnmapCallback().OnMap(reinterpret_cast<uptr>(res), kSize2); 153*810390e3Srobert atomic_store(&map1_[idx], reinterpret_cast<uptr>(res), 154*810390e3Srobert memory_order_release); 155*810390e3Srobert } 156*810390e3Srobert return res; 157*810390e3Srobert } 158*810390e3Srobert 159*810390e3Srobert mutable StaticSpinMutex mu_; 160*810390e3Srobert mutable atomic_uintptr_t map1_[kSize1]; 161*810390e3Srobert }; 162*810390e3Srobert 163*810390e3Srobert template <u64 kSize, typename AddressSpaceViewTy = LocalAddressSpaceView> 164*810390e3Srobert using FlatByteMap = FlatMap<u8, kSize, AddressSpaceViewTy>; 165*810390e3Srobert 166*810390e3Srobert template <u64 kSize1, u64 kSize2, 167*810390e3Srobert typename AddressSpaceViewTy = LocalAddressSpaceView, 168*810390e3Srobert class MapUnmapCallback = NoOpMapUnmapCallback> 169*810390e3Srobert using TwoLevelByteMap = 170*810390e3Srobert TwoLevelMap<u8, kSize1, kSize2, AddressSpaceViewTy, MapUnmapCallback>; 171*810390e3Srobert } // namespace __sanitizer 172*810390e3Srobert 173*810390e3Srobert #endif 174