1349cc55cSDimitry Andric //===-- sanitizer_stack_store.h ---------------------------------*- C++ -*-===//
2349cc55cSDimitry Andric //
3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6349cc55cSDimitry Andric //
7349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
8349cc55cSDimitry Andric 
9349cc55cSDimitry Andric #ifndef SANITIZER_STACK_STORE_H
10349cc55cSDimitry Andric #define SANITIZER_STACK_STORE_H
11349cc55cSDimitry Andric 
12349cc55cSDimitry Andric #include "sanitizer_atomic.h"
134824e7fdSDimitry Andric #include "sanitizer_common.h"
14349cc55cSDimitry Andric #include "sanitizer_internal_defs.h"
15349cc55cSDimitry Andric #include "sanitizer_mutex.h"
16349cc55cSDimitry Andric #include "sanitizer_stacktrace.h"
17349cc55cSDimitry Andric 
18349cc55cSDimitry Andric namespace __sanitizer {
19349cc55cSDimitry Andric 
20349cc55cSDimitry Andric class StackStore {
214824e7fdSDimitry Andric   static constexpr uptr kBlockSizeFrames = 0x100000;
224824e7fdSDimitry Andric   static constexpr uptr kBlockCount = 0x1000;
234824e7fdSDimitry Andric   static constexpr uptr kBlockSizeBytes = kBlockSizeFrames * sizeof(uptr);
244824e7fdSDimitry Andric 
25349cc55cSDimitry Andric  public:
264824e7fdSDimitry Andric   enum class Compression : u8 {
274824e7fdSDimitry Andric     None = 0,
280eae32dcSDimitry Andric     Delta,
290eae32dcSDimitry Andric     LZW,
304824e7fdSDimitry Andric   };
314824e7fdSDimitry Andric 
32349cc55cSDimitry Andric   constexpr StackStore() = default;
33349cc55cSDimitry Andric 
344824e7fdSDimitry Andric   using Id = u32;  // Enough for 2^32 * sizeof(uptr) bytes of traces.
354824e7fdSDimitry Andric   static_assert(u64(kBlockCount) * kBlockSizeFrames == 1ull << (sizeof(Id) * 8),
364824e7fdSDimitry Andric                 "");
37349cc55cSDimitry Andric 
384824e7fdSDimitry Andric   Id Store(const StackTrace &trace,
394824e7fdSDimitry Andric            uptr *pack /* number of blocks completed by this call */);
40349cc55cSDimitry Andric   StackTrace Load(Id id);
414824e7fdSDimitry Andric   uptr Allocated() const;
424824e7fdSDimitry Andric 
434824e7fdSDimitry Andric   // Packs all blocks which don't expect any more writes. A block is going to be
444824e7fdSDimitry Andric   // packed once. As soon trace from that block was requested, it will unpack
454824e7fdSDimitry Andric   // and stay unpacked after that.
464824e7fdSDimitry Andric   // Returns the number of released bytes.
474824e7fdSDimitry Andric   uptr Pack(Compression type);
48349cc55cSDimitry Andric 
490eae32dcSDimitry Andric   void LockAll();
500eae32dcSDimitry Andric   void UnlockAll();
510eae32dcSDimitry Andric 
52349cc55cSDimitry Andric   void TestOnlyUnmap();
53349cc55cSDimitry Andric 
54349cc55cSDimitry Andric  private:
554824e7fdSDimitry Andric   friend class StackStoreTest;
GetBlockIdx(uptr frame_idx)564824e7fdSDimitry Andric   static constexpr uptr GetBlockIdx(uptr frame_idx) {
574824e7fdSDimitry Andric     return frame_idx / kBlockSizeFrames;
584824e7fdSDimitry Andric   }
59349cc55cSDimitry Andric 
GetInBlockIdx(uptr frame_idx)604824e7fdSDimitry Andric   static constexpr uptr GetInBlockIdx(uptr frame_idx) {
614824e7fdSDimitry Andric     return frame_idx % kBlockSizeFrames;
624824e7fdSDimitry Andric   }
634824e7fdSDimitry Andric 
IdToOffset(Id id)644824e7fdSDimitry Andric   static constexpr uptr IdToOffset(Id id) {
654824e7fdSDimitry Andric     CHECK_NE(id, 0);
664824e7fdSDimitry Andric     return id - 1;  // Avoid zero as id.
674824e7fdSDimitry Andric   }
684824e7fdSDimitry Andric 
OffsetToId(Id id)694824e7fdSDimitry Andric   static constexpr uptr OffsetToId(Id id) {
704824e7fdSDimitry Andric     // This makes UINT32_MAX to 0 and it will be retrived as and empty stack.
714824e7fdSDimitry Andric     // But this is not a problem as we will not be able to store anything after
724824e7fdSDimitry Andric     // that anyway.
734824e7fdSDimitry Andric     return id + 1;  // Avoid zero as id.
744824e7fdSDimitry Andric   }
754824e7fdSDimitry Andric 
764824e7fdSDimitry Andric   uptr *Alloc(uptr count, uptr *idx, uptr *pack);
774824e7fdSDimitry Andric 
780eae32dcSDimitry Andric   void *Map(uptr size, const char *mem_type);
790eae32dcSDimitry Andric   void Unmap(void *addr, uptr size);
800eae32dcSDimitry Andric 
814824e7fdSDimitry Andric   // Total number of allocated frames.
824824e7fdSDimitry Andric   atomic_uintptr_t total_frames_ = {};
834824e7fdSDimitry Andric 
840eae32dcSDimitry Andric   // Tracks total allocated memory in bytes.
850eae32dcSDimitry Andric   atomic_uintptr_t allocated_ = {};
860eae32dcSDimitry Andric 
874824e7fdSDimitry Andric   // Each block will hold pointer to exactly kBlockSizeFrames.
884824e7fdSDimitry Andric   class BlockInfo {
894824e7fdSDimitry Andric     atomic_uintptr_t data_;
904824e7fdSDimitry Andric     // Counter to track store progress to know when we can Pack() the block.
914824e7fdSDimitry Andric     atomic_uint32_t stored_;
924824e7fdSDimitry Andric     // Protects alloc of new blocks.
934824e7fdSDimitry Andric     mutable StaticSpinMutex mtx_;
944824e7fdSDimitry Andric 
954824e7fdSDimitry Andric     enum class State : u8 {
964824e7fdSDimitry Andric       Storing = 0,
974824e7fdSDimitry Andric       Packed,
984824e7fdSDimitry Andric       Unpacked,
99349cc55cSDimitry Andric     };
100*04eeddc0SDimitry Andric     State state SANITIZER_GUARDED_BY(mtx_);
1014824e7fdSDimitry Andric 
1020eae32dcSDimitry Andric     uptr *Create(StackStore *store);
1034824e7fdSDimitry Andric 
1044824e7fdSDimitry Andric    public:
1054824e7fdSDimitry Andric     uptr *Get() const;
1060eae32dcSDimitry Andric     uptr *GetOrCreate(StackStore *store);
1070eae32dcSDimitry Andric     uptr *GetOrUnpack(StackStore *store);
1080eae32dcSDimitry Andric     uptr Pack(Compression type, StackStore *store);
1090eae32dcSDimitry Andric     void TestOnlyUnmap(StackStore *store);
1104824e7fdSDimitry Andric     bool Stored(uptr n);
1114824e7fdSDimitry Andric     bool IsPacked() const;
Lock()112*04eeddc0SDimitry Andric     void Lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mtx_.Lock(); }
Unlock()113*04eeddc0SDimitry Andric     void Unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mtx_.Unlock(); }
1144824e7fdSDimitry Andric   };
1154824e7fdSDimitry Andric 
1164824e7fdSDimitry Andric   BlockInfo blocks_[kBlockCount] = {};
117349cc55cSDimitry Andric };
118349cc55cSDimitry Andric 
119349cc55cSDimitry Andric }  // namespace __sanitizer
120349cc55cSDimitry Andric 
121349cc55cSDimitry Andric #endif  // SANITIZER_STACK_STORE_H
122