[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [heaptrack/inject] /: Add -p/--pid option to attach heaptrack to a running process.
From:       Milian Wolff <mail () milianw ! de>
Date:       2014-12-09 0:17:12
Message-ID: E1Xy8UK-0005KF-7W () scm ! kde ! org
[Download RAW message or body]

Git commit 2c3eb0da70a138971752e7424d5e82026445fe44 by Milian Wolff.
Committed on 09/12/2014 at 00:13.
Pushed by mwolff into branch 'inject'.

Add -p/--pid option to attach heaptrack to a running process.

We use gdb to attach to the process, then call dlopen there and
finally an initialization hook in the new libheaptrack_inject.so.
To prevent an underflow in the total memory consumption, we
call malloc_info and parse that in heaptrack_interpret to get a
baseline for the current total memory consumption at the point
where we attached to the process.

The code is refactored into a shared libheaptrack.cpp with common
code, and two libraries, libheaptrack_inject.so and the old
libheaptrack_preload.so. The heaptrack bash script is adapted to
support both versions.

M  +7    -8    CMakeLists.txt
M  +45   -10   heaptrack.sh.cmake
M  +20   -11   heaptrack_inject.cpp
M  +20   -0    heaptrack_interpret.cpp
A  +277  -0    heaptrack_preload.cpp     [License: LGPL (v2+)]
M  +16   -2    heaptrack_print.cpp
M  +20   -241  libheaptrack.cpp
M  +2    -1    libheaptrack.h

http://commits.kde.org/heaptrack/2c3eb0da70a138971752e7424d5e82026445fe44

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 627ac80..4eba06b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,12 +46,10 @@ include_directories(
     ${LIBUNWIND_INCLUDE_DIR}
 )
 
-add_library(libheaptrack SHARED libheaptrack.cpp)
-# the final .so should be named libheaptrack, not liblibheaptrack
-set_target_properties(libheaptrack PROPERTIES OUTPUT_NAME heaptrack)
-target_link_libraries(libheaptrack ${CMAKE_DL_LIBS} backtrace rt)
-add_library(heaptrackinject SHARED heaptrack_inject.cpp)
-target_link_libraries(heaptrackinject PRIVATE libheaptrack)
+add_library(heaptrack_preload MODULE heaptrack_preload.cpp libheaptrack.cpp)
+target_link_libraries(heaptrack_preload PRIVATE ${CMAKE_DL_LIBS} backtrace rt)
+add_library(heaptrack_inject MODULE heaptrack_inject.cpp libheaptrack.cpp)
+target_link_libraries(heaptrack_inject PRIVATE ${CMAKE_DL_LIBS} backtrace rt)
 
 add_executable(heaptrack_interpret heaptrack_interpret.cpp)
 target_link_libraries(heaptrack_interpret backtrace)
@@ -70,11 +68,12 @@ file(RELATIVE_PATH LIBEXEC_REL_PATH
 
 file(RELATIVE_PATH LIB_REL_PATH
     "${CMAKE_INSTALL_PREFIX}/${BIN_INSTALL_DIR}"
-    "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}")
+    "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/heaptrack")
 
 configure_file(heaptrack.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/heaptrack @ONLY)
 
 install(TARGETS heaptrack_print RUNTIME DESTINATION ${BIN_INSTALL_DIR})
 install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/heaptrack DESTINATION \
                ${BIN_INSTALL_DIR})
-install(TARGETS libheaptrack LIBRARY DESTINATION ${LIB_INSTALL_DIR})
 install(TARGETS heaptrack_interpret RUNTIME DESTINATION ${LIBEXEC_INSTALL_DIR})
+install(TARGETS heaptrack_inject LIBRARY DESTINATION ${LIB_INSTALL_DIR}/heaptrack/)
+install(TARGETS heaptrack_preload LIBRARY DESTINATION ${LIB_INSTALL_DIR}/heaptrack/)
diff --git a/heaptrack.sh.cmake b/heaptrack.sh.cmake
index 4ad5d76..789ddda 100755
--- a/heaptrack.sh.cmake
+++ b/heaptrack.sh.cmake
@@ -47,6 +47,7 @@ usage() {
 }
 
 debug=
