tsan_mman.cc revision bd33d3ab168fcbcfe09a3fd6a971994b481e89d0
1//===-- tsan_mman.cc ------------------------------------------------------===//
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 ThreadSanitizer (TSan), a race detector.
11//
12//===----------------------------------------------------------------------===//
13#include "sanitizer_common/sanitizer_common.h"
14#include "sanitizer_common/sanitizer_placement_new.h"
15#include "tsan_mman.h"
16#include "tsan_rtl.h"
17#include "tsan_report.h"
18#include "tsan_flags.h"
19
20// May be overriden by front-end.
21extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
22  (void)ptr;
23  (void)size;
24}
25
26extern "C" void WEAK __tsan_free_hook(void *ptr) {
27  (void)ptr;
28}
29
30namespace __tsan {
31
32COMPILER_CHECK(sizeof(MBlock) == 16);
33
34void MBlock::Lock() {
35  atomic_uintptr_t *a = reinterpret_cast<atomic_uintptr_t*>(this);
36  uptr v = atomic_load(a, memory_order_relaxed);
37  for (int iter = 0;; iter++) {
38    if (v & 1) {
39      if (iter < 10)
40        proc_yield(20);
41      else
42        internal_sched_yield();
43      v = atomic_load(a, memory_order_relaxed);
44      continue;
45    }
46    if (atomic_compare_exchange_weak(a, &v, v | 1, memory_order_acquire))
47      break;
48  }
49}
50
51void MBlock::Unlock() {
52  atomic_uintptr_t *a = reinterpret_cast<atomic_uintptr_t*>(this);
53  uptr v = atomic_load(a, memory_order_relaxed);
54  DCHECK(v & 1);
55  atomic_store(a, v & ~1, memory_order_relaxed);
56}
57
58struct MapUnmapCallback {
59  void OnMap(uptr p, uptr size) const { }
60  void OnUnmap(uptr p, uptr size) const {
61    // We are about to unmap a chunk of user memory.
62    // Mark the corresponding shadow memory as not needed.
63    DontNeedShadowFor(p, size);
64  }
65};
66
67static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
68Allocator *allocator() {
69  return reinterpret_cast<Allocator*>(&allocator_placeholder);
70}
71
72void InitializeAllocator() {
73  allocator()->Init();
74}
75
76void AllocatorThreadStart(ThreadState *thr) {
77  allocator()->InitCache(&thr->alloc_cache);
78  internal_allocator()->InitCache(&thr->internal_alloc_cache);
79}
80
81void AllocatorThreadFinish(ThreadState *thr) {
82  allocator()->DestroyCache(&thr->alloc_cache);
83  internal_allocator()->DestroyCache(&thr->internal_alloc_cache);
84}
85
86void AllocatorPrintStats() {
87  allocator()->PrintStats();
88}
89
90static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
91  if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
92    return;
93  Context *ctx = CTX();
94  StackTrace stack;
95  stack.ObtainCurrent(thr, pc);
96  ThreadRegistryLock l(ctx->thread_registry);
97  ScopedReport rep(ReportTypeSignalUnsafe);
98  if (!IsFiredSuppression(ctx, rep, stack)) {
99    rep.AddStack(&stack);
100    OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
101  }
102}
103
104void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
105  CHECK_GT(thr->in_rtl, 0);
106  if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
107    return AllocatorReturnNull();
108  void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
109  if (p == 0)
110    return 0;
111  MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
112  b->Init(sz, thr->tid, CurrentStackId(thr, pc));
113  if (CTX() && CTX()->initialized)
114    MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
115  DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
116  SignalUnsafeCall(thr, pc);
117  return p;
118}
119
120void user_free(ThreadState *thr, uptr pc, void *p) {
121  CHECK_GT(thr->in_rtl, 0);
122  CHECK_NE(p, (void*)0);
123  DPrintf("#%d: free(%p)\n", thr->tid, p);
124  MBlock *b = (MBlock*)allocator()->GetMetaData(p);
125  if (b->ListHead()) {
126    MBlock::ScopedLock l(b);
127    for (SyncVar *s = b->ListHead(); s;) {
128      SyncVar *res = s;
129      s = s->next;
130      StatInc(thr, StatSyncDestroyed);
131      res->mtx.Lock();
132      res->mtx.Unlock();
133      DestroyAndFree(res);
134    }
135    b->ListReset();
136  }
137  if (CTX() && CTX()->initialized && thr->in_rtl == 1)
138    MemoryRangeFreed(thr, pc, (uptr)p, b->Size());
139  allocator()->Deallocate(&thr->alloc_cache, p);
140  SignalUnsafeCall(thr, pc);
141}
142
143void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
144  CHECK_GT(thr->in_rtl, 0);
145  void *p2 = 0;
146  // FIXME: Handle "shrinking" more efficiently,
147  // it seems that some software actually does this.
148  if (sz) {
149    p2 = user_alloc(thr, pc, sz);
150    if (p2 == 0)
151      return 0;
152    if (p) {
153      MBlock *b = user_mblock(thr, p);
154      CHECK_NE(b, 0);
155      internal_memcpy(p2, p, min(b->Size(), sz));
156    }
157  }
158  if (p)
159    user_free(thr, pc, p);
160  return p2;
161}
162
163uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) {
164  CHECK_GT(thr->in_rtl, 0);
165  if (p == 0)
166    return 0;
167  MBlock *b = (MBlock*)allocator()->GetMetaData(p);
168  return b ? b->Size() : 0;
169}
170
171MBlock *user_mblock(ThreadState *thr, void *p) {
172  CHECK_NE(p, 0);
173  Allocator *a = allocator();
174  void *b = a->GetBlockBegin(p);
175  if (b == 0)
176    return 0;
177  return (MBlock*)a->GetMetaData(b);
178}
179
180void invoke_malloc_hook(void *ptr, uptr size) {
181  Context *ctx = CTX();
182  ThreadState *thr = cur_thread();
183  if (ctx == 0 || !ctx->initialized || thr->in_rtl)
184    return;
185  __tsan_malloc_hook(ptr, size);
186}
187
188void invoke_free_hook(void *ptr) {
189  Context *ctx = CTX();
190  ThreadState *thr = cur_thread();
191  if (ctx == 0 || !ctx->initialized || thr->in_rtl)
192    return;
193  __tsan_free_hook(ptr);
194}
195
196void *internal_alloc(MBlockType typ, uptr sz) {
197  ThreadState *thr = cur_thread();
198  CHECK_GT(thr->in_rtl, 0);
199  CHECK_LE(sz, InternalSizeClassMap::kMaxSize);
200  if (thr->nomalloc) {
201    thr->nomalloc = 0;  // CHECK calls internal_malloc().
202    CHECK(0);
203  }
204  return InternalAlloc(sz, &thr->internal_alloc_cache);
205}
206
207void internal_free(void *p) {
208  ThreadState *thr = cur_thread();
209  CHECK_GT(thr->in_rtl, 0);
210  if (thr->nomalloc) {
211    thr->nomalloc = 0;  // CHECK calls internal_malloc().
212    CHECK(0);
213  }
214  InternalFree(p, &thr->internal_alloc_cache);
215}
216
217}  // namespace __tsan
218
219using namespace __tsan;
220
221extern "C" {
222uptr __tsan_get_current_allocated_bytes() {
223  u64 stats[AllocatorStatCount];
224  allocator()->GetStats(stats);
225  u64 m = stats[AllocatorStatMalloced];
226  u64 f = stats[AllocatorStatFreed];
227  return m >= f ? m - f : 1;
228}
229
230uptr __tsan_get_heap_size() {
231  u64 stats[AllocatorStatCount];
232  allocator()->GetStats(stats);
233  u64 m = stats[AllocatorStatMmapped];
234  u64 f = stats[AllocatorStatUnmapped];
235  return m >= f ? m - f : 1;
236}
237
238uptr __tsan_get_free_bytes() {
239  return 1;
240}
241
242uptr __tsan_get_unmapped_bytes() {
243  return 1;
244}
245
246uptr __tsan_get_estimated_allocated_size(uptr size) {
247  return size;
248}
249
250bool __tsan_get_ownership(void *p) {
251  return allocator()->GetBlockBegin(p) != 0;
252}
253
254uptr __tsan_get_allocated_size(void *p) {
255  if (p == 0)
256    return 0;
257  p = allocator()->GetBlockBegin(p);
258  if (p == 0)
259    return 0;
260  MBlock *b = (MBlock*)allocator()->GetMetaData(p);
261  return b->Size();
262}
263
264void __tsan_on_thread_idle() {
265  ThreadState *thr = cur_thread();
266  allocator()->SwallowCache(&thr->alloc_cache);
267  internal_allocator()->SwallowCache(&thr->internal_alloc_cache);
268}
269}  // extern "C"
270