forked from OSchip/llvm-project
[tsan] Move some suppressions-related code to common.
Factor out code to be reused in LSan. Also switch from linked list to vector. llvm-svn: 184957
This commit is contained in:
parent
a3d1c02356
commit
d109eb052b
|
@ -13,6 +13,7 @@ set(SANITIZER_SOURCES
|
|||
sanitizer_printf.cc
|
||||
sanitizer_stackdepot.cc
|
||||
sanitizer_stacktrace.cc
|
||||
sanitizer_suppressions.cc
|
||||
sanitizer_symbolizer_itanium.cc
|
||||
sanitizer_symbolizer_linux.cc
|
||||
sanitizer_symbolizer_mac.cc
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
//===-- sanitizer_suppressions.cc -----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// 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"
|
||||
};
|
||||
|
||||
bool TemplateMatch(char *templ, const char *str) {
|
||||
if (str == 0 || str[0] == 0)
|
||||
return false;
|
||||
char *tpos;
|
||||
const char *spos;
|
||||
while (templ && templ[0]) {
|
||||
if (templ[0] == '*') {
|
||||
templ++;
|
||||
continue;
|
||||
}
|
||||
if (str[0] == 0)
|
||||
return false;
|
||||
tpos = (char*)internal_strchr(templ, '*');
|
||||
if (tpos != 0)
|
||||
tpos[0] = 0;
|
||||
spos = internal_strstr(str, templ);
|
||||
str = spos + internal_strlen(templ);
|
||||
templ = tpos;
|
||||
if (tpos)
|
||||
tpos[0] = '*';
|
||||
if (spos == 0)
|
||||
return 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 file\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;
|
||||
suppressions_.push_back(s);
|
||||
}
|
||||
if (end[0] == 0)
|
||||
break;
|
||||
line = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
uptr SuppressionContext::SuppressionCount() {
|
||||
return suppressions_.size();
|
||||
}
|
||||
|
||||
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
|
|
@ -0,0 +1,58 @@
|
|||
//===-- sanitizer_suppressions.h --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_SUPPRESSIONS_H
|
||||
#define SANITIZER_SUPPRESSIONS_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
enum SuppressionType {
|
||||
SuppressionNone,
|
||||
SuppressionRace,
|
||||
SuppressionMutex,
|
||||
SuppressionThread,
|
||||
SuppressionSignal,
|
||||
SuppressionTypeCount
|
||||
};
|
||||
|
||||
struct Suppression {
|
||||
SuppressionType type;
|
||||
char *templ;
|
||||
unsigned hit_count;
|
||||
};
|
||||
|
||||
class SuppressionContext {
|
||||
public:
|
||||
SuppressionContext() : suppressions_(1), can_parse_(true) {}
|
||||
void Parse(const char *str);
|
||||
bool Match(const char* str, SuppressionType type, Suppression **s);
|
||||
uptr SuppressionCount();
|
||||
void GetMatched(InternalMmapVector<Suppression *> *matched);
|
||||
|
||||
private:
|
||||
InternalMmapVector<Suppression> suppressions_;
|
||||
bool can_parse_;
|
||||
|
||||
friend class SuppressionContextTest;
|
||||
};
|
||||
|
||||
const char *SuppressionTypeString(SuppressionType t);
|
||||
|
||||
// Exposed for testing.
|
||||
bool TemplateMatch(char *templ, const char *str);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SUPPRESSIONS_H
|
|
@ -16,6 +16,7 @@ set(SANITIZER_UNITTESTS
|
|||
sanitizer_stackdepot_test.cc
|
||||
sanitizer_stacktrace_test.cc
|
||||
sanitizer_stoptheworld_test.cc
|
||||
sanitizer_suppressions_test.cc
|
||||
sanitizer_test_main.cc
|
||||
sanitizer_thread_registry_test.cc
|
||||
)
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
//===-- sanitizer_suppressions_test.cc ------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
static bool MyMatch(const char *templ, const char *func) {
|
||||
char tmp[1024];
|
||||
strcpy(tmp, templ); // NOLINT
|
||||
return TemplateMatch(tmp, func);
|
||||
}
|
||||
|
||||
TEST(Suppressions, Match) {
|
||||
EXPECT_TRUE(MyMatch("foobar", "foobar"));
|
||||
EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix"));
|
||||
EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar", "foobar"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz"));
|
||||
|
||||
EXPECT_FALSE(MyMatch("foo", "baz"));
|
||||
EXPECT_FALSE(MyMatch("foobarbaz", "foobar"));
|
||||
EXPECT_FALSE(MyMatch("foobarbaz", "barbaz"));
|
||||
EXPECT_FALSE(MyMatch("foo*bar", "foobaz"));
|
||||
EXPECT_FALSE(MyMatch("foo*bar", "foo_baz"));
|
||||
}
|
||||
|
||||
TEST(Suppressions, TypeStrings) {
|
||||
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionNone), "none"));
|
||||
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionRace), "race"));
|
||||
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionMutex), "mutex"));
|
||||
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread"));
|
||||
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal"));
|
||||
// Ensure this test is up-to-date when suppression types are added.
|
||||
CHECK_EQ(SuppressionTypeCount, 5);
|
||||
}
|
||||
|
||||
class SuppressionContextTest : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() { ctx_ = new(placeholder_) SuppressionContext; }
|
||||
virtual void TearDown() { ctx_->~SuppressionContext(); }
|
||||
|
||||
protected:
|
||||
InternalMmapVector<Suppression> *Suppressions() {
|
||||
return &ctx_->suppressions_;
|
||||
}
|
||||
SuppressionContext *ctx_;
|
||||
ALIGNED(64) char placeholder_[sizeof(SuppressionContext)];
|
||||
};
|
||||
|
||||
TEST_F(SuppressionContextTest, Parse) {
|
||||
ctx_->Parse(
|
||||
"race:foo\n"
|
||||
" race:bar\n" // NOLINT
|
||||
"race:baz \n" // NOLINT
|
||||
"# a comment\n"
|
||||
"race:quz\n"
|
||||
); // NOLINT
|
||||
EXPECT_EQ((unsigned)4, ctx_->SuppressionCount());
|
||||
EXPECT_EQ((*Suppressions())[3].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz"));
|
||||
EXPECT_EQ((*Suppressions())[2].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz"));
|
||||
EXPECT_EQ((*Suppressions())[1].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar"));
|
||||
EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionContextTest, Parse2) {
|
||||
ctx_->Parse(
|
||||
" # first line comment\n" // NOLINT
|
||||
" race:bar \n" // NOLINT
|
||||
"race:baz* *baz\n"
|
||||
"# a comment\n"
|
||||
"# last line comment\n"
|
||||
); // NOLINT
|
||||
EXPECT_EQ((unsigned)2, ctx_->SuppressionCount());
|
||||
EXPECT_EQ((*Suppressions())[1].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "baz* *baz"));
|
||||
EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "bar"));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionContextTest, Parse3) {
|
||||
ctx_->Parse(
|
||||
"# last suppression w/o line-feed\n"
|
||||
"race:foo\n"
|
||||
"race:bar"
|
||||
); // NOLINT
|
||||
EXPECT_EQ((unsigned)2, ctx_->SuppressionCount());
|
||||
EXPECT_EQ((*Suppressions())[1].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar"));
|
||||
EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionContextTest, ParseType) {
|
||||
ctx_->Parse(
|
||||
"race:foo\n"
|
||||
"thread:bar\n"
|
||||
"mutex:baz\n"
|
||||
"signal:quz\n"
|
||||
); // NOLINT
|
||||
EXPECT_EQ((unsigned)4, ctx_->SuppressionCount());
|
||||
EXPECT_EQ((*Suppressions())[3].type, SuppressionSignal);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz"));
|
||||
EXPECT_EQ((*Suppressions())[2].type, SuppressionMutex);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz"));
|
||||
EXPECT_EQ((*Suppressions())[1].type, SuppressionThread);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar"));
|
||||
EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo"));
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
|
@ -162,7 +162,6 @@ class ReportDesc;
|
|||
class RegionAlloc;
|
||||
class StackTrace;
|
||||
struct MBlock;
|
||||
struct Suppression;
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "sanitizer_common/sanitizer_thread_registry.h"
|
||||
#include "tsan_clock.h"
|
||||
#include "tsan_defs.h"
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "tsan_suppressions.h"
|
||||
#include "tsan_rtl.h"
|
||||
#include "tsan_flags.h"
|
||||
|
@ -28,7 +30,7 @@ extern "C" const char *WEAK __tsan_default_suppressions() {
|
|||
|
||||
namespace __tsan {
|
||||
|
||||
static Suppression *g_suppressions;
|
||||
static SuppressionContext* g_ctx;
|
||||
|
||||
static char *ReadFile(const char *filename) {
|
||||
if (filename == 0 || filename[0] == 0)
|
||||
|
@ -62,87 +64,14 @@ static char *ReadFile(const char *filename) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
bool SuppressionMatch(char *templ, const char *str) {
|
||||
if (str == 0 || str[0] == 0)
|
||||
return false;
|
||||
char *tpos;
|
||||
const char *spos;
|
||||
while (templ && templ[0]) {
|
||||
if (templ[0] == '*') {
|
||||
templ++;
|
||||
continue;
|
||||
}
|
||||
if (str[0] == 0)
|
||||
return false;
|
||||
tpos = (char*)internal_strchr(templ, '*');
|
||||
if (tpos != 0)
|
||||
tpos[0] = 0;
|
||||
spos = internal_strstr(str, templ);
|
||||
str = spos + internal_strlen(templ);
|
||||
templ = tpos;
|
||||
if (tpos)
|
||||
tpos[0] = '*';
|
||||
if (spos == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Suppression *SuppressionParse(Suppression *head, const char* supp) {
|
||||
const char *line = supp;
|
||||
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--;
|
||||
SuppressionType stype;
|
||||
if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) {
|
||||
stype = SuppressionRace;
|
||||
line += sizeof("race:") - 1;
|
||||
} else if (0 == internal_strncmp(line, "thread:",
|
||||
sizeof("thread:") - 1)) {
|
||||
stype = SuppressionThread;
|
||||
line += sizeof("thread:") - 1;
|
||||
} else if (0 == internal_strncmp(line, "mutex:",
|
||||
sizeof("mutex:") - 1)) {
|
||||
stype = SuppressionMutex;
|
||||
line += sizeof("mutex:") - 1;
|
||||
} else if (0 == internal_strncmp(line, "signal:",
|
||||
sizeof("signal:") - 1)) {
|
||||
stype = SuppressionSignal;
|
||||
line += sizeof("signal:") - 1;
|
||||
} else {
|
||||
Printf("ThreadSanitizer: failed to parse suppressions file\n");
|
||||
Die();
|
||||
}
|
||||
Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
|
||||
sizeof(Suppression));
|
||||
s->next = head;
|
||||
head = s;
|
||||
s->type = stype;
|
||||
s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1);
|
||||
internal_memcpy(s->templ, line, end2 - line);
|
||||
s->templ[end2 - line] = 0;
|
||||
s->hit_count = 0;
|
||||
}
|
||||
if (end[0] == 0)
|
||||
break;
|
||||
line = end + 1;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
void InitializeSuppressions() {
|
||||
ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
|
||||
g_ctx = new(placeholder_) SuppressionContext;
|
||||
const char *supp = ReadFile(flags()->suppressions);
|
||||
g_suppressions = SuppressionParse(0, supp);
|
||||
g_ctx->Parse(supp);
|
||||
#ifndef TSAN_GO
|
||||
supp = __tsan_default_suppressions();
|
||||
g_suppressions = SuppressionParse(g_suppressions, supp);
|
||||
g_ctx->Parse(supp);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -166,71 +95,59 @@ SuppressionType conv(ReportType typ) {
|
|||
}
|
||||
|
||||
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
|
||||
if (g_suppressions == 0 || stack == 0)
|
||||
return 0;
|
||||
CHECK(g_ctx);
|
||||
if (!g_ctx->SuppressionCount() || stack == 0) return 0;
|
||||
SuppressionType stype = conv(typ);
|
||||
if (stype == SuppressionNone)
|
||||
return 0;
|
||||
Suppression *s;
|
||||
for (const ReportStack *frame = stack; frame; frame = frame->next) {
|
||||
for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
|
||||
if (stype == supp->type &&
|
||||
(SuppressionMatch(supp->templ, frame->func) ||
|
||||
SuppressionMatch(supp->templ, frame->file) ||
|
||||
SuppressionMatch(supp->templ, frame->module))) {
|
||||
DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
|
||||
supp->hit_count++;
|
||||
*sp = supp;
|
||||
return frame->pc;
|
||||
}
|
||||
if (g_ctx->Match(frame->func, stype, &s) ||
|
||||
g_ctx->Match(frame->file, stype, &s) ||
|
||||
g_ctx->Match(frame->module, stype, &s)) {
|
||||
DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
|
||||
s->hit_count++;
|
||||
*sp = s;
|
||||
return frame->pc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
|
||||
if (g_suppressions == 0 || loc == 0 || loc->type != ReportLocationGlobal)
|
||||
CHECK(g_ctx);
|
||||
if (!g_ctx->SuppressionCount() || loc == 0 ||
|
||||
loc->type != ReportLocationGlobal)
|
||||
return 0;
|
||||
SuppressionType stype = conv(typ);
|
||||
if (stype == SuppressionNone)
|
||||
return 0;
|
||||
for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
|
||||
if (stype == supp->type &&
|
||||
(SuppressionMatch(supp->templ, loc->name) ||
|
||||
SuppressionMatch(supp->templ, loc->file) ||
|
||||
SuppressionMatch(supp->templ, loc->module))) {
|
||||
DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
|
||||
supp->hit_count++;
|
||||
*sp = supp;
|
||||
Suppression *s;
|
||||
if (g_ctx->Match(loc->name, stype, &s) ||
|
||||
g_ctx->Match(loc->file, stype, &s) ||
|
||||
g_ctx->Match(loc->module, stype, &s)) {
|
||||
DPrintf("ThreadSanitizer: matched suppression '%s'\n", templ);
|
||||
s->hit_count++;
|
||||
*sp = s;
|
||||
return loc->addr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *SuppTypeStr(SuppressionType t) {
|
||||
switch (t) {
|
||||
case SuppressionNone: return "none";
|
||||
case SuppressionRace: return "race";
|
||||
case SuppressionMutex: return "mutex";
|
||||
case SuppressionThread: return "thread";
|
||||
case SuppressionSignal: return "signal";
|
||||
}
|
||||
CHECK(0);
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void PrintMatchedSuppressions() {
|
||||
int hit_count = 0;
|
||||
for (Suppression *supp = g_suppressions; supp; supp = supp->next)
|
||||
hit_count += supp->hit_count;
|
||||
if (hit_count == 0)
|
||||
CHECK(g_ctx);
|
||||
InternalMmapVector<Suppression *> matched(1);
|
||||
g_ctx->GetMatched(&matched);
|
||||
if (!matched.size())
|
||||
return;
|
||||
Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n",
|
||||
hit_count, (int)internal_getpid());
|
||||
for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
|
||||
if (supp->hit_count == 0)
|
||||
continue;
|
||||
Printf("%d %s:%s\n", supp->hit_count, SuppTypeStr(supp->type), supp->templ);
|
||||
int hit_count = 0;
|
||||
for (uptr i = 0; i < matched.size(); i++)
|
||||
hit_count += matched[i]->hit_count;
|
||||
Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count,
|
||||
(int)internal_getpid());
|
||||
for (uptr i = 0; i < matched.size(); i++) {
|
||||
Printf("%d %s:%s\n", matched[i]->hit_count,
|
||||
SuppressionTypeString(matched[i]->type), matched[i]->templ);
|
||||
}
|
||||
}
|
||||
} // namespace __tsan
|
||||
|
|
|
@ -13,33 +13,15 @@
|
|||
#ifndef TSAN_SUPPRESSIONS_H
|
||||
#define TSAN_SUPPRESSIONS_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "tsan_report.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
// Exposed for testing.
|
||||
enum SuppressionType {
|
||||
SuppressionNone,
|
||||
SuppressionRace,
|
||||
SuppressionMutex,
|
||||
SuppressionThread,
|
||||
SuppressionSignal
|
||||
};
|
||||
|
||||
struct Suppression {
|
||||
Suppression *next;
|
||||
SuppressionType type;
|
||||
char *templ;
|
||||
int hit_count;
|
||||
};
|
||||
|
||||
void InitializeSuppressions();
|
||||
void FinalizeSuppressions();
|
||||
void PrintMatchedSuppressions();
|
||||
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
|
||||
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp);
|
||||
Suppression *SuppressionParse(Suppression *head, const char* supp);
|
||||
bool SuppressionMatch(char *templ, const char *str);
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ set(TSAN_UNIT_TESTS
|
|||
tsan_mutex_test.cc
|
||||
tsan_shadow_test.cc
|
||||
tsan_stack_test.cc
|
||||
tsan_suppressions_test.cc
|
||||
tsan_sync_test.cc
|
||||
tsan_vector_test.cc
|
||||
)
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
//===-- tsan_suppressions_test.cc -----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer (TSan), a race detector.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "tsan_suppressions.h"
|
||||
#include "tsan_rtl.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
TEST(Suppressions, Parse) {
|
||||
ScopedInRtl in_rtl;
|
||||
Suppression *supp0 = SuppressionParse(0,
|
||||
"race:foo\n"
|
||||
" race:bar\n" // NOLINT
|
||||
"race:baz \n" // NOLINT
|
||||
"# a comment\n"
|
||||
"race:quz\n"
|
||||
); // NOLINT
|
||||
Suppression *supp = supp0;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "quz"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "baz"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "bar"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "foo"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ((Suppression*)0, supp);
|
||||
}
|
||||
|
||||
TEST(Suppressions, Parse2) {
|
||||
ScopedInRtl in_rtl;
|
||||
Suppression *supp0 = SuppressionParse(0,
|
||||
" # first line comment\n" // NOLINT
|
||||
" race:bar \n" // NOLINT
|
||||
"race:baz* *baz\n"
|
||||
"# a comment\n"
|
||||
"# last line comment\n"
|
||||
); // NOLINT
|
||||
Suppression *supp = supp0;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "baz* *baz"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "bar"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ((Suppression*)0, supp);
|
||||
}
|
||||
|
||||
TEST(Suppressions, Parse3) {
|
||||
ScopedInRtl in_rtl;
|
||||
Suppression *supp0 = SuppressionParse(0,
|
||||
"# last suppression w/o line-feed\n"
|
||||
"race:foo\n"
|
||||
"race:bar"
|
||||
); // NOLINT
|
||||
Suppression *supp = supp0;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "bar"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "foo"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ((Suppression*)0, supp);
|
||||
}
|
||||
|
||||
TEST(Suppressions, ParseType) {
|
||||
ScopedInRtl in_rtl;
|
||||
Suppression *supp0 = SuppressionParse(0,
|
||||
"race:foo\n"
|
||||
"thread:bar\n"
|
||||
"mutex:baz\n"
|
||||
"signal:quz\n"
|
||||
); // NOLINT
|
||||
Suppression *supp = supp0;
|
||||
EXPECT_EQ(supp->type, SuppressionSignal);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "quz"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionMutex);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "baz"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionThread);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "bar"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ(supp->type, SuppressionRace);
|
||||
EXPECT_EQ(0, strcmp(supp->templ, "foo"));
|
||||
supp = supp->next;
|
||||
EXPECT_EQ((Suppression*)0, supp);
|
||||
}
|
||||
|
||||
static bool MyMatch(const char *templ, const char *func) {
|
||||
char tmp[1024];
|
||||
strcpy(tmp, templ); // NOLINT
|
||||
return SuppressionMatch(tmp, func);
|
||||
}
|
||||
|
||||
TEST(Suppressions, Match) {
|
||||
EXPECT_TRUE(MyMatch("foobar", "foobar"));
|
||||
EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix"));
|
||||
EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar", "foobar"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz"));
|
||||
EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz"));
|
||||
|
||||
EXPECT_FALSE(MyMatch("foo", "baz"));
|
||||
EXPECT_FALSE(MyMatch("foobarbaz", "foobar"));
|
||||
EXPECT_FALSE(MyMatch("foobarbaz", "barbaz"));
|
||||
EXPECT_FALSE(MyMatch("foo*bar", "foobaz"));
|
||||
EXPECT_FALSE(MyMatch("foo*bar", "foo_baz"));
|
||||
}
|
||||
|
||||
} // namespace __tsan
|
Loading…
Reference in New Issue