blob: 14f13e620815290988108001ed27c9d891340e8f [file] [log] [blame]
//===-- sanitizer_suppressions.cc -----------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Suppression parsing/matching code shared between TSan and LSan.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_suppressions.h"
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
namespace __sanitizer {
static const char *const kTypeStrings[SuppressionTypeCount] = {
"none", "race", "mutex", "thread", "signal", "leak", "called_from_lib"
};
bool TemplateMatch(char *templ, const char *str) {
if (str == 0 || str[0] == 0)
return false;
bool start = false;
if (templ && templ[0] == '^') {
start = true;
templ++;
}
bool asterisk = false;
while (templ && templ[0]) {
if (templ[0] == '*') {
templ++;
start = false;
asterisk = true;
continue;
}
if (templ[0] == '$')
return str[0] == 0 || asterisk;
if (str[0] == 0)
return false;
char *tpos = (char*)internal_strchr(templ, '*');
char *tpos1 = (char*)internal_strchr(templ, '$');
if (tpos == 0 || (tpos1 && tpos1 < tpos))
tpos = tpos1;
if (tpos != 0)
tpos[0] = 0;
const char *str0 = str;
const char *spos = internal_strstr(str, templ);
str = spos + internal_strlen(templ);
templ = tpos;
if (tpos)
tpos[0] = tpos == tpos1 ? '$' : '*';
if (spos == 0)
return false;
if (start && spos != str0)
return false;
start = false;
asterisk = false;
}
return true;
}
bool SuppressionContext::Match(const char *str, SuppressionType type,
Suppression **s) {
can_parse_ = false;
uptr i;
for (i = 0; i < suppressions_.size(); i++)
if (type == suppressions_[i].type &&
TemplateMatch(suppressions_[i].templ, str))
break;
if (i == suppressions_.size()) return false;
*s = &suppressions_[i];
return true;
}
static const char *StripPrefix(const char *str, const char *prefix) {
while (str && *str == *prefix) {
str++;
prefix++;
}
if (!*prefix)
return str;
return 0;
}
void SuppressionContext::Parse(const char *str) {
// Context must not mutate once Match has been called.
CHECK(can_parse_);
const char *line = str;
while (line) {
while (line[0] == ' ' || line[0] == '\t')
line++;
const char *end = internal_strchr(line, '\n');
if (end == 0)
end = line + internal_strlen(line);
if (line != end && line[0] != '#') {
const char *end2 = end;
while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
end2--;
int type;
for (type = 0; type < SuppressionTypeCount; type++) {
const char *next_char = StripPrefix(line, kTypeStrings[type]);
if (next_char && *next_char == ':') {
line = ++next_char;
break;
}
}
if (type == SuppressionTypeCount) {
Printf("%s: failed to parse suppressions\n", SanitizerToolName);
Die();
}
Suppression s;
s.type = static_cast<SuppressionType>(type);
s.templ = (char*)InternalAlloc(end2 - line + 1);
internal_memcpy(s.templ, line, end2 - line);
s.templ[end2 - line] = 0;
s.hit_count = 0;
s.weight = 0;
suppressions_.push_back(s);
}
if (end[0] == 0)
break;
line = end + 1;
}
}
uptr SuppressionContext::SuppressionCount() const {
return suppressions_.size();
}
const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
CHECK_LT(i, suppressions_.size());
return &suppressions_[i];
}
void SuppressionContext::GetMatched(
InternalMmapVector<Suppression *> *matched) {
for (uptr i = 0; i < suppressions_.size(); i++)
if (suppressions_[i].hit_count)
matched->push_back(&suppressions_[i]);
}
const char *SuppressionTypeString(SuppressionType t) {
CHECK(t < SuppressionTypeCount);
return kTypeStrings[t];
}
} // namespace __sanitizer