+pid=
 client=
 clientargs=
 
@@ -62,6 +63,26 @@ while true; do
             usage
             exit 0
             ;;
+        "-p") ;&
+        "--pid")
+            pid=$2
+            if [ -z "$pid" ]; then
+                echo "Missing PID argument."
+                exit 1
+            fi
+            client=$(ps --no-headers -c -o comm -p $pid)
+            if [ -z "$client" ]; then
+                echo "Cannot attach to unknown process with PID $pid."
+                exit 1
+            fi
+            shift 2
+            echo $@
+            if [ ! -z "$@" ]; then
+                echo "You cannot specify a debuggee and a pid at the same time."
+                exit 1
+            fi
+            break
+            ;;
         *)
             if [ ! -x "$(which $1 2> /dev/null)" ]; then
                 echo "Error: Debuggee \"$1\" is not an executable."
@@ -72,7 +93,7 @@ while true; do
             client="$1"
             shift 1
             clientargs="$@"
-            break;
+            break
             ;;
     esac
 done
@@ -92,12 +113,19 @@ if [ ! -f "$INTERPRETER" ]; then
 fi
 INTERPRETER=$(readlink -f "$INTERPRETER")
 
-LIBHEAPTRACK="$EXE_PATH/$LIB_REL_PATH/libheaptrack.so"
-if [ ! -f "$LIBHEAPTRACK" ]; then
-    echo "Could not find heaptrack preload library$LIBHEAPTRACK"
+LIBHEAPTRACK_PRELOAD="$EXE_PATH/$LIB_REL_PATH/libheaptrack_preload.so"
+if [ ! -f "$LIBHEAPTRACK_PRELOAD" ]; then
+    echo "Could not find heaptrack preload library $LIBHEAPTRACK_PRELOAD"
+    exit 1
+fi
+LIBHEAPTRACK_PRELOAD=$(readlink -f "$LIBHEAPTRACK_PRELOAD")
+
+LIBHEAPTRACK_INJECT="$EXE_PATH/$LIB_REL_PATH/libheaptrack_inject.so"
+if [ ! -f "$LIBHEAPTRACK_INJECT" ]; then
+    echo "Could not find heaptrack inject library $LIBHEAPTRACK_INJECT"
     exit 1
 fi
-LIBHEAPTRACK=$(readlink -f "$LIBHEAPTRACK")
+LIBHEAPTRACK_INJECT=$(readlink -f "$LIBHEAPTRACK_INJECT")
 
 # setup named pipe to read data from
 pipe=/tmp/heaptrack_fifo$$
@@ -112,12 +140,19 @@ debuggee=$!
 echo "starting application, this might take some time..."
 echo "output will be written to $output"
 
-if [ -z "$debug" ]; then
-  LD_PRELOAD=$LIBHEAPTRACK DUMP_HEAPTRACK_OUTPUT="$pipe" $client $clientargs
+if [ -z "$debug" ] && [ -z "$pid" ]; then
+  LD_PRELOAD=$LIBHEAPTRACK_PRELOAD DUMP_HEAPTRACK_OUTPUT="$pipe" $client $clientargs
 else
-  gdb --eval-command="set environment LD_PRELOAD=$LIBHEAPTRACK" \
-      --eval-command="set environment DUMP_HEAPTRACK_OUTPUT=$pipe" \
-      --eval-command="run" --args $client $clientargs
+  if [ -z "$pid" ]; then
+    gdb --eval-command="set environment LD_PRELOAD=$LIBHEAPTRACK_PRELOAD" \
+        --eval-command="set environment DUMP_HEAPTRACK_OUTPUT=$pipe" \
+        --eval-command="run" --args $client $clientargs
+  else
+    gdb -p $pid \
+        --eval-command="call (void) dlopen(\"$LIBHEAPTRACK_INJECT\", 0x002)" \
+        --eval-command="call (void) init_heaptrack_inject(\"$pipe\")" \
+        --eval-command="detach" --eval-command="quit"
+  fi
 fi
 
 wait $debuggee
diff --git a/heaptrack_inject.cpp b/heaptrack_inject.cpp
index bccba2d..8f54613 100644
--- a/heaptrack_inject.cpp
+++ b/heaptrack_inject.cpp
@@ -20,9 +20,10 @@
 #include "libheaptrack.h"
 
 #include <link.h>
