Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -13,6 +13,7 @@ sanitizer_printf.cc sanitizer_stackdepot.cc sanitizer_stacktrace.cc + sanitizer_suppressions.cc sanitizer_symbolizer_itanium.cc sanitizer_symbolizer_linux.cc sanitizer_symbolizer_mac.cc Index: lib/sanitizer_common/sanitizer_suppressions.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_suppressions.h @@ -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 *matched); + + private: + InternalMmapVector 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 Index: lib/sanitizer_common/sanitizer_suppressions.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_suppressions.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(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 *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 Index: lib/sanitizer_common/tests/CMakeLists.txt =================================================================== --- lib/sanitizer_common/tests/CMakeLists.txt +++ lib/sanitizer_common/tests/CMakeLists.txt @@ -16,6 +16,7 @@ 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 ) Index: lib/sanitizer_common/tests/sanitizer_suppressions_test.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/tests/sanitizer_suppressions_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 + +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 *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 Index: lib/tsan/rtl/tsan_defs.h =================================================================== --- lib/tsan/rtl/tsan_defs.h +++ lib/tsan/rtl/tsan_defs.h @@ -162,7 +162,6 @@ class RegionAlloc; class StackTrace; struct MBlock; -struct Suppression; } // namespace __tsan Index: lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h @@ -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" Index: lib/tsan/rtl/tsan_suppressions.h =================================================================== --- lib/tsan/rtl/tsan_suppressions.h +++ lib/tsan/rtl/tsan_suppressions.h @@ -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 Index: lib/tsan/rtl/tsan_suppressions.cc =================================================================== --- lib/tsan/rtl/tsan_suppressions.cc +++ lib/tsan/rtl/tsan_suppressions.cc @@ -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 @@ 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 @@ 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 @@ } 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 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 Index: lib/tsan/tests/unit/CMakeLists.txt =================================================================== --- lib/tsan/tests/unit/CMakeLists.txt +++ lib/tsan/tests/unit/CMakeLists.txt @@ -5,7 +5,6 @@ tsan_mutex_test.cc tsan_shadow_test.cc tsan_stack_test.cc - tsan_suppressions_test.cc tsan_sync_test.cc tsan_vector_test.cc ) Index: lib/tsan/tests/unit/tsan_suppressions_test.cc =================================================================== --- lib/tsan/tests/unit/tsan_suppressions_test.cc +++ /dev/null @@ -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 - -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