1 //===-- xray_segmented_array.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 // This file is a part of XRay, a dynamic runtime instrumentation system.
11 //
12 // Defines the implementation of a segmented array, with fixed-size segments
13 // backing the segments.
14 //
15 //===----------------------------------------------------------------------===//
16 #ifndef XRAY_SEGMENTED_ARRAY_H
17 #define XRAY_SEGMENTED_ARRAY_H
18 
19 #include "sanitizer_common/sanitizer_allocator.h"
20 #include "xray_allocator.h"
21 #include "xray_utils.h"
22 #include <cassert>
23 #include <type_traits>
24 #include <utility>
25 
26 namespace __xray {
27 
28 /// The Array type provides an interface similar to std::vector<...> but does
29 /// not shrink in size. Once constructed, elements can be appended but cannot be
30 /// removed. The implementation is heavily dependent on the contract provided by
31 /// the Allocator type, in that all memory will be released when the Allocator
32 /// is destroyed. When an Array is destroyed, it will destroy elements in the
33 /// backing store but will not free the memory.
34 template <class T> class Array {
35   struct SegmentBase {
36     SegmentBase *Prev;
37     SegmentBase *Next;
38   };
39 
40   // We want each segment of the array to be cache-line aligned, and elements of
41   // the array be offset from the beginning of the segment.
42   struct Segment : SegmentBase {
43     char Data[1];
44   };
45 
46 public:
47   // Each segment of the array will be laid out with the following assumptions:
48   //
49   //   - Each segment will be on a cache-line address boundary (kCacheLineSize
50   //     aligned).
51   //
52   //   - The elements will be accessed through an aligned pointer, dependent on
53   //     the alignment of T.
54   //
55   //   - Each element is at least two-pointers worth from the beginning of the
56   //     Segment, aligned properly, and the rest of the elements are accessed
57   //     through appropriate alignment.
58   //
59   // We then compute the size of the segment to follow this logic:
60   //
61   //   - Compute the number of elements that can fit within
62   //     kCacheLineSize-multiple segments, minus the size of two pointers.
63   //
64   //   - Request cacheline-multiple sized elements from the allocator.
65   static constexpr size_t AlignedElementStorageSize =
66       sizeof(typename std::aligned_storage<sizeof(T), alignof(T)>::type);
67 
68   static constexpr size_t SegmentSize =
69       nearest_boundary(sizeof(Segment) + next_pow2(sizeof(T)), kCacheLineSize);
70 
71   using AllocatorType = Allocator<SegmentSize>;
72 
73   static constexpr size_t ElementsPerSegment =
74       (SegmentSize - sizeof(Segment)) / next_pow2(sizeof(T));
75 
76   static_assert(ElementsPerSegment > 0,
77                 "Must have at least 1 element per segment.");
78 
79   static SegmentBase SentinelSegment;
80 
81   using size_type = size_t;
82 
83 private:
84   AllocatorType *Alloc;
85   SegmentBase *Head = &SentinelSegment;
86   SegmentBase *Tail = &SentinelSegment;
87   size_t Size = 0;
88 
89   // Here we keep track of segments in the freelist, to allow us to re-use
90   // segments when elements are trimmed off the end.
91   SegmentBase *Freelist = &SentinelSegment;
92 
93   Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
94     // We need to handle the case in which enough elements have been trimmed to
95     // allow us to re-use segments we've allocated before. For this we look into
96     // the Freelist, to see whether we need to actually allocate new blocks or
97     // just re-use blocks we've already seen before.
98     if (Freelist != &SentinelSegment) {
99       auto *FreeSegment = Freelist;
100       Freelist = FreeSegment->Next;
101       FreeSegment->Next = &SentinelSegment;
102       Freelist->Prev = &SentinelSegment;
103       return static_cast<Segment *>(FreeSegment);
104     }
105 
106     auto SegmentBlock = Alloc->Allocate();
107     if (SegmentBlock.Data == nullptr)
108       return nullptr;
109 
110     // Placement-new the Segment element at the beginning of the SegmentBlock.
111     auto S = reinterpret_cast<Segment *>(SegmentBlock.Data);
112     new (S) SegmentBase{&SentinelSegment, &SentinelSegment};
113     return S;
114   }
115 
116   Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
117     DCHECK_EQ(Head, &SentinelSegment);
118     DCHECK_EQ(Tail, &SentinelSegment);
119     auto Segment = NewSegment();
120     if (Segment == nullptr)
121       return nullptr;
122     DCHECK_EQ(Segment->Next, &SentinelSegment);
123     DCHECK_EQ(Segment->Prev, &SentinelSegment);
124     Head = Tail = static_cast<SegmentBase *>(Segment);
125     return Segment;
126   }
127 
128   Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
129     auto S = NewSegment();
130     if (S == nullptr)
131       return nullptr;
132     DCHECK_NE(Tail, &SentinelSegment);
133     DCHECK_EQ(Tail->Next, &SentinelSegment);
134     DCHECK_EQ(S->Prev, &SentinelSegment);
135     DCHECK_EQ(S->Next, &SentinelSegment);
136     Tail->Next = S;
137     S->Prev = Tail;
138     Tail = S;
139     return static_cast<Segment *>(Tail);
140   }
141 
142   // This Iterator models a BidirectionalIterator.
143   template <class U> class Iterator {
144     SegmentBase *S = &SentinelSegment;
145     size_t Offset = 0;
146     size_t Size = 0;
147 
148   public:
149     Iterator(SegmentBase *IS, size_t Off, size_t S) XRAY_NEVER_INSTRUMENT
150         : S(IS),
151           Offset(Off),
152           Size(S) {}
153     Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
154     Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
155     Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
156     Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
157     Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
158     ~Iterator() XRAY_NEVER_INSTRUMENT = default;
159 
160     Iterator &operator++() XRAY_NEVER_INSTRUMENT {
161       if (++Offset % ElementsPerSegment || Offset == Size)
162         return *this;
163 
164       // At this point, we know that Offset % N == 0, so we must advance the
165       // segment pointer.
166       DCHECK_EQ(Offset % ElementsPerSegment, 0);
167       DCHECK_NE(Offset, Size);
168       DCHECK_NE(S, &SentinelSegment);
169       DCHECK_NE(S->Next, &SentinelSegment);
170       S = S->Next;
171       DCHECK_NE(S, &SentinelSegment);
172       return *this;
173     }
174 
175     Iterator &operator--() XRAY_NEVER_INSTRUMENT {
176       DCHECK_NE(S, &SentinelSegment);
177       DCHECK_GT(Offset, 0);
178 
179       auto PreviousOffset = Offset--;
180       if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) {
181         DCHECK_NE(S->Prev, &SentinelSegment);
182         S = S->Prev;
183       }
184 
185       return *this;
186     }
187 
188     Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
189       Iterator Copy(*this);
190       ++(*this);
191       return Copy;
192     }
193 
194     Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
195       Iterator Copy(*this);
196       --(*this);
197       return Copy;
198     }
199 
200     template <class V, class W>
201     friend bool operator==(const Iterator<V> &L,
202                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
203       return L.S == R.S && L.Offset == R.Offset;
204     }
205 
206     template <class V, class W>
207     friend bool operator!=(const Iterator<V> &L,
208                            const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
209       return !(L == R);
210     }
211 
212     U &operator*() const XRAY_NEVER_INSTRUMENT {
213       DCHECK_NE(S, &SentinelSegment);
214       auto RelOff = Offset % ElementsPerSegment;
215 
216       // We need to compute the character-aligned pointer, offset from the
217       // segment's Data location to get the element in the position of Offset.
218       auto Base = static_cast<Segment *>(S)->Data;
219       auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
220       return *reinterpret_cast<U *>(AlignedOffset);
221     }
222 
223     U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
224   };
225 
226 public:
227   explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT : Alloc(&A) {}
228 
229   Array(const Array &) = delete;
230   Array(Array &&O) NOEXCEPT : Alloc(O.Alloc),
231                               Head(O.Head),
232                               Tail(O.Tail),
233                               Size(O.Size) {
234     O.Head = &SentinelSegment;
235     O.Tail = &SentinelSegment;
236     O.Size = 0;
237   }
238 
239   bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
240 
241   AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
242     DCHECK_NE(Alloc, nullptr);
243     return *Alloc;
244   }
245 
246   size_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
247 
248   T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
249     if (UNLIKELY(Head == &SentinelSegment))
250       if (InitHeadAndTail() == nullptr)
251         return nullptr;
252 
253     auto Offset = Size % ElementsPerSegment;
254     if (UNLIKELY(Size != 0 && Offset == 0))
255       if (AppendNewSegment() == nullptr)
256         return nullptr;
257 
258     auto Base = static_cast<Segment *>(Tail)->Data;
259     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
260     auto Position = reinterpret_cast<T *>(AlignedOffset);
261     *Position = E;
262     ++Size;
263     return Position;
264   }
265 
266   template <class... Args>
267   T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
268     if (UNLIKELY(Head == &SentinelSegment))
269       if (InitHeadAndTail() == nullptr)
270         return nullptr;
271 
272     auto Offset = Size % ElementsPerSegment;
273     auto *LatestSegment = Tail;
274     if (UNLIKELY(Size != 0 && Offset == 0)) {
275       LatestSegment = AppendNewSegment();
276       if (LatestSegment == nullptr)
277         return nullptr;
278     }
279 
280     DCHECK_NE(Tail, &SentinelSegment);
281     auto Base = static_cast<Segment *>(LatestSegment)->Data;
282     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
283     auto Position = reinterpret_cast<T *>(AlignedOffset);
284 
285     // In-place construct at Position.
286     new (Position) T{std::forward<Args>(args)...};
287     ++Size;
288     return reinterpret_cast<T *>(Position);
289   }
290 
291   T &operator[](size_t Offset) const XRAY_NEVER_INSTRUMENT {
292     DCHECK_LE(Offset, Size);
293     // We need to traverse the array enough times to find the element at Offset.
294     auto S = Head;
295     while (Offset >= ElementsPerSegment) {
296       S = S->Next;
297       Offset -= ElementsPerSegment;
298       DCHECK_NE(S, &SentinelSegment);
299     }
300     auto Base = static_cast<Segment *>(S)->Data;
301     auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
302     auto Position = reinterpret_cast<T *>(AlignedOffset);
303     return *reinterpret_cast<T *>(Position);
304   }
305 
306   T &front() const XRAY_NEVER_INSTRUMENT {
307     DCHECK_NE(Head, &SentinelSegment);
308     DCHECK_NE(Size, 0u);
309     return *begin();
310   }
311 
312   T &back() const XRAY_NEVER_INSTRUMENT {
313     DCHECK_NE(Tail, &SentinelSegment);
314     DCHECK_NE(Size, 0u);
315     auto It = end();
316     --It;
317     return *It;
318   }
319 
320   template <class Predicate>
321   T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
322     if (empty())
323       return nullptr;
324 
325     auto E = end();
326     for (auto I = begin(); I != E; ++I)
327       if (P(*I))
328         return &(*I);
329 
330     return nullptr;
331   }
332 
333   /// Remove N Elements from the end. This leaves the blocks behind, and not
334   /// require allocation of new blocks for new elements added after trimming.
335   void trim(size_t Elements) XRAY_NEVER_INSTRUMENT {
336     if (Elements == 0)
337       return;
338 
339     auto OldSize = Size;
340     Elements = Elements >= Size ? Size : Elements;
341     Size -= Elements;
342 
343     DCHECK_NE(Head, &SentinelSegment);
344     DCHECK_NE(Tail, &SentinelSegment);
345 
346     for (auto SegmentsToTrim = (nearest_boundary(OldSize, ElementsPerSegment) -
347                                 nearest_boundary(Size, ElementsPerSegment)) /
348                                ElementsPerSegment;
349          SegmentsToTrim > 0; --SegmentsToTrim) {
350 
351       // We want to short-circuit if the trace is already empty.
352       if (Head == &SentinelSegment && Head == Tail)
353         return;
354 
355       // Put the tail into the Freelist.
356       auto *FreeSegment = Tail;
357       Tail = Tail->Prev;
358       if (Tail == &SentinelSegment)
359         Head = Tail;
360       else
361         Tail->Next = &SentinelSegment;
362 
363       DCHECK_EQ(Tail->Next, &SentinelSegment);
364       FreeSegment->Next = Freelist;
365       FreeSegment->Prev = &SentinelSegment;
366       if (Freelist != &SentinelSegment)
367         Freelist->Prev = FreeSegment;
368       Freelist = FreeSegment;
369     }
370   }
371 
372   // Provide iterators.
373   Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
374     return Iterator<T>(Head, 0, Size);
375   }
376   Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
377     return Iterator<T>(Tail, Size, Size);
378   }
379   Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
380     return Iterator<const T>(Head, 0, Size);
381   }
382   Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
383     return Iterator<const T>(Tail, Size, Size);
384   }
385 };
386 
387 // We need to have this storage definition out-of-line so that the compiler can
388 // ensure that storage for the SentinelSegment is defined and has a single
389 // address.
390 template <class T>
391 typename Array<T>::SegmentBase Array<T>::SentinelSegment{
392     &Array<T>::SentinelSegment, &Array<T>::SentinelSegment};
393 
394 } // namespace __xray
395 
396 #endif // XRAY_SEGMENTED_ARRAY_H
397