159 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- dd_rtl.cpp --------------------------------------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "dd_rtl.h"
 | |
| #include "sanitizer_common/sanitizer_common.h"
 | |
| #include "sanitizer_common/sanitizer_placement_new.h"
 | |
| #include "sanitizer_common/sanitizer_flags.h"
 | |
| #include "sanitizer_common/sanitizer_flag_parser.h"
 | |
| #include "sanitizer_common/sanitizer_stacktrace.h"
 | |
| #include "sanitizer_common/sanitizer_stackdepot.h"
 | |
| 
 | |
| namespace __dsan {
 | |
| 
 | |
| static Context *ctx;
 | |
| 
 | |
| static u32 CurrentStackTrace(Thread *thr, uptr skip) {
 | |
|   BufferedStackTrace stack;
 | |
|   thr->ignore_interceptors = true;
 | |
|   stack.Unwind(1000, 0, 0, 0, 0, 0, false);
 | |
|   thr->ignore_interceptors = false;
 | |
|   if (stack.size <= skip)
 | |
|     return 0;
 | |
|   return StackDepotPut(StackTrace(stack.trace + skip, stack.size - skip));
 | |
| }
 | |
| 
 | |
| static void PrintStackTrace(Thread *thr, u32 stk) {
 | |
|   StackTrace stack = StackDepotGet(stk);
 | |
|   thr->ignore_interceptors = true;
 | |
|   stack.Print();
 | |
|   thr->ignore_interceptors = false;
 | |
| }
 | |
| 
 | |
| static void ReportDeadlock(Thread *thr, DDReport *rep) {
 | |
|   if (rep == 0)
 | |
|     return;
 | |
|   BlockingMutexLock lock(&ctx->report_mutex);
 | |
|   Printf("==============================\n");
 | |
|   Printf("WARNING: lock-order-inversion (potential deadlock)\n");
 | |
|   for (int i = 0; i < rep->n; i++) {
 | |
|     Printf("Thread %d locks mutex %llu while holding mutex %llu:\n",
 | |
|       rep->loop[i].thr_ctx, rep->loop[i].mtx_ctx1, rep->loop[i].mtx_ctx0);
 | |
|     PrintStackTrace(thr, rep->loop[i].stk[1]);
 | |
|     if (rep->loop[i].stk[0]) {
 | |
|       Printf("Mutex %llu was acquired here:\n",
 | |
|         rep->loop[i].mtx_ctx0);
 | |
|       PrintStackTrace(thr, rep->loop[i].stk[0]);
 | |
|     }
 | |
|   }
 | |
|   Printf("==============================\n");
 | |
| }
 | |
| 
 | |
| Callback::Callback(Thread *thr)
 | |
|     : thr(thr) {
 | |
|   lt = thr->dd_lt;
 | |
|   pt = thr->dd_pt;
 | |
| }
 | |
| 
 | |
| u32 Callback::Unwind() {
 | |
|   return CurrentStackTrace(thr, 3);
 | |
| }
 | |
| 
 | |
| static void InitializeFlags() {
 | |
|   Flags *f = flags();
 | |
| 
 | |
|   // Default values.
 | |
|   f->second_deadlock_stack = false;
 | |
| 
 | |
|   SetCommonFlagsDefaults();
 | |
|   {
 | |
|     // Override some common flags defaults.
 | |
|     CommonFlags cf;
 | |
|     cf.CopyFrom(*common_flags());
 | |
|     cf.allow_addr2line = true;
 | |
|     OverrideCommonFlags(cf);
 | |
|   }
 | |
| 
 | |
|   // Override from command line.
 | |
|   FlagParser parser;
 | |
|   RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack);
 | |
|   RegisterCommonFlags(&parser);
 | |
|   parser.ParseStringFromEnv("DSAN_OPTIONS");
 | |
|   SetVerbosity(common_flags()->verbosity);
 | |
| }
 | |
| 
 | |
| void Initialize() {
 | |
|   static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];
 | |
|   ctx = new(ctx_mem) Context();
 | |
| 
 | |
|   InitializeInterceptors();
 | |
|   InitializeFlags();
 | |
|   ctx->dd = DDetector::Create(flags());
 | |
| }
 | |
| 
 | |
| void ThreadInit(Thread *thr) {
 | |
|   static atomic_uintptr_t id_gen;
 | |
|   uptr id = atomic_fetch_add(&id_gen, 1, memory_order_relaxed);
 | |
|   thr->dd_pt = ctx->dd->CreatePhysicalThread();
 | |
|   thr->dd_lt = ctx->dd->CreateLogicalThread(id);
 | |
| }
 | |
| 
 | |
| void ThreadDestroy(Thread *thr) {
 | |
|   ctx->dd->DestroyPhysicalThread(thr->dd_pt);
 | |
|   ctx->dd->DestroyLogicalThread(thr->dd_lt);
 | |
| }
 | |
| 
 | |
| void MutexBeforeLock(Thread *thr, uptr m, bool writelock) {
 | |
|   if (thr->ignore_interceptors)
 | |
|     return;
 | |
|   Callback cb(thr);
 | |
|   {
 | |
|     MutexHashMap::Handle h(&ctx->mutex_map, m);
 | |
|     if (h.created())
 | |
|       ctx->dd->MutexInit(&cb, &h->dd);
 | |
|     ctx->dd->MutexBeforeLock(&cb, &h->dd, writelock);
 | |
|   }
 | |
|   ReportDeadlock(thr, ctx->dd->GetReport(&cb));
 | |
| }
 | |
| 
 | |
| void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock) {
 | |
|   if (thr->ignore_interceptors)
 | |
|     return;
 | |
|   Callback cb(thr);
 | |
|   {
 | |
|     MutexHashMap::Handle h(&ctx->mutex_map, m);
 | |
|     if (h.created())
 | |
|       ctx->dd->MutexInit(&cb, &h->dd);
 | |
|     ctx->dd->MutexAfterLock(&cb, &h->dd, writelock, trylock);
 | |
|   }
 | |
|   ReportDeadlock(thr, ctx->dd->GetReport(&cb));
 | |
| }
 | |
| 
 | |
| void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock) {
 | |
|   if (thr->ignore_interceptors)
 | |
|     return;
 | |
|   Callback cb(thr);
 | |
|   {
 | |
|     MutexHashMap::Handle h(&ctx->mutex_map, m);
 | |
|     ctx->dd->MutexBeforeUnlock(&cb, &h->dd, writelock);
 | |
|   }
 | |
|   ReportDeadlock(thr, ctx->dd->GetReport(&cb));
 | |
| }
 | |
| 
 | |
| void MutexDestroy(Thread *thr, uptr m) {
 | |
|   if (thr->ignore_interceptors)
 | |
|     return;
 | |
|   Callback cb(thr);
 | |
|   MutexHashMap::Handle h(&ctx->mutex_map, m, true);
 | |
|   if (!h.exists())
 | |
|     return;
 | |
|   ctx->dd->MutexDestroy(&cb, &h->dd);
 | |
| }
 | |
| 
 | |
| }  // namespace __dsan
 |