Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Unified Diff: third_party/tcmalloc/chromium/src/heap-checker.cc

Issue 7050034: Merge google-perftools r109 (the current contents of third_party/tcmalloc/vendor) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/tcmalloc/chromium/src/heap-checker.cc
===================================================================
--- third_party/tcmalloc/chromium/src/heap-checker.cc (revision 88335)
+++ third_party/tcmalloc/chromium/src/heap-checker.cc (working copy)
@@ -106,6 +106,28 @@
using std::less;
using std::char_traits;
+// If current process is being ptrace()d, 'TracerPid' in /proc/self/status
+// will be non-zero.
+static bool IsDebuggerAttached(void) { // only works under linux, probably
+ char buf[256]; // TracerPid comes relatively earlier in status output
+ int fd = open("/proc/self/status", O_RDONLY);
+ if (fd == -1) {
+ return false; // Can't tell for sure.
+ }
+ const int len = read(fd, buf, sizeof(buf));
+ bool rc = false;
+ if (len > 0) {
+ const char *const kTracerPid = "TracerPid:\t";
+ buf[len - 1] = '\0';
+ const char *p = strstr(buf, kTracerPid);
+ if (p != NULL) {
+ rc = (strncmp(p + strlen(kTracerPid), "0\n", 2) != 0);
+ }
+ }
+ close(fd);
+ return rc;
+}
+
// This is the default if you don't link in -lprofiler
extern "C" {
ATTRIBUTE_WEAK PERFTOOLS_DLL_DECL bool ProfilingIsEnabledForAllThreads();
@@ -163,12 +185,6 @@
// use 1 if any alignment is ok.
// heap_check_test_pointer_alignment flag guides if we try the value of 1.
// The larger it can be, the lesser is the chance of missing real leaks.
-//
-// sizeof(void)* is correct. However gold (the new linker) has a bug where it
-// sometimes places global pointers on 4-byte boundaries, even when pointers
-// are 8 bytes long. While we are fixing the linker, degrade to 4-byte
-// alignment on all targets. http://b/1226481
-//
static const size_t kPointerSourceAlignment = sizeof(void*);
DEFINE_int32(heap_check_pointer_source_alignment,
EnvToInt("HEAP_CHECK_POINTER_SOURCE_ALIGNMENT",
@@ -304,6 +320,9 @@
if (p) alloc_count_ -= 1;
LowLevelAlloc::Free(p);
}
+ static void Free(void* p, size_t /* n */) {
+ Free(p);
+ }
// destruct, free, and make *p to be NULL
template<typename T> static void DeleteAndNull(T** p) {
(*p)->~T();
@@ -795,7 +814,7 @@
// pthread_setspecific (which can be the only pointer to a heap object).
IsLibraryNamed(library, "/libdl") ||
// library loaders leak some "system" heap that we don't care about
- IsLibraryNamed(library, "/libcrypto")
+ IsLibraryNamed(library, "/libcrypto") ||
// Sometimes libcrypto of OpenSSH is compiled with -fomit-frame-pointer
// (any library can be, of course, but this one often is because speed
// is so important for making crypto usable). We ignore all its
@@ -803,6 +822,10 @@
// to ignore allocations done in files/symbols that match
// "default_malloc_ex|default_realloc_ex"
// but that doesn't work when the end-result binary is stripped.
+ IsLibraryNamed(library, "/libjvm") ||
+ // JVM has a lot of leaks we don't care about.
+ IsLibraryNamed(library, "/libzip")
+ // The JVM leaks java.util.zip.Inflater after loading classes.
) {
depth = 1; // only disable allocation calls directly from the library code
} else if (IsLibraryNamed(library, "/ld")
@@ -859,6 +882,8 @@
int64 inode;
char *permissions, *filename;
bool saw_shared_lib = false;
+ bool saw_nonzero_inode = false;
+ bool saw_shared_lib_with_nonzero_inode = false;
while (it.Next(&start_address, &end_address, &permissions,
&file_offset, &inode, &filename)) {
if (start_address >= end_address) {
@@ -874,10 +899,25 @@
// do things in this loop.
continue;
}
- // Determine if any shared libraries are present.
- if (inode != 0 && strstr(filename, "lib") && strstr(filename, ".so")) {
+ // Determine if any shared libraries are present (this is the same
+ // list of extensions as is found in pprof). We want to ignore
+ // 'fake' libraries with inode 0 when determining. However, some
+ // systems don't share inodes via /proc, so we turn off this check
+ // if we don't see any evidence that we're getting inode info.
+ if (inode != 0) {
+ saw_nonzero_inode = true;
+ }
+ 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.
+ strstr(filename, ".dylib") || strstr(filename, ".bundle")) {
saw_shared_lib = true;
+ if (inode != 0) {
+ saw_shared_lib_with_nonzero_inode = true;
+ }
}
+
switch (proc_maps_task) {
case DISABLE_LIBRARY_ALLOCS:
// All lines starting like
@@ -895,6 +935,12 @@
RAW_CHECK(0, "");
}
}
+ // If /proc/self/maps is reporting inodes properly (we saw a
+ // non-zero inode), then we only say we saw a shared lib if we saw a
+ // 'real' one, with a non-zero inode.
+ if (saw_nonzero_inode) {
+ saw_shared_lib = saw_shared_lib_with_nonzero_inode;
+ }
if (!saw_shared_lib) {
RAW_LOG(ERROR, "No shared libs detected. Will likely report false leak "
"positives for statically linked executables.");
@@ -962,7 +1008,8 @@
// specially via self_thread_stack, not here:
if (thread_pids[i] == self_thread_pid) continue;
RAW_VLOG(11, "Handling thread with pid %d", thread_pids[i]);
-#if defined(HAVE_LINUX_PTRACE_H) && defined(HAVE_SYS_SYSCALL_H) && defined(DUMPER)
+#if (defined(__i386__) || defined(__x86_64)) && \
+ defined(HAVE_LINUX_PTRACE_H) && defined(HAVE_SYS_SYSCALL_H) && defined(DUMPER)
i386_regs thread_regs;
#define sys_ptrace(r, p, a, d) syscall(SYS_ptrace, (r), (p), (a), (d))
// We use sys_ptrace to avoid thread locking
@@ -1633,6 +1680,13 @@
return true;
}
+ // Update global_region_caller_ranges. They may need to change since
+ // e.g. initialization because shared libraries might have been loaded or
+ // unloaded.
+ Allocator::DeleteAndNullIfNot(&global_region_caller_ranges);
+ ProcMapsResult pm_result = UseProcMapsLocked(DISABLE_LIBRARY_ALLOCS);
+ RAW_CHECK(pm_result == PROC_MAPS_USED, "");
+
// Keep track of number of internally allocated objects so we
// can detect leaks in the heap-leak-checket itself
const int initial_allocs = Allocator::alloc_count();
@@ -1645,18 +1699,6 @@
MemoryRegionMap::LockHolder ml;
int a_local_var; // Use our stack ptr to make stack data live:
- // Sanity check that nobody is messing with the hooks we need:
- // Important to have it here: else we can misteriously SIGSEGV
- // in IgnoreLiveObjectsLocked inside ListAllProcessThreads's callback
- // by looking into a region that got unmapped w/o our knowledge.
- MemoryRegionMap::CheckMallocHooks();
- if (MallocHook::GetNewHook() != NewHook ||
- MallocHook::GetDeleteHook() != DeleteHook) {
- RAW_LOG(FATAL, "Had our new/delete MallocHook-s replaced. "
- "Are you using another MallocHook client? "
- "Use --heap_check=\"\" to avoid this conflict.");
- }
-
// Make the heap profile, other threads are locked out.
HeapProfileTable::Snapshot* base =
reinterpret_cast<HeapProfileTable::Snapshot*>(start_snapshot_);
@@ -1833,9 +1875,6 @@
if (!FLAGS_heap_check_after_destructors) DoMainHeapCheck();
}
-// defined below
-static int GetCommandLineFrom(const char* file, char* cmdline, int size);
-
static bool internal_init_start_has_run = false;
// Called exactly once, before main() (but hopefully just before).
@@ -1859,29 +1898,20 @@
// turns out we do not need checking in the end; can stop profiling
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");
+ TurnItselfOffLocked();
+ return;
}
}
// Changing this to false can be useful when debugging heap-checker itself:
- if (!FLAGS_heap_check_run_under_gdb) {
- // See if heap checker should turn itself off because we are
- // running under gdb (to avoid conflicts over ptrace-ing rights):
- char name_buf[15+15];
- snprintf(name_buf, sizeof(name_buf),
- "/proc/%d/cmdline", static_cast<int>(getppid()));
- char cmdline[1024*8]; // /proc/*/cmdline is at most 4Kb anyway usually
- int size = GetCommandLineFrom(name_buf, cmdline, sizeof(cmdline)-1);
- cmdline[size] = '\0';
- // look for "gdb" in the executable's name:
- const char* last = strrchr(cmdline, '/');
- if (last) last += 1;
- else last = cmdline;
- if (strncmp(last, "gdb", 3) == 0) {
- RAW_LOG(WARNING, "We seem to be running under gdb; will turn itself off");
- SpinLockHolder l(&heap_checker_lock);
- TurnItselfOffLocked();
- return;
- }
+ 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);
+ TurnItselfOffLocked();
+ return;
}
{ SpinLockHolder l(&heap_checker_lock);
@@ -2084,98 +2114,15 @@
}
}
-//----------------------------------------------------------------------
-// HeapLeakChecker global constructor/destructor ordering components
-//----------------------------------------------------------------------
-
-static bool in_initial_malloc_hook = false;
-
-#ifdef HAVE___ATTRIBUTE__ // we need __attribute__((weak)) for this to work
-#define INSTALLED_INITIAL_MALLOC_HOOKS
-
-void HeapLeakChecker_BeforeConstructors(); // below
-
-// Helper for InitialMallocHook_* below
-static inline void InitHeapLeakCheckerFromMallocHook() {
- { SpinLockHolder l(&heap_checker_lock);
- RAW_CHECK(!in_initial_malloc_hook,
- "Something did not reset initial MallocHook-s");
- in_initial_malloc_hook = true;
- }
- // Initialize heap checker on the very first allocation/mmap/sbrk call:
- HeapLeakChecker_BeforeConstructors();
- { SpinLockHolder l(&heap_checker_lock);
- in_initial_malloc_hook = false;
- }
-}
-
-// These will owerwrite the weak definitions in malloc_hook.cc:
-
-// Important to have this to catch the first allocation call from the binary:
-extern void InitialMallocHook_New(const void* ptr, size_t size) {
- InitHeapLeakCheckerFromMallocHook();
- // record this first allocation as well (if we need to):
- MallocHook::InvokeNewHook(ptr, size);
-}
-
-// Important to have this to catch the first mmap call (say from tcmalloc):
-extern void InitialMallocHook_MMap(const void* result,
- const void* start,
- size_t size,
- int protection,
- int flags,
- int fd,
- off_t offset) {
- InitHeapLeakCheckerFromMallocHook();
- // record this first mmap as well (if we need to):
- MallocHook::InvokeMmapHook(
- result, start, size, protection, flags, fd, offset);
-}
-
-// Important to have this to catch the first sbrk call (say from tcmalloc):
-extern void InitialMallocHook_Sbrk(const void* result, std::ptrdiff_t increment) {
- InitHeapLeakCheckerFromMallocHook();
- // record this first sbrk as well (if we need to):
- MallocHook::InvokeSbrkHook(result, increment);
-}
-
// static
-void CancelInitialMallocHooks() {
- if (MallocHook::GetNewHook() == InitialMallocHook_New) {
- MallocHook::SetNewHook(NULL);
- }
- RAW_DCHECK(MallocHook::GetNewHook() == NULL, "");
- if (MallocHook::GetMmapHook() == InitialMallocHook_MMap) {
- MallocHook::SetMmapHook(NULL);
- }
- RAW_DCHECK(MallocHook::GetMmapHook() == NULL, "");
- if (MallocHook::GetSbrkHook() == InitialMallocHook_Sbrk) {
- MallocHook::SetSbrkHook(NULL);
- }
- RAW_DCHECK(MallocHook::GetSbrkHook() == NULL, "");
-}
-
-#else
-
-// static
-void CancelInitialMallocHooks() {}
-
-#endif
-
-// static
void HeapLeakChecker::BeforeConstructorsLocked() {
RAW_DCHECK(heap_checker_lock.IsHeld(), "");
RAW_CHECK(!constructor_heap_profiling,
"BeforeConstructorsLocked called multiple times");
- CancelInitialMallocHooks();
// Set hooks early to crash if 'new' gets called before we make heap_profile,
// and make sure no other hooks existed:
- if (MallocHook::SetNewHook(NewHook) != NULL ||
- MallocHook::SetDeleteHook(DeleteHook) != NULL) {
- RAW_LOG(FATAL, "Had other new/delete MallocHook-s set. "
- "Somehow leak checker got activated "
- "after something else have set up these hooks.");
- }
+ RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
+ RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
constructor_heap_profiling = true;
MemoryRegionMap::Init(1);
// Set up MemoryRegionMap with (at least) one caller stack frame to record
@@ -2198,12 +2145,9 @@
RAW_CHECK(heap_checker_on, "");
RAW_VLOG(heap_checker_info_level, "Turning perftools heap leak checking off");
heap_checker_on = false;
- // Unset our hooks checking they were the ones set:
- if (MallocHook::SetNewHook(NULL) != NewHook ||
- MallocHook::SetDeleteHook(NULL) != DeleteHook) {
- RAW_LOG(FATAL, "Had our new/delete MallocHook-s replaced. "
- "Are you using another MallocHook client?");
- }
+ // Unset our hooks checking they were set:
+ RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
+ RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
Allocator::DeleteAndNull(&heap_profile);
// free our optional global data:
Allocator::DeleteAndNullIfNot(&ignored_objects);
@@ -2215,57 +2159,13 @@
RAW_CHECK(!heap_checker_on, "");
}
-// Read in the command line from 'file' into 'cmdline' and return the size read
-// 'size' is the space available in 'cmdline'.
-// We need this because we don't yet have argv/argc.
-// CAVEAT: 'file' (some /proc/*/cmdline) usually contains the command line
-// already truncated (to 4K on Linux).
-// Arguments in cmdline will be '\0'-terminated,
-// the first one will be the binary's name.
-static int GetCommandLineFrom(const char* file, char* cmdline, int size) {
- // This routine is only used to check if we're running under gdb, so
- // it's ok if this #if fails and the routine is a no-op.
- //
- // This function is called before memory allocation hooks are set up
- // so we must not have any memory allocations in it. We use syscall
- // versions of open/read/close here because we don't trust the non-syscall
- // versions: they might 'accidentally' cause a memory allocation.
- // Here's a real-life problem scenario we had:
- // 1) A program LD_PRELOADed a library called list_file_used.a
- // 2) list_file_used intercepted open/read/close and called dlsym()
- // 3) dlsym() called pthread_setspecific() which called malloc().
- // This malloced memory is 'hidden' from the heap-checker. By
- // definition, this thread-local data is live, and everything it points
- // to is live (not a memory leak) as well. But because this memory
- // was hidden from the heap-checker, everything it points to was
- // taken to be orphaned, and therefore, a memory leak.
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__MINGW32__)
- // Use a win32 call to get the command line.
- const char* command_line = ::GetCommandLine();
- strncpy(cmdline, command_line, size);
- cmdline[size - 1] = '\0';
- return strlen(cmdline);
-#elif defined(HAVE_SYS_SYSCALL_H)
- int fd = syscall(SYS_open, file, O_RDONLY);
- int result = 0;
- if (fd >= 0) {
- ssize_t r;
- while ((r = syscall(SYS_read, fd, cmdline + result, size)) > 0) {
- result += r;
- size -= r;
- }
- syscall(SYS_close, fd);
- }
- return result;
-#else
- return 0;
-#endif
-}
-
extern bool heap_leak_checker_bcad_variable; // in heap-checker-bcad.cc
static bool has_called_before_constructors = false;
+// TODO(maxim): inline this function with
+// MallocHook_InitAtFirstAllocation_HeapLeakChecker, and also rename
+// HeapLeakChecker::BeforeConstructorsLocked.
void HeapLeakChecker_BeforeConstructors() {
SpinLockHolder l(&heap_checker_lock);
// We can be called from several places: the first mmap/sbrk/alloc call
@@ -2304,11 +2204,19 @@
#endif
if (need_heap_check) {
HeapLeakChecker::BeforeConstructorsLocked();
- } else { // cancel our initial hooks
- CancelInitialMallocHooks();
}
}
+// This function overrides the weak function defined in malloc_hook.cc and
+// called by one of the initial malloc hooks (malloc_hook.cc) when the very
+// first memory allocation or an mmap/sbrk happens. This ensures that
+// HeapLeakChecker is initialized and installs all its hooks early enough to
+// track absolutely all memory allocations and all memory region acquisitions
+// via mmap and sbrk.
+extern "C" void MallocHook_InitAtFirstAllocation_HeapLeakChecker() {
+ HeapLeakChecker_BeforeConstructors();
+}
+
// This function is executed after all global object destructors run.
void HeapLeakChecker_AfterDestructors() {
{ SpinLockHolder l(&heap_checker_lock);
« no previous file with comments | « third_party/tcmalloc/chromium/src/google/tcmalloc.h.in ('k') | third_party/tcmalloc/chromium/src/heap-checker-bcad.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698