aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tsan/dd/dd_rtl.cpp
blob: 2095217586a8677cf0e001b6d2601e7317054778 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//===-- 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