1 //===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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.
15 //===----------------------------------------------------------------------===//
17 #ifndef SANITIZER_QUARANTINE_H
18 #define SANITIZER_QUARANTINE_H
20 #include "sanitizer_internal_defs.h"
21 #include "sanitizer_mutex.h"
22 #include "sanitizer_list.h"
24 namespace __sanitizer {
26 template<typename Node> class QuarantineCache;
28 struct QuarantineBatch {
29 static const uptr kSize = 1021;
30 QuarantineBatch *next;
36 COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13)); // 8Kb.
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>
45 typedef QuarantineCache<Callback> Cache;
47 explicit Quarantine(LinkerInitialized)
48 : cache_(LINKER_INITIALIZED) {
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;
58 uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
59 uptr GetCacheSize() const { return max_cache_size_; }
61 void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
62 c->Enqueue(cb, ptr, size);
63 if (c->Size() > max_cache_size_)
67 void NOINLINE Drain(Cache *c, Callback cb) {
69 SpinMutexLock l(&cache_mutex_);
72 if (cache_.Size() > GetSize() && recycle_mutex_.TryLock())
76 void PrintStats() const {
77 // It assumes that the world is stopped, just as the allocator's PrintStats.
83 char pad0_[kCacheLineSize];
84 atomic_uintptr_t max_size_;
85 atomic_uintptr_t min_size_;
87 char pad1_[kCacheLineSize];
88 SpinMutex cache_mutex_;
89 SpinMutex recycle_mutex_;
91 char pad2_[kCacheLineSize];
93 void NOINLINE Recycle(Callback cb) {
95 uptr min_size = atomic_load(&min_size_, memory_order_acquire);
97 SpinMutexLock l(&cache_mutex_);
98 while (cache_.Size() > min_size) {
99 QuarantineBatch *b = cache_.DequeueBatch();
103 recycle_mutex_.Unlock();
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]);
123 // Per-thread cache of memory blocks.
124 template<typename Callback>
125 class QuarantineCache {
127 explicit QuarantineCache(LinkerInitialized) {
136 return atomic_load(&size_, memory_order_relaxed);
139 void Enqueue(Callback cb, void *ptr, uptr size) {
140 if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) {
142 size += sizeof(QuarantineBatch); // Count the batch in Quarantine size.
144 QuarantineBatch *b = list_.back();
146 b->batch[b->count++] = ptr;
151 void Transfer(QuarantineCache *c) {
152 list_.append_back(&c->list_);
154 atomic_store(&c->size_, 0, memory_order_relaxed);
157 void EnqueueBatch(QuarantineBatch *b) {
162 QuarantineBatch *DequeueBatch() {
165 QuarantineBatch *b = list_.front();
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) {
177 total_quarantine_bytes += (*it).size;
178 total_quarantine_chunks += (*it).count;
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);
187 typedef IntrusiveList<QuarantineBatch> List;
190 atomic_uintptr_t size_;
192 void SizeAdd(uptr add) {
193 atomic_store(&size_, Size() + add, memory_order_relaxed);
195 void SizeSub(uptr sub) {
196 atomic_store(&size_, Size() - sub, memory_order_relaxed);
199 NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
200 QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
208 } // namespace __sanitizer
210 #endif // SANITIZER_QUARANTINE_H