Add pool-allocator API for heaptrack.
authorMilian Wolff <milian.wolff@kdab.com>
Mon, 20 Jun 2016 09:42:59 +0000 (11:42 +0200)
committerMilian Wolff <milian.wolff@kdab.com>
Mon, 20 Jun 2016 13:03:28 +0000 (15:03 +0200)
This is a header-only API that you can use to add support for
pool-allocators or other custom allocators to heaptrack. Simply
include the header and call the `heaptrack_report_*` macros in
your custom allocator. When you trace your code under heaptrack,
the allocations will then be tracked. If you don't run under
heaptrack, nothing will happen.

    #include <heaptrack.h>

    void* MyPool::allocate(size_t s)
    {
        void* ptr = /* allocate s */;
        heaptrack_report_malloc(ptr, s);
        return ptr;
    }

    void MyPool::free(void* ptr)
    {
        heaptrack_report_free(ptr);
        /* actually free */
    }

CMakeLists.txt
heaptrack_api.h [new file with mode: 0644]

index 4ecf600..71e885e 100644 (file)
@@ -131,5 +131,6 @@ install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/heaptrack DESTINATION ${BIN_INSTALL
 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/)
+install(FILES heaptrack_api.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
 
 feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/heaptrack_api.h b/heaptrack_api.h
new file mode 100644 (file)
index 0000000..b59b9a0
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2016 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.
+ */
+
+/**
+ * @file heaptrack_api.h
+ *
+ * This file defines a public API for heaptrack to be used in applications and
+ * libraries that implement custom allocators that do not use malloc internally.
+ *
+ * It should be enough to include this header in your code and then add calls
+ * to @c heaptrack_report_alloc, @c heaptrack_report_realloc and
+ * @c heaptrack_report_free to your code. By default, nothing will happen.
+ * Once you run your code within heaptrack though, this information will be
+ * picked up and included in the heap profile data.
+ *
+ * Note: If you use static linking, or have a custom allocator in your main
+ * executable, then you must define HEAPTRACK_API_DLSYM before including
+ * this header and link against libdl to make this work properly. The other,
+ * more common, case of pool allocators in shared libraries will work with the
+ * default implementation that relies on weak symbols and the dynamic linker
+ * on resolving the symbols for us directly.
+ */
+
+#ifndef HEAPTRACK_API_H
+#define HEAPTRACK_API_H
+
+#include <stdlib.h>
+
+#ifndef HEAPTRACK_API_DLSYM
+
+/**
+ * By default we rely on weak symbols that get resolved by the dynamic linker.
+ * The weak symbols defined here are usually zero, but become non-zero when
+ * we run the application with libheaptrack_preload.so loaded in.
+ *
+ * Note that this does not support run-time attaching yet.
+ * Also note that this won't work inside your main executable, nor when you use
+ * static linking. In these cases, the dlsym code below should be used instead.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+__attribute__((weak)) void heaptrack_malloc(void* ptr, size_t size);
+__attribute__((weak)) void heaptrack_realloc(void* ptr_in, size_t size, void* ptr_out);
+__attribute__((weak)) void heaptrack_free(void* ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define heaptrack_report_alloc(ptr, size) if (heaptrack_malloc) heaptrack_malloc(ptr, size)
+
+#define heaptrack_report_realloc(ptr_in, size, ptr_out) if (heaptrack_realloc) heaptrack_realloc(ptr_in, size, ptr_out)
+
+#define heaptrack_report_free(ptr) if (heaptrack_free) heaptrack_free(ptr)
+
+#else // HEAPTRACK_API_DLSYM
+
+/**
+ * Alternatively, we rely on dlsym to dynamically resolve the symbol to the
+ * heaptrack API functions at runtime. This works reliably, also when using
+ * static linking or when calling these functions from within your main
+ * executable. The caveat is that you need to link to libdl dynamically.
+ *
+ * @code
+ *
+ *   gcc -DHEAPTRACK_API_DLSYM=1 -ldl ...
+ * @endcode
+ */
+
+#ifndef __USE_GNU
+// required for RTLD_NEXT
+#define __USE_GNU
+#endif
+
+#include <dlfcn.h>
+
+struct heaptrack_api_t
+{
+    void (*malloc) (void *, size_t);
+    void (*free) (void *);
+    void (*realloc) (void *, size_t, void *);
+};
+static struct heaptrack_api_t heaptrack_api = {0, 0, 0};
+
+void heaptrack_init_api()
+{
+    static int initialized = 0;
+    if (!initialized) {
+        void* sym = dlsym(RTLD_NEXT, "heaptrack_malloc");
+        if (sym)
+            heaptrack_api.malloc = (void (*) (void *, size_t)) sym;
+
+        sym = dlsym(RTLD_NEXT, "heaptrack_realloc");
+        if (sym)
+            heaptrack_api.realloc = (void (*) (void *, size_t, void *)) sym;
+
+        sym = dlsym(RTLD_NEXT, "heaptrack_free");
+        if (sym)
+            heaptrack_api.free = (void (*) (void *)) sym;
+
+        initialized = 1;
+    }
+}
+
+#define heaptrack_report_alloc(ptr, size) \
+    do { \
+        heaptrack_init_api(); \
+        if (heaptrack_api.malloc) heaptrack_api.malloc(ptr, size); \
+    } while(0)
+
+#define heaptrack_report_realloc(ptr_in, size, ptr_out) \
+    do { \
+        heaptrack_init_api(); \
+        if (heaptrack_api.realloc) heaptrack_api.realloc(ptr_in, size, ptr_out); \
+    } while(0)
+
+#define heaptrack_report_free(ptr) \
+    do { \
+        heaptrack_init_api(); \
+        if (heaptrack_api.free) heaptrack_api.free(ptr); \
+    } while(0)
+
+#endif // HEAPTRACK_API_DLSYM
+
+/**
+ * Optionally, you can let heaptrack mimick the Valgrind pool-allocator API.
+ *
+ * This won't work nicely when you want to enable both, Valgrind and heaptrack.
+ * Otherwise, it's an easy way to make your code ready for both tools.
+ */
+#ifdef HEAPTRACK_DEFINE_VALGRIND_MACROS
+
+#define VALGRIND_DISABLE_ERROR_REPORTING
+#define VALGRIND_ENABLE_ERROR_REPORTING
+#define VALGRIND_CREATE_MEMPOOL(...)
+#define VALGRIND_DESTROY_MEMPOOL(...)
+#define VALGRIND_MAKE_MEM_NOACCESS(...)
+
+#define VALGRIND_MEMPOOL_ALLOC(pool, ptr, size) heaptrack_report_alloc(ptr, size)
+#define VALGRIND_MEMPOOL_FREE(pool, ptr) heaptrack_report_free(ptr)
+
+#endif
+
+#endif // HEAPTRACK_API_H