-#include <string.h>
-#include <stdlib.h>
+#include <cstring>
+#include <cstdlib>
 #include <sys/mman.h>
+#include <malloc.h>
 
 #include <type_traits>
 
@@ -155,7 +156,8 @@ struct hook
     {
         static_assert(sizeof(&Hook::hook) == sizeof(void*), "Mismatched pointer \
                sizes");
         static_assert(std::is_convertible<decltype(&Hook::hook), \
                decltype(Hook::original)>::value,
-                    "hook is not compatible to original function");
+                      "hook is not compatible to original function");
+        static_assert(&Hook::hook != Hook::original, "Recursion detected");
         // TODO: why is (void*) cast allowed, but not reinterpret_cast?
         return {Hook::name, (void*)(&Hook::hook)};
     }
@@ -228,8 +230,8 @@ void try_overwrite_symbols(const ElfW(Dyn) *dyn, const ElfW(Addr) \
base)  
 int iterate_phdrs(dl_phdr_info *info, size_t /*size*/, void *data)
 {
-    if (strstr(info->dlpi_name, "/libheaptrackinject.so")) {
-        // do not
+    if (strstr(info->dlpi_name, "/libheaptrack_inject.so")) {
+        // prevent infinite recursion: do not overwrite our own symbols
         return 0;
     }
 
@@ -242,13 +244,20 @@ int iterate_phdrs(dl_phdr_info *info, size_t /*size*/, void \
*data)  return 0;
 }
 
-struct InitializeInjection
+}
+
+extern "C" {
+
+void init_heaptrack_inject(const char *outputFileName)
 {
-    InitializeInjection()
-    {
+    heaptrack_init(outputFileName, [] () {
         dl_iterate_phdr(&iterate_phdrs, nullptr);
-        heaptrack_init();
-    }
-} initialize;
+    }, [] () {
+        auto out = heaptrack_output_file();
+        fprintf(out, "A BEGIN_MALLOC_INFO\n");
+        malloc_info(0, out);
+        fprintf(out, "\nA END_MALLOC_INFO\n");
+    });
+}
 
 }
diff --git a/heaptrack_interpret.cpp b/heaptrack_interpret.cpp
index 3a4a8e4..fde77ec 100644
--- a/heaptrack_interpret.cpp
+++ b/heaptrack_interpret.cpp
@@ -24,6 +24,7 @@
  */
 
 #include <iostream>
+#include <sstream>
 #include <unordered_map>
 #include <vector>
 #include <tuple>
@@ -31,7 +32,10 @@
 #include <stdio_ext.h>
 
 #include <cxxabi.h>
+
 #include <boost/algorithm/string/predicate.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
 
 #include "libbacktrace/backtrace.h"
 #include "linereader.h"
@@ -378,6 +382,22 @@ int main(int /*argc*/, char** /*argv*/)
             const auto ipId = data.addIp(instructionPointer);
             // trace point, map current output index to parent index
             fprintf(stdout, "t %lx %lx\n", ipId, parentIndex);
