[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:
Sergey Matveev 2013-06-26 15:37:14 +00:00
parent a3d1c02356
commit d109eb052b
11 changed files with 362 additions and 271 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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

View File

@ -162,7 +162,6 @@ class ReportDesc;
class RegionAlloc;
class StackTrace;
struct MBlock;
struct Suppression;
} // namespace __tsan

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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