From 9fcd788fb4e9d8b3be8bd375e47a825f95022178 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Thu, 14 Sep 2017 17:35:29 +0300 Subject: [PATCH] Add approximate trace of mmaps/munmaps from dlopen/dlclose. --- src/track/heaptrack_preload.cpp | 140 ++++++++++++++++++++++++++++++++ src/track/libheaptrack.cpp | 90 +++++++++----------- src/track/libheaptrack.h | 54 ++++++++++++ src/track/trace.h | 7 ++ 4 files changed, 241 insertions(+), 50 deletions(-) diff --git a/src/track/heaptrack_preload.cpp b/src/track/heaptrack_preload.cpp index eaf712a..f5acc89 100644 --- a/src/track/heaptrack_preload.cpp +++ b/src/track/heaptrack_preload.cpp @@ -22,10 +22,12 @@ #include #include #include +#include #include #include +#include #include using namespace std; @@ -48,6 +50,51 @@ namespace __gnu_cxx { __attribute__((weak)) extern void __freeres(); } +static bool isExiting = false; + +static int dl_iterate_phdr_get_maps(struct dl_phdr_info* info, size_t /*size*/, void* data) +{ + auto maps = (map> *) data; + + const char* fileName = info->dlpi_name; + if (!fileName || !fileName[0]) { + fileName = "x"; + } + + debugLog("dl_iterate_phdr_get_maps: %s %zx", fileName, info->dlpi_addr); + + for (int i = 0; i < info->dlpi_phnum; i++) { + const auto& phdr = info->dlpi_phdr[i]; + + if (phdr.p_type == PT_LOAD) { + constexpr uintptr_t pageMask = (uintptr_t) 0xfff; + + uintptr_t start = (info->dlpi_addr + phdr.p_vaddr) & ~pageMask; + uintptr_t end = (info->dlpi_addr + phdr.p_vaddr + phdr.p_memsz + pageMask) & ~pageMask; + + void *addr = (void *) start; + size_t size = (size_t) (end - start); + + int prot = 0; + + if (phdr.p_flags & PF_R) + prot |= PROT_READ; + if (phdr.p_flags & PF_W) + prot |= PROT_WRITE; + if (phdr.p_flags & PF_X) + prot |= PROT_EXEC; + + if (maps->find(addr) == maps->end()) { + maps->insert(make_pair(addr, make_pair(size, prot))); + } else { + debugLog("dl_iterate_phdr_get_maps: repeated section address %s %zx", fileName, info->dlpi_addr); + } + } + } + + return 0; +} + namespace { namespace hooks { @@ -142,6 +189,8 @@ void init() if (&__libc_freeres) { __libc_freeres(); } + + isExiting = true; }); heaptrack_init(getenv("DUMP_HEAPTRACK_OUTPUT"), [] { @@ -170,6 +219,20 @@ void init() unsetenv("DUMP_HEAPTRACK_OUTPUT"); }, nullptr, nullptr); + + { + map> map; + + dl_iterate_phdr(&dl_iterate_phdr_get_maps, &map); + + vector>> newMmaps; + + for (const auto & section : map) { + newMmaps.push_back(section); + } + + heaptrack_dlopen(newMmaps, true, reinterpret_cast(hooks::dlopen.original)); + } } } } @@ -362,10 +425,46 @@ void* dlopen(const char* filename, int flag) noexcept hooks::init(); } + map> map_before, map_after; + + if (!RecursionGuard::isActive) { + RecursionGuard guard; + dl_iterate_phdr(&dl_iterate_phdr_get_maps, &map_before); + } + void* ret = hooks::dlopen(filename, flag); if (ret) { heaptrack_invalidate_module_cache(); + + if (!RecursionGuard::isActive) { + RecursionGuard guard; + dl_iterate_phdr(&dl_iterate_phdr_get_maps, &map_after); + } + + if(map_after.size() < map_before.size()) { + debugLog("dlopen: count of sections after dlopen is less than before: %p %s %x", ret, filename, flag); + } else if (map_after.size() != map_before.size()) { + vector>> newMmaps; + + if (!RecursionGuard::isActive) { + RecursionGuard guard; + + for (const auto & section_after : map_after) { + if (map_before.find(section_after.first) == map_before.end()) { + newMmaps.push_back(section_after); + } + } + } + + heaptrack_dlopen(newMmaps, false, reinterpret_cast(hooks::dlopen.original)); + + if (!RecursionGuard::isActive) { + RecursionGuard guard; + + newMmaps.clear(); + } + } } return ret; @@ -377,12 +476,53 @@ int dlclose(void* handle) noexcept hooks::init(); } + map> map_before, map_after; + + if (!isExiting) { + if (!RecursionGuard::isActive) { + RecursionGuard guard; + dl_iterate_phdr(&dl_iterate_phdr_get_maps, &map_before); + } + } + int ret = hooks::dlclose(handle); if (!ret) { heaptrack_invalidate_module_cache(); + + if (!isExiting) { + if (!RecursionGuard::isActive) { + RecursionGuard guard; + dl_iterate_phdr(&dl_iterate_phdr_get_maps, &map_after); + } + + if(map_after.size() > map_before.size()) { + debugLog("dlopen: count of sections after dlclose is greater than before: %p", handle); + } else if (map_after.size() != map_before.size()) { + vector> munmaps; + + if (!RecursionGuard::isActive) { + RecursionGuard guard; + + for (const auto & section_before : map_before) { + if (map_after.find(section_before.first) == map_after.end()) { + munmaps.push_back(make_pair(section_before.first, section_before.second.first)); + } + } + } + + heaptrack_dlclose(munmaps); + + if (!RecursionGuard::isActive) { + RecursionGuard guard; + + munmaps.clear(); + } + } + } } return ret; } + } diff --git a/src/track/libheaptrack.cpp b/src/track/libheaptrack.cpp index 3108eed..b1d8ac4 100644 --- a/src/track/libheaptrack.cpp +++ b/src/track/libheaptrack.cpp @@ -59,34 +59,9 @@ using namespace std; -namespace { - -enum DebugVerbosity -{ - NoDebugOutput, - MinimalOutput, - VerboseOutput, - VeryVerboseOutput, -}; - -// change this to add more debug output to stderr -constexpr const DebugVerbosity s_debugVerbosity = NoDebugOutput; +thread_local bool RecursionGuard::isActive = false; -/** - * Call this to optionally show debug information but give the compiler - * a hand in removing it all if debug output is disabled. - */ -template -inline void debugLog(const char fmt[], Args... args) -{ - if (debugLevel <= s_debugVerbosity) { - flockfile(stderr); - fprintf(stderr, "heaptrack debug [%d]: ", static_cast(debugLevel)); - fprintf(stderr, fmt, args...); - fputc('\n', stderr); - funlockfile(stderr); - } -} +namespace { /** * Set to true in an atexit handler. In such conditions, the stop callback @@ -100,29 +75,6 @@ atomic s_atexit{false}; */ atomic s_forceCleanup{false}; -/** - * A per-thread handle guard to prevent infinite recursion, which should be - * acquired before doing any special symbol handling. - */ -struct RecursionGuard -{ - RecursionGuard() - : wasLocked(isActive) - { - isActive = true; - } - - ~RecursionGuard() - { - isActive = wasLocked; - } - - const bool wasLocked; - static thread_local bool isActive; -}; - -thread_local bool RecursionGuard::isActive = false; - void writeVersion(FILE* out) { fprintf(out, "v %x %x\n", HEAPTRACK_VERSION, HEAPTRACK_FILE_FORMAT_VERSION); @@ -793,6 +745,44 @@ void heaptrack_stop() heaptrack.shutdown(); } +void heaptrack_dlopen(const vector>> &newMmaps, bool isPreloaded, void *dlopenOriginal) +{ + if (!RecursionGuard::isActive) { + RecursionGuard guard; + + debugLog("heaptrack_dlopen(... [%d] ...)", newMmaps.size()); + + Trace trace; + + if (isPreloaded) + { + trace.fill(dlopenOriginal); + } else { + trace.fill(2); + } + + HeapTrack heaptrack(guard); + + for (const auto &mmapRecord : newMmaps) { + heaptrack.handleMmap(mmapRecord.first, mmapRecord.second.first, mmapRecord.second.second, -2 /* FIXME: */, trace); + } + } +} + +void heaptrack_dlclose(const vector> &unmaps) +{ + if (!RecursionGuard::isActive) { + RecursionGuard guard; + + debugLog("heaptrack_dlclose(... [%d] ...)", unmaps.size()); + + HeapTrack heaptrack(guard); + for (const auto &unmapRecord : unmaps) { + heaptrack.handleMunmap(unmapRecord.first, unmapRecord.second); + } + } +} + void heaptrack_malloc(void* ptr, size_t size) { if (ptr && !RecursionGuard::isActive) { diff --git a/src/track/libheaptrack.h b/src/track/libheaptrack.h index 5ac1be1..d7e15f7 100644 --- a/src/track/libheaptrack.h +++ b/src/track/libheaptrack.h @@ -18,7 +18,58 @@ #include +#include +#include + #ifdef __cplusplus +enum DebugVerbosity +{ + NoDebugOutput, + MinimalOutput, + VerboseOutput, + VeryVerboseOutput, +}; + +// change this to add more debug output to stderr +constexpr const DebugVerbosity s_debugVerbosity = NoDebugOutput; + +/** + * Call this to optionally show debug information but give the compiler + * a hand in removing it all if debug output is disabled. + */ +template +inline void debugLog(const char fmt[], Args... args) +{ + if (debugLevel <= s_debugVerbosity) { + flockfile(stderr); + fprintf(stderr, "heaptrack debug [%d]: ", static_cast(debugLevel)); + fprintf(stderr, fmt, args...); + fputc('\n', stderr); + funlockfile(stderr); + } +} + +/** + * A per-thread handle guard to prevent infinite recursion, which should be + * acquired before doing any special symbol handling. + */ +struct RecursionGuard +{ + RecursionGuard() + : wasLocked(isActive) + { + isActive = true; + } + + ~RecursionGuard() + { + isActive = wasLocked; + } + + const bool wasLocked; + static thread_local bool isActive; +}; + extern "C" { #endif @@ -30,6 +81,9 @@ void heaptrack_init(const char* outputFileName, heaptrack_callback_t initCallbac void heaptrack_stop(); +void heaptrack_dlopen(const std::vector>> &newMmaps, bool isPreloaded, void *dlopenOriginal); +void heaptrack_dlclose(const std::vector> &unmaps); + void heaptrack_malloc(void* ptr, size_t size); void heaptrack_free(void* ptr); diff --git a/src/track/trace.h b/src/track/trace.h index 288f6c4..31a0678 100644 --- a/src/track/trace.h +++ b/src/track/trace.h @@ -71,6 +71,13 @@ struct Trace return m_size > 0; } + void fill(void *addr) + { + m_size = 2; + m_skip = 0; + m_data[0] = addr; + m_data[1] = addr; + } private: int m_size = 0; int m_skip = 0; -- 2.34.1