Index: third_party/tcmalloc/chromium/src/heap-checker.cc |
=================================================================== |
--- third_party/tcmalloc/chromium/src/heap-checker.cc (revision 124832) |
+++ third_party/tcmalloc/chromium/src/heap-checker.cc (working copy) |
@@ -52,7 +52,7 @@ |
#include <time.h> |
#include <assert.h> |
-#if defined(HAVE_LINUX_PTRACE_H) |
+#ifdef HAVE_LINUX_PTRACE_H |
#include <linux/ptrace.h> |
#endif |
#ifdef HAVE_SYS_SYSCALL_H |
@@ -73,20 +73,20 @@ |
#include <algorithm> |
#include <functional> |
-#include <gperftools/heap-checker.h> |
+#include <google/heap-checker.h> |
#include "base/basictypes.h" |
#include "base/googleinit.h" |
#include "base/logging.h" |
-#include <gperftools/stacktrace.h> |
+#include <google/stacktrace.h> |
#include "base/commandlineflags.h" |
#include "base/elfcore.h" // for i386_regs |
#include "base/thread_lister.h" |
#include "heap-profile-table.h" |
#include "base/low_level_alloc.h" |
#include "malloc_hook-inl.h" |
-#include <gperftools/malloc_hook.h> |
-#include <gperftools/malloc_extension.h> |
+#include <google/malloc_hook.h> |
+#include <google/malloc_extension.h> |
#include "maybe_threads.h" |
#include "memory_region_map.h" |
#include "base/spinlock.h" |
@@ -144,7 +144,7 @@ |
"\"minimal\", \"normal\", \"strict\", " |
"\"draconian\", \"as-is\", and \"local\" " |
" or the empty string are the supported choices. " |
- "(See HeapLeakChecker_InternalInitStart for details.)"); |
+ "(See HeapLeakChecker::InternalInitStart for details.)"); |
DEFINE_bool(heap_check_report, true, "Obsolete"); |
@@ -282,6 +282,10 @@ |
static const int heap_checker_info_level = 0; |
//---------------------------------------------------------------------- |
+// Cancel our InitialMallocHook_* if present. |
+static void CancelInitialMallocHooks(); // defined below |
+ |
+//---------------------------------------------------------------------- |
// HeapLeakChecker's own memory allocator that is |
// independent of the normal program allocator. |
//---------------------------------------------------------------------- |
@@ -549,23 +553,6 @@ |
//---------------------------------------------------------------------- |
-// We've seen reports that strstr causes heap-checker crashes in some |
-// libc's (?): |
-// http://code.google.com/p/gperftools/issues/detail?id=263 |
-// It's simple enough to use our own. This is not in time-critical code. |
-static const char* hc_strstr(const char* s1, const char* s2) { |
- const size_t len = strlen(s2); |
- RAW_CHECK(len > 0, "Unexpected empty string passed to strstr()"); |
- for (const char* p = strchr(s1, *s2); p != NULL; p = strchr(p+1, *s2)) { |
- if (strncmp(p, s2, len) == 0) { |
- return p; |
- } |
- } |
- return NULL; |
-} |
- |
-//---------------------------------------------------------------------- |
- |
// Our hooks for MallocHook |
static void NewHook(const void* ptr, size_t size) { |
if (ptr != NULL) { |
@@ -573,11 +560,6 @@ |
const bool ignore = (counter > 0); |
RAW_VLOG(16, "Recording Alloc: %p of %"PRIuS "; %d", ptr, size, |
int(counter)); |
- |
- // Fetch the caller's stack trace before acquiring heap_checker_lock. |
- void* stack[HeapProfileTable::kMaxStackDepth]; |
- int depth = HeapProfileTable::GetCallerStackTrace(0, stack); |
- |
{ SpinLockHolder l(&heap_checker_lock); |
if (size > max_heap_object_size) max_heap_object_size = size; |
uintptr_t addr = AsInt(ptr); |
@@ -585,7 +567,7 @@ |
addr += size; |
if (addr > max_heap_address) max_heap_address = addr; |
if (heap_checker_on) { |
- heap_profile->RecordAlloc(ptr, size, depth, stack); |
+ heap_profile->RecordAlloc(ptr, size, 0); |
if (ignore) { |
heap_profile->MarkAsIgnored(ptr); |
} |
@@ -788,8 +770,6 @@ |
} |
} |
-static const char kUnnamedProcSelfMapEntry[] = "UNNAMED"; |
- |
// This function takes some fields from a /proc/self/maps line: |
// |
// start_address start address of a memory region. |
@@ -807,9 +787,7 @@ |
RAW_DCHECK(heap_checker_lock.IsHeld(), ""); |
// Ignore non-writeable regions. |
if (strchr(permissions, 'w') == NULL) return; |
- if (filename == NULL || *filename == '\0') { |
- filename = kUnnamedProcSelfMapEntry; |
- } |
+ if (filename == NULL || *filename == '\0') filename = "UNNAMED"; |
RAW_VLOG(11, "Looking into %s: 0x%" PRIxPTR "..0x%" PRIxPTR, |
filename, start_address, end_address); |
(*library_live_objects)[filename]. |
@@ -821,7 +799,7 @@ |
// See if 'library' from /proc/self/maps has base name 'library_base' |
// i.e. contains it and has '.' or '-' after it. |
static bool IsLibraryNamed(const char* library, const char* library_base) { |
- const char* p = hc_strstr(library, library_base); |
+ const char* p = strstr(library, library_base); |
size_t sz = strlen(library_base); |
return p != NULL && (p[sz] == '.' || p[sz] == '-'); |
} |
@@ -933,11 +911,11 @@ |
if (inode != 0) { |
saw_nonzero_inode = true; |
} |
- if ((hc_strstr(filename, "lib") && hc_strstr(filename, ".so")) || |
- hc_strstr(filename, ".dll") || |
+ if ((strstr(filename, "lib") && strstr(filename, ".so")) || |
+ strstr(filename, ".dll") || |
// not all .dylib filenames start with lib. .dylib is big enough |
// that we are unlikely to get false matches just checking that. |
- hc_strstr(filename, ".dylib") || hc_strstr(filename, ".bundle")) { |
+ strstr(filename, ".dylib") || strstr(filename, ".bundle")) { |
saw_shared_lib = true; |
if (inode != 0) { |
saw_shared_lib_with_nonzero_inode = true; |
@@ -1413,31 +1391,6 @@ |
} |
} |
if (size < sizeof(void*)) continue; |
- |
-#ifdef NO_FRAME_POINTER |
- // Frame pointer omission requires us to use libunwind, which uses direct |
- // mmap and munmap system calls, and that needs special handling. |
- if (name2 == kUnnamedProcSelfMapEntry) { |
- static const uintptr_t page_mask = ~(getpagesize() - 1); |
- const uintptr_t addr = reinterpret_cast<uintptr_t>(object); |
- if ((addr & page_mask) == 0 && (size & page_mask) == 0) { |
- // This is an object we slurped from /proc/self/maps. |
- // It may or may not be readable at this point. |
- // |
- // In case all the above conditions made a mistake, and the object is |
- // not related to libunwind, we also verify that it's not readable |
- // before ignoring it. |
- if (msync(const_cast<char*>(object), size, MS_ASYNC) != 0) { |
- // Skip unreadable object, so we don't crash trying to sweep it. |
- RAW_VLOG(0, "Ignoring inaccessible object [%p, %p) " |
- "(msync error %d (%s))", |
- object, object + size, errno, strerror(errno)); |
- continue; |
- } |
- } |
- } |
-#endif |
- |
const char* const max_object = object + size - sizeof(void*); |
while (object <= max_object) { |
// potentially unaligned load: |
@@ -1510,7 +1463,7 @@ |
} |
// static |
-void HeapLeakChecker::DoIgnoreObject(const void* ptr) { |
+void HeapLeakChecker::IgnoreObject(const void* ptr) { |
SpinLockHolder l(&heap_checker_lock); |
if (!heap_checker_on) return; |
size_t object_size; |
@@ -1524,7 +1477,7 @@ |
IgnoredObjectsMap; |
} |
if (!ignored_objects->insert(make_pair(AsInt(ptr), object_size)).second) { |
- RAW_LOG(WARNING, "Object at %p is already being ignored", ptr); |
+ RAW_LOG(FATAL, "Object at %p is already being ignored", ptr); |
} |
} |
} |
@@ -1914,18 +1867,16 @@ |
heap_cleanups_ = NULL; |
} |
-// Program exit heap cleanup registered as a module object destructor. |
+// Program exit heap cleanup registered with atexit(). |
// Will not get executed when we crash on a signal. |
// |
-void HeapLeakChecker_RunHeapCleanups() { |
- if (FLAGS_heap_check == "local") // don't check heap in this mode |
- return; |
+/*static*/ void HeapLeakChecker::RunHeapCleanups() { |
{ SpinLockHolder l(&heap_checker_lock); |
// can get here (via forks?) with other pids |
if (heap_checker_pid != getpid()) return; |
} |
HeapCleaner::RunHeapCleanups(); |
- if (!FLAGS_heap_check_after_destructors) HeapLeakChecker::DoMainHeapCheck(); |
+ if (!FLAGS_heap_check_after_destructors) DoMainHeapCheck(); |
} |
static bool internal_init_start_has_run = false; |
@@ -1940,26 +1891,21 @@ |
// We can't hold heap_checker_lock throughout because it would deadlock |
// on a memory allocation since our new/delete hooks can be on. |
// |
-void HeapLeakChecker_InternalInitStart() { |
+/*static*/ void HeapLeakChecker::InternalInitStart() { |
{ SpinLockHolder l(&heap_checker_lock); |
RAW_CHECK(!internal_init_start_has_run, |
"Heap-check constructor called twice. Perhaps you both linked" |
" in the heap checker, and also used LD_PRELOAD to load it?"); |
internal_init_start_has_run = true; |
-#ifdef ADDRESS_SANITIZER |
- // AddressSanitizer's custom malloc conflicts with HeapChecker. |
- FLAGS_heap_check = ""; |
-#endif |
- |
if (FLAGS_heap_check.empty()) { |
// turns out we do not need checking in the end; can stop profiling |
- HeapLeakChecker::TurnItselfOffLocked(); |
+ TurnItselfOffLocked(); |
return; |
} else if (RunningOnValgrind()) { |
// There is no point in trying -- we'll just fail. |
RAW_LOG(WARNING, "Can't run under Valgrind; will turn itself off"); |
- HeapLeakChecker::TurnItselfOffLocked(); |
+ TurnItselfOffLocked(); |
return; |
} |
} |
@@ -1968,7 +1914,7 @@ |
if (!FLAGS_heap_check_run_under_gdb && IsDebuggerAttached()) { |
RAW_LOG(WARNING, "Someone is ptrace()ing us; will turn itself off"); |
SpinLockHolder l(&heap_checker_lock); |
- HeapLeakChecker::TurnItselfOffLocked(); |
+ TurnItselfOffLocked(); |
return; |
} |
@@ -2019,25 +1965,15 @@ |
RAW_LOG(FATAL, "Unsupported heap_check flag: %s", |
FLAGS_heap_check.c_str()); |
} |
- // FreeBSD doesn't seem to honor atexit execution order: |
- // http://code.google.com/p/gperftools/issues/detail?id=375 |
- // Since heap-checking before destructors depends on atexit running |
- // at the right time, on FreeBSD we always check after, even in the |
- // less strict modes. This just means FreeBSD is always a bit |
- // stricter in its checking than other OSes. |
-#ifdef __FreeBSD__ |
- FLAGS_heap_check_after_destructors = true; |
-#endif |
- |
{ SpinLockHolder l(&heap_checker_lock); |
RAW_DCHECK(heap_checker_pid == getpid(), ""); |
heap_checker_on = true; |
RAW_DCHECK(heap_profile, ""); |
- HeapLeakChecker::ProcMapsResult pm_result = HeapLeakChecker::UseProcMapsLocked(HeapLeakChecker::DISABLE_LIBRARY_ALLOCS); |
+ ProcMapsResult pm_result = UseProcMapsLocked(DISABLE_LIBRARY_ALLOCS); |
// might neeed to do this more than once |
// if one later dynamically loads libraries that we want disabled |
- if (pm_result != HeapLeakChecker::PROC_MAPS_USED) { // can't function |
- HeapLeakChecker::TurnItselfOffLocked(); |
+ if (pm_result != PROC_MAPS_USED) { // can't function |
+ TurnItselfOffLocked(); |
return; |
} |
} |
@@ -2091,6 +2027,8 @@ |
"-- Performance may suffer"); |
if (FLAGS_heap_check != "local") { |
+ // Schedule registered heap cleanup |
+ atexit(RunHeapCleanups); |
HeapLeakChecker* main_hc = new HeapLeakChecker(); |
SpinLockHolder l(&heap_checker_lock); |
RAW_DCHECK(main_heap_checker == NULL, |
@@ -2123,22 +2061,9 @@ |
// We want this to run early as well, but not so early as |
// ::BeforeConstructors (we want flag assignments to have already |
// happened, for instance). Initializer-registration does the trick. |
-REGISTER_MODULE_INITIALIZER(init_start, HeapLeakChecker_InternalInitStart()); |
-REGISTER_MODULE_DESTRUCTOR(init_start, HeapLeakChecker_RunHeapCleanups()); |
+REGISTER_MODULE_INITIALIZER(init_start, HeapLeakChecker::InternalInitStart()); |
// static |
-bool HeapLeakChecker::NoGlobalLeaksMaybeSymbolize( |
- ShouldSymbolize should_symbolize) { |
- // we never delete or change main_heap_checker once it's set: |
- HeapLeakChecker* main_hc = GlobalChecker(); |
- if (main_hc) { |
- RAW_VLOG(10, "Checking for whole-program memory leaks"); |
- return main_hc->DoNoLeaks(should_symbolize); |
- } |
- return true; |
-} |
- |
-// static |
bool HeapLeakChecker::DoMainHeapCheck() { |
if (FLAGS_heap_check_delay_seconds > 0) { |
sleep(FLAGS_heap_check_delay_seconds); |
@@ -2149,13 +2074,7 @@ |
do_main_heap_check = false; // will do it now; no need to do it more |
} |
- // The program is over, so it's safe to symbolize addresses (which |
- // requires a fork) because no serious work is expected to be done |
- // after this. Symbolizing is really useful -- knowing what |
- // function has a leak is better than knowing just an address -- |
- // and while we can only safely symbolize once in a program run, |
- // now is the time (after all, there's no "later" that would be better). |
- if (!NoGlobalLeaksMaybeSymbolize(SYMBOLIZE)) { |
+ if (!NoGlobalLeaks()) { |
if (FLAGS_heap_check_identify_leaks) { |
RAW_LOG(FATAL, "Whole-program memory leaks found."); |
} |
@@ -2175,8 +2094,19 @@ |
// static |
bool HeapLeakChecker::NoGlobalLeaks() { |
- // symbolizing requires a fork, which isn't safe to do in general. |
- return NoGlobalLeaksMaybeSymbolize(DO_NOT_SYMBOLIZE); |
+ // we never delete or change main_heap_checker once it's set: |
+ HeapLeakChecker* main_hc = GlobalChecker(); |
+ if (main_hc) { |
+ RAW_VLOG(10, "Checking for whole-program memory leaks"); |
+ // The program is over, so it's safe to symbolize addresses (which |
+ // requires a fork) because no serious work is expected to be done |
+ // after this. Symbolizing is really useful -- knowing what |
+ // function has a leak is better than knowing just an address -- |
+ // and while we can only safely symbolize once in a program run, |
+ // now is the time (after all, there's no "later" that would be better). |
+ return main_hc->DoNoLeaks(SYMBOLIZE); |
+ } |
+ return true; |
} |
// static |
@@ -2194,10 +2124,6 @@ |
RAW_DCHECK(heap_checker_lock.IsHeld(), ""); |
RAW_CHECK(!constructor_heap_profiling, |
"BeforeConstructorsLocked called multiple times"); |
-#ifdef ADDRESS_SANITIZER |
- // AddressSanitizer's custom malloc conflicts with HeapChecker. |
- return; |
-#endif |
// Set hooks early to crash if 'new' gets called before we make heap_profile, |
// and make sure no other hooks existed: |
RAW_CHECK(MallocHook::AddNewHook(&NewHook), ""); |