tsan_sync.cc revision 5d71de26cedae3dafc17449fe0182045c0bd20e8
1//===-- tsan_sync.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_placement_new.h"
14#include "tsan_sync.h"
15#include "tsan_rtl.h"
16#include "tsan_mman.h"
17
18namespace __tsan {
19
20void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
21
22SyncVar::SyncVar()
23    : mtx(MutexTypeSyncVar, StatMtxSyncVar) {
24  Reset();
25}
26
27void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
28  this->addr = addr;
29  this->uid = uid;
30  this->next = 0;
31
32  creation_stack_id = 0;
33  if (kCppMode)  // Go does not use them
34    creation_stack_id = CurrentStackId(thr, pc);
35  if (flags()->detect_deadlocks)
36    DDMutexInit(thr, pc, this);
37}
38
39void SyncVar::Reset() {
40  uid = 0;
41  creation_stack_id = 0;
42  owner_tid = kInvalidTid;
43  last_lock = 0;
44  recursion = 0;
45  is_rw = 0;
46  is_recursive = 0;
47  is_broken = 0;
48  is_linker_init = 0;
49
50  clock.Zero();
51  read_clock.Reset();
52}
53
54MetaMap::MetaMap() {
55  atomic_store(&uid_gen_, 0, memory_order_relaxed);
56}
57
58void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
59  u32 idx = block_alloc_.Alloc(&thr->block_cache);
60  MBlock *b = block_alloc_.Map(idx);
61  b->siz = sz;
62  b->tid = thr->tid;
63  b->stk = CurrentStackId(thr, pc);
64  u32 *meta = MemToMeta(p);
65  DCHECK_EQ(*meta, 0);
66  *meta = idx | kFlagBlock;
67}
68
69uptr MetaMap::FreeBlock(ThreadState *thr, uptr pc, uptr p) {
70  MBlock* b = GetBlock(p);
71  if (b == 0)
72    return 0;
73  uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
74  FreeRange(thr, pc, p, sz);
75  return sz;
76}
77
78void MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
79  u32 *meta = MemToMeta(p);
80  u32 *end = MemToMeta(p + sz);
81  if (end == meta)
82    end++;
83  for (; meta < end; meta++) {
84    u32 idx = *meta;
85    *meta = 0;
86    for (;;) {
87      if (idx == 0)
88        break;
89      if (idx & kFlagBlock) {
90        block_alloc_.Free(&thr->block_cache, idx & ~kFlagMask);
91        break;
92      } else if (idx & kFlagSync) {
93        DCHECK(idx & kFlagSync);
94        SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
95        u32 next = s->next;
96        s->Reset();
97        sync_alloc_.Free(&thr->sync_cache, idx & ~kFlagMask);
98        idx = next;
99      } else {
100        CHECK(0);
101      }
102    }
103  }
104}
105
106MBlock* MetaMap::GetBlock(uptr p) {
107  u32 *meta = MemToMeta(p);
108  u32 idx = *meta;
109  for (;;) {
110    if (idx == 0)
111      return 0;
112    if (idx & kFlagBlock)
113      return block_alloc_.Map(idx & ~kFlagMask);
114    DCHECK(idx & kFlagSync);
115    SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
116    idx = s->next;
117  }
118}
119
120SyncVar* MetaMap::GetOrCreateAndLock(ThreadState *thr, uptr pc,
121                              uptr addr, bool write_lock) {
122  return GetAndLock(thr, pc, addr, write_lock, true);
123}
124
125SyncVar* MetaMap::GetIfExistsAndLock(uptr addr) {
126  return GetAndLock(0, 0, addr, true, false);
127}
128
129SyncVar* MetaMap::GetAndLock(ThreadState *thr, uptr pc,
130                             uptr addr, bool write_lock, bool create) {
131  u32 *meta = MemToMeta(addr);
132  u32 idx0 = *meta;
133  u32 myidx = 0;
134  SyncVar *mys = 0;
135  for (;;) {
136    u32 idx = idx0;
137    for (;;) {
138      if (idx == 0)
139        break;
140      if (idx & kFlagBlock)
141        break;
142      DCHECK(idx & kFlagSync);
143      SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
144      if (s->addr == addr) {
145        if (myidx != 0) {
146          mys->Reset();
147          sync_alloc_.Free(&thr->sync_cache, myidx);
148        }
149        if (write_lock)
150          s->mtx.Lock();
151        else
152          s->mtx.ReadLock();
153        return s;
154      }
155      idx = s->next;
156    }
157    if (!create)
158      return 0;
159    if (*meta != idx0) {
160      idx0 = *meta;
161      continue;
162    }
163
164    if (myidx == 0) {
165      const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
166      myidx = sync_alloc_.Alloc(&thr->sync_cache);
167      mys = sync_alloc_.Map(myidx);
168      mys->Init(thr, pc, addr, uid);
169    }
170    mys->next = idx0;
171    if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
172        myidx | kFlagSync, memory_order_release)) {
173      if (write_lock)
174        mys->mtx.Lock();
175      else
176        mys->mtx.ReadLock();
177      return mys;
178    }
179  }
180}
181
182void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
183  // src and dst can overlap,
184  // there are no concurrent accesses to the regions (e.g. stop-the-world).
185  CHECK_NE(src, dst);
186  CHECK_NE(sz, 0);
187  uptr diff = dst - src;
188  u32 *src_meta = MemToMeta(src);
189  u32 *dst_meta = MemToMeta(dst);
190  u32 *src_meta_end = MemToMeta(src + sz);
191  uptr inc = 1;
192  if (dst > src) {
193    src_meta = MemToMeta(src + sz) - 1;
194    dst_meta = MemToMeta(dst + sz) - 1;
195    src_meta_end = MemToMeta(src) - 1;
196    inc = -1;
197  }
198  for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
199    CHECK_EQ(*dst_meta, 0);
200    u32 idx = *src_meta;
201    *src_meta = 0;
202    *dst_meta = idx;
203    // Patch the addresses in sync objects.
204    while (idx != 0) {
205      if (idx & kFlagBlock)
206        break;
207      CHECK(idx & kFlagSync);
208      SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
209      s->addr += diff;
210      idx = s->next;
211    }
212  }
213}
214
215void MetaMap::OnThreadIdle(ThreadState *thr) {
216  block_alloc_.FlushCache(&thr->block_cache);
217  sync_alloc_.FlushCache(&thr->sync_cache);
218}
219
220}  // namespace __tsan
221