[compiler-rt] r185152 - [lsan] Add suppression support.

Sergey Matveev earthdok at google.com
Fri Jun 28 07:38:31 PDT 2013


Author: smatveev
Date: Fri Jun 28 09:38:31 2013
New Revision: 185152

URL: http://llvm.org/viewvc/llvm-project?rev=185152&view=rev
Log:
[lsan] Add suppression support.

Added:
    compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_default.cc
    compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc
    compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp
Modified:
    compiler-rt/trunk/lib/lsan/lit_tests/AsanConfig/lit.cfg
    compiler-rt/trunk/lib/lsan/lit_tests/LsanConfig/lit.cfg
    compiler-rt/trunk/lib/lsan/lit_tests/lit.common.cfg
    compiler-rt/trunk/lib/lsan/lsan_common.cc
    compiler-rt/trunk/lib/lsan/lsan_common.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h
    compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc

Modified: compiler-rt/trunk/lib/lsan/lit_tests/AsanConfig/lit.cfg
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/AsanConfig/lit.cfg?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/AsanConfig/lit.cfg (original)
+++ compiler-rt/trunk/lib/lsan/lit_tests/AsanConfig/lit.cfg Fri Jun 28 09:38:31 2013
@@ -24,3 +24,4 @@ config.substitutions.append( ("%clangxx_
                                                 clang_lsan_cxxflags + " ")) )
 
 config.environment['ASAN_OPTIONS'] = 'detect_leaks=1'
+config.environment['ASAN_SYMBOLIZER_PATH'] = config.llvm_symbolizer_path

Modified: compiler-rt/trunk/lib/lsan/lit_tests/LsanConfig/lit.cfg
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/LsanConfig/lit.cfg?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/LsanConfig/lit.cfg (original)
+++ compiler-rt/trunk/lib/lsan/lit_tests/LsanConfig/lit.cfg Fri Jun 28 09:38:31 2013
@@ -22,3 +22,5 @@ clang_lsan_cxxflags = config.clang_cxxfl
 
 config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " +
                                                 clang_lsan_cxxflags + " ")) )
+
+config.environment['LSAN_SYMBOLIZER_PATH'] = config.llvm_symbolizer_path

Added: compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_default.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_default.cc?rev=185152&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_default.cc (added)
+++ compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_default.cc Fri Jun 28 09:38:31 2013
@@ -0,0 +1,29 @@
+// Test for ScopedDisabler.
+// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
+// RUN: %clangxx_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+extern "C"
+const char *__lsan_default_suppressions() {
+  return "leak:*LSanTestLeakingFunc*";
+}
+
+void LSanTestLeakingFunc() {
+  void *p = malloc(666);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+}
+
+int main() {
+  LSanTestLeakingFunc();
+  void *q = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", q);
+  return 0;
+}
+// CHECK: Suppressions used:
+// CHECK: 1 666 *LSanTestLeakingFunc*
+// CHECK: SUMMARY: LeakSanitizer: 1337 byte(s) leaked in 1 allocation(s)

Added: compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc?rev=185152&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc (added)
+++ compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc Fri Jun 28 09:38:31 2013
@@ -0,0 +1,29 @@
+// Test for ScopedDisabler.
+// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
+// RUN: %clangxx_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+extern "C"
+const char *__lsan_default_suppressions() {
+  return "leak:*LSanTestLeakingFunc*";
+}
+
+void LSanTestLeakingFunc() {
+  void *p = malloc(666);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+}
+
+int main() {
+  LSanTestLeakingFunc();
+  void *q = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", q);
+  return 0;
+}
+// CHECK: Suppressions used:
+// CHECK: 1 666 *LSanTestLeakingFunc*
+// CHECK: SUMMARY: LeakSanitizer: 1337 byte(s) leaked in 1 allocation(s)

Added: compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp?rev=185152&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp (added)
+++ compiler-rt/trunk/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp Fri Jun 28 09:38:31 2013
@@ -0,0 +1 @@
+leak:*LSanTestLeakingFunc*

Modified: compiler-rt/trunk/lib/lsan/lit_tests/lit.common.cfg
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lit_tests/lit.common.cfg?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lit_tests/lit.common.cfg (original)
+++ compiler-rt/trunk/lib/lsan/lit_tests/lit.common.cfg Fri Jun 28 09:38:31 2013
@@ -12,6 +12,10 @@ def get_required_attr(config, attr_name)
               "to lit.site.cfg " % attr_name)
   return attr_value
 
