tsan_suppressions.cc revision 39968339a07d790aadcf27534f92a0de8c0c90fb
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 "tsan_suppressions.h"
17#include "tsan_rtl.h"
18#include "tsan_flags.h"
19#include "tsan_mman.h"
20#include "tsan_platform.h"
21
22// Can be overriden in frontend.
23#ifndef TSAN_GO
24extern "C" const char *WEAK __tsan_default_suppressions() {
25  return 0;
26}
27#endif
28
29namespace __tsan {
30
31static Suppression *g_suppressions;
32
33static char *ReadFile(const char *filename) {
34  if (filename == 0 || filename[0] == 0)
35    return 0;
36  InternalScopedBuffer<char> tmp(4*1024);
37  if (filename[0] == '/' || GetPwd() == 0)
38    internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
39  else
40    internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
41  uptr openrv = OpenFile(tmp.data(), false);
42  if (internal_iserror(openrv)) {
43    Printf("ThreadSanitizer: failed to open suppressions file '%s'\n",
44               tmp.data());
45    Die();
46  }
47  fd_t fd = openrv;
48  const uptr fsize = internal_filesize(fd);
49  if (fsize == (uptr)-1) {
50    Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
51               tmp.data());
52    Die();
53  }
54  char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
55  if (fsize != internal_read(fd, buf, fsize)) {
56    Printf("ThreadSanitizer: failed to read suppressions file '%s'\n",
57               tmp.data());
58    Die();
59  }
60  internal_close(fd);
61  buf[fsize] = 0;
62  return buf;
63}
64
65bool SuppressionMatch(char *templ, const char *str) {
66  if (str == 0 || str[0] == 0)
67    return false;
68  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;
80    spos = internal_strstr(str, templ);
81    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
91Suppression *SuppressionParse(Suppression *head, const char* supp) {
92  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;
104      if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) {
105        stype = SuppressionRace;
106        line += sizeof("race:") - 1;
107      } else if (0 == internal_strncmp(line, "thread:",
108          sizeof("thread:") - 1)) {
109        stype = SuppressionThread;
110        line += sizeof("thread:") - 1;
111      } else if (0 == internal_strncmp(line, "mutex:",
112          sizeof("mutex:") - 1)) {
113        stype = SuppressionMutex;
114        line += sizeof("mutex:") - 1;
115      } else if (0 == internal_strncmp(line, "signal:",
116          sizeof("signal:") - 1)) {
117        stype = SuppressionSignal;
118        line += sizeof("signal:") - 1;
119      } else {
120        Printf("ThreadSanitizer: failed to parse suppressions file\n");
121        Die();
122      }
123      Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
124          sizeof(Suppression));
125      s->next = head;
126      head = s;
127      s->type = stype;
128      s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1);
129      internal_memcpy(s->templ, line, end2 - line);
130      s->templ[end2 - line] = 0;
131      s->hit_count = 0;
132    }
133    if (end[0] == 0)
134      break;
135    line = end + 1;
136  }
137  return head;
138}
139
140void InitializeSuppressions() {
141  const char *supp = ReadFile(flags()->suppressions);
142  g_suppressions = SuppressionParse(0, supp);
143#ifndef TSAN_GO
144  supp = __tsan_default_suppressions();
145  g_suppressions = SuppressionParse(g_suppressions, supp);
146#endif
147}
148
149SuppressionType conv(ReportType typ) {
150  if (typ == ReportTypeRace)
151    return SuppressionRace;
152  else if (typ == ReportTypeVptrRace)
153    return SuppressionRace;
154  else if (typ == ReportTypeUseAfterFree)
155    return SuppressionNone;
156  else if (typ == ReportTypeThreadLeak)
157    return SuppressionThread;
158  else if (typ == ReportTypeMutexDestroyLocked)
159    return SuppressionMutex;
160  else if (typ == ReportTypeSignalUnsafe)
161    return SuppressionSignal;
162  else if (typ == ReportTypeErrnoInSignal)
163    return SuppressionNone;
164  Printf("ThreadSanitizer: unknown report type %d\n", typ),
165  Die();
166}
167
168uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
169  if (g_suppressions == 0 || stack == 0)
170    return 0;
171  SuppressionType stype = conv(typ);
172  if (stype == SuppressionNone)
173    return 0;
174  for (const ReportStack *frame = stack; frame; frame = frame->next) {
175    for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
176      if (stype == supp->type &&
177          (SuppressionMatch(supp->templ, frame->func) ||
178           SuppressionMatch(supp->templ, frame->file) ||
179           SuppressionMatch(supp->templ, frame->module))) {
180        DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
181        supp->hit_count++;
182        *sp = supp;
183        return frame->pc;
184      }
185    }
186  }
187  return 0;
188}
189
190uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
191  if (g_suppressions == 0 || loc == 0 || loc->type != ReportLocationGlobal)
192    return 0;
193  SuppressionType stype = conv(typ);
194  if (stype == SuppressionNone)
195    return 0;
196  for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
197    if (stype == supp->type &&
198        (SuppressionMatch(supp->templ, loc->name) ||
199         SuppressionMatch(supp->templ, loc->file) ||
200         SuppressionMatch(supp->templ, loc->module))) {
201      DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
202      supp->hit_count++;
203      *sp = supp;
204      return loc->addr;
205    }
206  }
207  return 0;
208}
209
210static const char *SuppTypeStr(SuppressionType t) {
211  switch (t) {
212  case SuppressionNone:   return "none";
213  case SuppressionRace:   return "race";
214  case SuppressionMutex:  return "mutex";
215  case SuppressionThread: return "thread";
216  case SuppressionSignal: return "signal";
217  }
218  CHECK(0);
219  return "unknown";
220}
221
222void PrintMatchedSuppressions() {
223  int hit_count = 0;
224  for (Suppression *supp = g_suppressions; supp; supp = supp->next)
225    hit_count += supp->hit_count;
226  if (hit_count == 0)
227    return;
228  Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n",
229      hit_count, (int)internal_getpid());
230  for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
231    if (supp->hit_count == 0)
232      continue;
233    Printf("%d %s:%s\n", supp->hit_count, SuppTypeStr(supp->type), supp->templ);
234  }
235}
236}  // namespace __tsan
237