aboutsummaryrefslogtreecommitdiff
path: root/libsanitizer/lsan
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2013-12-05 09:18:38 +0000
committerKostya Serebryany <kcc@gcc.gnu.org>2013-12-05 09:18:38 +0000
commitdf77f0e4ec043bc4fa155efbd5c1c74ce73d2b50 (patch)
tree20d85354103063e38b162a6a90b7ae51fb4b6104 /libsanitizer/lsan
parent649d196dbd78a119786f204d36b7c5d4dcb3a949 (diff)
libsanitizer merge from upstream r196090
From-SVN: r205695
Diffstat (limited to 'libsanitizer/lsan')
-rw-r--r--libsanitizer/lsan/Makefile.am1
-rw-r--r--libsanitizer/lsan/Makefile.in4
-rw-r--r--libsanitizer/lsan/lsan.cc31
-rw-r--r--libsanitizer/lsan/lsan.h6
-rw-r--r--libsanitizer/lsan/lsan_allocator.cc9
-rw-r--r--libsanitizer/lsan/lsan_common.cc39
-rw-r--r--libsanitizer/lsan/lsan_common.h2
-rw-r--r--libsanitizer/lsan/lsan_interceptors.cc54
-rw-r--r--libsanitizer/lsan/lsan_preinit.cc24
-rw-r--r--libsanitizer/lsan/lsan_thread.cc4
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();
}