//===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Simple ring buffer. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_RING_BUFFER_H #define SANITIZER_RING_BUFFER_H #include "sanitizer_common.h" namespace __sanitizer { // RingBuffer: fixed-size ring buffer optimized for speed of push(). // T should be a POD type and sizeof(T) should be divisible by sizeof(void*). // At creation, all elements are zero. template class RingBuffer { public: COMPILER_CHECK(sizeof(T) % sizeof(void *) == 0); static RingBuffer *New(uptr Size) { void *Ptr = MmapOrDie(SizeInBytes(Size), "RingBuffer"); RingBuffer *RB = reinterpret_cast(Ptr); uptr End = reinterpret_cast(Ptr) + SizeInBytes(Size); RB->last_ = RB->next_ = reinterpret_cast(End - sizeof(T)); return RB; } void Delete() { UnmapOrDie(this, SizeInBytes(size())); } uptr size() const { return last_ + 1 - reinterpret_cast(reinterpret_cast(this) + 2 * sizeof(T *)); } static uptr SizeInBytes(uptr Size) { return Size * sizeof(T) + 2 * sizeof(T*); } uptr SizeInBytes() { return SizeInBytes(size()); } void push(T t) { *next_ = t; next_--; static_assert((sizeof(T) % sizeof(T *)) == 0, "The condition below works only if sizeof(T) is divisible by " "sizeof(T*)."); if (next_ <= reinterpret_cast(&next_)) next_ = last_; } T operator[](uptr Idx) const { CHECK_LT(Idx, size()); sptr IdxNext = Idx + 1; if (IdxNext > last_ - next_) IdxNext -= size(); return next_[IdxNext]; } private: RingBuffer() {} ~RingBuffer() {} RingBuffer(const RingBuffer&) = delete; // Data layout: // LNDDDDDDDD // D: data elements. // L: last_, always points to the last data element. // N: next_, initially equals to last_, is decremented on every push, // wraps around if it's less or equal than its own address. T *last_; T *next_; T data_[1]; // flexible array. }; // A ring buffer with externally provided storage that encodes its state in 8 // bytes. Has significant constraints on size and alignment of storage. // See a comment in hwasan/hwasan_thread_list.h for the motivation behind this. #if SANITIZER_WORDSIZE == 64 template class CompactRingBuffer { // Top byte of long_ stores the buffer size in pages. // Lower bytes store the address of the next buffer element. static constexpr int kPageSizeBits = 12; static constexpr int kSizeShift = 56; static constexpr int kSizeBits = 64 - kSizeShift; static constexpr uptr kNextMask = (1ULL << kSizeShift) - 1; uptr GetStorageSize() const { return (long_ >> kSizeShift) << kPageSizeBits; } static uptr SignExtend(uptr x) { return ((sptr)x) << kSizeBits >> kSizeBits; } void Init(void *storage, uptr size) { CHECK_EQ(sizeof(CompactRingBuffer), sizeof(void *)); CHECK(IsPowerOfTwo(size)); CHECK_GE(size, 1 << kPageSizeBits); CHECK_LE(size, 128 << kPageSizeBits); CHECK_EQ(size % 4096, 0); CHECK_EQ(size % sizeof(T), 0); uptr st = (uptr)storage; CHECK_EQ(st % (size * 2), 0); CHECK_EQ(st, SignExtend(st & kNextMask)); long_ = (st & kNextMask) | ((size >> kPageSizeBits) << kSizeShift); } void SetNext(const T *next) { long_ = (long_ & ~kNextMask) | ((uptr)next & kNextMask); } public: CompactRingBuffer(void *storage, uptr size) { Init(storage, size); } // A copy constructor of sorts. CompactRingBuffer(const CompactRingBuffer &other, void *storage) { uptr size = other.GetStorageSize(); internal_memcpy(storage, other.StartOfStorage(), size); Init(storage, size); uptr Idx = other.Next() - (const T *)other.StartOfStorage(); SetNext((const T *)storage + Idx); } T *Next() const { return (T *)(SignExtend(long_ & kNextMask)); } void *StartOfStorage() const { return (void *)((uptr)Next() & ~(GetStorageSize() - 1)); } void *EndOfStorage() const { return (void *)((uptr)StartOfStorage() + GetStorageSize()); } uptr size() const { return GetStorageSize() / sizeof(T); } void push(T t) { T *next = Next(); *next = t; next++; next = (T *)((uptr)next & ~GetStorageSize()); SetNext(next); } const T &operator[](uptr Idx) const { CHECK_LT(Idx, size()); const T *Begin = (const T *)StartOfStorage(); sptr StorageIdx = Next() - Begin; StorageIdx -= (sptr)(Idx + 1); if (StorageIdx < 0) StorageIdx += size(); return Begin[StorageIdx]; } public: ~CompactRingBuffer() {} CompactRingBuffer(const CompactRingBuffer &) = delete; uptr long_; }; #endif } // namespace __sanitizer #endif // SANITIZER_RING_BUFFER_H