]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h
Merge compiler-rt r291274.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_quarantine.h
1 //===-- sanitizer_quarantine.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 // Memory quarantine for AddressSanitizer and potentially other tools.
11 // Quarantine caches some specified amount of memory in per-thread caches,
12 // then evicts to global FIFO queue. When the queue reaches specified threshold,
13 // oldest memory is recycled.
14 //
15 //===----------------------------------------------------------------------===//
16
17 #ifndef SANITIZER_QUARANTINE_H
18 #define SANITIZER_QUARANTINE_H
19
20 #include "sanitizer_internal_defs.h"
21 #include "sanitizer_mutex.h"
22 #include "sanitizer_list.h"
23
24 namespace __sanitizer {
25
26 template<typename Node> class QuarantineCache;
27
28 struct QuarantineBatch {
29   static const uptr kSize = 1021;
30   QuarantineBatch *next;
31   uptr size;
32   uptr count;
33   void *batch[kSize];
34 };
35
36 COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13));  // 8Kb.
37
38 // The callback interface is:
39 // void Callback::Recycle(Node *ptr);
40 // void *cb.Allocate(uptr size);
41 // void cb.Deallocate(void *ptr);
42 template<typename Callback, typename Node>
43 class Quarantine {
44  public:
45   typedef QuarantineCache<Callback> Cache;
46
47   explicit Quarantine(LinkerInitialized)
48       : cache_(LINKER_INITIALIZED) {
49   }
50
51   void Init(uptr size, uptr cache_size) {
52     atomic_store(&max_size_, size, memory_order_release);
53     atomic_store(&min_size_, size / 10 * 9,
54                  memory_order_release); // 90% of max size.
55     max_cache_size_ = cache_size;
56   }
57
58   uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
59   uptr GetCacheSize() const { return max_cache_size_; }
60
61   void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
62     c->Enqueue(cb, ptr, size);
63     if (c->Size() > max_cache_size_)
64       Drain(c, cb);
65   }
66
67   void NOINLINE Drain(Cache *c, Callback cb) {
68     {
69       SpinMutexLock l(&cache_mutex_);
70       cache_.Transfer(c);
71     }
72     if (cache_.Size() > GetSize() && recycle_mutex_.TryLock())
73       Recycle(cb);
74   }
75
76   void PrintStats() const {
77     // It assumes that the world is stopped, just as the allocator's PrintStats.
78     cache_.PrintStats();
79   }
80
81  private:
82   // Read-only data.
83   char pad0_[kCacheLineSize];
84   atomic_uintptr_t max_size_;
85   atomic_uintptr_t min_size_;
86   uptr max_cache_size_;
87   char pad1_[kCacheLineSize];
88   SpinMutex cache_mutex_;
89   SpinMutex recycle_mutex_;
90   Cache cache_;
91   char pad2_[kCacheLineSize];
92
93   void NOINLINE Recycle(Callback cb) {
94     Cache tmp;
95     uptr min_size = atomic_load(&min_size_, memory_order_acquire);
96     {
97       SpinMutexLock l(&cache_mutex_);
98       while (cache_.Size() > min_size) {
99         QuarantineBatch *b = cache_.DequeueBatch();
100         tmp.EnqueueBatch(b);
101       }
102     }
103     recycle_mutex_.Unlock();
104     DoRecycle(&tmp, cb);
105   }
106
107   void NOINLINE DoRecycle(Cache *c, Callback cb) {
108     while (QuarantineBatch *b = c->DequeueBatch()) {
109       const uptr kPrefetch = 16;
110       CHECK(kPrefetch <= ARRAY_SIZE(b->batch));
111       for (uptr i = 0; i < kPrefetch; i++)
112         PREFETCH(b->batch[i]);
113       for (uptr i = 0, count = b->count; i < count; i++) {
114         if (i + kPrefetch < count)
115           PREFETCH(b->batch[i + kPrefetch]);
116         cb.Recycle((Node*)b->batch[i]);
117       }
118       cb.Deallocate(b);
119     }
120   }
121 };
122
123 // Per-thread cache of memory blocks.
124 template<typename Callback>
125 class QuarantineCache {
126  public:
127   explicit QuarantineCache(LinkerInitialized) {
128   }
129
130   QuarantineCache()
131       : size_() {
132     list_.clear();
133   }
134
135   uptr Size() const {
136     return atomic_load(&size_, memory_order_relaxed);
137   }
138
139   void Enqueue(Callback cb, void *ptr, uptr size) {
140     if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) {
141       AllocBatch(cb);
142       size += sizeof(QuarantineBatch);  // Count the batch in Quarantine size.
143     }
144     QuarantineBatch *b = list_.back();
145     CHECK(b);
146     b->batch[b->count++] = ptr;
147     b->size += size;
148     SizeAdd(size);
149   }
150
151   void Transfer(QuarantineCache *c) {
152     list_.append_back(&c->list_);
153     SizeAdd(c->Size());
154     atomic_store(&c->size_, 0, memory_order_relaxed);
155   }
156
157   void EnqueueBatch(QuarantineBatch *b) {
158     list_.push_back(b);
159     SizeAdd(b->size);
160   }
161
162   QuarantineBatch *DequeueBatch() {
163     if (list_.empty())
164       return nullptr;
165     QuarantineBatch *b = list_.front();
166     list_.pop_front();
167     SizeSub(b->size);
168     return b;
169   }
170
171   void PrintStats() const {
172     uptr batch_count = 0;
173     uptr total_quarantine_bytes = 0;
174     uptr total_quarantine_chunks = 0;
175     for (List::ConstIterator it = list_.begin(); it != list_.end(); ++it) {
176       batch_count++;
177       total_quarantine_bytes += (*it).size;
178       total_quarantine_chunks += (*it).count;
179     }
180     Printf("Global quarantine stats: batches: %zd; bytes: %zd; chunks: %zd "
181            "(capacity: %zd chunks)\n",
182            batch_count, total_quarantine_bytes, total_quarantine_chunks,
183            batch_count * QuarantineBatch::kSize);
184   }
185
186  private:
187   typedef IntrusiveList<QuarantineBatch> List;
188
189   List list_;
190   atomic_uintptr_t size_;
191
192   void SizeAdd(uptr add) {
193     atomic_store(&size_, Size() + add, memory_order_relaxed);
194   }
195   void SizeSub(uptr sub) {
196     atomic_store(&size_, Size() - sub, memory_order_relaxed);
197   }
198
199   NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
200     QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
201     CHECK(b);
202     b->count = 0;
203     b->size = 0;
204     list_.push_back(b);
205     return b;
206   }
207 };
208 } // namespace __sanitizer
209
210 #endif // SANITIZER_QUARANTINE_H