xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_ring_buffer.h (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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