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