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
17namespace __dsan {
18
19static Context *ctx;
20
21static 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
31static 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
38static 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
57Callback::Callback(Thread *thr)
58 : thr(thr) {
59 lt = thr->dd_lt;
60 pt = thr->dd_pt;
61}
62
63u32 Callback::Unwind() {
64 return CurrentStackTrace(thr, skip: 3);
65}
66
67static 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
90void 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
99void 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
106void ThreadDestroy(Thread *thr) {
107 ctx->dd->DestroyPhysicalThread(pt: thr->dd_pt);
108 ctx->dd->DestroyLogicalThread(lt: thr->dd_lt);
109}
110
111void 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
124void 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
137void 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
148void 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