1 //===-- sanitizer_stack_store.h ---------------------------------*- 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef SANITIZER_STACK_STORE_H 10 #define SANITIZER_STACK_STORE_H 11 12 #include "sanitizer_atomic.h" 13 #include "sanitizer_common.h" 14 #include "sanitizer_internal_defs.h" 15 #include "sanitizer_mutex.h" 16 #include "sanitizer_stacktrace.h" 17 18 namespace __sanitizer { 19 20 class StackStore { 21 static constexpr uptr kBlockSizeFrames = 0x100000; 22 static constexpr uptr kBlockCount = 0x1000; 23 static constexpr uptr kBlockSizeBytes = kBlockSizeFrames * sizeof(uptr); 24 25 public: 26 enum class Compression : u8 { 27 None = 0, 28 Delta, 29 LZW, 30 }; 31 32 constexpr StackStore() = default; 33 34 using Id = u32; // Enough for 2^32 * sizeof(uptr) bytes of traces. 35 static_assert(u64(kBlockCount) * kBlockSizeFrames == 1ull << (sizeof(Id) * 8), 36 ""); 37 38 Id Store(const StackTrace &trace, 39 uptr *pack /* number of blocks completed by this call */); 40 StackTrace Load(Id id); 41 uptr Allocated() const; 42 43 // Packs all blocks which don't expect any more writes. A block is going to be 44 // packed once. As soon trace from that block was requested, it will unpack 45 // and stay unpacked after that. 46 // Returns the number of released bytes. 47 uptr Pack(Compression type); 48 49 void LockAll(); 50 void UnlockAll(); 51 52 void TestOnlyUnmap(); 53 54 private: 55 friend class StackStoreTest; GetBlockIdx(uptr frame_idx)56 static constexpr uptr GetBlockIdx(uptr frame_idx) { 57 return frame_idx / kBlockSizeFrames; 58 } 59 GetInBlockIdx(uptr frame_idx)60 static constexpr uptr GetInBlockIdx(uptr frame_idx) { 61 return frame_idx % kBlockSizeFrames; 62 } 63 IdToOffset(Id id)64 static constexpr uptr IdToOffset(Id id) { 65 CHECK_NE(id, 0); 66 return id - 1; // Avoid zero as id. 67 } 68 OffsetToId(Id id)69 static constexpr uptr OffsetToId(Id id) { 70 // This makes UINT32_MAX to 0 and it will be retrived as and empty stack. 71 // But this is not a problem as we will not be able to store anything after 72 // that anyway. 73 return id + 1; // Avoid zero as id. 74 } 75 76 uptr *Alloc(uptr count, uptr *idx, uptr *pack); 77 78 void *Map(uptr size, const char *mem_type); 79 void Unmap(void *addr, uptr size); 80 81 // Total number of allocated frames. 82 atomic_uintptr_t total_frames_ = {}; 83 84 // Tracks total allocated memory in bytes. 85 atomic_uintptr_t allocated_ = {}; 86 87 // Each block will hold pointer to exactly kBlockSizeFrames. 88 class BlockInfo { 89 atomic_uintptr_t data_; 90 // Counter to track store progress to know when we can Pack() the block. 91 atomic_uint32_t stored_; 92 // Protects alloc of new blocks. 93 mutable StaticSpinMutex mtx_; 94 95 enum class State : u8 { 96 Storing = 0, 97 Packed, 98 Unpacked, 99 }; 100 State state SANITIZER_GUARDED_BY(mtx_); 101 102 uptr *Create(StackStore *store); 103 104 public: 105 uptr *Get() const; 106 uptr *GetOrCreate(StackStore *store); 107 uptr *GetOrUnpack(StackStore *store); 108 uptr Pack(Compression type, StackStore *store); 109 void TestOnlyUnmap(StackStore *store); 110 bool Stored(uptr n); 111 bool IsPacked() const; Lock()112 void Lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mtx_.Lock(); } Unlock()113 void Unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mtx_.Unlock(); } 114 }; 115 116 BlockInfo blocks_[kBlockCount] = {}; 117 }; 118 119 } // namespace __sanitizer 120 121 #endif // SANITIZER_STACK_STORE_H 122