blob: 977c008b88bab17924e8668edb6692f422046988 [file] [log] [blame]
Alexey Samsonov603c4be2012-06-04 13:55:19 +00001//===-- tsan_suppressions.cc ----------------------------------------------===//
Kostya Serebryany7ac41482012-05-10 13:48:04 +00002//
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
Alexey Samsonov0969bcf2012-06-18 08:44:30 +000014#include "sanitizer_common/sanitizer_common.h"
Alexey Samsonov7f9c5a22012-06-05 07:46:31 +000015#include "sanitizer_common/sanitizer_libc.h"
Kostya Serebryany7ac41482012-05-10 13:48:04 +000016#include "tsan_suppressions.h"
17#include "tsan_rtl.h"
18#include "tsan_flags.h"
19#include "tsan_mman.h"
20#include "tsan_platform.h"
21
Dmitry Vyukovc2b9f1c2013-01-24 13:50:10 +000022// Can be overriden in frontend.
23#ifndef TSAN_GO
24extern "C" const char *WEAK __tsan_default_suppressions() {
25 return 0;
26}
27#endif
28
Kostya Serebryany7ac41482012-05-10 13:48:04 +000029namespace __tsan {
30
31static Suppression *g_suppressions;
32
33static char *ReadFile(const char *filename) {
34 if (filename == 0 || filename[0] == 0)
35 return 0;
Alexey Samsonov14c8bd72012-08-22 07:25:52 +000036 InternalScopedBuffer<char> tmp(4*1024);
Dmitry Vyukov6422c322012-12-04 07:27:32 +000037 if (filename[0] == '/' || GetPwd() == 0)
Alexey Samsonov1dc4cf72012-09-05 07:23:44 +000038 internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
Kostya Serebryany7ac41482012-05-10 13:48:04 +000039 else
Alexey Samsonov1dc4cf72012-09-05 07:23:44 +000040 internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
Peter Collingbourne9578a3e2013-05-08 14:43:49 +000041 uptr openrv = OpenFile(tmp.data(), false);
42 if (internal_iserror(openrv)) {
Alexey Samsonovb1fe3022012-11-02 12:17:51 +000043 Printf("ThreadSanitizer: failed to open suppressions file '%s'\n",
Alexey Samsonov14c8bd72012-08-22 07:25:52 +000044 tmp.data());
Kostya Serebryany7ac41482012-05-10 13:48:04 +000045 Die();
46 }
Peter Collingbourne9578a3e2013-05-08 14:43:49 +000047 fd_t fd = openrv;
Kostya Serebryany7ac41482012-05-10 13:48:04 +000048 const uptr fsize = internal_filesize(fd);
49 if (fsize == (uptr)-1) {
Alexey Samsonovb1fe3022012-11-02 12:17:51 +000050 Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
Alexey Samsonov14c8bd72012-08-22 07:25:52 +000051 tmp.data());
Kostya Serebryany7ac41482012-05-10 13:48:04 +000052 Die();
53 }
54 char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
55 if (fsize != internal_read(fd, buf, fsize)) {
Alexey Samsonovb1fe3022012-11-02 12:17:51 +000056 Printf("ThreadSanitizer: failed to read suppressions file '%s'\n",
Alexey Samsonov14c8bd72012-08-22 07:25:52 +000057 tmp.data());
Kostya Serebryany7ac41482012-05-10 13:48:04 +000058 Die();
59 }
60 internal_close(fd);
61 buf[fsize] = 0;
62 return buf;
63}
64
65bool SuppressionMatch(char *templ, const char *str) {
Dmitry Vyukov20f60c52012-05-31 13:18:11 +000066 if (str == 0 || str[0] == 0)
67 return false;
Kostya Serebryany7ac41482012-05-10 13:48:04 +000068 char *tpos;
69 const char *spos;
70 while (templ && templ[0]) {
71 if (templ[0] == '*') {
72 templ++;
73 continue;
74 }
75 if (str[0] == 0)
76 return false;
77 tpos = (char*)internal_strchr(templ, '*');
78 if (tpos != 0)
79 tpos[0] = 0;
Dmitry Vyukovd51a1a12012-06-27 21:00:23 +000080 spos = internal_strstr(str, templ);
Kostya Serebryany7ac41482012-05-10 13:48:04 +000081 str = spos + internal_strlen(templ);
82 templ = tpos;
83 if (tpos)
84 tpos[0] = '*';
85 if (spos == 0)
86 return false;
87 }
88 return true;
89}
90
Dmitry Vyukovc2b9f1c2013-01-24 13:50:10 +000091Suppression *SuppressionParse(Suppression *head, const char* supp) {
Kostya Serebryany7ac41482012-05-10 13:48:04 +000092 const char *line = supp;
93 while (line) {
94 while (line[0] == ' ' || line[0] == '\t')
95 line++;
96 const char *end = internal_strchr(line, '\n');
97 if (end == 0)
98 end = line + internal_strlen(line);
99 if (line != end && line[0] != '#') {
100 const char *end2 = end;
101 while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
102 end2--;
103 SuppressionType stype;
Dmitry Vyukovd51a1a12012-06-27 21:00:23 +0000104 if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) {
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000105 stype = SuppressionRace;
106 line += sizeof("race:") - 1;
Dmitry Vyukovd51a1a12012-06-27 21:00:23 +0000107 } else if (0 == internal_strncmp(line, "thread:",
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000108 sizeof("thread:") - 1)) {
109 stype = SuppressionThread;
110 line += sizeof("thread:") - 1;
Dmitry Vyukovd51a1a12012-06-27 21:00:23 +0000111 } else if (0 == internal_strncmp(line, "mutex:",
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000112 sizeof("mutex:") - 1)) {
113 stype = SuppressionMutex;
114 line += sizeof("mutex:") - 1;
Dmitry Vyukovd51a1a12012-06-27 21:00:23 +0000115 } else if (0 == internal_strncmp(line, "signal:",
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000116 sizeof("signal:") - 1)) {
117 stype = SuppressionSignal;
118 line += sizeof("signal:") - 1;
119 } else {
Alexey Samsonovb1fe3022012-11-02 12:17:51 +0000120 Printf("ThreadSanitizer: failed to parse suppressions file\n");
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000121 Die();
122 }
123 Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
124 sizeof(Suppression));
125 s->next = head;
126 head = s;
127 s->type = stype;
Dmitry Vyukov20f60c52012-05-31 13:18:11 +0000128 s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1);
Dmitry Vyukovd51a1a12012-06-27 21:00:23 +0000129 internal_memcpy(s->templ, line, end2 - line);
Dmitry Vyukov20f60c52012-05-31 13:18:11 +0000130 s->templ[end2 - line] = 0;
Dmitry Vyukovf754eb52013-03-27 17:59:57 +0000131 s->hit_count = 0;
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000132 }
133 if (end[0] == 0)
134 break;
135 line = end + 1;
136 }
137 return head;
138}
139
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000140void InitializeSuppressions() {
Dmitry Vyukovc2b9f1c2013-01-24 13:50:10 +0000141 const char *supp = ReadFile(flags()->suppressions);
142 g_suppressions = SuppressionParse(0, supp);
143#ifndef TSAN_GO
144 supp = __tsan_default_suppressions();
Dmitry Vyukov15190a62013-02-14 11:03:45 +0000145 g_suppressions = SuppressionParse(g_suppressions, supp);
Dmitry Vyukovc2b9f1c2013-01-24 13:50:10 +0000146#endif
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000147}
148
Dmitry Vyukovf754eb52013-03-27 17:59:57 +0000149uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000150 if (g_suppressions == 0 || stack == 0)
Dmitry Vyukov158c6ac2012-10-05 15:51:32 +0000151 return 0;
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000152 SuppressionType stype;
153 if (typ == ReportTypeRace)
154 stype = SuppressionRace;
Dmitry Vyukov24cdfee2013-05-29 11:23:54 +0000155 else if (typ == ReportTypeVptrRace)
156 stype = SuppressionRace;
157 else if (typ == ReportTypeUseAfterFree)
158 return 0;
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000159 else if (typ == ReportTypeThreadLeak)
160 stype = SuppressionThread;
161 else if (typ == ReportTypeMutexDestroyLocked)
162 stype = SuppressionMutex;
163 else if (typ == ReportTypeSignalUnsafe)
164 stype = SuppressionSignal;
Dmitry Vyukov24cdfee2013-05-29 11:23:54 +0000165 else if (typ == ReportTypeErrnoInSignal)
Dmitry Vyukov158c6ac2012-10-05 15:51:32 +0000166 return 0;
Dmitry Vyukov24cdfee2013-05-29 11:23:54 +0000167 else
168 Printf("ThreadSanitizer: unknown report type %d\n", typ), Die();
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000169 for (const ReportStack *frame = stack; frame; frame = frame->next) {
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000170 for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
Dmitry Vyukova13749b2012-05-31 14:24:10 +0000171 if (stype == supp->type &&
172 (SuppressionMatch(supp->templ, frame->func) ||
Dmitry Vyukovc2b9f1c2013-01-24 13:50:10 +0000173 SuppressionMatch(supp->templ, frame->file) ||
174 SuppressionMatch(supp->templ, frame->module))) {
Dmitry Vyukov20f60c52012-05-31 13:18:11 +0000175 DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
Dmitry Vyukovf754eb52013-03-27 17:59:57 +0000176 supp->hit_count++;
177 *sp = supp;
Dmitry Vyukov158c6ac2012-10-05 15:51:32 +0000178 return frame->pc;
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000179 }
180 }
181 }
Dmitry Vyukov158c6ac2012-10-05 15:51:32 +0000182 return 0;
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000183}
Dmitry Vyukovf754eb52013-03-27 17:59:57 +0000184
185static const char *SuppTypeStr(SuppressionType t) {
186 switch (t) {
187 case SuppressionRace: return "race";
188 case SuppressionMutex: return "mutex";
189 case SuppressionThread: return "thread";
190 case SuppressionSignal: return "signal";
191 }
192 CHECK(0);
193 return "unknown";
194}
195
196void PrintMatchedSuppressions() {
197 int hit_count = 0;
198 for (Suppression *supp = g_suppressions; supp; supp = supp->next)
199 hit_count += supp->hit_count;
200 if (hit_count == 0)
201 return;
202 Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n",
Peter Collingbourne0b694fc2013-05-17 16:56:53 +0000203 hit_count, (int)internal_getpid());
Dmitry Vyukovf754eb52013-03-27 17:59:57 +0000204 for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
205 if (supp->hit_count == 0)
206 continue;
207 Printf("%d %s:%s\n", supp->hit_count, SuppTypeStr(supp->type), supp->templ);
208 }
209}
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000210} // namespace __tsan