blob: d15f27fd4a832311e6340d1aece6f39429d3f8b7 [file] [log] [blame]
Kostya Serebryanyd709a362018-08-29 21:00:01 +00001//===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Simple ring buffer.
11//
12//===----------------------------------------------------------------------===//
13#ifndef SANITIZER_RING_BUFFER_H
14#define SANITIZER_RING_BUFFER_H
15
16#include "sanitizer_common.h"
17
18namespace __sanitizer {
19// RingBuffer<T>: fixed-size ring buffer optimized for speed of push().
20// T should be a POD type and sizeof(T) should be divisible by sizeof(void*).
21// At creation, all elements are zero.
22template<class T>
23class RingBuffer {
24 public:
25 COMPILER_CHECK(sizeof(T) % sizeof(void *) == 0);
26 static RingBuffer *New(uptr Size) {
27 void *Ptr = MmapOrDie(SizeInBytes(Size), "RingBuffer");
28 RingBuffer *RB = reinterpret_cast<RingBuffer*>(Ptr);
29 uptr End = reinterpret_cast<uptr>(Ptr) + SizeInBytes(Size);
30 RB->last_ = RB->next_ = reinterpret_cast<T*>(End - sizeof(T));
31 return RB;
32 }
33 void Delete() {
34 UnmapOrDie(this, SizeInBytes(size()));
35 }
36 uptr size() const {
37 return last_ + 1 -
38 reinterpret_cast<T *>(reinterpret_cast<uptr>(this) +
39 2 * sizeof(T *));
40 }
Kostya Serebryany68902c72018-09-05 23:52:31 +000041
Kostya Serebryany950a1a32018-09-06 22:08:41 +000042 static uptr SizeInBytes(uptr Size) {
43 return Size * sizeof(T) + 2 * sizeof(T*);
44 }
45
Kostya Serebryany68902c72018-09-05 23:52:31 +000046 uptr SizeInBytes() { return SizeInBytes(size()); }
47
Kostya Serebryanyd709a362018-08-29 21:00:01 +000048 void push(T t) {
49 *next_ = t;
50 next_--;
51 // The condition below works only if sizeof(T) is divisible by sizeof(T*).
52 if (next_ <= reinterpret_cast<T*>(&next_))
53 next_ = last_;
54 }
55
56 T operator[](uptr Idx) const {
57 CHECK_LT(Idx, size());
58 sptr IdxNext = Idx + 1;
59 if (IdxNext > last_ - next_)
60 IdxNext -= size();
61 return next_[IdxNext];
62 }
63
64 private:
65 RingBuffer() {}
66 ~RingBuffer() {}
67 RingBuffer(const RingBuffer&) = delete;
68
Kostya Serebryanyd709a362018-08-29 21:00:01 +000069 // Data layout:
70 // LNDDDDDDDD
71 // D: data elements.
72 // L: last_, always points to the last data element.
73 // N: next_, initially equals to last_, is decremented on every push,
74 // wraps around if it's less or equal than its own address.
Kostya Serebryanyd709a362018-08-29 21:00:01 +000075 T *last_;
76 T *next_;
77 T data_[1]; // flexible array.
78};
79
Evgeniy Stepanov090f0f92018-09-24 23:03:34 +000080// A ring buffer with externally provided storage that encodes its state in 8
81// bytes. Has significant constraints on size and alignment of storage.
82// See a comment in hwasan/hwasan_thread_list.h for the motivation behind this.
83#if SANITIZER_WORDSIZE == 64
84template <class T>
85class CompactRingBuffer {
86 // Top byte of long_ stores the buffer size in pages.
87 // Lower bytes store the address of the next buffer element.
88 static constexpr int kPageSizeBits = 12;
89 static constexpr int kSizeShift = 56;
90 static constexpr uptr kNextMask = (1ULL << kSizeShift) - 1;
91
92 uptr GetStorageSize() const { return (long_ >> kSizeShift) << kPageSizeBits; }
93
94 void Init(void *storage, uptr size) {
95 CHECK_EQ(sizeof(CompactRingBuffer<T>), sizeof(void *));
96 CHECK(IsPowerOfTwo(size));
97 CHECK_GE(size, 1 << kPageSizeBits);
98 CHECK_LE(size, 128 << kPageSizeBits);
99 CHECK_EQ(size % 4096, 0);
100 CHECK_EQ(size % sizeof(T), 0);
101 CHECK_EQ((uptr)storage % (size * 2), 0);
102 long_ = (uptr)storage | ((size >> kPageSizeBits) << kSizeShift);
103 }
104
105 void SetNext(const T *next) {
106 long_ = (long_ & ~kNextMask) | (uptr)next;
107 }
108
109 public:
110 CompactRingBuffer(void *storage, uptr size) {
111 Init(storage, size);
112 }
113
114 // A copy constructor of sorts.
115 CompactRingBuffer(const CompactRingBuffer &other, void *storage) {
116 uptr size = other.GetStorageSize();
117 internal_memcpy(storage, other.StartOfStorage(), size);
118 Init(storage, size);
119 uptr Idx = other.Next() - (const T *)other.StartOfStorage();
120 SetNext((const T *)storage + Idx);
121 }
122
123 T *Next() const { return (T *)(long_ & kNextMask); }
124
125 void *StartOfStorage() const {
126 return (void *)((uptr)Next() & ~(GetStorageSize() - 1));
127 }
128
129 void *EndOfStorage() const {
130 return (void *)((uptr)StartOfStorage() + GetStorageSize());
131 }
132
133 uptr size() const { return GetStorageSize() / sizeof(T); }
134
135 void push(T t) {
136 T *next = Next();
137 *next = t;
138 next++;
139 next = (T *)((uptr)next & ~GetStorageSize());
140 SetNext(next);
141 }
142
143 T operator[](uptr Idx) const {
144 CHECK_LT(Idx, size());
145 const T *Begin = (const T *)StartOfStorage();
146 sptr StorageIdx = Next() - Begin;
147 StorageIdx -= (sptr)(Idx + 1);
148 if (StorageIdx < 0)
149 StorageIdx += size();
150 return Begin[StorageIdx];
151 }
152
153 public:
154 ~CompactRingBuffer() {}
155 CompactRingBuffer(const CompactRingBuffer &) = delete;
156
157 uptr long_;
158};
159#endif
Kostya Serebryanyd709a362018-08-29 21:00:01 +0000160} // namespace __sanitizer
161
162#endif // SANITIZER_RING_BUFFER_H