Home | History | Annotate | Download | only in rtl
      1 //===-- tsan_rtl_mutex.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 
     14 #include "tsan_rtl.h"
     15 #include "tsan_flags.h"
     16 #include "tsan_sync.h"
     17 #include "tsan_report.h"
     18 #include "tsan_symbolize.h"
     19 #include "tsan_platform.h"
     20 
     21 namespace __tsan {
     22 
     23 void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
     24                  bool rw, bool recursive, bool linker_init) {
     25   Context *ctx = CTX();
     26   CHECK_GT(thr->in_rtl, 0);
     27   DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
     28   StatInc(thr, StatMutexCreate);
     29   if (!linker_init && IsAppMem(addr)) {
     30     CHECK(!thr->is_freeing);
     31     thr->is_freeing = true;
     32     MemoryWrite(thr, pc, addr, kSizeLog1);
     33     thr->is_freeing = false;
     34   }
     35   SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
     36   s->is_rw = rw;
     37   s->is_recursive = recursive;
     38   s->is_linker_init = linker_init;
     39   s->mtx.Unlock();
     40 }
     41 
     42 void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
     43   Context *ctx = CTX();
     44   CHECK_GT(thr->in_rtl, 0);
     45   DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
     46   StatInc(thr, StatMutexDestroy);
     47 #ifndef TSAN_GO
     48   // Global mutexes not marked as LINKER_INITIALIZED
     49   // cause tons of not interesting reports, so just ignore it.
     50   if (IsGlobalVar(addr))
     51     return;
     52 #endif
     53   SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
     54   if (s == 0)
     55     return;
     56   if (IsAppMem(addr)) {
     57     CHECK(!thr->is_freeing);
     58     thr->is_freeing = true;
     59     MemoryWrite(thr, pc, addr, kSizeLog1);
     60     thr->is_freeing = false;
     61   }
     62   if (flags()->report_destroy_locked
     63       && s->owner_tid != SyncVar::kInvalidTid
     64       && !s->is_broken) {
     65     s->is_broken = true;
     66     ThreadRegistryLock l(ctx->thread_registry);
     67     ScopedReport rep(ReportTypeMutexDestroyLocked);
     68     rep.AddMutex(s);
     69     StackTrace trace;
     70     trace.ObtainCurrent(thr, pc);
     71     rep.AddStack(&trace);
     72     FastState last(s->last_lock);
     73     RestoreStack(last.tid(), last.epoch(), &trace, 0);
     74     rep.AddStack(&trace);
     75     rep.AddLocation(s->addr, 1);
     76     OutputReport(ctx, rep);
     77   }
     78   thr->mset.Remove(s->GetId());
     79   DestroyAndFree(s);
     80 }
     81 
     82 void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
     83   CHECK_GT(thr->in_rtl, 0);
     84   DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
     85   if (IsAppMem(addr))
     86     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
     87   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
     88   thr->fast_state.IncrementEpoch();
     89   TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
     90   if (s->owner_tid == SyncVar::kInvalidTid) {
     91     CHECK_EQ(s->recursion, 0);
     92     s->owner_tid = thr->tid;
     93     s->last_lock = thr->fast_state.raw();
     94   } else if (s->owner_tid == thr->tid) {
     95     CHECK_GT(s->recursion, 0);
     96   } else {
     97     Printf("ThreadSanitizer WARNING: double lock\n");
     98     PrintCurrentStack(thr, pc);
     99   }
    100   if (s->recursion == 0) {
    101     StatInc(thr, StatMutexLock);
    102     thr->clock.set(thr->tid, thr->fast_state.epoch());
    103     thr->clock.acquire(&s->clock);
    104     StatInc(thr, StatSyncAcquire);
    105     thr->clock.acquire(&s->read_clock);
    106     StatInc(thr, StatSyncAcquire);
    107   } else if (!s->is_recursive) {
    108     StatInc(thr, StatMutexRecLock);
    109   }
    110   s->recursion++;
    111   thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
    112   s->mtx.Unlock();
    113 }
    114 
    115 void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
    116   CHECK_GT(thr->in_rtl, 0);
    117   DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
    118   if (IsAppMem(addr))
    119     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
    120   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
    121   thr->fast_state.IncrementEpoch();
    122   TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
    123   if (s->recursion == 0) {
    124     if (!s->is_broken) {
    125       s->is_broken = true;
    126       Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
    127       PrintCurrentStack(thr, pc);
    128     }
    129   } else if (s->owner_tid != thr->tid) {
    130     if (!s->is_broken) {
    131       s->is_broken = true;
    132       Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
    133       PrintCurrentStack(thr, pc);
    134     }
    135   } else {
    136     s->recursion--;
    137     if (s->recursion == 0) {
    138       StatInc(thr, StatMutexUnlock);
    139       s->owner_tid = SyncVar::kInvalidTid;
    140       thr->clock.set(thr->tid, thr->fast_state.epoch());
    141       thr->fast_synch_epoch = thr->fast_state.epoch();
    142       thr->clock.ReleaseStore(&s->clock);
    143       StatInc(thr, StatSyncRelease);
    144     } else {
    145       StatInc(thr, StatMutexRecUnlock);
    146     }
    147   }
    148   thr->mset.Del(s->GetId(), true);
    149   s->mtx.Unlock();
    150 }
    151 
    152 void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
    153   CHECK_GT(thr->in_rtl, 0);
    154   DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
    155   StatInc(thr, StatMutexReadLock);
    156   if (IsAppMem(addr))
    157     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
    158   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
    159   thr->fast_state.IncrementEpoch();
    160   TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
    161   if (s->owner_tid != SyncVar::kInvalidTid) {
    162     Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
    163     PrintCurrentStack(thr, pc);
    164   }
    165   thr->clock.set(thr->tid, thr->fast_state.epoch());
    166   thr->clock.acquire(&s->clock);
    167   s->last_lock = thr->fast_state.raw();
    168   StatInc(thr, StatSyncAcquire);
    169   thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
    170   s->mtx.ReadUnlock();
    171 }
    172 
    173 void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
    174   CHECK_GT(thr->in_rtl, 0);
    175   DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
    176   StatInc(thr, StatMutexReadUnlock);
    177   if (IsAppMem(addr))
    178     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
    179   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
    180   thr->fast_state.IncrementEpoch();
    181   TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
    182   if (s->owner_tid != SyncVar::kInvalidTid) {
    183     Printf("ThreadSanitizer WARNING: read unlock of a write "
    184                "locked mutex\n");
    185     PrintCurrentStack(thr, pc);
    186   }
    187   thr->clock.set(thr->tid, thr->fast_state.epoch());
    188   thr->fast_synch_epoch = thr->fast_state.epoch();
    189   thr->clock.release(&s->read_clock);
    190   StatInc(thr, StatSyncRelease);
    191   s->mtx.Unlock();
    192   thr->mset.Del(s->GetId(), false);
    193 }
    194 
    195 void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
    196   CHECK_GT(thr->in_rtl, 0);
    197   DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
    198   if (IsAppMem(addr))
    199     MemoryReadAtomic(thr, pc, addr, kSizeLog1);
    200   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
    201   bool write = true;
    202   if (s->owner_tid == SyncVar::kInvalidTid) {
    203     // Seems to be read unlock.
    204     write = false;
    205     StatInc(thr, StatMutexReadUnlock);
    206     thr->fast_state.IncrementEpoch();
    207     TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
    208     thr->clock.set(thr->tid, thr->fast_state.epoch());
    209     thr->fast_synch_epoch = thr->fast_state.epoch();
    210     thr->clock.release(&s->read_clock);
    211     StatInc(thr, StatSyncRelease);
    212   } else if (s->owner_tid == thr->tid) {
    213     // Seems to be write unlock.
    214     thr->fast_state.IncrementEpoch();
    215     TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
    216     CHECK_GT(s->recursion, 0);
    217     s->recursion--;
    218     if (s->recursion == 0) {
    219       StatInc(thr, StatMutexUnlock);
    220       s->owner_tid = SyncVar::kInvalidTid;
    221       // FIXME: Refactor me, plz.
    222       // The sequence of events is quite tricky and doubled in several places.
    223       // First, it's a bug to increment the epoch w/o writing to the trace.
    224       // Then, the acquire/release logic can be factored out as well.
    225       thr->clock.set(thr->tid, thr->fast_state.epoch());
    226       thr->fast_synch_epoch = thr->fast_state.epoch();
    227       thr->clock.ReleaseStore(&s->clock);
    228       StatInc(thr, StatSyncRelease);
    229     } else {
    230       StatInc(thr, StatMutexRecUnlock);
    231     }
    232   } else if (!s->is_broken) {
    233     s->is_broken = true;
    234     Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
    235     PrintCurrentStack(thr, pc);
    236   }
    237   thr->mset.Del(s->GetId(), write);
    238   s->mtx.Unlock();
    239 }
    240 
    241 void Acquire(ThreadState *thr, uptr pc, uptr addr) {
    242   CHECK_GT(thr->in_rtl, 0);
    243   DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
    244   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
    245   thr->clock.set(thr->tid, thr->fast_state.epoch());
    246   thr->clock.acquire(&s->clock);
    247   StatInc(thr, StatSyncAcquire);
    248   s->mtx.ReadUnlock();
    249 }
    250 
    251 static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) {
    252   ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
    253   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
    254   if (tctx->status == ThreadStatusRunning)
    255     thr->clock.set(tctx->tid, tctx->thr->fast_state.epoch());
    256   else
    257     thr->clock.set(tctx->tid, tctx->epoch1);
    258 }
    259 
    260 void AcquireGlobal(ThreadState *thr, uptr pc) {
    261   ThreadRegistryLock l(CTX()->thread_registry);
    262   CTX()->thread_registry->RunCallbackForEachThreadLocked(
    263       UpdateClockCallback, thr);
    264 }
    265 
    266 void Release(ThreadState *thr, uptr pc, uptr addr) {
    267   CHECK_GT(thr->in_rtl, 0);
    268   DPrintf("#%d: Release %zx\n", thr->tid, addr);
    269   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
    270   thr->clock.set(thr->tid, thr->fast_state.epoch());
    271   thr->clock.release(&s->clock);
    272   StatInc(thr, StatSyncRelease);
    273   s->mtx.Unlock();
    274 }
    275 
    276 void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
    277   CHECK_GT(thr->in_rtl, 0);
    278   DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
    279   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
    280   thr->clock.set(thr->tid, thr->fast_state.epoch());
    281   thr->clock.ReleaseStore(&s->clock);
    282   StatInc(thr, StatSyncRelease);
    283   s->mtx.Unlock();
    284 }
    285 
    286 #ifndef TSAN_GO
    287 static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
    288   ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
    289   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
    290   if (tctx->status == ThreadStatusRunning)
    291     thr->last_sleep_clock.set(tctx->tid, tctx->thr->fast_state.epoch());
    292   else
    293     thr->last_sleep_clock.set(tctx->tid, tctx->epoch1);
    294 }
    295 
    296 void AfterSleep(ThreadState *thr, uptr pc) {
    297   thr->last_sleep_stack_id = CurrentStackId(thr, pc);
    298   ThreadRegistryLock l(CTX()->thread_registry);
    299   CTX()->thread_registry->RunCallbackForEachThreadLocked(
    300       UpdateSleepClockCallback, thr);
    301 }
    302 #endif
    303 
    304 }  // namespace __tsan
    305