summaryrefslogtreecommitdiff
path: root/src/heap-checker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/heap-checker.cc')
-rw-r--r--src/heap-checker.cc78
1 files changed, 61 insertions, 17 deletions
diff --git a/src/heap-checker.cc b/src/heap-checker.cc
index c0ea994..f081f97 100644
--- a/src/heap-checker.cc
+++ b/src/heap-checker.cc
@@ -451,12 +451,14 @@ static bool IsLibraryNamed(const char* library, const char* library_base) {
void HeapLeakChecker::DisableLibraryAllocs(const char* library,
void* start_address,
void* end_address) {
+ int depth = 0;
// TODO(maxim): maybe this should be extended to also use objdump
// and pick the text portion of the library more precisely.
if (IsLibraryNamed(library, "/libpthread") ||
- // pthread has a lot of small "system" leaks we don't care about
+ // libpthread has a lot of small "system" leaks we don't care about.
+ // In particular it allocates memory to store data supplied via
+ // pthread_setspecific (which can be the only pointer to a heap object).
IsLibraryNamed(library, "/libdl") ||
- IsLibraryNamed(library, "/ld") ||
// library loaders leak some "system" heap that we don't care about
IsLibraryNamed(library, "/libcrypto")
// Sometimes libcrypto of OpenSSH is compiled with -fomit-frame-pointer
@@ -464,16 +466,36 @@ void HeapLeakChecker::DisableLibraryAllocs(const char* library,
// is so important for making crypto usable). We ignore all its
// allocations because we can't see the call stacks.
) {
+ depth = 1; // only disable allocation calls directly from the library code
+ } else if (IsLibraryNamed(library, "/ld")
+ // library loader leaks some "system" heap
+ // (e.g. thread-local storage) that we don't care about
+ ) {
+ depth = 2; // disable allocation calls directly from the library code
+ // and at depth 2 from it.
+ // We need depth 2 here solely because of a libc bug that
+ // forces us to jump through __memalign_hook and MemalignOverride hoops
+ // in tcmalloc.cc.
+ // Those buggy __libc_memalign() calls are in ld-linux.so and happen for
+ // thread-local storage allocations that we want to ignore here.
+ // We go with the depth-2 hack as a workaround for this libc bug:
+ // otherwise we'd need to extend MallocHook interface
+ // so that correct stack depth adjustment can be propagated from
+ // the exceptional case of MemalignOverride.
+ // Using depth 2 here should not mask real leaks because ld-linux.so
+ // does not call user code.
+ }
+ if (depth) {
HeapProfiler::MESSAGE(1, "HeapChecker: "
- "Disabling direct allocations from %s :\n",
- library);
+ "Disabling allocations from %s at depth %d:\n",
+ library, depth);
DisableChecksFromTo(start_address, end_address,
- 1); // only disable allocation calls directly
- // from the library code
+ depth);
}
}
-void HeapLeakChecker::UseProcMaps(ProcMapsTask proc_maps_task) {
+HeapLeakChecker::ProcMapsResult
+HeapLeakChecker::UseProcMaps(ProcMapsTask proc_maps_task) {
FILE* const fp = fopen("/proc/self/maps", "r");
if (!fp) {
int errsv = errno;
@@ -481,27 +503,29 @@ void HeapLeakChecker::UseProcMaps(ProcMapsTask proc_maps_task) {
"Could not open /proc/self/maps: errno=%d. "
"Libraries will not be handled correctly.\n",
errsv);
- return;
+ return CANT_OPEN_PROC_MAPS;
}
char proc_map_line[1024];
+ bool saw_shared_lib = false;
while (fgets(proc_map_line, sizeof(proc_map_line), fp) != NULL) {
// All lines starting like
// "401dc000-4030f000 r??p 00132000 03:01 13991972 lib/bin"
// identify a data and code sections of a shared library or our binary
uint64 start_address, end_address, file_offset, inode;
int size;
- char permissions[5];
+ char permissions[5], *filename;
if (sscanf(proc_map_line, LLX"-"LLX" %4s "LLX" %*x:%*x "LLD" %n",
&start_address, &end_address, permissions,
&file_offset, &inode, &size) != 5) continue;
proc_map_line[strlen(proc_map_line) - 1] = '\0'; // zap the newline
+ filename = proc_map_line + size;
HeapProfiler::MESSAGE(4, "HeapChecker: "
"Looking at /proc/self/maps line:\n %s\n",
proc_map_line);
if (proc_maps_task == DISABLE_LIBRARY_ALLOCS &&
strncmp(permissions, "r-xp", 4) == 0 && inode != 0) {
if (start_address >= end_address) abort();
- DisableLibraryAllocs(proc_map_line + size,
+ DisableLibraryAllocs(filename,
reinterpret_cast<void*>(start_address),
reinterpret_cast<void*>(end_address));
}
@@ -514,8 +538,21 @@ void HeapLeakChecker::UseProcMaps(ProcMapsTask proc_maps_task) {
if (start_address >= end_address) abort();
RecordGlobalDataLocked(proc_map_line + size, start_address, file_offset);
}
+ // Determine if any shared libraries are present.
+ if (strstr(filename, "lib") && strstr(filename, ".so")) {
+ saw_shared_lib = true;
+ }
}
fclose(fp);
+
+ if (!saw_shared_lib) {
+ HeapProfiler::MESSAGE(-1, "HeapChecker: "
+ "No shared libs detected. "
+ "Will likely report false leak positives "
+ "for statically linked executables.\n");
+ return NO_SHARED_LIBS_IN_PROC_MAPS;
+ }
+ return PROC_MAPS_USED;
}
// Total number and size of live objects dropped from the profile.
@@ -527,11 +564,12 @@ static int64 live_bytes_total = 0;
static int last_num_threads = 0;
static pid_t* last_thread_pids = NULL;
-// Callback for GetAllProcessThreads to ignore
+// Callback for ListAllProcessThreads to ignore
// thread stacks and registers for all our threads.
static int IgnoreLiveThreads(void* parameter,
int num_threads,
- pid_t* thread_pids) {
+ pid_t* thread_pids,
+ va_list ap) {
last_num_threads = num_threads;
assert(last_thread_pids == NULL);
last_thread_pids = new pid_t[num_threads];
@@ -547,7 +585,7 @@ static int IgnoreLiveThreads(void* parameter,
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
- // because this is called from GetAllProcessThreads
+ // because this is called from ListAllProcessThreads
// when all but this thread are suspended.
// (This does not seem to matter much though: allocations and
// logging with HeapProfiler::MESSAGE seem to work just fine.)
@@ -600,8 +638,8 @@ IgnoreAllLiveObjectsLocked(const StackExtent& self_stack) {
if (HeapProfiler::ignored_objects_) abort();
HeapProfiler::ignored_objects_ = new HeapProfiler::IgnoredObjectSet;
// Record global data as live:
- // We need to do it before we stop the threads in GetAllProcessThreads below;
- // otherwise deadlocks are possible
+ // We need to do it before we stop the threads in ListAllProcessThreads
+ // below; otherwise deadlocks are possible
// when we try to fork to execute objdump in UseProcMaps.
if (FLAGS_heap_check_ignore_global_live) {
library_live_objects = new LibraryLiveObjectsStacks;
@@ -613,7 +651,7 @@ IgnoreAllLiveObjectsLocked(const StackExtent& self_stack) {
// and keep them suspended for the whole time of liveness checking
// (they can't (de)allocate due to profiler's lock but they could still
// mess with the pointer graph while we walk it).
- int r = GetAllProcessThreads(NULL, IgnoreLiveThreads);
+ int r = ListAllProcessThreads(NULL, IgnoreLiveThreads);
if (r == -1) {
HeapProfiler::MESSAGE(0, "HeapChecker: Could not find thread stacks; "
"may get false leak reports\n");
@@ -1312,9 +1350,15 @@ void HeapLeakChecker::InternalInitStart(const string& heap_check_type) {
assert(heap_checker_pid == getpid());
heap_checker_on = true;
if (!HeapProfiler::is_on_) abort();
- UseProcMaps(DISABLE_LIBRARY_ALLOCS);
+ ProcMapsResult pm_result = UseProcMaps(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) {
+ heap_checker_on = false;
+ HeapProfiler::MESSAGE(0, "HeapChecker: Turning itself off\n");
+ HeapProfiler::StopForLeaks();
+ return;
+ }
// make a good place and name for heap profile leak dumps
profile_prefix = new string(dump_directory());