+# Setup path to external LLVM symbolizer to run LeakSanitizer output tests.
+llvm_tools_dir = get_required_attr(config, 'llvm_tools_dir')
+config.llvm_symbolizer_path  = os.path.join(llvm_tools_dir, "llvm-symbolizer")
+
 # Setup source root.
 lsan_lit_src_root = get_required_attr(config, 'lsan_lit_src_root')
 config.test_source_root = os.path.join(lsan_lit_src_root, 'TestCases')

Modified: compiler-rt/trunk/lib/lsan/lsan_common.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.cc?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.cc (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.cc Fri Jun 28 09:38:31 2013
@@ -16,9 +16,11 @@
 
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
 #include "sanitizer_common/sanitizer_stacktrace.h"
 #include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
 
 #if CAN_SANITIZE_LEAKS
 namespace __lsan {
@@ -38,6 +40,7 @@ static void InitializeFlags() {
   f->resolution = 0;
   f->max_leaks = 0;
   f->exitcode = 23;
+  f->suppressions="";
   f->use_registers = true;
   f->use_globals = true;
   f->use_stacks = true;
@@ -63,17 +66,39 @@ static void InitializeFlags() {
     ParseFlag(options, &f->log_pointers, "log_pointers");
     ParseFlag(options, &f->log_threads, "log_threads");
     ParseFlag(options, &f->exitcode, "exitcode");
+    ParseFlag(options, &f->suppressions, "suppressions");
   }
 }
 
+SuppressionContext *suppression_ctx;
+
+void InitializeSuppressions() {
+  CHECK(!suppression_ctx);
+  ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
+  suppression_ctx = new(placeholder_) SuppressionContext;
+  char *suppressions_from_file;
+  uptr buffer_size;
+  if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file,
+                       &buffer_size, 1 << 26 /* max_len */))
+    suppression_ctx->Parse(suppressions_from_file);
+  if (flags()->suppressions[0] && !buffer_size) {
+    Printf("LeakSanitizer: failed to read suppressions file '%s'\n",
+           flags()->suppressions);
+    Die();
+  }
+  if (&__lsan_default_suppressions)
+    suppression_ctx->Parse(__lsan_default_suppressions());
+}
+
 void InitCommonLsan() {
   InitializeFlags();
+  InitializeSuppressions();
   InitializePlatformSpecificModules();
 }
 
 static inline bool CanBeAHeapPointer(uptr p) {
   // Since our heap is located in mmap-ed memory, we can assume a sensible lower
-  // boundary on heap addresses.
+  // bound on heap addresses.
   const uptr kMinAddress = 4 * 4096;
   if (p < kMinAddress) return false;
 #ifdef __x86_64__
@@ -158,7 +183,7 @@ static void ProcessThreads(SuspendedThre
         // signal handler on alternate stack). Again, consider the entire stack
         // range to be reachable.
         if (flags()->log_threads)
-          Report("WARNING: stack_pointer not in stack_range.\n");
+          Report("WARNING: stack pointer not in stack range.\n");
       } else {
         // Shrink the stack range to ignore out-of-scope values.
         stack_begin = sp;
@@ -285,6 +310,21 @@ static void PrintLeakedCb(uptr chunk, vo
   }
 }
 
+static void PrintMatchedSuppressions() {
+  InternalMmapVector<Suppression *> matched(1);
+  suppression_ctx->GetMatched(&matched);
+  if (!matched.size())
+    return;
+  const char *line = "-----------------------------------------------------";
+  Printf("%s\n", line);
+  Printf("Suppressions used:\n");
+  Printf("  count      bytes template\n");
+  for (uptr i = 0; i < matched.size(); i++)
+    Printf("%7zu %10zu %s\n", static_cast<uptr>(matched[i]->hit_count),
+           matched[i]->weight, matched[i]->templ);
+  Printf("%s\n\n", line);
+}
+
 static void PrintLeaked() {
   Printf("\n");
   Printf("Reporting individual objects:\n");
@@ -330,16 +370,46 @@ void DoLeakCheck() {
     Die();
   }
   if (!param.leak_report.IsEmpty()) {
+    uptr unsuppressed_count = param.leak_report.ApplySuppressions();
+    if (!unsuppressed_count) return;
     Printf("\n================================================================="
            "\n");
     Report("ERROR: LeakSanitizer: detected memory leaks\n");
     param.leak_report.PrintLargest(flags()->max_leaks);
+    PrintMatchedSuppressions();
     param.leak_report.PrintSummary();
     if (flags()->exitcode)
       internal__exit(flags()->exitcode);
   }
 }
 
+static Suppression *GetSuppressionForAddr(uptr addr) {
+  static const uptr kMaxAddrFrames = 16;
+  InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
+  for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo();
+  uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(),
+                                                    kMaxAddrFrames);
+  for (uptr i = 0; i < addr_frames_num; i++) {
+    Suppression* s;
+    if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) ||
+        suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s) ||
+        suppression_ctx->Match(addr_frames[i].module, SuppressionLeak, &s))
+      return s;
+  }
+  return 0;
+}
+
+static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
+  uptr size = 0;
+  const uptr *trace = StackDepotGet(stack_trace_id, &size);
+  for (uptr i = 0; i < size; i++) {
+    Suppression *s =
+        GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i]));
+    if (s) return s;
+  }
+  return 0;
+}
+
 ///// LeakReport implementation. /////
 
 // A hard limit on the number of distinct leaks, to avoid quadratic complexity
