Initial import of ThreadSanitizer.
https://data-race-test.googlecode.com/svn/trunk@3163
Change-Id: I8a134064048ae808e3c68b2eae10b795e15b9459
diff --git a/tsan/suppressions.cc b/tsan/suppressions.cc
new file mode 100644
index 0000000..3f4787d
--- /dev/null
+++ b/tsan/suppressions.cc
@@ -0,0 +1,391 @@
+/* Copyright (c) 2008-2010, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file is part of ThreadSanitizer, a dynamic data race detector.
+// Author: Evgeniy Stepanov.
+
+// This file contains the parser for valgrind-compatible suppressions.
+
+#include "common_util.h"
+#include "ts_util.h"
+
+#include "suppressions.h"
+
+// TODO(eugenis): convert checks to warning messages.
+// TODO(eugenis): write tests for incorrect syntax.
+
+enum LocationType {
+ LT_STAR, // ...
+ LT_OBJ, // obj:
+ LT_FUN, // fun:
+};
+
+struct Location {
+ LocationType type;
+ string name;
+};
+
+struct StackTraceTemplate {
+ vector<Location> locations;
+};
+
+struct Suppression {
+ string name;
+ set<string> tools;
+ string warning_name;
+ // Extra information available for some suppression types.
+ // ex.: Memcheck:Param
+ string extra;
+ vector<StackTraceTemplate> templates;
+};
+
+class Parser {
+ public:
+ explicit Parser(const string &str)
+ : buffer_(str), next_(buffer_.c_str()),
+ end_(buffer_.c_str() + buffer_.size()), line_no_(0), error_(false) {}
+
+ bool NextSuppression(Suppression* suppression);
+ bool GetError();
+ string GetErrorString();
+ int GetLineNo();
+
+ private:
+ bool Eof() { return next_ >= end_; }
+ string NextLine();
+ string NextLineSkipComments();
+ void PutBackSkipComments(string line);
+ bool ParseSuppressionToolsLine(Suppression* supp, string line);
+ bool IsExtraLine(string line);
+ bool ParseStackTraceLine(StackTraceTemplate* trace, string line);
+ bool NextStackTraceTemplate(StackTraceTemplate* trace, bool* last);
+
+ void SetError(string desc);
+
+ const string& buffer_;
+ const char* next_;
+ const char* end_;
+ stack<string> put_back_stack_;
+
+ int line_no_;
+ bool error_;
+ string error_string_;
+};
+
+#define PARSER_CHECK(cond, desc) do {\
+ if (!(cond)) {\
+ SetError(desc);\
+ return false;\
+ }} while (0)
+
+void Parser::SetError(string desc) {
+ error_ = true;
+ error_string_ = desc;
+}
+
+bool Parser::GetError() {
+ return error_;
+}
+
+string Parser::GetErrorString() {
+ return error_string_;
+}
+
+int Parser::GetLineNo() {
+ return line_no_;
+}
+
+string Parser::NextLine() {
+ const char* first = next_;
+ while (!Eof() && *next_ != '\n') {
+ ++next_;
+ }
+ string line(first, next_ - first);
+ if (*next_ == '\n') {
+ ++next_;
+ }
+ ++line_no_;
+ return line;
+}
+
+string Parser::NextLineSkipComments() {
+ string line;
+ if (!put_back_stack_.empty()) {
+ line = put_back_stack_.top();
+ put_back_stack_.pop();
+ return line;
+ }
+ while (!Eof()) {
+ line = NextLine();
+ // Skip empty lines.
+ if (line.empty())
+ continue;
+ // Skip comments.
+ if (line[0] == '#')
+ continue;
+ const char* p = line.c_str();
+ const char* e = p + line.size();
+ // Strip whitespace.
+ while (p < e && (*p == ' ' || *p == '\t'))
+ ++p;
+ if (p >= e)
+ continue;
+ const char* last = e - 1;
+ while (last > p && (*last == ' ' || *last == '\t'))
+ --last;
+ return string(p, last - p + 1);
+ }
+ return "";
+}
+
+void Parser::PutBackSkipComments(string line) {
+ put_back_stack_.push(line);
+}
+
+bool Parser::ParseSuppressionToolsLine(Suppression* supp, string line) {
+ size_t idx = line.find(':');
+ PARSER_CHECK(idx != string::npos, "expected ':' in tools line");
+ string s1 = line.substr(0, idx);
+ string s2 = line.substr(idx + 1);
+ PARSER_CHECK(!s1.empty(), "expected non-empty tool(s) name");
+ PARSER_CHECK(!s2.empty(), "expected non-empty warning name");
+ size_t idx2;
+ while ((idx2 = s1.find(',')) != string::npos) {
+ supp->tools.insert(s1.substr(0, idx2));
+ s1.erase(0, idx2 + 1);
+ }
+ supp->tools.insert(s1);
+ supp->warning_name = s2;
+ return true;
+}
+
+bool Parser::ParseStackTraceLine(StackTraceTemplate* trace, string line) {
+ if (line == "...") {
+ Location location = {LT_STAR, ""};
+ trace->locations.push_back(location);
+ return true;
+ } else {
+ size_t idx = line.find(':');
+ PARSER_CHECK(idx != string::npos, "expected ':' in stack trace line");
+ string s1 = line.substr(0, idx);
+ string s2 = line.substr(idx + 1);
+ if (s1 == "obj") {
+ Location location = {LT_OBJ, s2};
+ trace->locations.push_back(location);
+ return true;
+ } else if (s1 == "fun") {
+ Location location = {LT_FUN, s2};
+ trace->locations.push_back(location);
+ return true;
+ } else {
+ SetError("bad stack trace line");
+ return false;
+ }
+ }
+}
+
+// Checks if this line can not be parsed by Parser::NextStackTraceTemplate
+// and, therefore, is an extra information for the suppression.
+bool Parser::IsExtraLine(string line) {
+ if (line == "..." || line == "{" || line == "}")
+ return false;
+ if (line.size() < 4)
+ return true;
+ string prefix = line.substr(0, 4);
+ return !(prefix == "obj:" || prefix == "fun:");
+}
+
+bool Parser::NextStackTraceTemplate(StackTraceTemplate* trace,
+ bool* last_stack_trace) {
+ string line = NextLineSkipComments();
+ if (line == "}") { // No more stack traces in multi-trace syntax
+ *last_stack_trace = true;
+ return false;
+ }
+
+ if (line == "{") { // A multi-trace syntax
+ line = NextLineSkipComments();
+ } else {
+ *last_stack_trace = true;
+ }
+
+ while (true) {
+ if (!ParseStackTraceLine(trace, line))
+ return false;
+ line = NextLineSkipComments();
+ if (line == "}")
+ break;
+ }
+ return true;
+}
+
+bool Parser::NextSuppression(Suppression* supp) {
+ string line;
+ line = NextLineSkipComments();
+ if (line.empty())
+ return false;
+ // Opening {
+ PARSER_CHECK(line == "{", "expected '{'");
+ // Suppression name.
+ line = NextLineSkipComments();
+ PARSER_CHECK(!line.empty(), "expected suppression name");
+ supp->name = line;
+ // tool[,tool]:warning_name.
+ line = NextLineSkipComments();
+ PARSER_CHECK(!line.empty(), "expected tool[, tool]:warning_name line");
+ if (!ParseSuppressionToolsLine(supp, line))
+ return false;
+ if (0) { // Not used currently. May still be needed later.
+ // A possible extra line.
+ line = NextLineSkipComments();
+ if (IsExtraLine(line))
+ supp->extra = line;
+ else
+ PutBackSkipComments(line);
+ }
+ // Everything else.
+ bool done = false;
+ while (!done) {
+ StackTraceTemplate trace;
+ if (NextStackTraceTemplate(&trace, &done))
+ supp->templates.push_back(trace);
+ if (error_)
+ return false;
+ }
+ // TODO(eugenis): Do we need to check for empty traces?
+ return true;
+}
+
+struct Suppressions::SuppressionsRep {
+ vector<Suppression> suppressions;
+ string error_string_;
+ int error_line_no_;
+};
+
+Suppressions::Suppressions() : rep_(new SuppressionsRep) {}
+
+Suppressions::~Suppressions() {
+ delete rep_;
+}
+
+int Suppressions::ReadFromString(const string &str) {
+ int sizeBefore = rep_->suppressions.size();
+ Parser parser(str);
+ Suppression supp;
+ while (parser.NextSuppression(&supp)) {
+ rep_->suppressions.push_back(supp);
+ }
+ if (parser.GetError()) {
+ rep_->error_string_ = parser.GetErrorString();
+ rep_->error_line_no_ = parser.GetLineNo();
+ return -1;
+ }
+ return rep_->suppressions.size() - sizeBefore;
+}
+
+string Suppressions::GetErrorString() {
+ return rep_->error_string_;
+}
+
+int Suppressions::GetErrorLineNo() {
+ return rep_->error_line_no_;
+}
+
+struct MatcherContext {
+ MatcherContext(
+ const vector<string>& function_names_mangled_,
+ const vector<string>& function_names_demangled_,
+ const vector<string>& object_names_) :
+ function_names_mangled(function_names_mangled_),
+ function_names_demangled(function_names_demangled_),
+ object_names(object_names_),
+ tmpl(NULL)
+ {}
+
+ const vector<string>& function_names_mangled;
+ const vector<string>& function_names_demangled;
+ const vector<string>& object_names;
+ StackTraceTemplate* tmpl;
+};
+
+static bool MatchStackTraceRecursive(MatcherContext ctx, int trace_index,
+ int tmpl_index) {
+ const int trace_size = ctx.function_names_mangled.size();
+ const int tmpl_size = ctx.tmpl->locations.size();
+ while (trace_index < trace_size && tmpl_index < tmpl_size) {
+ Location& location = ctx.tmpl->locations[tmpl_index];
+ if (location.type == LT_STAR) {
+ ++tmpl_index;
+ while (trace_index < trace_size) {
+ if (MatchStackTraceRecursive(ctx, trace_index++, tmpl_index))
+ return true;
+ }
+ return false;
+ } else {
+ bool match = false;
+ if (location.type == LT_OBJ) {
+ match = StringMatch(location.name, ctx.object_names[trace_index]);
+ } else {
+ CHECK(location.type == LT_FUN);
+ match =
+ StringMatch(location.name, ctx.function_names_mangled[trace_index]) ||
+ StringMatch(location.name, ctx.function_names_demangled[trace_index]);
+ }
+ if (match) {
+ ++trace_index;
+ ++tmpl_index;
+ } else {
+ return false;
+ }
+ }
+ }
+ return tmpl_index == tmpl_size;
+}
+
+bool Suppressions::StackTraceSuppressed(string tool_name, string warning_name,
+ const vector<string>& function_names_mangled,
+ const vector<string>& function_names_demangled,
+ const vector<string>& object_names,
+ string *name_of_suppression) {
+ MatcherContext ctx(function_names_mangled, function_names_demangled,
+ object_names);
+ for (vector<Suppression>::iterator it = rep_->suppressions.begin();
+ it != rep_->suppressions.end(); ++it) {
+ if (it->warning_name != warning_name ||
+ it->tools.find(tool_name) == it->tools.end())
+ continue;
+ for (vector<StackTraceTemplate>::iterator it2 = it->templates.begin();
+ it2 != it->templates.end(); ++it2) {
+ ctx.tmpl = &*it2;
+ bool result = MatchStackTraceRecursive(ctx, 0, 0);
+ if (result) {
+ *name_of_suppression = it->name;
+ return true;
+ }
+ }
+ }
+ return false;
+}