diff options
author | kcc <kcc@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-05 09:18:38 +0000 |
---|---|---|
committer | kcc <kcc@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-05 09:18:38 +0000 |
commit | 4fc7b5acfc1d42a0701c8fff726a3ebe7f563dd9 (patch) | |
tree | 20d85354103063e38b162a6a90b7ae51fb4b6104 /libsanitizer/lsan | |
parent | 50e6c257ee5ad435e3a736a1375ccc7639fd9244 (diff) | |
download | gcc-4fc7b5acfc1d42a0701c8fff726a3ebe7f563dd9.tar.gz |
libsanitizer merge from upstream r196090
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@205695 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libsanitizer/lsan')
-rw-r--r-- | libsanitizer/lsan/Makefile.am | 1 | ||||
-rw-r--r-- | libsanitizer/lsan/Makefile.in | 4 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan.cc | 31 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan.h | 6 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_allocator.cc | 9 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common.cc | 39 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_common.h | 2 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_interceptors.cc | 54 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_preinit.cc | 24 | ||||
-rw-r--r-- | libsanitizer/lsan/lsan_thread.cc | 4 |
10 files changed, 124 insertions, 50 deletions
diff --git a/libsanitizer/lsan/Makefile.am b/libsanitizer/lsan/Makefile.am index 36fd6058841..4784d7cbdc3 100644 --- a/libsanitizer/lsan/Makefile.am +++ b/libsanitizer/lsan/Makefile.am @@ -22,6 +22,7 @@ lsan_files = \ lsan.cc \ lsan_allocator.cc \ lsan_interceptors.cc \ + lsan_preinit.cc \ lsan_thread.cc libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files) diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in index 9296b7048ab..b09469e0990 100644 --- a/libsanitizer/lsan/Makefile.in +++ b/libsanitizer/lsan/Makefile.in @@ -83,7 +83,7 @@ liblsan_la_DEPENDENCIES = \ $(am__DEPENDENCIES_1) am__objects_1 = lsan_common.lo lsan_common_linux.lo am__objects_2 = $(am__objects_1) lsan.lo lsan_allocator.lo \ - lsan_interceptors.lo lsan_thread.lo + lsan_interceptors.lo lsan_preinit.lo lsan_thread.lo am_liblsan_la_OBJECTS = $(am__objects_2) liblsan_la_OBJECTS = $(am_liblsan_la_OBJECTS) liblsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ @@ -264,6 +264,7 @@ lsan_files = \ lsan.cc \ lsan_allocator.cc \ lsan_interceptors.cc \ + lsan_preinit.cc \ lsan_thread.cc libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files) @@ -400,6 +401,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_interceptors.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_preinit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_thread.Plo@am__quote@ .cc.o: diff --git a/libsanitizer/lsan/lsan.cc b/libsanitizer/lsan/lsan.cc index 500da50622c..270979a78e7 100644 --- a/libsanitizer/lsan/lsan.cc +++ b/libsanitizer/lsan/lsan.cc @@ -18,26 +18,30 @@ #include "lsan_common.h" #include "lsan_thread.h" +bool lsan_inited; +bool lsan_init_is_running; + namespace __lsan { static void InitializeCommonFlags() { CommonFlags *cf = common_flags(); + SetCommonFlagsDefaults(cf); cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf->symbolize = true; - cf->strip_path_prefix = ""; - cf->fast_unwind_on_malloc = true; cf->malloc_context_size = 30; cf->detect_leaks = true; - cf->leak_check_at_exit = true; - ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS")); + ParseCommonFlagsFromString(cf, GetEnv("LSAN_OPTIONS")); } -void Init() { - static bool inited; - if (inited) +} // namespace __lsan + +using namespace __lsan; // NOLINT + +extern "C" void __lsan_init() { + CHECK(!lsan_init_is_running); + if (lsan_inited) return; - inited = true; + lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; InitializeCommonFlags(); InitializeAllocator(); @@ -51,13 +55,14 @@ void Init() { // Start symbolizer process if necessary. if (common_flags()->symbolize) { - getSymbolizer() - ->InitializeExternal(common_flags()->external_symbolizer_path); + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); } InitCommonLsan(); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) Atexit(DoLeakCheck); + lsan_inited = true; + lsan_init_is_running = false; } - -} // namespace __lsan diff --git a/libsanitizer/lsan/lsan.h b/libsanitizer/lsan/lsan.h index 18ff5da6281..8a5030ce878 100644 --- a/libsanitizer/lsan/lsan.h +++ b/libsanitizer/lsan/lsan.h @@ -15,7 +15,11 @@ namespace __lsan { -void Init(); void InitializeInterceptors(); } // namespace __lsan + +extern bool lsan_inited; +extern bool lsan_init_is_running; + +extern "C" void __lsan_init(); diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc index 66af603e656..ce47dfcd215 100644 --- a/libsanitizer/lsan/lsan_allocator.cc +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -18,6 +18,8 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_common.h" +extern "C" void *memset(void *ptr, int value, uptr num); + namespace __lsan { static const uptr kMaxAllowedMallocSize = 8UL << 30; @@ -32,7 +34,7 @@ struct ChunkMetadata { }; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, - sizeof(ChunkMetadata), CompactSizeClassMap> PrimaryAllocator; + sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator; typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, @@ -78,7 +80,10 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); return 0; } - void *p = allocator.Allocate(&cache, size, alignment, cleared); + void *p = allocator.Allocate(&cache, size, alignment, false); + // Do not rely on the allocator to clear the memory (it's slow). + if (cleared && allocator.FromPrimary(p)) + memset(p, 0, size); RegisterAllocation(stack, p, size); return p; } diff --git a/libsanitizer/lsan/lsan_common.cc b/libsanitizer/lsan/lsan_common.cc index ce82430f48b..bbc5b5f0378 100644 --- a/libsanitizer/lsan/lsan_common.cc +++ b/libsanitizer/lsan/lsan_common.cc @@ -91,8 +91,12 @@ void InitializeSuppressions() { void InitCommonLsan() { InitializeFlags(); - InitializeSuppressions(); - InitializePlatformSpecificModules(); + if (common_flags()->detect_leaks) { + // Initialization which can fail or print warnings should only be done if + // LSan is actually enabled. + InitializeSuppressions(); + InitializePlatformSpecificModules(); + } } class Decorator: private __sanitizer::AnsiColorDecorator { @@ -136,6 +140,8 @@ void ScanRangeForPointers(uptr begin, uptr end, if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue; uptr chunk = PointsIntoChunk(p); if (!chunk) continue; + // Pointers to self don't count. This matters when tag == kIndirectlyLeaked. + if (chunk == begin) continue; LsanMetadata m(chunk); // Reachable beats ignored beats leaked. if (m.tag() == kReachable) continue; @@ -149,6 +155,11 @@ void ScanRangeForPointers(uptr begin, uptr end, } } +void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { + Frontier *frontier = reinterpret_cast<Frontier *>(arg); + ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); +} + // Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { @@ -197,6 +208,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, } ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", kReachable); + ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier); } if (flags()->use_tls) { @@ -261,6 +273,8 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // The check here is relatively expensive, so we do this in a separate flood // fill. That way we can skip the check for chunks that are reachable // otherwise. + if (flags()->log_pointers) + Report("Processing platform-specific allocations.\n"); ProcessPlatformSpecificAllocations(&frontier); FloodFillTag(&frontier, kReachable); @@ -281,8 +295,7 @@ static void PrintStackTraceById(u32 stack_trace_id) { CHECK(stack_trace_id); uptr size = 0; const uptr *trace = StackDepotGet(stack_trace_id, &size); - StackTrace::PrintStack(trace, size, common_flags()->symbolize, - common_flags()->strip_path_prefix, 0); + StackTrace::PrintStack(trace, size); } // ForEachChunk callback. Aggregates unreachable chunks into a LeakReport. @@ -400,8 +413,8 @@ 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 = - getSymbolizer()->SymbolizeCode(addr, addr_frames.data(), kMaxAddrFrames); + uptr addr_frames_num = Symbolizer::Get()->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) || @@ -479,7 +492,6 @@ void LeakReport::PrintLargest(uptr num_leaks_to_print) { leaks_[i].total_size, leaks_[i].hit_count); Printf("%s", d.End()); PrintStackTraceById(leaks_[i].stack_trace_id); - Printf("\n"); leaks_printed++; if (leaks_printed == num_leaks_to_print) break; } @@ -497,12 +509,11 @@ void LeakReport::PrintSummary() { bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } - const int kMaxSummaryLength = 128; InternalScopedBuffer<char> summary(kMaxSummaryLength); - internal_snprintf(summary.data(), kMaxSummaryLength, - "LeakSanitizer: %zu byte(s) leaked in %zu allocation(s).", - bytes, allocations); - __sanitizer_report_error_summary(summary.data()); + internal_snprintf(summary.data(), summary.size(), + "%zu byte(s) leaked in %zu allocation(s).", bytes, + allocations); + ReportErrorSummary(summary.data()); } uptr LeakReport::ApplySuppressions() { @@ -528,6 +539,8 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __lsan_ignore_object(const void *p) { #if CAN_SANITIZE_LEAKS + if (!common_flags()->detect_leaks) + return; // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not // locked. BlockingMutexLock l(&global_mutex); @@ -552,7 +565,7 @@ void __lsan_disable() { SANITIZER_INTERFACE_ATTRIBUTE void __lsan_enable() { #if CAN_SANITIZE_LEAKS - if (!__lsan::disable_counter) { + if (!__lsan::disable_counter && common_flags()->detect_leaks) { Report("Unmatched call to __lsan_enable().\n"); Die(); } diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h index 7906ecb9177..5d9b4eb62e1 100644 --- a/libsanitizer/lsan/lsan_common.h +++ b/libsanitizer/lsan/lsan_common.h @@ -133,6 +133,8 @@ void UnlockThreadRegistry(); bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end); +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg); // If called from the main thread, updates the main thread's TID in the thread // registry. We need this to handle processes that fork() without a subsequent // exec(), which invalidates the recorded TID. To update it, we must call diff --git a/libsanitizer/lsan/lsan_interceptors.cc b/libsanitizer/lsan/lsan_interceptors.cc index 40ddc7773e2..1940902ef83 100644 --- a/libsanitizer/lsan/lsan_interceptors.cc +++ b/libsanitizer/lsan/lsan_interceptors.cc @@ -42,11 +42,17 @@ int pthread_setspecific(unsigned key, const void *v); stack_top = t->stack_end(); \ stack_bottom = t->stack_begin(); \ } \ - GetStackTrace(&stack, __sanitizer::common_flags()->malloc_context_size, \ - StackTrace::GetCurrentPc(), \ - GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ + stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ } +#define ENSURE_LSAN_INITED do { \ + CHECK(!lsan_init_is_running); \ + if (!lsan_inited) \ + __lsan_init(); \ +} while (0) + ///// Malloc/free interceptors. ///// const bool kAlwaysClearMemory = true; @@ -56,38 +62,49 @@ namespace std { } INTERCEPTOR(void*, malloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Allocate(stack, size, 1, kAlwaysClearMemory); } INTERCEPTOR(void, free, void *p) { - Init(); + ENSURE_LSAN_INITED; Deallocate(p); } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (lsan_init_is_running) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; size *= nmemb; return Allocate(stack, size, 1, true); } INTERCEPTOR(void*, realloc, void *q, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Reallocate(stack, q, size, 1); } INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Allocate(stack, size, alignment, kAlwaysClearMemory); } INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); // FIXME: Return ENOMEM if user requested more than max alloc size. @@ -95,7 +112,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { } INTERCEPTOR(void*, valloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; if (size == 0) size = GetPageSizeCached(); @@ -103,7 +120,7 @@ INTERCEPTOR(void*, valloc, uptr size) { } INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - Init(); + ENSURE_LSAN_INITED; return GetMallocUsableSize(ptr); } @@ -122,7 +139,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) { } INTERCEPTOR(void*, pvalloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); @@ -136,7 +153,7 @@ INTERCEPTOR(void*, pvalloc, uptr size) { INTERCEPTOR(void, cfree, void *p) ALIAS("free"); #define OPERATOR_NEW_BODY \ - Init(); \ + ENSURE_LSAN_INITED; \ GET_STACK_TRACE; \ return Allocate(stack, size, 1, kAlwaysClearMemory); @@ -150,7 +167,7 @@ INTERCEPTOR_ATTRIBUTE void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } #define OPERATOR_DELETE_BODY \ - Init(); \ + ENSURE_LSAN_INITED; \ Deallocate(ptr); INTERCEPTOR_ATTRIBUTE @@ -190,9 +207,6 @@ struct ThreadParam { atomic_uintptr_t tid; }; -// PTHREAD_DESTRUCTOR_ITERATIONS from glibc. -const uptr kPthreadDestructorIterations = 4; - extern "C" void *__lsan_thread_start_func(void *arg) { ThreadParam *p = (ThreadParam*)arg; void* (*callback)(void *arg) = p->callback; @@ -215,14 +229,14 @@ extern "C" void *__lsan_thread_start_func(void *arg) { INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void *), void *param) { - Init(); + ENSURE_LSAN_INITED; EnsureMainThreadIDIsCorrect(); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } - AdjustStackSizeLinux(attr, 0); + AdjustStackSizeLinux(attr); int detached = 0; pthread_attr_getdetachstate(attr, &detached); ThreadParam p; @@ -243,7 +257,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, } INTERCEPTOR(int, pthread_join, void *th, void **ret) { - Init(); + ENSURE_LSAN_INITED; int tid = ThreadTid((uptr)th); int res = REAL(pthread_join)(th, ret); if (res == 0) diff --git a/libsanitizer/lsan/lsan_preinit.cc b/libsanitizer/lsan/lsan_preinit.cc new file mode 100644 index 00000000000..856f9f78787 --- /dev/null +++ b/libsanitizer/lsan/lsan_preinit.cc @@ -0,0 +1,24 @@ +//===-- lsan_preinit.cc ---------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// +// Call __lsan_init at the very early stage of process startup. +//===----------------------------------------------------------------------===// + +#include "lsan.h" + +#ifndef LSAN_USE_PREINIT_ARRAY +#define LSAN_USE_PREINIT_ARRAY 1 +#endif + +#if LSAN_USE_PREINIT_ARRAY && !defined(PIC) + // We force __lsan_init to be called before anyone else by placing it into + // .preinit_array section. + __attribute__((section(".preinit_array"), used)) + void (*__local_lsan_preinit)(void) = __lsan_init; +#endif diff --git a/libsanitizer/lsan/lsan_thread.cc b/libsanitizer/lsan/lsan_thread.cc index c260972cb47..07f9d0ab439 100644 --- a/libsanitizer/lsan/lsan_thread.cc +++ b/libsanitizer/lsan/lsan_thread.cc @@ -143,6 +143,10 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, return true; } +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { +} + void LockThreadRegistry() { thread_registry->Lock(); } |