diff options
author | Sergey Matveev <earthdok@google.com> | 2013-06-28 14:38:31 +0000 |
---|---|---|
committer | Sergey Matveev <earthdok@google.com> | 2013-06-28 14:38:31 +0000 |
commit | 2b19ee3da8164f62a49e2c593986a59169fcb3e8 (patch) | |
tree | 80cb6cb60ef1594a66c8e31f7d33964f29cab45d /compiler-rt/lib/lsan | |
parent | 7323383bd725396571c47650ecd4de2708b11b17 (diff) | |
download | bcm5719-llvm-2b19ee3da8164f62a49e2c593986a59169fcb3e8.tar.gz bcm5719-llvm-2b19ee3da8164f62a49e2c593986a59169fcb3e8.zip |
[lsan] Add suppression support.
llvm-svn: 185152
Diffstat (limited to 'compiler-rt/lib/lsan')
8 files changed, 175 insertions, 10 deletions
diff --git a/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg b/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg index 9bf3e9cc708..eb242442b02 100644 --- a/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg +++ b/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg @@ -24,3 +24,4 @@ config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + clang_lsan_cxxflags + " ")) ) config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' +config.environment['ASAN_SYMBOLIZER_PATH'] = config.llvm_symbolizer_path diff --git a/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg b/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg index 2273ca26fa7..bc1d5481c15 100644 --- a/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg +++ b/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg @@ -22,3 +22,5 @@ clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=leak " config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + clang_lsan_cxxflags + " ")) ) + +config.environment['LSAN_SYMBOLIZER_PATH'] = config.llvm_symbolizer_path diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_default.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_default.cc new file mode 100644 index 00000000000..92bf5a248de --- /dev/null +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_default.cc @@ -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) diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc new file mode 100644 index 00000000000..92bf5a248de --- /dev/null +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc @@ -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) diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp b/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp new file mode 100644 index 00000000000..8d8e560cba4 --- /dev/null +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp @@ -0,0 +1 @@ +leak:*LSanTestLeakingFunc* diff --git a/compiler-rt/lib/lsan/lit_tests/lit.common.cfg b/compiler-rt/lib/lsan/lit_tests/lit.common.cfg index dfae8db4c89..cf5ccd50932 100644 --- a/compiler-rt/lib/lsan/lit_tests/lit.common.cfg +++ b/compiler-rt/lib/lsan/lit_tests/lit.common.cfg @@ -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') diff --git a/compiler-rt/lib/lsan/lsan_common.cc b/compiler-rt/lib/lsan/lsan_common.cc index 1f0bf970c70..ab3ea77e341 100644 --- a/compiler-rt/lib/lsan/lsan_common.cc +++ b/compiler-rt/lib/lsan/lsan_common.cc @@ -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(SuspendedThreadsList const &suspended_threads, // 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, void *arg) { } } +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, uptr leaked_size, ChunkTag tag) { } 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, const Leak &leak2) { 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 diff --git a/compiler-rt/lib/lsan/lsan_common.h b/compiler-rt/lib/lsan/lsan_common.h index 0ff596cee89..74e6a8175e7 100644 --- a/compiler-rt/lib/lsan/lsan_common.h +++ b/compiler-rt/lib/lsan/lsan_common.h @@ -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 |