@@ -361,7 +431,7 @@ void LeakReport::Add(u32 stack_trace_id,
     }
   if (leaks_.size() == kMaxLeaksConsidered) return;
   Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id,
-                is_directly_leaked };
+                is_directly_leaked, /* is_suppressed */ false };
   leaks_.push_back(leak);
 }
 
@@ -369,26 +439,33 @@ static bool IsLarger(const Leak &leak1,
   return leak1.total_size > leak2.total_size;
 }
 
-void LeakReport::PrintLargest(uptr max_leaks) {
+void LeakReport::PrintLargest(uptr num_leaks_to_print) {
   CHECK(leaks_.size() <= kMaxLeaksConsidered);
   Printf("\n");
   if (leaks_.size() == kMaxLeaksConsidered)
     Printf("Too many leaks! Only the first %zu leaks encountered will be "
            "reported.\n",
            kMaxLeaksConsidered);
-  if (max_leaks > 0 && max_leaks < leaks_.size())
-    Printf("The %zu largest leak(s):\n", max_leaks);
+
+  uptr unsuppressed_count = 0;
+  for (uptr i = 0; i < leaks_.size(); i++)
+    if (!leaks_[i].is_suppressed) unsuppressed_count++;
+  if (num_leaks_to_print > 0 && num_leaks_to_print < unsuppressed_count)
+    Printf("The %zu largest leak(s):\n", num_leaks_to_print);
   InternalSort(&leaks_, leaks_.size(), IsLarger);
-  max_leaks = max_leaks > 0 ? Min(max_leaks, leaks_.size()) : leaks_.size();
-  for (uptr i = 0; i < max_leaks; i++) {
+  uptr leaks_printed = 0;
+  for (uptr i = 0; i < leaks_.size(); i++) {
+    if (leaks_[i].is_suppressed) continue;
     Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
            leaks_[i].is_directly_leaked ? "Direct" : "Indirect",
            leaks_[i].total_size, leaks_[i].hit_count);
     PrintStackTraceById(leaks_[i].stack_trace_id);
     Printf("\n");
+    leaks_printed = 0;
+    if (leaks_printed == num_leaks_to_print) break;
   }
-  if (max_leaks < leaks_.size()) {
-    uptr remaining = leaks_.size() - max_leaks;
+  if (leaks_printed < unsuppressed_count) {
+    uptr remaining = unsuppressed_count - leaks_printed;
     Printf("Omitting %zu more leak(s).\n", remaining);
   }
 }
@@ -397,6 +474,7 @@ void LeakReport::PrintSummary() {
   CHECK(leaks_.size() <= kMaxLeaksConsidered);
   uptr bytes = 0, allocations = 0;
   for (uptr i = 0; i < leaks_.size(); i++) {
+      if (leaks_[i].is_suppressed) continue;
       bytes += leaks_[i].total_size;
       allocations += leaks_[i].hit_count;
   }
@@ -404,6 +482,21 @@ void LeakReport::PrintSummary() {
       "SUMMARY: LeakSanitizer: %zu byte(s) leaked in %zu allocation(s).\n\n",
       bytes, allocations);
 }
+
+uptr LeakReport::ApplySuppressions() {
+  uptr unsuppressed_count = 0;
+  for (uptr i = 0; i < leaks_.size(); i++) {
+    Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
+    if (s) {
+      s->weight += leaks_[i].total_size;
+      s->hit_count += leaks_[i].hit_count;
+      leaks_[i].is_suppressed = true;
+    } else {
+    unsuppressed_count++;
+    }
+  }
+  return unsuppressed_count;
+}
 }  // namespace __lsan
 #endif  // CAN_SANITIZE_LEAKS
 

Modified: compiler-rt/trunk/lib/lsan/lsan_common.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.h?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.h (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.h Fri Jun 28 09:38:31 2013
@@ -51,6 +51,8 @@ struct Flags {
   int max_leaks;
   // If nonzero kill the process with this exit code upon finding leaks.
   int exitcode;
+  // Suppressions file name.
+  const char* suppressions;
 
   // Flags controlling the root set of reachable memory.
   // Global variables (.data and .bss).
@@ -81,6 +83,7 @@ struct Leak {
   uptr total_size;
   u32 stack_trace_id;
   bool is_directly_leaked;
+  bool is_suppressed;
 };
 
 // Aggregates leaks by stack trace prefix.
@@ -91,6 +94,7 @@ class LeakReport {
   void PrintLargest(uptr max_leaks);
   void PrintSummary();
   bool IsEmpty() { return leaks_.size() == 0; }
+  uptr ApplySuppressions();
  private:
   InternalMmapVector<Leak> leaks_;
 };
@@ -157,6 +161,8 @@ class LsanMetadata {
 extern "C" {
 int __lsan_is_turned_off() SANITIZER_WEAK_ATTRIBUTE
     SANITIZER_INTERFACE_ATTRIBUTE;
+const char *__lsan_default_suppressions() SANITIZER_WEAK_ATTRIBUTE
+    SANITIZER_INTERFACE_ATTRIBUTE;
 }  // extern "C"
 
 #endif  // LSAN_COMMON_H

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc Fri Jun 28 09:38:31 2013
@@ -20,7 +20,7 @@
 namespace __sanitizer {
 
 static const char *const kTypeStrings[SuppressionTypeCount] = {
-  "none", "race", "mutex", "thread", "signal"
+  "none", "race", "mutex", "thread", "signal", "leak"
 };
 
 bool TemplateMatch(char *templ, const char *str) {
@@ -95,7 +95,7 @@ void SuppressionContext::Parse(const cha
         }
       }
       if (type == SuppressionTypeCount) {
-        Printf("%s: failed to parse suppressions file\n", SanitizerToolName);
+        Printf("%s: failed to parse suppressions\n", SanitizerToolName);
         Die();
       }
       Suppression s;
@@ -104,6 +104,7 @@ void SuppressionContext::Parse(const cha
       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)

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h Fri Jun 28 09:38:31 2013
@@ -24,6 +24,7 @@ enum SuppressionType {
   SuppressionMutex,
   SuppressionThread,
   SuppressionSignal,
+  SuppressionLeak,
   SuppressionTypeCount
 };
 
@@ -31,6 +32,7 @@ struct Suppression {
   SuppressionType type;
   char *templ;
   unsigned hit_count;
+  uptr weight;
 };
 
 class SuppressionContext {

Modified: compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc?rev=185152&r1=185151&r2=185152&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc Fri Jun 28 09:38:31 2013
@@ -45,8 +45,9 @@ TEST(Suppressions, TypeStrings) {
   CHECK(!internal_strcmp(SuppressionTypeString(SuppressionMutex), "mutex"));
   CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread"));
   CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal"));
+  CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak"));
   // Ensure this test is up-to-date when suppression types are added.
-  CHECK_EQ(SuppressionTypeCount, 5);
+  CHECK_EQ(SuppressionTypeCount, 6);
 }
 
 class SuppressionContextTest : public ::testing::Test {





More information about the llvm-commits mailing list