+        } else if (reader.mode() == 'A') {
+            // we attached, and now have to parse the malloc_info to get some \
baseline +            stringstream xml;
+            while (reader.getLine(cin) && reader.mode() != 'A') {
+                xml << reader.line();
+            }
+            boost::property_tree::ptree pt;
+            boost::property_tree::read_xml(xml, pt);
+            size_t current = 0;
+            for (const auto& element : pt.get_child("malloc")) {
+                if (element.first == "system" && \
element.second.get<string>("<xmlattr>.type") == "current") { +                    \
current = element.second.get<size_t>("<xmlattr>.size"); +                    break;
+                }
+            }
+            fprintf(stdout, "A %lx\n", current);
         } else {
             fputs(reader.line().c_str(), stdout);
             fputc('\n', stdout);
diff --git a/heaptrack_preload.cpp b/heaptrack_preload.cpp
new file mode 100644
index 0000000..d151d11
--- /dev/null
+++ b/heaptrack_preload.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2014 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "libheaptrack.h"
+
+#include <dlfcn.h>
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+
+#include <type_traits>
+#include <atomic>
+
+using namespace std;
+
+namespace {
+
+namespace hooks {
+
+template<typename SignatureT>
+struct hook
+{
+    hook(const char* const name)
+        : name(name)
+    {}
+
+    using Signature = SignatureT*;
+    Signature original = nullptr;
+
+    const char * const name;
+    void init()
+    {
+        auto ret = dlsym(RTLD_NEXT, name);
+        if (!ret) {
+            fprintf(stderr, "Could not find original function %s\n", name);
+            abort();
+        }
+        original = reinterpret_cast<Signature>(ret);
+    }
+
+    template<typename... Args>
+    auto operator() (Args... args) const -> decltype(original(args...))
+    {
+        return original(args...);
+    }
+
+    explicit operator bool () const
+    {
+        return original;
+    }
+};
+
+hook<decltype(::malloc)> malloc{"malloc"};
+hook<decltype(::free)> free{"free"};
+hook<decltype(::calloc)> calloc{"calloc"};
+hook<decltype(::cfree)> cfree{"cfree"};
+hook<decltype(::realloc)> realloc{"realloc"};
+hook<decltype(::posix_memalign)> posix_memalign{"posix_memalign"};
+hook<decltype(::valloc)> valloc{"valloc"};
+hook<decltype(::aligned_alloc)> aligned_alloc{"aligned_alloc"};
+hook<decltype(::dlopen)> dlopen{"dlopen"};
+hook<decltype(::dlclose)> dlclose{"dlclose"};
+
+/**
+ * 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. + */
+void* dummy_calloc(size_t num, size_t size)
+{
+    const size_t MAX_SIZE = 1024;
+    static char* buf[MAX_SIZE];
+    static size_t offset = 0;
+    if (!offset) {
+        memset(buf, 0, MAX_SIZE);
+    }
+    size_t oldOffset = offset;
+    offset += num * size;
+    if (offset >= MAX_SIZE) {
+        fprintf(stderr, "failed to initialize, dummy calloc buf size exhausted: %lu \
requested, %lu available\n", offset, MAX_SIZE); +        abort();
+    }
+    return buf + oldOffset;
+}
+
+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();
+        hooks::cfree.init();
+        hooks::realloc.init();
+        hooks::posix_memalign.init();
+        hooks::valloc.init();
+        hooks::aligned_alloc.init();
+
+        // cleanup environment to prevent tracing of child apps
+        unsetenv("LD_PRELOAD");
+    }, nullptr);
+}
+
+}
+
+}
+
+extern "C" {
+
+/// TODO: memalign, pvalloc, ...?
+
+void* malloc(size_t size)
+{
+    if (!hooks::malloc) {
+        hooks::init();
+    }
+
+    void* ptr = hooks::malloc(size);
+    heaptrack_malloc(ptr, size);
+    return ptr;
+}
+
+void free(void* ptr)
+{
+    if (!hooks::free) {
+        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
+    heaptrack_free(ptr);
+
+    hooks::free(ptr);
+}
+
+void* realloc(void* ptr, size_t size)
+{
+    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)
+{
+    if (!hooks::calloc) {
+        hooks::init();
+    }
+
+    void* ret = hooks::calloc(num, size);
+
+    if (ret) {
+        heaptrack_malloc(ret, num * size);
+    }
+
+    return ret;
+}
+
+void cfree(void* ptr)
+{
+    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);
+}
+
+int posix_memalign(void **memptr, size_t alignment, size_t size)
+{
+    if (!hooks::posix_memalign) {
+        hooks::init();
+    }
+
+    int ret = hooks::posix_memalign(memptr, alignment, size);
+
+    if (!ret) {
+        heaptrack_malloc(*memptr, size);
+    }
+
+    return ret;
+}
+
+void* aligned_alloc(size_t alignment, size_t size)
+{
+    if (!hooks::aligned_alloc) {
+        hooks::init();
+    }
+
+    void* ret = hooks::aligned_alloc(alignment, size);
+
+    if (ret) {
+        heaptrack_malloc(ret, size);
+    }
+
+    return ret;
+}
+
+void* valloc(size_t size)
+{
+    if (!hooks::valloc) {
+        hooks::init();
+    }
+
+    void* ret = hooks::valloc(size);
+
+    if (ret) {
+        heaptrack_malloc(ret, size);
+    }
+
+    return ret;
+}
+
+void *dlopen(const char *filename, int flag)
+{
+    if (!hooks::dlopen) {
+        hooks::init();
+    }
+
+    void* ret = hooks::dlopen(filename, flag);
+
+    if (ret) {
+        heaptrack_invalidate_module_cache();
+    }
+
+    return ret;
+}
+
+int dlclose(void *handle)
+{
+    if (!hooks::dlclose) {
+        hooks::init();
+    }
+
+    int ret = hooks::dlclose(handle);
+
+    if (!ret) {
+        heaptrack_invalidate_module_cache();
+    }
+
+    return ret;
+}
+
+}
diff --git a/heaptrack_print.cpp b/heaptrack_print.cpp
index d91ba74..4d3d33b 100644
--- a/heaptrack_print.cpp
+++ b/heaptrack_print.cpp
@@ -503,7 +503,9 @@ struct AccumulatedTraceData
                 }
                 auto ip = activeAllocations.find(ptr);
                 if (ip == activeAllocations.end()) {
-                    cerr << "unknown pointer in line: " << reader.line() << endl;
+                    if (!fromAttached) {
+                        cerr << "unknown pointer in line: " << reader.line() << \
endl; +                    }
                     continue;
                 }
                 const auto info = ip->second;
