summaryrefslogtreecommitdiffstats
path: root/libnetdata
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2022-10-13 08:04:14 +0300
committerGitHub <noreply@github.com>2022-10-13 08:04:14 +0300
commit2974f525ec703329ef6ad079d8f6c685cfab11ad (patch)
treeb0e289a5fee787d764ee37cb8b5c2ac7bcd1bd20 /libnetdata
parentc805a9afad71ac96e703d599cbd6f54c29142ca7 (diff)
overload libc memory allocators with custom ones to trace all allocations (#13810)
* overload libc memory allocators with custom ones to trace all allocations * grab libc pointers for external c plugins * use -ldl when necessary; fallback to work without dlsym when it is not available * initialize global variable * add optional dl libs * dynamically link every library function when needed for the first time * prevent crashes on musl libc * another attempt * dont dereference function * attempt no 3 * attempt no 4 * cleanup - all attempts failed * dont enable tracing of allocations * missing parenthesis
Diffstat (limited to 'libnetdata')
-rw-r--r--libnetdata/libnetdata.c153
-rw-r--r--libnetdata/libnetdata.h11
2 files changed, 152 insertions, 12 deletions
diff --git a/libnetdata/libnetdata.c b/libnetdata/libnetdata.c
index 16781f39bb..374d1df717 100644
--- a/libnetdata/libnetdata.c
+++ b/libnetdata/libnetdata.c
@@ -34,6 +34,123 @@ const char *program_version = VERSION;
#warning NETDATA_TRACE_ALLOCATIONS ENABLED
#include "Judy.h"
+#ifdef HAVE_DLSYM
+#include <dlfcn.h>
+
+typedef void (*libc_function_t)(void);
+
+static void *malloc_first_run(size_t size);
+static void *(*libc_malloc)(size_t) = malloc_first_run;
+
+static void *calloc_first_run(size_t n, size_t size);
+static void *(*libc_calloc)(size_t, size_t) = calloc_first_run;
+
+static void *realloc_first_run(void *ptr, size_t size);
+static void *(*libc_realloc)(void *, size_t) = realloc_first_run;
+
+static void free_first_run(void *ptr);
+static void (*libc_free)(void *) = free_first_run;
+
+static char *strdup_first_run(const char *s);
+static char *(*libc_strdup)(const char *) = strdup_first_run;
+
+static size_t malloc_usable_size_first_run(void *ptr);
+#ifdef HAVE_MALLOC_USABLE_SIZE
+static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size_first_run;
+#else
+static size_t (*libc_malloc_usable_size)(void *) = NULL;
+#endif
+
+static void link_system_library_function(libc_function_t *func_pptr, const char *name, bool required) {
+ *func_pptr = dlsym(RTLD_NEXT, name);
+ if(!*func_pptr && required) {
+ fprintf(stderr, "FATAL: Cannot find system's %s() function.\n", name);
+ abort();
+ }
+}
+
+static void *malloc_first_run(size_t size) {
+ link_system_library_function((libc_function_t *) &libc_malloc, "malloc", true);
+ return libc_malloc(size);
+}
+
+static void *calloc_first_run(size_t n, size_t size) {
+ link_system_library_function((libc_function_t *) &libc_calloc, "calloc", true);
+ return libc_calloc(n, size);
+}
+
+static void *realloc_first_run(void *ptr, size_t size) {
+ link_system_library_function((libc_function_t *) &libc_realloc, "realloc", true);
+ return libc_realloc(ptr, size);
+}
+
+static void free_first_run(void *ptr) {
+ link_system_library_function((libc_function_t *) &libc_free, "free", true);
+ libc_free(ptr);
+}
+
+static char *strdup_first_run(const char *s) {
+ link_system_library_function((libc_function_t *) &libc_strdup, "strdup", true);
+ return libc_strdup(s);
+}
+
+static size_t malloc_usable_size_first_run(void *ptr) {
+ link_system_library_function((libc_function_t *) &libc_malloc_usable_size, "malloc_usable_size", false);
+
+ if(libc_malloc_usable_size)
+ return libc_malloc_usable_size(ptr);
+ else
+ return 0;
+}
+
+void *malloc(size_t size) {
+ return mallocz(size);
+}
+
+void *calloc(size_t n, size_t size) {
+ return callocz(n, size);
+}
+
+void *realloc(void *ptr, size_t size) {
+ return reallocz(ptr, size);
+}
+
+void *reallocarray(void *ptr, size_t n, size_t size) {
+ return reallocz(ptr, n * size);
+}
+
+void free(void *ptr) {
+ freez(ptr);
+}
+
+char *strdup(const char *s) {
+ return strdupz(s);
+}
+
+size_t malloc_usable_size(void *ptr) {
+ return mallocz_usable_size(ptr);
+}
+#else // !HAVE_DLSYM
+
+static void *(*libc_malloc)(size_t) = malloc;
+static void *(*libc_calloc)(size_t, size_t) = calloc;
+static void *(*libc_realloc)(void *, size_t) = realloc;
+static void (*libc_free)(void *) = free;
+static char *(*libc_strdup)(const char *) = strdup;
+
+#ifdef HAVE_MALLOC_USABLE_SIZE
+static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size;
+#else
+static size_t (*libc_malloc_usable_size)(void *) = NULL;
+#endif
+
+#endif // HAVE_DLSYM
+
+
+void posix_memfree(void *ptr) {
+ libc_free(ptr);
+}
+
Word_t JudyMalloc(Word_t Words) {
Word_t Addr;
@@ -57,7 +174,7 @@ void JudyFreeVirtual(void * PWord, Word_t Words) {
#define MALLOC_ALIGNMENT (sizeof(uintptr_t) * 2)
#define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED)
-#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - (size % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
+#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - ((size) % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
struct malloc_header_signature {
uint32_t magic;
@@ -100,7 +217,7 @@ static struct malloc_trace *malloc_trace_find_or_create(const char *file, const
struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp);
if(!t) {
- t = calloc(1, sizeof(struct malloc_trace));
+ t = libc_calloc(1, sizeof(struct malloc_trace));
if(!t) fatal("No memory");
t->line = line;
t->function = function;
@@ -140,7 +257,7 @@ void *mallocz_int(size_t size, const char *file, const char *function, size_t li
size_t_atomic_count(add, p->allocations, 1);
size_t_atomic_bytes(add, p->bytes, size);
- struct malloc_header *t = (struct malloc_header *)malloc(malloc_header_size + size);
+ struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
t->signature.magic = 0x0BADCAFE;
t->signature.trace = p;
@@ -162,7 +279,7 @@ void *callocz_int(size_t nmemb, size_t size, const char *file, const char *funct
size_t_atomic_count(add, p->allocations, 1);
size_t_atomic_bytes(add, p->bytes, size);
- struct malloc_header *t = (struct malloc_header *)calloc(1, malloc_header_size + size);
+ struct malloc_header *t = (struct malloc_header *)libc_calloc(1, malloc_header_size + size);
if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
t->signature.magic = 0x0BADCAFE;
t->signature.trace = p;
@@ -184,7 +301,7 @@ char *strdupz_int(const char *s, const char *file, const char *function, size_t
size_t_atomic_count(add, p->allocations, 1);
size_t_atomic_bytes(add, p->bytes, size);
- struct malloc_header *t = (struct malloc_header *)malloc(malloc_header_size + size);
+ struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
if (unlikely(!t)) fatal("strdupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
t->signature.magic = 0x0BADCAFE;
t->signature.trace = p;
@@ -216,7 +333,7 @@ void *reallocz_int(void *ptr, size_t size, const char *file, const char *functio
struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
if(!t)
- return realloc(ptr, size);
+ return libc_realloc(ptr, size);
if(t->signature.size == size) return ptr;
size_t_atomic_count(add, t->signature.trace->free_calls, 1);
@@ -228,7 +345,7 @@ void *reallocz_int(void *ptr, size_t size, const char *file, const char *functio
size_t_atomic_count(add, p->allocations, 1);
size_t_atomic_bytes(add, p->bytes, size);
- t = (struct malloc_header *)realloc(t, malloc_header_size + size);
+ t = (struct malloc_header *)libc_realloc(t, malloc_header_size + size);
if (unlikely(!t)) fatal("reallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
t->signature.magic = 0x0BADCAFE;
t->signature.trace = p;
@@ -242,12 +359,26 @@ void *reallocz_int(void *ptr, size_t size, const char *file, const char *functio
return (void *)&t->data;
}
+size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line) {
+ if(unlikely(!ptr)) return 0;
+
+ struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
+ if(!t) {
+ if(libc_malloc_usable_size)
+ return libc_malloc_usable_size(ptr);
+ else
+ return 0;
+ }
+
+ return t->signature.size;
+}
+
void freez_int(void *ptr, const char *file, const char *function, size_t line) {
if(unlikely(!ptr)) return;
struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
if(!t) {
- free(ptr);
+ libc_free(ptr);
return;
}
@@ -260,7 +391,7 @@ void freez_int(void *ptr, const char *file, const char *function, size_t line) {
memset(t, 0, malloc_header_size + t->signature.size);
#endif
- free(t);
+ libc_free(t);
}
#else
@@ -293,6 +424,10 @@ void *reallocz(void *ptr, size_t size) {
return p;
}
+void posix_memfree(void *ptr) {
+ free(ptr);
+}
+
#endif
// --------------------------------------------------------------------------------------------------------------------
diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h
index dc199ffb87..496a7fe4bc 100644
--- a/libnetdata/libnetdata.h
+++ b/libnetdata/libnetdata.h
@@ -15,9 +15,10 @@ extern "C" {
#define NETDATA_INTERNAL_CHECKS 1
#endif
-#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
-#define NETDATA_TRACE_ALLOCATIONS 1
-#endif
+// NETDATA_TRACE_ALLOCATIONS does not work under musl libc, so don't enable it
+//#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
+//#define NETDATA_TRACE_ALLOCATIONS 1
+//#endif
#define OS_LINUX 1
#define OS_FREEBSD 2
@@ -312,12 +313,14 @@ int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data
#define mallocz(size) mallocz_int(size, __FILE__, __FUNCTION__, __LINE__)
#define reallocz(ptr, size) reallocz_int(ptr, size, __FILE__, __FUNCTION__, __LINE__)
#define freez(ptr) freez_int(ptr, __FILE__, __FUNCTION__, __LINE__)
+#define mallocz_usable_size(ptr) mallocz_usable_size_int(ptr, __FILE__, __FUNCTION__, __LINE__)
char *strdupz_int(const char *s, const char *file, const char *function, size_t line);
void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line);
void *mallocz_int(size_t size, const char *file, const char *function, size_t line);
void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line);
void freez_int(void *ptr, const char *file, const char *function, size_t line);
+size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line);
#else // NETDATA_TRACE_ALLOCATIONS
char *strdupz(const char *s) MALLOCLIKE NEVERNULL;
@@ -327,6 +330,8 @@ void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL;
void freez(void *ptr);
#endif // NETDATA_TRACE_ALLOCATIONS
+void posix_memfree(void *ptr);
+
void json_escape_string(char *dst, const char *src, size_t size);
void json_fix_string(char *s);