summaryrefslogtreecommitdiff
path: root/lib/tsan/dd/dd_rtl.cc
blob: aed9debef64052e5b791ccf784ba24de005c7b50 (plain)
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
//===-- dd_rtl.cc ---------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#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_stacktrace.h"
#include "sanitizer_common/sanitizer_stackdepot.h"

namespace __dsan {

static Context *ctx;

static u32 CurrentStackTrace(Thread *thr, uptr skip) {
  StackTrace trace;
  thr->ignore_interceptors = true;
  trace.Unwind(1000, 0, 0, 0, 0, 0, false);
  thr->ignore_interceptors = false;
  if (trace.size <= skip)
    return 0;
  return StackDepotPut(trace.trace + skip, trace.size - skip);
}

static void PrintStackTrace(Thread *thr, u32 stk) {
  uptr size = 0;
  const uptr *trace = StackDepotGet(stk, &size);
  thr->ignore_interceptors = true;
  StackTrace::PrintStack(trace, size);
  thr->ignore_interceptors = false;
}

static void ReportDeadlock(Thread *thr, DDReport *rep) {
  if (rep == 0)
    return;
  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]);
  }
  Printf("==============================\n");
}

Callback::Callback(Thread *thr)
    : thr(thr) {
  lt = thr->dd_lt;
  pt = thr->dd_pt;
}

u32 Callback::Unwind() {
  return CurrentStackTrace(thr, 3);
}

void Initialize() {
  static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1];
  ctx = new(ctx_mem) Context();

  InitializeInterceptors();
  ParseCommonFlagsFromString(flags(), GetEnv("DSAN_OPTIONS"));
  //common_flags()->allow_addr2line = true;
  common_flags()->symbolize = true;
  ctx->dd = DDetector::Create();
}

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