@@ -511,7 +513,9 @@ struct AccumulatedTraceData
 
                 auto& allocation = findAllocation(info.traceIndex);
                 if (!allocation.allocations || allocation.leaked < info.size) {
-                    cerr << "inconsistent allocation info, underflowed allocations \
of " << info.traceIndex << endl; +                    if (!fromAttached) {
+                        cerr << "inconsistent allocation info, underflowed \
allocations of " << info.traceIndex << endl; +                    }
                     allocation.leaked = 0;
                     allocation.allocations = 0;
                 } else {
@@ -537,6 +541,15 @@ struct AccumulatedTraceData
                 if (massifOut.is_open()) {
                     writeMassifHeader(reader.line().c_str() + 2);
                 }
+            } else if (reader.mode() == 'A') {
+                size_t current = 0;
+                if (!(reader >> current)) {
+                    cerr << "Failed to read current size after attaching." << endl;
+                    continue;
+                }
+                leaked = current;
+                peak = current;
+                fromAttached = true;
             } else {
                 cerr << "failed to parse line: " << reader.line() << endl;
             }
@@ -557,6 +570,7 @@ struct AccumulatedTraceData
     bool shortenTemplates = false;
     bool mergeBacktraces = true;
     bool printHistogram = false;
+    bool fromAttached = false;
     ofstream massifOut;
     double massifThreshold = 1;
     size_t massifDetailedFreq = 1;
diff --git a/libheaptrack.cpp b/libheaptrack.cpp
index 2c4e3be..c6f8c9c 100644
--- a/libheaptrack.cpp
+++ b/libheaptrack.cpp
@@ -29,7 +29,6 @@
 #include <cstdlib>
 #include <stdio_ext.h>
 #include <fcntl.h>
-#include <dlfcn.h>
 #include <link.h>
 
 #include <atomic>
