summaryrefslogtreecommitdiffstats
path: root/libnetdata/libnetdata.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnetdata/libnetdata.c')
-rw-r--r--libnetdata/libnetdata.c576
1 files changed, 480 insertions, 96 deletions
diff --git a/libnetdata/libnetdata.c b/libnetdata/libnetdata.c
index 5b6c541ed..cc04a97eb 100644
--- a/libnetdata/libnetdata.c
+++ b/libnetdata/libnetdata.c
@@ -30,128 +30,367 @@ const char *program_version = VERSION;
// its lifetime), these can be used to override the default system allocation
// routines.
-#ifdef NETDATA_LOG_ALLOCATIONS
-#warning NETDATA_LOG_ALLOCATIONS ENABLED - set log_thread_memory_allocations=1 on any thread to log all its allocations - or use log_allocations() to log them on demand
-
-static __thread struct memory_statistics {
- volatile ssize_t malloc_calls_made;
- volatile ssize_t calloc_calls_made;
- volatile ssize_t realloc_calls_made;
- volatile ssize_t strdup_calls_made;
- volatile ssize_t free_calls_made;
- volatile ssize_t memory_calls_made;
- volatile ssize_t allocated_memory;
- volatile ssize_t mmapped_memory;
-} memory_statistics = { 0, 0, 0, 0, 0, 0, 0, 0 };
-
-__thread size_t log_thread_memory_allocations = 0;
-
-inline void log_allocations_int(const char *file, const char *function, const unsigned long line) {
- static __thread struct memory_statistics old = { 0, 0, 0, 0, 0, 0, 0, 0 };
-
- fprintf(stderr, "%s MEMORY ALLOCATIONS: (%04lu@%s:%s): Allocated %zd KiB (%+zd B), mmapped %zd KiB (%+zd B): : malloc %zd (%+zd), calloc %zd (%+zd), realloc %zd (%+zd), strdup %zd (%+zd), free %zd (%+zd)\n",
- netdata_thread_tag(),
- line, file, function,
- (memory_statistics.allocated_memory + 512) / 1024, memory_statistics.allocated_memory - old.allocated_memory,
- (memory_statistics.mmapped_memory + 512) / 1024, memory_statistics.mmapped_memory - old.mmapped_memory,
- memory_statistics.malloc_calls_made, memory_statistics.malloc_calls_made - old.malloc_calls_made,
- memory_statistics.calloc_calls_made, memory_statistics.calloc_calls_made - old.calloc_calls_made,
- memory_statistics.realloc_calls_made, memory_statistics.realloc_calls_made - old.realloc_calls_made,
- memory_statistics.strdup_calls_made, memory_statistics.strdup_calls_made - old.strdup_calls_made,
- memory_statistics.free_calls_made, memory_statistics.free_calls_made - old.free_calls_made
- );
-
- memcpy(&old, &memory_statistics, sizeof(struct memory_statistics));
-}
-
-static inline void mmap_accounting(size_t size) {
- if(log_thread_memory_allocations) {
- memory_statistics.memory_calls_made++;
- memory_statistics.mmapped_memory += size;
+#ifdef NETDATA_TRACE_ALLOCATIONS
+#warning NETDATA_TRACE_ALLOCATIONS ENABLED
+#include "Judy.h"
+
+#if defined(HAVE_DLSYM) && defined(ENABLE_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();
}
}
-void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size) {
- memory_statistics.memory_calls_made++;
- memory_statistics.malloc_calls_made++;
- memory_statistics.allocated_memory += size;
+static void *malloc_first_run(size_t size) {
+ link_system_library_function((libc_function_t *) &libc_malloc, "malloc", true);
+ return libc_malloc(size);
+}
- if(log_thread_memory_allocations)
- log_allocations_int(file, function, line);
+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);
+}
- size_t *n = (size_t *)malloc(sizeof(size_t) + size);
- if (unlikely(!n)) fatal("mallocz() cannot allocate %zu bytes of memory.", size);
- *n = size;
- return (void *)&n[1];
+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);
}
-void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size) {
- size = nmemb * 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;
+
+#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
- memory_statistics.memory_calls_made++;
- memory_statistics.calloc_calls_made++;
- memory_statistics.allocated_memory += size;
- if(log_thread_memory_allocations)
- log_allocations_int(file, function, line);
+#endif // HAVE_DLSYM
- size_t *n = (size_t *)calloc(1, sizeof(size_t) + size);
- if (unlikely(!n)) fatal("callocz() cannot allocate %zu bytes of memory.", size);
- *n = size;
- return (void *)&n[1];
+
+void posix_memfree(void *ptr) {
+ libc_free(ptr);
+}
+
+Word_t JudyMalloc(Word_t Words) {
+ Word_t Addr;
+
+ Addr = (Word_t) mallocz(Words * sizeof(Word_t));
+ return(Addr);
+}
+void JudyFree(void * PWord, Word_t Words) {
+ (void)Words;
+ freez(PWord);
+}
+Word_t JudyMallocVirtual(Word_t Words) {
+ Word_t Addr;
+
+ Addr = (Word_t) mallocz(Words * sizeof(Word_t));
+ return(Addr);
+}
+void JudyFreeVirtual(void * PWord, Word_t Words) {
+ (void)Words;
+ freez(PWord);
+}
+
+#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)
+
+struct malloc_header_signature {
+ uint32_t magic;
+ uint32_t size;
+ struct malloc_trace *trace;
+};
+
+struct malloc_header {
+ struct malloc_header_signature signature;
+ uint8_t padding[(sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) ? MALLOC_ALIGNMENT - (sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) : 0];
+ uint8_t data[];
+};
+
+static size_t malloc_header_size = sizeof(struct malloc_header);
+
+int malloc_trace_compare(void *A, void *B) {
+ struct malloc_trace *a = A;
+ struct malloc_trace *b = B;
+ return strcmp(a->function, b->function);
+}
+
+static avl_tree_lock malloc_trace_index = {
+ .avl_tree = {
+ .root = NULL,
+ .compar = malloc_trace_compare},
+ .rwlock = NETDATA_RWLOCK_INITIALIZER
+};
+
+int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data) {
+ return avl_traverse_lock(&malloc_trace_index, callback, data);
}
-void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size) {
- if(!ptr) return mallocz_int(file, function, line, size);
+NEVERNULL WARNUNUSED
+static struct malloc_trace *malloc_trace_find_or_create(const char *file, const char *function, size_t line) {
+ struct malloc_trace tmp = {
+ .line = line,
+ .function = function,
+ .file = file,
+ };
+
+ struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp);
+ if(!t) {
+ t = libc_calloc(1, sizeof(struct malloc_trace));
+ if(!t) fatal("No memory");
+ t->line = line;
+ t->function = function;
+ t->file = file;
+
+ struct malloc_trace *t2 = (struct malloc_trace *)avl_insert_lock(&malloc_trace_index, (avl_t *)t);
+ if(t2 != t)
+ free(t);
+
+ t = t2;
+ }
+
+ if(!t)
+ fatal("Cannot insert to AVL");
+
+ return t;
+}
+
+void malloc_trace_mmap(size_t size) {
+ struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
+ size_t_atomic_count(add, p->mmap_calls, 1);
+ size_t_atomic_count(add, p->allocations, 1);
+ size_t_atomic_bytes(add, p->bytes, size);
+}
+
+void malloc_trace_munmap(size_t size) {
+ struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
+ size_t_atomic_count(add, p->munmap_calls, 1);
+ size_t_atomic_count(sub, p->allocations, 1);
+ size_t_atomic_bytes(sub, p->bytes, size);
+}
+
+void *mallocz_int(size_t size, const char *file, const char *function, size_t line) {
+ struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
- size_t *n = (size_t *)ptr;
- n--;
- size_t old_size = *n;
+ size_t_atomic_count(add, p->malloc_calls, 1);
+ size_t_atomic_count(add, p->allocations, 1);
+ size_t_atomic_bytes(add, p->bytes, size);
- n = realloc(n, sizeof(size_t) + size);
- if (unlikely(!n)) fatal("reallocz() cannot allocate %zu bytes of memory (from %zu bytes).", size, old_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;
+ t->signature.size = size;
- memory_statistics.memory_calls_made++;
- memory_statistics.realloc_calls_made++;
- memory_statistics.allocated_memory += (size - old_size);
- if(log_thread_memory_allocations)
- log_allocations_int(file, function, line);
+#ifdef NETDATA_INTERNAL_CHECKS
+ for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
+ t->padding[i] = 0xFF;
+#endif
+
+ return (void *)&t->data;
+}
+
+void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line) {
+ struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
+ size = nmemb * size;
- *n = size;
- return (void *)&n[1];
+ size_t_atomic_count(add, p->calloc_calls, 1);
+ size_t_atomic_count(add, p->allocations, 1);
+ size_t_atomic_bytes(add, p->bytes, 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;
+ t->signature.size = size;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
+ t->padding[i] = 0xFF;
+#endif
+
+ return &t->data;
}
-char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s) {
+char *strdupz_int(const char *s, const char *file, const char *function, size_t line) {
+ struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
size_t size = strlen(s) + 1;
- memory_statistics.memory_calls_made++;
- memory_statistics.strdup_calls_made++;
- memory_statistics.allocated_memory += size;
- if(log_thread_memory_allocations)
- log_allocations_int(file, function, line);
+ size_t_atomic_count(add, p->strdup_calls, 1);
+ size_t_atomic_count(add, p->allocations, 1);
+ size_t_atomic_bytes(add, p->bytes, size);
- size_t *n = (size_t *)malloc(sizeof(size_t) + size);
- if (unlikely(!n)) fatal("strdupz() cannot allocate %zu bytes of memory.", 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;
+ t->signature.size = size;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
+ t->padding[i] = 0xFF;
+#endif
+
+ memcpy(&t->data, s, size);
+ return (char *)&t->data;
+}
+
+static struct malloc_header *malloc_get_header(void *ptr, const char *caller, const char *file, const char *function, size_t line) {
+ uint8_t *ret = (uint8_t *)ptr - malloc_header_size;
+ struct malloc_header *t = (struct malloc_header *)ret;
+
+ if(t->signature.magic != 0x0BADCAFE) {
+ error("pointer %p is not our pointer (called %s() from %zu@%s, %s()).", ptr, caller, line, file, function);
+ return NULL;
+ }
- *n = size;
- char *t = (char *)&n[1];
- strcpy(t, s);
return t;
}
-void freez_int(const char *file, const char *function, const unsigned long line, void *ptr) {
+void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line) {
+ if(!ptr) return mallocz_int(size, file, function, line);
+
+ struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
+ if(!t)
+ return libc_realloc(ptr, size);
+
+ if(t->signature.size == size) return ptr;
+ size_t_atomic_count(add, t->signature.trace->free_calls, 1);
+ size_t_atomic_count(sub, t->signature.trace->allocations, 1);
+ size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
+
+ struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
+ size_t_atomic_count(add, p->realloc_calls, 1);
+ size_t_atomic_count(add, p->allocations, 1);
+ size_t_atomic_bytes(add, p->bytes, 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;
+ t->signature.size = size;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
+ t->padding[i] = 0xFF;
+#endif
+
+ 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;
- size_t *n = (size_t *)ptr;
- n--;
- size_t size = *n;
+ struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
+ if(!t) {
+ libc_free(ptr);
+ return;
+ }
+
+ size_t_atomic_count(add, t->signature.trace->free_calls, 1);
+ size_t_atomic_count(sub, t->signature.trace->allocations, 1);
+ size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
- memory_statistics.memory_calls_made++;
- memory_statistics.free_calls_made++;
- memory_statistics.allocated_memory -= size;
- if(log_thread_memory_allocations)
- log_allocations_int(file, function, line);
+#ifdef NETDATA_INTERNAL_CHECKS
+ // it should crash if it is used after freeing it
+ memset(t, 0, malloc_header_size + t->signature.size);
+#endif
- free(n);
+ libc_free(t);
}
#else
@@ -184,6 +423,10 @@ void *reallocz(void *ptr, size_t size) {
return p;
}
+void posix_memfree(void *ptr) {
+ free(ptr);
+}
+
#endif
// --------------------------------------------------------------------------------------------------------------------
@@ -1031,8 +1274,8 @@ void *netdata_mmap(const char *filename, size_t size, int flags, int ksm) {
mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd_for_mmap, 0);
if (mem != MAP_FAILED) {
-#ifdef NETDATA_LOG_ALLOCATIONS
- mmap_accounting(size);
+#ifdef NETDATA_TRACE_ALLOCATIONS
+ malloc_trace_mmap(size);
#endif
// if we have a file open, but we didn't give it to mmap(),
@@ -1059,6 +1302,13 @@ cleanup:
return mem;
}
+int netdata_munmap(void *ptr, size_t size) {
+#ifdef NETDATA_TRACE_ALLOCATIONS
+ malloc_trace_munmap(size);
+#endif
+ return munmap(ptr, size);
+}
+
int memory_file_save(const char *filename, void *mem, size_t size) {
char tmpfilename[FILENAME_MAX + 1];
@@ -1535,6 +1785,122 @@ char *find_and_replace(const char *src, const char *find, const char *replace, c
return value;
}
+inline int pluginsd_space(char c) {
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case '=':
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+inline int config_isspace(char c)
+{
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case ',':
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+// split a text into words, respecting quotes
+inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover)
+{
+ char *s = str, quote = 0;
+ size_t i = 0;
+ int rec = 0;
+ char *recover = recover_input;
+
+ // skip all white space
+ while (unlikely(custom_isspace(*s)))
+ s++;
+
+ // check for quote
+ if (unlikely(*s == '\'' || *s == '"')) {
+ quote = *s; // remember the quote
+ s++; // skip the quote
+ }
+
+ // store the first word
+ words[i++] = s;
+
+ // while we have something
+ while (likely(*s)) {
+ // if it is escape
+ if (unlikely(*s == '\\' && s[1])) {
+ s += 2;
+ continue;
+ }
+
+ // if it is quote
+ else if (unlikely(*s == quote)) {
+ quote = 0;
+ if (recover && rec < max_recover) {
+ recover_location[rec++] = s;
+ *recover++ = *s;
+ }
+ *s = ' ';
+ continue;
+ }
+
+ // if it is a space
+ else if (unlikely(quote == 0 && custom_isspace(*s))) {
+ // terminate the word
+ if (recover && rec < max_recover) {
+ if (!rec || recover_location[rec-1] != s) {
+ recover_location[rec++] = s;
+ *recover++ = *s;
+ }
+ }
+ *s++ = '\0';
+
+ // skip all white space
+ while (likely(custom_isspace(*s)))
+ s++;
+
+ // check for quote
+ if (unlikely(*s == '\'' || *s == '"')) {
+ quote = *s; // remember the quote
+ s++; // skip the quote
+ }
+
+ // if we reached the end, stop
+ if (unlikely(!*s))
+ break;
+
+ // store the next word
+ if (likely(i < max_words))
+ words[i++] = s;
+ else
+ break;
+ }
+
+ // anything else
+ else
+ s++;
+ }
+
+ if (i < max_words)
+ words[i] = NULL;
+
+ return i;
+}
+
+inline size_t pluginsd_split_words(char *str, char **words, size_t max_words, char *recover_input, char **recover_location, int max_recover)
+{
+ return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover);
+}
bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx) {
if (unlikely(!ptr))
@@ -1550,3 +1916,21 @@ void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value) {
else
ptr->data[idx / 64] &= ~(1ULL << (idx % 64));
}
+
+bool run_command_and_copy_output_to_stdout(const char *command, int max_line_length) {
+ pid_t pid;
+ FILE *fp = netdata_popen(command, &pid, NULL);
+
+ if(fp) {
+ char buffer[max_line_length + 1];
+ while (fgets(buffer, max_line_length, fp))
+ fprintf(stdout, "%s", buffer);
+ }
+ else {
+ error("Failed to execute command '%s'.", command);
+ return false;
+ }
+
+ netdata_pclose(NULL, fp, pid);
+ return true;
+}