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 24 extern "C" const char *WEAK __tsan_default_suppressions() { 25 return 0; 26 } 27 #endif 28 29 namespace __tsan { 30 31 static Suppression *g_suppressions; 32 33 static 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 fd_t fd = OpenFile(tmp.data(), false); 42 if (fd == kInvalidFd) { 43 Printf("ThreadSanitizer: failed to open suppressions file '%s'\n", 44 tmp.data()); 45 Die(); 46 } 47 const uptr fsize = internal_filesize(fd); 48 if (fsize == (uptr)-1) { 49 Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n", 50 tmp.data()); 51 Die(); 52 } 53 char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1); 54 if (fsize != internal_read(fd, buf, fsize)) { 55 Printf("ThreadSanitizer: failed to read suppressions file '%s'\n", 56 tmp.data()); 57 Die(); 58 } 59 internal_close(fd); 60 buf[fsize] = 0; 61 return buf; 62 } 63 64 bool SuppressionMatch(char *templ, const char *str) { 65 if (str == 0 || str[0] == 0) 66 return false; 67 char *tpos; 68 const char *spos; 69 while (templ && templ[0]) { 70 if (templ[0] == '*') { 71 templ++; 72 continue; 73 } 74 if (str[0] == 0) 75 return false; 76 tpos = (char*)internal_strchr(templ, '*'); 77 if (tpos != 0) 78 tpos[0] = 0; 79 spos = internal_strstr(str, templ); 80 str = spos + internal_strlen(templ); 81 templ = tpos; 82 if (tpos) 83 tpos[0] = '*'; 84 if (spos == 0) 85 return false; 86 } 87 return true; 88 } 89 90 Suppression *SuppressionParse(Suppression *head, const char* supp) { 91 const char *line = supp; 92 while (line) { 93 while (line[0] == ' ' || line[0] == '\t') 94 line++; 95 const char *end = internal_strchr(line, '\n'); 96 if (end == 0) 97 end = line + internal_strlen(line); 98 if (line != end && line[0] != '#') { 99 const char *end2 = end; 100 while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) 101 end2--; 102 SuppressionType stype; 103 if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) { 104 stype = SuppressionRace; 105 line += sizeof("race:") - 1; 106 } else if (0 == internal_strncmp(line, "thread:", 107 sizeof("thread:") - 1)) { 108 stype = SuppressionThread; 109 line += sizeof("thread:") - 1; 110 } else if (0 == internal_strncmp(line, "mutex:", 111 sizeof("mutex:") - 1)) { 112 stype = SuppressionMutex; 113 line += sizeof("mutex:") - 1; 114 } else if (0 == internal_strncmp(line, "signal:", 115 sizeof("signal:") - 1)) { 116 stype = SuppressionSignal; 117 line += sizeof("signal:") - 1; 118 } else { 119 Printf("ThreadSanitizer: failed to parse suppressions file\n"); 120 Die(); 121 } 122 Suppression *s = (Suppression*)internal_alloc(MBlockSuppression, 123 sizeof(Suppression)); 124 s->next = head; 125 head = s; 126 s->type = stype; 127 s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1); 128 internal_memcpy(s->templ, line, end2 - line); 129 s->templ[end2 - line] = 0; 130 } 131 if (end[0] == 0) 132 break; 133 line = end + 1; 134 } 135 return head; 136 } 137 138 void InitializeSuppressions() { 139 const char *supp = ReadFile(flags()->suppressions); 140 g_suppressions = SuppressionParse(0, supp); 141 #ifndef TSAN_GO 142 supp = __tsan_default_suppressions(); 143 g_suppressions = SuppressionParse(g_suppressions, supp); 144 #endif 145 } 146 147 uptr IsSuppressed(ReportType typ, const ReportStack *stack) { 148 if (g_suppressions == 0 || stack == 0) 149 return 0; 150 SuppressionType stype; 151 if (typ == ReportTypeRace) 152 stype = SuppressionRace; 153 else if (typ == ReportTypeThreadLeak) 154 stype = SuppressionThread; 155 else if (typ == ReportTypeMutexDestroyLocked) 156 stype = SuppressionMutex; 157 else if (typ == ReportTypeSignalUnsafe) 158 stype = SuppressionSignal; 159 else 160 return 0; 161 for (const ReportStack *frame = stack; frame; frame = frame->next) { 162 for (Suppression *supp = g_suppressions; supp; supp = supp->next) { 163 if (stype == supp->type && 164 (SuppressionMatch(supp->templ, frame->func) || 165 SuppressionMatch(supp->templ, frame->file) || 166 SuppressionMatch(supp->templ, frame->module))) { 167 DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); 168 return frame->pc; 169 } 170 } 171 } 172 return 0; 173 } 174 } // namespace __tsan 175