1 //===-- sanitizer_stackdepot.cc -------------------------------------------===//
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 // This file is shared between AddressSanitizer and ThreadSanitizer
11 // run-time libraries.
12 //===----------------------------------------------------------------------===//
14 #include "sanitizer_stackdepot.h"
15 #include "sanitizer_common.h"
16 #include "sanitizer_internal_defs.h"
17 #include "sanitizer_mutex.h"
18 #include "sanitizer_atomic.h"
20 namespace __sanitizer {
22 const int kTabSize = 1024 * 1024; // Hash table size.
23 const int kPartBits = 8;
24 const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;
25 const int kPartCount = 1 << kPartBits; // Number of subparts in the table.
26 const int kPartSize = kTabSize / kPartCount;
27 const int kMaxId = 1 << kPartShift;
34 uptr stack[1]; // [size]
38 StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
39 atomic_uintptr_t region_pos; // Region allocator for StackDesc's.
40 atomic_uintptr_t region_end;
41 atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's.
42 atomic_uint32_t seq[kPartCount]; // Unique id generators.
45 static StackDepotStats stats;
47 StackDepotStats *StackDepotGetStats() {
51 static u32 hash(const uptr *stack, uptr size) {
53 const u32 m = 0x5bd1e995;
54 const u32 seed = 0x9747b28c;
56 u32 h = seed ^ (size * sizeof(uptr));
57 for (uptr i = 0; i < size; i++) {
71 static StackDesc *tryallocDesc(uptr memsz) {
72 // Optimisic lock-free allocation, essentially try to bump the region ptr.
74 uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);
75 uptr end = atomic_load(&depot.region_end, memory_order_acquire);
76 if (cmp == 0 || cmp + memsz > end)
78 if (atomic_compare_exchange_weak(
79 &depot.region_pos, &cmp, cmp + memsz,
80 memory_order_acquire))
81 return (StackDesc*)cmp;
85 static StackDesc *allocDesc(uptr size) {
86 // First, try to allocate optimisitically.
87 uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
88 StackDesc *s = tryallocDesc(memsz);
91 // If failed, lock, retry and alloc new superblock.
92 SpinMutexLock l(&depot.mtx);
94 s = tryallocDesc(memsz);
97 atomic_store(&depot.region_pos, 0, memory_order_relaxed);
98 uptr allocsz = 64 * 1024;
101 uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
102 stats.mapped += allocsz;
103 atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
104 atomic_store(&depot.region_pos, mem, memory_order_release);
108 static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {
109 // Searches linked list s for the stack, returns its id.
110 for (; s; s = s->link) {
111 if (s->hash == hash && s->size == size) {
113 for (; i < size; i++) {
114 if (stack[i] != s->stack[i])
124 static StackDesc *lock(atomic_uintptr_t *p) {
125 // Uses the pointer lsb as mutex.
126 for (int i = 0;; i++) {
127 uptr cmp = atomic_load(p, memory_order_relaxed);
129 && atomic_compare_exchange_weak(p, &cmp, cmp | 1,
130 memory_order_acquire))
131 return (StackDesc*)cmp;
135 internal_sched_yield();
139 static void unlock(atomic_uintptr_t *p, StackDesc *s) {
140 DCHECK_EQ((uptr)s & 1, 0);
141 atomic_store(p, (uptr)s, memory_order_release);
144 u32 StackDepotPut(const uptr *stack, uptr size) {
145 if (stack == 0 || size == 0)
147 uptr h = hash(stack, size);
148 atomic_uintptr_t *p = &depot.tab[h % kTabSize];
149 uptr v = atomic_load(p, memory_order_consume);
150 StackDesc *s = (StackDesc*)(v & ~1);
151 // First, try to find the existing stack.
152 u32 id = find(s, stack, size, h);
155 // If failed, lock, retry and insert new.
156 StackDesc *s2 = lock(p);
158 id = find(s2, stack, size, h);
164 uptr part = (h % kTabSize) / kPartSize;
165 id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
167 CHECK_LT(id, kMaxId);
168 id |= part << kPartShift;
170 CHECK_EQ(id & (1u << 31), 0);
175 internal_memcpy(s->stack, stack, size * sizeof(uptr));
181 const uptr *StackDepotGet(u32 id, uptr *size) {
184 CHECK_EQ(id & (1u << 31), 0);
185 // High kPartBits contain part id, so we need to scan at most kPartSize lists.
186 uptr part = id >> kPartShift;
187 for (int i = 0; i != kPartSize; i++) {
188 uptr idx = part * kPartSize + i;
189 CHECK_LT(idx, kTabSize);
190 atomic_uintptr_t *p = &depot.tab[idx];
191 uptr v = atomic_load(p, memory_order_consume);
192 StackDesc *s = (StackDesc*)(v & ~1);
193 for (; s; s = s->link) {
204 bool StackDepotReverseMap::IdDescPair::IdComparator(
205 const StackDepotReverseMap::IdDescPair &a,
206 const StackDepotReverseMap::IdDescPair &b) {
210 StackDepotReverseMap::StackDepotReverseMap()
211 : map_(StackDepotGetStats()->n_uniq_ids + 100) {
212 for (int idx = 0; idx < kTabSize; idx++) {
213 atomic_uintptr_t *p = &depot.tab[idx];
214 uptr v = atomic_load(p, memory_order_consume);
215 StackDesc *s = (StackDesc*)(v & ~1);
216 for (; s; s = s->link) {
217 IdDescPair pair = {s->id, s};
218 map_.push_back(pair);
221 InternalSort(&map_, map_.size(), IdDescPair::IdComparator);
224 const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
225 if (!map_.size()) return 0;
226 IdDescPair pair = {id, 0};
227 uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,
228 IdDescPair::IdComparator);
229 if (idx > map_.size()) {
233 StackDesc *desc = map_[idx].desc;
238 } // namespace __sanitizer