xref: /freebsd/contrib/llvm-project/compiler-rt/lib/tsan/dd/dd_rtl.cpp (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 //===-- dd_rtl.cpp --------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "dd_rtl.h"
10 #include "sanitizer_common/sanitizer_common.h"
11 #include "sanitizer_common/sanitizer_placement_new.h"
12 #include "sanitizer_common/sanitizer_flags.h"
13 #include "sanitizer_common/sanitizer_flag_parser.h"
14 #include "sanitizer_common/sanitizer_stacktrace.h"
15 #include "sanitizer_common/sanitizer_stackdepot.h"
16 
17 namespace __dsan {
18 
19 static Context *ctx;
20 
21 static u32 CurrentStackTrace(Thread *thr, uptr skip) {
22   BufferedStackTrace stack;
23   thr->ignore_interceptors = true;
24   stack.Unwind(1000, 0, 0, 0, 0, 0, false);
25   thr->ignore_interceptors = false;
26   if (stack.size <= skip)
27     return 0;
28   return StackDepotPut(StackTrace(stack.trace + skip, stack.size - skip));
29 }
30 
31 static void PrintStackTrace(Thread *thr, u32 stk) {
32   StackTrace stack = StackDepotGet(stk);
33   thr->ignore_interceptors = true;
34   stack.Print();
35   thr->ignore_interceptors = false;
36 }
37 
38 static void ReportDeadlock(Thread *thr, DDReport *rep) {
39   if (rep == 0)
40     return;
41   Lock lock(&ctx->report_mutex);
42   Printf("==============================\n");
43   Printf("WARNING: lock-order-inversion (potential deadlock)\n");
44   for (int i = 0; i < rep->n; i++) {
45     Printf("Thread %lld locks mutex %llu while holding mutex %llu:\n",
46            rep->loop[i].thr_ctx, rep->loop[i].mtx_ctx1, rep->loop[i].mtx_ctx0);
47     PrintStackTrace(thr, rep->loop[i].stk[1]);
48     if (rep->loop[i].stk[0]) {
49       Printf("Mutex %llu was acquired here:\n",
50         rep->loop[i].mtx_ctx0);
51       PrintStackTrace(thr, rep->loop[i].stk[0]);
52     }
53   }
54   Printf("==============================\n");
55 }
56 
57 Callback::Callback(Thread *thr)
58     : thr(thr) {
59   lt = thr->dd_lt;
60   pt = thr->dd_pt;
61 }
62 
63 u32 Callback::Unwind() {
64   return CurrentStackTrace(thr, 3);
65 }
66 
67 static void InitializeFlags() {
68   Flags *f = flags();
69 
70   // Default values.
71   f->second_deadlock_stack = false;
72 
73   SetCommonFlagsDefaults();
74   {
75     // Override some common flags defaults.
76     CommonFlags cf;
77     cf.CopyFrom(*common_flags());
78     cf.allow_addr2line = true;
79     OverrideCommonFlags(cf);
80   }
81 
82   // Override from command line.
83   FlagParser parser;
84   RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack);
85   RegisterCommonFlags(&parser);
86   parser.ParseStringFromEnv("DSAN_OPTIONS");
87   SetVerbosity(common_flags()->verbosity);
88 }
89 
90 void Initialize() {
91   static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];
92   ctx = new(ctx_mem) Context();
93 
94   InitializeInterceptors();
95   InitializeFlags();
96   ctx->dd = DDetector::Create(flags());
97 }
98 
99 void ThreadInit(Thread *thr) {
100   static atomic_uintptr_t id_gen;
101   uptr id = atomic_fetch_add(&id_gen, 1, memory_order_relaxed);
102   thr->dd_pt = ctx->dd->CreatePhysicalThread();
103   thr->dd_lt = ctx->dd->CreateLogicalThread(id);
104 }
105 
106 void ThreadDestroy(Thread *thr) {
107   ctx->dd->DestroyPhysicalThread(thr->dd_pt);
108   ctx->dd->DestroyLogicalThread(thr->dd_lt);
109 }
110 
111 void MutexBeforeLock(Thread *thr, uptr m, bool writelock) {
112   if (thr->ignore_interceptors)
113     return;
114   Callback cb(thr);
115   {
116     MutexHashMap::Handle h(&ctx->mutex_map, m);
117     if (h.created())
118       ctx->dd->MutexInit(&cb, &h->dd);
119     ctx->dd->MutexBeforeLock(&cb, &h->dd, writelock);
120   }
121   ReportDeadlock(thr, ctx->dd->GetReport(&cb));
122 }
123 
124 void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock) {
125   if (thr->ignore_interceptors)
126     return;
127   Callback cb(thr);
128   {
129     MutexHashMap::Handle h(&ctx->mutex_map, m);
130     if (h.created())
131       ctx->dd->MutexInit(&cb, &h->dd);
132     ctx->dd->MutexAfterLock(&cb, &h->dd, writelock, trylock);
133   }
134   ReportDeadlock(thr, ctx->dd->GetReport(&cb));
135 }
136 
137 void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock) {
138   if (thr->ignore_interceptors)
139     return;
140   Callback cb(thr);
141   {
142     MutexHashMap::Handle h(&ctx->mutex_map, m);
143     ctx->dd->MutexBeforeUnlock(&cb, &h->dd, writelock);
144   }
145   ReportDeadlock(thr, ctx->dd->GetReport(&cb));
146 }
147 
148 void MutexDestroy(Thread *thr, uptr m) {
149   if (thr->ignore_interceptors)
150     return;
151   Callback cb(thr);
152   MutexHashMap::Handle h(&ctx->mutex_map, m, true);
153   if (!h.exists())
154     return;
155   ctx->dd->MutexDestroy(&cb, &h->dd);
156 }
157 
158 }  // namespace __dsan
159