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(max_depth: 1000, pc: 0, bp: 0, context: 0, stack_top: 0, stack_bottom: 0, request_fast_unwind: false); |
25 | thr->ignore_interceptors = false; |
26 | if (stack.size <= skip) |
27 | return 0; |
28 | return StackDepotPut(stack: StackTrace(stack.trace + skip, stack.size - skip)); |
29 | } |
30 | |
31 | static void PrintStackTrace(Thread *thr, u32 stk) { |
32 | StackTrace stack = StackDepotGet(id: 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(format: "==============================\n" ); |
43 | Printf(format: "WARNING: lock-order-inversion (potential deadlock)\n" ); |
44 | for (int i = 0; i < rep->n; i++) { |
45 | Printf(format: "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, stk: rep->loop[i].stk[1]); |
48 | if (rep->loop[i].stk[0]) { |
49 | Printf(format: "Mutex %llu was acquired here:\n" , |
50 | rep->loop[i].mtx_ctx0); |
51 | PrintStackTrace(thr, stk: rep->loop[i].stk[0]); |
52 | } |
53 | } |
54 | Printf(format: "==============================\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, skip: 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(other: *common_flags()); |
78 | cf.allow_addr2line = true; |
79 | OverrideCommonFlags(cf); |
80 | } |
81 | |
82 | // Override from command line. |
83 | FlagParser parser; |
84 | RegisterFlag(parser: &parser, name: "second_deadlock_stack" , desc: "" , var: &f->second_deadlock_stack); |
85 | RegisterCommonFlags(parser: &parser); |
86 | parser.ParseStringFromEnv(env_name: "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: flags()); |
97 | } |
98 | |
99 | void ThreadInit(Thread *thr) { |
100 | static atomic_uintptr_t id_gen; |
101 | uptr id = atomic_fetch_add(a: &id_gen, v: 1, mo: memory_order_relaxed); |
102 | thr->dd_pt = ctx->dd->CreatePhysicalThread(); |
103 | thr->dd_lt = ctx->dd->CreateLogicalThread(ctx: id); |
104 | } |
105 | |
106 | void ThreadDestroy(Thread *thr) { |
107 | ctx->dd->DestroyPhysicalThread(pt: thr->dd_pt); |
108 | ctx->dd->DestroyLogicalThread(lt: 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: &cb, m: &h->dd); |
119 | ctx->dd->MutexBeforeLock(cb: &cb, m: &h->dd, wlock: writelock); |
120 | } |
121 | ReportDeadlock(thr, rep: ctx->dd->GetReport(cb: &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: &cb, m: &h->dd); |
132 | ctx->dd->MutexAfterLock(cb: &cb, m: &h->dd, wlock: writelock, trylock); |
133 | } |
134 | ReportDeadlock(thr, rep: ctx->dd->GetReport(cb: &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: &cb, m: &h->dd, wlock: writelock); |
144 | } |
145 | ReportDeadlock(thr, rep: ctx->dd->GetReport(cb: &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: &cb, m: &h->dd); |
156 | } |
157 | |
158 | } // namespace __dsan |
159 | |