@@ -54,28 +53,6 @@ using namespace std;
 
 namespace {
 
-using malloc_t = void* (*) (size_t);
-using free_t = void (*) (void*);
-using cfree_t = void (*) (void*);
-using realloc_t = void* (*) (void*, size_t);
-using calloc_t = void* (*) (size_t, size_t);
-using posix_memalign_t = int (*) (void **, size_t, size_t);
-using valloc_t = void* (*) (size_t);
-using aligned_alloc_t = void* (*) (size_t, size_t);
-using dlopen_t = void* (*) (const char*, int);
-using dlclose_t = int (*) (void*);
-
-malloc_t real_malloc = nullptr;
-free_t real_free = nullptr;
-cfree_t real_cfree = nullptr;
-realloc_t real_realloc = nullptr;
-calloc_t real_calloc = nullptr;
-posix_memalign_t real_posix_memalign = nullptr;
-valloc_t real_valloc = nullptr;
-aligned_alloc_t real_aligned_alloc = nullptr;
-dlopen_t real_dlopen = nullptr;
-dlclose_t real_dlclose = nullptr;
-
 // threadsafe stuff
 atomic<bool> moduleCacheDirty(true);
 
@@ -119,12 +96,6 @@ private:
 
 thread_local bool HandleGuard::inHandler = false;
 
-string env(const char* variable)
-{
-    const char* value = getenv(variable);
-    return value ? string(value) : string();
-}
-
 void writeExe(FILE* out)
 {
     const int BUF_SIZE = 1023;
@@ -160,11 +131,14 @@ void child_fork();
 
 struct Data
 {
-    Data()
+    Data(const char *outputFileName_)
     {
         pthread_atfork(&prepare_fork, &parent_fork, &child_fork);
 
-        string outputFileName = env("DUMP_HEAPTRACK_OUTPUT");
+        string outputFileName;
+        if (outputFileName_) {
+            outputFileName.assign(outputFileName_);
+        }
         if (outputFileName.empty()) {
             // env var might not be set when linked directly into an executable
             outputFileName = "heaptrack.$$";
@@ -192,7 +166,6 @@ struct Data
 
         // cleanup environment to prevent tracing of child apps
         unsetenv("DUMP_HEAPTRACK_OUTPUT");
-        unsetenv("LD_PRELOAD");
 
         // print a backtrace in every interval
         timer.setInterval(0, 1000 * 1000 * 10);
@@ -314,61 +287,23 @@ void child_fork()
     HandleGuard::inHandler = true;
 }
 
-template<typename T>
-T findReal(const char* name)
-{
-    auto ret = dlsym(RTLD_NEXT, name);
-    if (!ret) {
-        fprintf(stderr, "Could not find original function %s\n", name);
-        abort();
-    }
-    return reinterpret_cast<T>(ret);
 }
+extern "C" {
 
-/**
- * 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.
- */
-void* dummy_calloc(size_t num, size_t size)
+void heaptrack_init(const char *outputFileName, void (*initCallbackBefore) (), void \
(*initCallbackAfter) ())  {
-    const size_t MAX_SIZE = 1024;
-    static char* buf[MAX_SIZE];
-    static size_t offset = 0;
-    if (!offset) {
-        memset(buf, 0, MAX_SIZE);
-    }
-    size_t oldOffset = offset;
-    offset += num * size;
-    if (offset >= MAX_SIZE) {
-        fprintf(stderr, "failed to initialize, dummy calloc buf size exhausted: %lu \
                requested, %lu available\n", offset, MAX_SIZE);
-        abort();
-    }
-    return buf + oldOffset;
-}
+    HandleGuard guard;
 
-void init()
-{
     static once_flag once;
-    call_once(once, [] {
-        if (data || HandleGuard::inHandler) {
+    call_once(once, [=] {
+        if (data) {
             fprintf(stderr, "initialization recursion detected\n");
             abort();
         }
 
-        HandleGuard guard;
-
-        real_calloc = &dummy_calloc;
-        real_calloc = findReal<calloc_t>("calloc");
-        real_dlopen = findReal<dlopen_t>("dlopen");
-        real_dlclose = findReal<dlclose_t>("dlclose");
-        real_malloc = findReal<malloc_t>("malloc");
-        real_free = findReal<free_t>("free");
-        real_cfree = findReal<cfree_t>("cfree");
-        real_realloc = findReal<realloc_t>("realloc");
-        real_posix_memalign = findReal<posix_memalign_t>("posix_memalign");
-        real_valloc = findReal<valloc_t>("valloc");
-        real_aligned_alloc = findReal<aligned_alloc_t>("aligned_alloc");
+        if (initCallbackBefore) {
+            initCallbackBefore();
+        }
 
         if (unw_set_caching_policy(unw_local_addr_space, UNW_CACHE_PER_THREAD)) {
             fprintf(stderr, "Failed to enable per-thread libunwind caching.\n");
@@ -377,16 +312,17 @@ void init()
             fprintf(stderr, "Failed to set libunwind cache size.\n");
         }
 
-        data.reset(new Data);
-    });
-}
+        data.reset(new Data(outputFileName));
 
+        if (initCallbackAfter) {
+            initCallbackAfter();
+        }
+    });
 }
-extern "C" {
 
-void heaptrack_init()
+FILE* heaptrack_output_file()
 {
-    init();
+    return data ? data->out : nullptr;
 }
 
 void heaptrack_malloc(void* ptr, size_t size)
@@ -421,161 +357,4 @@ void heaptrack_invalidate_module_cache()
     moduleCacheDirty = true;
 }
 
-/// TODO: memalign, pvalloc, ...?
-
-void* malloc(size_t size)
-{
-    if (!real_malloc) {
-        init();
-    }
-
-    void* ptr = real_malloc(size);
-    heaptrack_malloc(ptr, size);
-    return ptr;
-}
-
-void free(void* ptr)
-{
-    if (!real_free) {
-        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
-    heaptrack_free(ptr);
-
-    real_free(ptr);
-}
-
-void* realloc(void* ptr, size_t size)
-{
-    if (!real_realloc) {
-        init();
-    }
-
-    void* ret = real_realloc(ptr, size);
-
-    if (ret && !HandleGuard::inHandler && data) {
-        HandleGuard guard;
-        if (ptr) {
-            data->handleFree(ptr);
-        }
-        data->handleMalloc(ret, size);
-    }
-
-    return ret;
-}
-
-void* calloc(size_t num, size_t size)
-{
-    if (!real_calloc) {
-        init();
-    }
-
-    void* ret = real_calloc(num, size);
-
-    if (ret && !HandleGuard::inHandler && data) {
-        HandleGuard guard;
-        data->handleMalloc(ret, num*size);
-    }
-
-    return ret;
-}
-
-void cfree(void* ptr)
-{
-    if (!real_cfree) {
-        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 && !HandleGuard::inHandler && data) {
-        HandleGuard guard;
-        data->handleFree(ptr);
-    }
-
-    real_cfree(ptr);
-}
-
-int posix_memalign(void **memptr, size_t alignment, size_t size)
-{
-    if (!real_posix_memalign) {
-        init();
-    }
-
-    int ret = real_posix_memalign(memptr, alignment, size);
-
-    if (!ret && !HandleGuard::inHandler && data) {
-        HandleGuard guard;
-        data->handleMalloc(*memptr, size);
-    }
-
-    return ret;
-}
-
-void* aligned_alloc(size_t alignment, size_t size)
-{
-    if (!real_aligned_alloc) {
-        init();
-    }
-
-    void* ret = real_aligned_alloc(alignment, size);
-
-    if (ret && !HandleGuard::inHandler && data) {
-        HandleGuard guard;
-        data->handleMalloc(ret, size);
-    }
-
-    return ret;
-}
-
-void* valloc(size_t size)
-{
-    if (!real_valloc) {
-        init();
-    }
-
-    void* ret = real_valloc(size);
-
-    if (ret && !HandleGuard::inHandler && data) {
-        HandleGuard guard;
-        data->handleMalloc(ret, size);
-    }
-
-    return ret;
-}
-
-void *dlopen(const char *filename, int flag)
-{
-    if (!real_dlopen) {
-        init();
-    }
-
-    void* ret = real_dlopen(filename, flag);
-
-    if (ret) {
-        moduleCacheDirty = true;
-    }
-
-    return ret;
-}
-
-int dlclose(void *handle)
-{
-    if (!real_dlclose) {
-        init();
-    }
-
-    int ret = real_dlclose(handle);
-
-    if (!ret) {
-        moduleCacheDirty = true;
-    }
-
-    return ret;
-}
-
 }
diff --git a/libheaptrack.h b/libheaptrack.h
index 15075b0..605b23f 100644
--- a/libheaptrack.h
+++ b/libheaptrack.h
@@ -20,7 +20,8 @@
 #include <cstdio>
 
 extern "C" {
-void heaptrack_init();
+void heaptrack_init(const char *outputFileName, void (*initCallbackBefore) (), void \
(*initCallbackAfter) ()); +FILE* heaptrack_output_file();
 void heaptrack_malloc(void *ptr, size_t size);
 void heaptrack_free(void *ptr);
 void heaptrack_realloc(void *ptr_in, size_t size, void *ptr_out);


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic