diff --git a/CMakeLists.txt b/CMakeLists.txt index bd45289..e313fb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,150 +1,151 @@ if (CMAKE_VERSION VERSION_LESS "2.8.12") cmake_minimum_required(VERSION 2.8.9) set(HEAPTRACK_BUILD_GUI OFF) else() cmake_minimum_required(VERSION 2.8.12) endif() project(heaptrack) enable_testing() if(NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE) endif() set(HEAPTRACK_VERSION_MAJOR 1) set(HEAPTRACK_VERSION_MINOR 1) set(HEAPTRACK_VERSION_PATCH 80) set(HEAPTRACK_LIB_VERSION 1.1.80) set(HEAPTRACK_LIB_SOVERSION 2) set(HEAPTRACK_FILE_FORMAT_VERSION 2) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(FeatureSummary) find_package(Boost 1.41.0 COMPONENTS system filesystem iostreams) find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) if (${Boost_IOSTREAMS_FOUND}) find_package(Zstd) endif() set_package_properties(Zstd PROPERTIES TYPE RECOMMENDED PURPOSE "Zstandard offers better (de)compression performance compared with gzip/zlib, making heaptrack faster and datafiles smaller.") if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set(HEAPTRACK_BUILD_TRACK_DEFAULT ON) set(HEAPTRACK_BUILD_INTERPRET_DEFAULT ON) else() set(HEAPTRACK_BUILD_TRACK_DEFAULT OFF) set(HEAPTRACK_BUILD_INTERPRET_DEFAULT OFF) endif() option( HEAPTRACK_BUILD_TRACK "Disable this option to skip building the tracker part for heaptrack, e.g. to only build the GUI." ${HEAPTRACK_BUILD_TRACK_DEFAULT} ) option( HEAPTRACK_BUILD_BACKTRACE "Enable this option to build libbacktrace. It will be enabled implicitly if HEAPTRACK_BUILD_INTERPRET is enabled." OFF ) option( HEAPTRACK_BUILD_INTERPRET "Disable this option to skip building the interpret part for heaptrack." ${HEAPTRACK_BUILD_INTERPRET_DEFAULT} ) if (HEAPTRACK_BUILD_INTERPRET) set(HEAPTRACK_BUILD_BACKTRACE ON) endif() if (CMAKE_CROSSCOMPILING) set(HEAPTRACK_BUILD_ANALYZE_DEFAULT OFF) else() set(HEAPTRACK_BUILD_ANALYZE_DEFAULT ON) endif() option( HEAPTRACK_BUILD_PRINT "Disable this option to skip building heaptrack_print, e.g. when you're cross-compiling." ${HEAPTRACK_BUILD_ANALYZE_DEFAULT} ) option( HEAPTRACK_BUILD_GUI "Disable this option to skip building the Qt5 / KF5 based GUI for heaptrack." ${HEAPTRACK_BUILD_ANALYZE_DEFAULT} ) option( HEAPTRACK_USE_LIBUNWIND "Define preferred unwind functionality - Libunwind as ON and unwind_tables as OFF." ON ) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) if (NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic") endif() include (CheckCXXSourceCompiles) check_cxx_source_compiles( "#include #include thread_local int tls; int main() { return 0; }" HAVE_CXX11_SUPPORT) if (NOT HAVE_CXX11_SUPPORT) message(FATAL_ERROR "Your compiler is too old and does not support the required C++11 features.") endif() # cfree() does not exist in glibc 2.26+. # See: https://bugs.kde.org/show_bug.cgi?id=383889 include(CheckSymbolExists) check_symbol_exists(cfree malloc.h HAVE_CFREE) +check_symbol_exists(valloc stdlib.h HAVE_VALLOC) set(BIN_INSTALL_DIR "bin") set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)") set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}") set(LIBEXEC_INSTALL_DIR "${LIB_INSTALL_DIR}/heaptrack/libexec") file(RELATIVE_PATH LIBEXEC_REL_PATH "${CMAKE_INSTALL_PREFIX}/${BIN_INSTALL_DIR}" "${CMAKE_INSTALL_PREFIX}/${LIBEXEC_INSTALL_DIR}") file(RELATIVE_PATH LIB_REL_PATH "${CMAKE_INSTALL_PREFIX}/${BIN_INSTALL_DIR}" "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/heaptrack") set(ECM_ENABLE_SANITIZERS "" CACHE STRING "semicolon-separated list of sanitizers to enable for code that is not injected into client applications") if (HEAPTRACK_BUILD_TRACK) if (HEAPTRACK_USE_LIBUNWIND) find_package(Libunwind REQUIRED) endif() check_cxx_source_compiles( "#include #include #include #include int main() { return 0; }" HAVE_LINUX_HEADERS) if (NOT HAVE_LINUX_HEADERS) message(FATAL_ERROR "You are missing some Linux headers required to compile heaptrack.") endif() endif() add_subdirectory(3rdparty) add_subdirectory(src) add_subdirectory(tests) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/track/heaptrack_preload.cpp b/src/track/heaptrack_preload.cpp index c9f1752..63110ce 100644 --- a/src/track/heaptrack_preload.cpp +++ b/src/track/heaptrack_preload.cpp @@ -1,327 +1,333 @@ /* * Copyright 2014-2017 Milian Wolff * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libheaptrack.h" #include "util/config.h" #include #include #include #include #include #include using namespace std; #if defined(_ISOC11_SOURCE) #define HAVE_ALIGNED_ALLOC 1 #else #define HAVE_ALIGNED_ALLOC 0 #endif namespace { namespace hooks { template struct hook { Signature original = nullptr; void init() noexcept { auto ret = dlsym(RTLD_NEXT, Base::identifier); if (!ret) { fprintf(stderr, "Could not find original function %s\n", Base::identifier); abort(); } original = reinterpret_cast(ret); } template auto operator()(Args... args) const noexcept -> decltype(original(args...)) { return original(args...); } explicit operator bool() const noexcept { return original; } }; #define HOOK(name) \ struct name##_t : public hook \ { \ static constexpr const char* identifier = #name; \ } name #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-attributes" HOOK(malloc); HOOK(free); HOOK(calloc); #if HAVE_CFREE HOOK(cfree); #endif HOOK(realloc); HOOK(posix_memalign); +#if HAVE_VALLOC HOOK(valloc); +#endif #if HAVE_ALIGNED_ALLOC HOOK(aligned_alloc); #endif HOOK(dlopen); HOOK(dlclose); #pragma GCC diagnostic pop #undef HOOK /** * Dummy implementation, since the call to dlsym from findReal triggers a call * to calloc. * * This is only called at startup and will eventually be replaced by the * "proper" calloc implementation. */ struct DummyPool { static const constexpr size_t MAX_SIZE = 1024; char buf[MAX_SIZE] = {0}; size_t offset = 0; bool isDummyAllocation(void* ptr) noexcept { return ptr >= buf && ptr < buf + MAX_SIZE; } void* alloc(size_t num, size_t size) noexcept { size_t oldOffset = offset; offset += num * size; if (offset >= MAX_SIZE) { fprintf(stderr, "failed to initialize, dummy calloc buf size exhausted: " "%zu requested, %zu available\n", offset, MAX_SIZE); abort(); } return buf + oldOffset; } }; DummyPool& dummyPool() { static DummyPool pool; return pool; } void* dummy_calloc(size_t num, size_t size) noexcept { return dummyPool().alloc(num, size); } void init() { heaptrack_init(getenv("DUMP_HEAPTRACK_OUTPUT"), [] { hooks::calloc.original = &dummy_calloc; hooks::calloc.init(); hooks::dlopen.init(); hooks::dlclose.init(); hooks::malloc.init(); hooks::free.init(); hooks::calloc.init(); #if HAVE_CFREE hooks::cfree.init(); #endif hooks::realloc.init(); hooks::posix_memalign.init(); +#if HAVE_VALLOC hooks::valloc.init(); +#endif #if HAVE_ALIGNED_ALLOC hooks::aligned_alloc.init(); #endif // cleanup environment to prevent tracing of child apps unsetenv("LD_PRELOAD"); unsetenv("DUMP_HEAPTRACK_OUTPUT"); }, nullptr, nullptr); } } } extern "C" { /// TODO: memalign, pvalloc, ...? void* malloc(size_t size) noexcept { if (!hooks::malloc) { hooks::init(); } void* ptr = hooks::malloc(size); heaptrack_malloc(ptr, size); return ptr; } void free(void* ptr) noexcept { if (!hooks::free) { hooks::init(); } if (hooks::dummyPool().isDummyAllocation(ptr)) { return; } // call handler before handing over the real free implementation // to ensure the ptr is not reused in-between and thus the output // stays consistent heaptrack_free(ptr); hooks::free(ptr); } void* realloc(void* ptr, size_t size) noexcept { if (!hooks::realloc) { hooks::init(); } void* ret = hooks::realloc(ptr, size); if (ret) { heaptrack_realloc(ptr, size, ret); } return ret; } void* calloc(size_t num, size_t size) noexcept { if (!hooks::calloc) { hooks::init(); } void* ret = hooks::calloc(num, size); if (ret) { heaptrack_malloc(ret, num * size); } return ret; } #if HAVE_CFREE void cfree(void* ptr) noexcept { if (!hooks::cfree) { hooks::init(); } // call handler before handing over the real free implementation // to ensure the ptr is not reused in-between and thus the output // stays consistent if (ptr) { heaptrack_free(ptr); } hooks::cfree(ptr); } #endif int posix_memalign(void** memptr, size_t alignment, size_t size) noexcept { if (!hooks::posix_memalign) { hooks::init(); } int ret = hooks::posix_memalign(memptr, alignment, size); if (!ret) { heaptrack_malloc(*memptr, size); } return ret; } #if HAVE_ALIGNED_ALLOC void* aligned_alloc(size_t alignment, size_t size) noexcept { if (!hooks::aligned_alloc) { hooks::init(); } void* ret = hooks::aligned_alloc(alignment, size); if (ret) { heaptrack_malloc(ret, size); } return ret; } #endif +#if HAVE_VALLOC void* valloc(size_t size) noexcept { if (!hooks::valloc) { hooks::init(); } void* ret = hooks::valloc(size); if (ret) { heaptrack_malloc(ret, size); } return ret; } +#endif void* dlopen(const char* filename, int flag) noexcept { if (!hooks::dlopen) { hooks::init(); } void* ret = hooks::dlopen(filename, flag); if (ret) { heaptrack_invalidate_module_cache(); } return ret; } int dlclose(void* handle) noexcept { if (!hooks::dlclose) { hooks::init(); } int ret = hooks::dlclose(handle); if (!ret) { heaptrack_invalidate_module_cache(); } return ret; } } diff --git a/src/util/config.h.cmake b/src/util/config.h.cmake index 861c1f7..6c9098a 100644 --- a/src/util/config.h.cmake +++ b/src/util/config.h.cmake @@ -1,36 +1,37 @@ /* * Copyright 2014-2017 Milian Wolff * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HEAPTRACK_CONFIG_H #define HEAPTRACK_CONFIG_H #define HEAPTRACK_VERSION_STRING "@HEAPTRACK_VERSION_MAJOR@.@HEAPTRACK_VERSION_MINOR@.@HEAPTRACK_VERSION_PATCH@" #define HEAPTRACK_VERSION_MAJOR @HEAPTRACK_VERSION_MAJOR@ #define HEAPTRACK_VERSION_MINOR @HEAPTRACK_VERSION_MINOR@ #define HEAPTRACK_VERSION_PATCH @HEAPTRACK_VERSION_PATCH@ #define HEAPTRACK_VERSION ((HEAPTRACK_VERSION_MAJOR<<16)|(HEAPTRACK_VERSION_MINOR<<8)|(HEAPTRACK_VERSION_PATCH)) #define HEAPTRACK_FILE_FORMAT_VERSION @HEAPTRACK_FILE_FORMAT_VERSION@ #define HEAPTRACK_DEBUG_BUILD @HEAPTRACK_DEBUG_BUILD@ // cfree() does not exist in glibc 2.26+. // See: https://bugs.kde.org/show_bug.cgi?id=383889 #cmakedefine01 HAVE_CFREE +#cmakedefine01 HAVE_VALLOC #endif // HEAPTRACK_CONFIG_H