]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //===-- tsan_suppressions.cc ----------------------------------------------===// |
2 | // | |
3 | // The LLVM Compiler Infrastructure | |
4 | // | |
5 | // This file is distributed under the University of Illinois Open Source | |
6 | // License. See LICENSE.TXT for details. | |
7 | // | |
8 | //===----------------------------------------------------------------------===// | |
9 | // | |
10 | // This file is a part of ThreadSanitizer (TSan), a race detector. | |
11 | // | |
12 | //===----------------------------------------------------------------------===// | |
13 | ||
14 | #include "sanitizer_common/sanitizer_common.h" | |
15 | #include "sanitizer_common/sanitizer_libc.h" | |
16 | #include "sanitizer_common/sanitizer_placement_new.h" | |
17 | #include "sanitizer_common/sanitizer_suppressions.h" | |
18 | #include "tsan_suppressions.h" | |
19 | #include "tsan_rtl.h" | |
20 | #include "tsan_flags.h" | |
21 | #include "tsan_mman.h" | |
22 | #include "tsan_platform.h" | |
23 | ||
7cac9316 | 24 | #if !SANITIZER_GO |
1a4d82fc JJ |
25 | // Suppressions for true/false positives in standard libraries. |
26 | static const char *const std_suppressions = | |
27 | // Libstdc++ 4.4 has data races in std::string. | |
28 | // See http://crbug.com/181502 for an example. | |
29 | "race:^_M_rep$\n" | |
30 | "race:^_M_is_leaked$\n" | |
31 | // False positive when using std <thread>. | |
32 | // Happens because we miss atomic synchronization in libstdc++. | |
33 | // See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. | |
34 | "race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n"; | |
35 | ||
36 | // Can be overriden in frontend. | |
3157f602 XL |
37 | SANITIZER_WEAK_DEFAULT_IMPL |
38 | const char *__tsan_default_suppressions() { | |
1a4d82fc JJ |
39 | return 0; |
40 | } | |
41 | #endif | |
42 | ||
43 | namespace __tsan { | |
44 | ||
92a42be0 SL |
45 | ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; |
46 | static SuppressionContext *suppression_ctx = nullptr; | |
47 | static const char *kSuppressionTypes[] = { | |
48 | kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex, | |
49 | kSuppressionThread, kSuppressionSignal, kSuppressionLib, | |
50 | kSuppressionDeadlock}; | |
1a4d82fc JJ |
51 | |
52 | void InitializeSuppressions() { | |
92a42be0 SL |
53 | CHECK_EQ(nullptr, suppression_ctx); |
54 | suppression_ctx = new (suppression_placeholder) // NOLINT | |
55 | SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); | |
56 | suppression_ctx->ParseFromFile(flags()->suppressions); | |
7cac9316 | 57 | #if !SANITIZER_GO |
92a42be0 SL |
58 | suppression_ctx->Parse(__tsan_default_suppressions()); |
59 | suppression_ctx->Parse(std_suppressions); | |
1a4d82fc JJ |
60 | #endif |
61 | } | |
62 | ||
92a42be0 SL |
63 | SuppressionContext *Suppressions() { |
64 | CHECK(suppression_ctx); | |
65 | return suppression_ctx; | |
1a4d82fc JJ |
66 | } |
67 | ||
92a42be0 | 68 | static const char *conv(ReportType typ) { |
1a4d82fc | 69 | if (typ == ReportTypeRace) |
92a42be0 | 70 | return kSuppressionRace; |
1a4d82fc | 71 | else if (typ == ReportTypeVptrRace) |
92a42be0 | 72 | return kSuppressionRace; |
1a4d82fc | 73 | else if (typ == ReportTypeUseAfterFree) |
92a42be0 SL |
74 | return kSuppressionRace; |
75 | else if (typ == ReportTypeVptrUseAfterFree) | |
76 | return kSuppressionRace; | |
2c00a5a8 XL |
77 | else if (typ == ReportTypeExternalRace) |
78 | return kSuppressionRace; | |
1a4d82fc | 79 | else if (typ == ReportTypeThreadLeak) |
92a42be0 | 80 | return kSuppressionThread; |
1a4d82fc | 81 | else if (typ == ReportTypeMutexDestroyLocked) |
92a42be0 | 82 | return kSuppressionMutex; |
1a4d82fc | 83 | else if (typ == ReportTypeMutexDoubleLock) |
92a42be0 | 84 | return kSuppressionMutex; |
5bcae85e SL |
85 | else if (typ == ReportTypeMutexInvalidAccess) |
86 | return kSuppressionMutex; | |
1a4d82fc | 87 | else if (typ == ReportTypeMutexBadUnlock) |
92a42be0 | 88 | return kSuppressionMutex; |
1a4d82fc | 89 | else if (typ == ReportTypeMutexBadReadLock) |
92a42be0 | 90 | return kSuppressionMutex; |
1a4d82fc | 91 | else if (typ == ReportTypeMutexBadReadUnlock) |
92a42be0 | 92 | return kSuppressionMutex; |
1a4d82fc | 93 | else if (typ == ReportTypeSignalUnsafe) |
92a42be0 | 94 | return kSuppressionSignal; |
1a4d82fc | 95 | else if (typ == ReportTypeErrnoInSignal) |
92a42be0 | 96 | return kSuppressionNone; |
1a4d82fc | 97 | else if (typ == ReportTypeDeadlock) |
92a42be0 | 98 | return kSuppressionDeadlock; |
5bcae85e | 99 | Printf("ThreadSanitizer: unknown report type %d\n", typ); |
1a4d82fc JJ |
100 | Die(); |
101 | } | |
102 | ||
92a42be0 SL |
103 | static uptr IsSuppressed(const char *stype, const AddressInfo &info, |
104 | Suppression **sp) { | |
105 | if (suppression_ctx->Match(info.function, stype, sp) || | |
106 | suppression_ctx->Match(info.file, stype, sp) || | |
107 | suppression_ctx->Match(info.module, stype, sp)) { | |
108 | VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ); | |
109 | atomic_fetch_add(&(*sp)->hit_count, 1, memory_order_relaxed); | |
110 | return info.address; | |
111 | } | |
112 | return 0; | |
113 | } | |
114 | ||
1a4d82fc | 115 | uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { |
92a42be0 SL |
116 | CHECK(suppression_ctx); |
117 | if (!suppression_ctx->SuppressionCount() || stack == 0 || | |
118 | !stack->suppressable) | |
1a4d82fc | 119 | return 0; |
92a42be0 SL |
120 | const char *stype = conv(typ); |
121 | if (0 == internal_strcmp(stype, kSuppressionNone)) | |
122 | return 0; | |
123 | for (const SymbolizedStack *frame = stack->frames; frame; | |
124 | frame = frame->next) { | |
125 | uptr pc = IsSuppressed(stype, frame->info, sp); | |
126 | if (pc != 0) | |
127 | return pc; | |
1a4d82fc | 128 | } |
92a42be0 SL |
129 | if (0 == internal_strcmp(stype, kSuppressionRace) && stack->frames != nullptr) |
130 | return IsSuppressed(kSuppressionRaceTop, stack->frames->info, sp); | |
1a4d82fc JJ |
131 | return 0; |
132 | } | |
133 | ||
134 | uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { | |
92a42be0 SL |
135 | CHECK(suppression_ctx); |
136 | if (!suppression_ctx->SuppressionCount() || loc == 0 || | |
137 | loc->type != ReportLocationGlobal || !loc->suppressable) | |
1a4d82fc | 138 | return 0; |
92a42be0 SL |
139 | const char *stype = conv(typ); |
140 | if (0 == internal_strcmp(stype, kSuppressionNone)) | |
1a4d82fc JJ |
141 | return 0; |
142 | Suppression *s; | |
92a42be0 SL |
143 | const DataInfo &global = loc->global; |
144 | if (suppression_ctx->Match(global.name, stype, &s) || | |
145 | suppression_ctx->Match(global.module, stype, &s)) { | |
146 | VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", s->templ); | |
147 | atomic_fetch_add(&s->hit_count, 1, memory_order_relaxed); | |
1a4d82fc | 148 | *sp = s; |
92a42be0 | 149 | return global.start; |
1a4d82fc JJ |
150 | } |
151 | return 0; | |
152 | } | |
153 | ||
154 | void PrintMatchedSuppressions() { | |
1a4d82fc | 155 | InternalMmapVector<Suppression *> matched(1); |
92a42be0 SL |
156 | CHECK(suppression_ctx); |
157 | suppression_ctx->GetMatched(&matched); | |
1a4d82fc JJ |
158 | if (!matched.size()) |
159 | return; | |
160 | int hit_count = 0; | |
161 | for (uptr i = 0; i < matched.size(); i++) | |
92a42be0 | 162 | hit_count += atomic_load_relaxed(&matched[i]->hit_count); |
1a4d82fc JJ |
163 | Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, |
164 | (int)internal_getpid()); | |
165 | for (uptr i = 0; i < matched.size(); i++) { | |
3157f602 XL |
166 | Printf("%d %s:%s\n", atomic_load_relaxed(&matched[i]->hit_count), |
167 | matched[i]->type, matched[i]->templ); | |
1a4d82fc JJ |
168 | } |
169 | } | |
170 | } // namespace __tsan |