10b57cec5SDimitry Andric //===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Simple ring buffer. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric #ifndef SANITIZER_RING_BUFFER_H 130b57cec5SDimitry Andric #define SANITIZER_RING_BUFFER_H 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "sanitizer_common.h" 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric namespace __sanitizer { 180b57cec5SDimitry Andric // RingBuffer<T>: fixed-size ring buffer optimized for speed of push(). 190b57cec5SDimitry Andric // T should be a POD type and sizeof(T) should be divisible by sizeof(void*). 200b57cec5SDimitry Andric // At creation, all elements are zero. 210b57cec5SDimitry Andric template<class T> 220b57cec5SDimitry Andric class RingBuffer { 230b57cec5SDimitry Andric public: 240b57cec5SDimitry Andric COMPILER_CHECK(sizeof(T) % sizeof(void *) == 0); 250b57cec5SDimitry Andric static RingBuffer *New(uptr Size) { 260b57cec5SDimitry Andric void *Ptr = MmapOrDie(SizeInBytes(Size), "RingBuffer"); 270b57cec5SDimitry Andric RingBuffer *RB = reinterpret_cast<RingBuffer*>(Ptr); 280b57cec5SDimitry Andric uptr End = reinterpret_cast<uptr>(Ptr) + SizeInBytes(Size); 290b57cec5SDimitry Andric RB->last_ = RB->next_ = reinterpret_cast<T*>(End - sizeof(T)); 300b57cec5SDimitry Andric return RB; 310b57cec5SDimitry Andric } 320b57cec5SDimitry Andric void Delete() { 330b57cec5SDimitry Andric UnmapOrDie(this, SizeInBytes(size())); 340b57cec5SDimitry Andric } 350b57cec5SDimitry Andric uptr size() const { 360b57cec5SDimitry Andric return last_ + 1 - 370b57cec5SDimitry Andric reinterpret_cast<T *>(reinterpret_cast<uptr>(this) + 380b57cec5SDimitry Andric 2 * sizeof(T *)); 390b57cec5SDimitry Andric } 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric static uptr SizeInBytes(uptr Size) { 420b57cec5SDimitry Andric return Size * sizeof(T) + 2 * sizeof(T*); 430b57cec5SDimitry Andric } 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric uptr SizeInBytes() { return SizeInBytes(size()); } 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric void push(T t) { 480b57cec5SDimitry Andric *next_ = t; 490b57cec5SDimitry Andric next_--; 50*5f757f3fSDimitry Andric static_assert((sizeof(T) % sizeof(T *)) == 0, 51*5f757f3fSDimitry Andric "The condition below works only if sizeof(T) is divisible by " 52*5f757f3fSDimitry Andric "sizeof(T*)."); 530b57cec5SDimitry Andric if (next_ <= reinterpret_cast<T*>(&next_)) 540b57cec5SDimitry Andric next_ = last_; 550b57cec5SDimitry Andric } 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric T operator[](uptr Idx) const { 580b57cec5SDimitry Andric CHECK_LT(Idx, size()); 590b57cec5SDimitry Andric sptr IdxNext = Idx + 1; 600b57cec5SDimitry Andric if (IdxNext > last_ - next_) 610b57cec5SDimitry Andric IdxNext -= size(); 620b57cec5SDimitry Andric return next_[IdxNext]; 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric private: 660b57cec5SDimitry Andric RingBuffer() {} 670b57cec5SDimitry Andric ~RingBuffer() {} 680b57cec5SDimitry Andric RingBuffer(const RingBuffer&) = delete; 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric // Data layout: 710b57cec5SDimitry Andric // LNDDDDDDDD 720b57cec5SDimitry Andric // D: data elements. 730b57cec5SDimitry Andric // L: last_, always points to the last data element. 740b57cec5SDimitry Andric // N: next_, initially equals to last_, is decremented on every push, 750b57cec5SDimitry Andric // wraps around if it's less or equal than its own address. 760b57cec5SDimitry Andric T *last_; 770b57cec5SDimitry Andric T *next_; 780b57cec5SDimitry Andric T data_[1]; // flexible array. 790b57cec5SDimitry Andric }; 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric // A ring buffer with externally provided storage that encodes its state in 8 820b57cec5SDimitry Andric // bytes. Has significant constraints on size and alignment of storage. 830b57cec5SDimitry Andric // See a comment in hwasan/hwasan_thread_list.h for the motivation behind this. 840b57cec5SDimitry Andric #if SANITIZER_WORDSIZE == 64 850b57cec5SDimitry Andric template <class T> 860b57cec5SDimitry Andric class CompactRingBuffer { 870b57cec5SDimitry Andric // Top byte of long_ stores the buffer size in pages. 880b57cec5SDimitry Andric // Lower bytes store the address of the next buffer element. 890b57cec5SDimitry Andric static constexpr int kPageSizeBits = 12; 900b57cec5SDimitry Andric static constexpr int kSizeShift = 56; 9181ad6265SDimitry Andric static constexpr int kSizeBits = 64 - kSizeShift; 920b57cec5SDimitry Andric static constexpr uptr kNextMask = (1ULL << kSizeShift) - 1; 930b57cec5SDimitry Andric 940b57cec5SDimitry Andric uptr GetStorageSize() const { return (long_ >> kSizeShift) << kPageSizeBits; } 950b57cec5SDimitry Andric 9681ad6265SDimitry Andric static uptr SignExtend(uptr x) { return ((sptr)x) << kSizeBits >> kSizeBits; } 9781ad6265SDimitry Andric 980b57cec5SDimitry Andric void Init(void *storage, uptr size) { 990b57cec5SDimitry Andric CHECK_EQ(sizeof(CompactRingBuffer<T>), sizeof(void *)); 1000b57cec5SDimitry Andric CHECK(IsPowerOfTwo(size)); 1010b57cec5SDimitry Andric CHECK_GE(size, 1 << kPageSizeBits); 1020b57cec5SDimitry Andric CHECK_LE(size, 128 << kPageSizeBits); 1030b57cec5SDimitry Andric CHECK_EQ(size % 4096, 0); 1040b57cec5SDimitry Andric CHECK_EQ(size % sizeof(T), 0); 10581ad6265SDimitry Andric uptr st = (uptr)storage; 10681ad6265SDimitry Andric CHECK_EQ(st % (size * 2), 0); 10781ad6265SDimitry Andric CHECK_EQ(st, SignExtend(st & kNextMask)); 10881ad6265SDimitry Andric long_ = (st & kNextMask) | ((size >> kPageSizeBits) << kSizeShift); 1090b57cec5SDimitry Andric } 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric void SetNext(const T *next) { 11281ad6265SDimitry Andric long_ = (long_ & ~kNextMask) | ((uptr)next & kNextMask); 1130b57cec5SDimitry Andric } 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric public: 1160b57cec5SDimitry Andric CompactRingBuffer(void *storage, uptr size) { 1170b57cec5SDimitry Andric Init(storage, size); 1180b57cec5SDimitry Andric } 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric // A copy constructor of sorts. 1210b57cec5SDimitry Andric CompactRingBuffer(const CompactRingBuffer &other, void *storage) { 1220b57cec5SDimitry Andric uptr size = other.GetStorageSize(); 1230b57cec5SDimitry Andric internal_memcpy(storage, other.StartOfStorage(), size); 1240b57cec5SDimitry Andric Init(storage, size); 1250b57cec5SDimitry Andric uptr Idx = other.Next() - (const T *)other.StartOfStorage(); 1260b57cec5SDimitry Andric SetNext((const T *)storage + Idx); 1270b57cec5SDimitry Andric } 1280b57cec5SDimitry Andric 12981ad6265SDimitry Andric T *Next() const { return (T *)(SignExtend(long_ & kNextMask)); } 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric void *StartOfStorage() const { 1320b57cec5SDimitry Andric return (void *)((uptr)Next() & ~(GetStorageSize() - 1)); 1330b57cec5SDimitry Andric } 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric void *EndOfStorage() const { 1360b57cec5SDimitry Andric return (void *)((uptr)StartOfStorage() + GetStorageSize()); 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric uptr size() const { return GetStorageSize() / sizeof(T); } 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric void push(T t) { 1420b57cec5SDimitry Andric T *next = Next(); 1430b57cec5SDimitry Andric *next = t; 1440b57cec5SDimitry Andric next++; 1450b57cec5SDimitry Andric next = (T *)((uptr)next & ~GetStorageSize()); 1460b57cec5SDimitry Andric SetNext(next); 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric const T &operator[](uptr Idx) const { 1500b57cec5SDimitry Andric CHECK_LT(Idx, size()); 1510b57cec5SDimitry Andric const T *Begin = (const T *)StartOfStorage(); 1520b57cec5SDimitry Andric sptr StorageIdx = Next() - Begin; 1530b57cec5SDimitry Andric StorageIdx -= (sptr)(Idx + 1); 1540b57cec5SDimitry Andric if (StorageIdx < 0) 1550b57cec5SDimitry Andric StorageIdx += size(); 1560b57cec5SDimitry Andric return Begin[StorageIdx]; 1570b57cec5SDimitry Andric } 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric public: 1600b57cec5SDimitry Andric ~CompactRingBuffer() {} 1610b57cec5SDimitry Andric CompactRingBuffer(const CompactRingBuffer &) = delete; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric uptr long_; 1640b57cec5SDimitry Andric }; 1650b57cec5SDimitry Andric #endif 1660b57cec5SDimitry Andric } // namespace __sanitizer 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric #endif // SANITIZER_RING_BUFFER_H 169