summaryrefslogtreecommitdiffstats
path: root/libnetdata/libnetdata.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnetdata/libnetdata.c')
-rw-r--r--libnetdata/libnetdata.c261
1 files changed, 137 insertions, 124 deletions
diff --git a/libnetdata/libnetdata.c b/libnetdata/libnetdata.c
index 18d022407..2997ce19e 100644
--- a/libnetdata/libnetdata.c
+++ b/libnetdata/libnetdata.c
@@ -11,7 +11,12 @@
#endif /* __FreeBSD__ || __APPLE__*/
struct rlimit rlimit_nofile = { .rlim_cur = 1024, .rlim_max = 1024 };
+
+#ifdef MADV_MERGEABLE
int enable_ksm = 1;
+#else
+int enable_ksm = 0;
+#endif
volatile sig_atomic_t netdata_exit = 0;
const char *program_version = VERSION;
@@ -26,6 +31,8 @@ const char *program_version = VERSION;
// 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;
@@ -39,23 +46,14 @@ static __thread struct memory_statistics {
__thread size_t log_thread_memory_allocations = 0;
-static inline void print_allocations(const char *file, const char *function, const unsigned long line, const char *type, size_t size) {
+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 iteration %zu MEMORY TRACE: %lu@%s : %s : %s : %zu\n",
- netdata_thread_tag(),
- log_thread_memory_allocations,
- line, file, function,
- type, size
- );
-
- fprintf(stderr, "%s iteration %zu MEMORY ALLOCATIONS: (%04lu@%-40.40s:%-40.40s): Allocated %zd KiB (%+zd B), mmapped %zd KiB (%+zd B): %s : malloc %zd (%+zd), calloc %zd (%+zd), realloc %zd (%+zd), strdup %zd (%+zd), free %zd (%+zd)\n",
+ 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(),
- log_thread_memory_allocations,
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,
- type,
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,
@@ -74,12 +72,12 @@ static inline void mmap_accounting(size_t size) {
}
void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size) {
- if(log_thread_memory_allocations) {
- memory_statistics.memory_calls_made++;
- memory_statistics.malloc_calls_made++;
- memory_statistics.allocated_memory += size;
- print_allocations(file, function, line, "malloc()", size);
- }
+ memory_statistics.memory_calls_made++;
+ memory_statistics.malloc_calls_made++;
+ memory_statistics.allocated_memory += size;
+
+ if(log_thread_memory_allocations)
+ log_allocations_int(file, function, line);
size_t *n = (size_t *)malloc(sizeof(size_t) + size);
if (unlikely(!n)) fatal("mallocz() cannot allocate %zu bytes of memory.", size);
@@ -90,12 +88,11 @@ void *mallocz_int(const char *file, const char *function, const unsigned long li
void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size) {
size = nmemb * size;
- if(log_thread_memory_allocations) {
- memory_statistics.memory_calls_made++;
- memory_statistics.calloc_calls_made++;
- memory_statistics.allocated_memory += size;
- print_allocations(file, function, line, "calloc()", size);
- }
+ 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);
size_t *n = (size_t *)calloc(1, sizeof(size_t) + size);
if (unlikely(!n)) fatal("callocz() cannot allocate %zu bytes of memory.", size);
@@ -113,12 +110,11 @@ void *reallocz_int(const char *file, const char *function, const unsigned long l
n = realloc(n, sizeof(size_t) + size);
if (unlikely(!n)) fatal("reallocz() cannot allocate %zu bytes of memory (from %zu bytes).", size, old_size);
- if(log_thread_memory_allocations) {
- memory_statistics.memory_calls_made++;
- memory_statistics.realloc_calls_made++;
- memory_statistics.allocated_memory += (size - old_size);
- print_allocations(file, function, line, "realloc()", size - old_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);
*n = size;
return (void *)&n[1];
@@ -127,12 +123,11 @@ void *reallocz_int(const char *file, const char *function, const unsigned long l
char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s) {
size_t size = strlen(s) + 1;
- if(log_thread_memory_allocations) {
- memory_statistics.memory_calls_made++;
- memory_statistics.strdup_calls_made++;
- memory_statistics.allocated_memory += size;
- print_allocations(file, function, line, "strdup()", size);
- }
+ 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 *n = (size_t *)malloc(sizeof(size_t) + size);
if (unlikely(!n)) fatal("strdupz() cannot allocate %zu bytes of memory.", size);
@@ -150,12 +145,11 @@ void freez_int(const char *file, const char *function, const unsigned long line,
n--;
size_t size = *n;
- if(log_thread_memory_allocations) {
- memory_statistics.memory_calls_made++;
- memory_statistics.free_calls_made++;
- memory_statistics.allocated_memory -= size;
- print_allocations(file, function, line, "free()", 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);
free(n);
}
@@ -583,7 +577,7 @@ unsigned char netdata_map_chart_ids[256] = {
[89] = 'y', // Y
[90] = 'z', // Z
[91] = '_', // [
- [92] = '/', // backslash
+ [92] = '_', // backslash
[93] = '_', // ]
[94] = '_', // ^
[95] = '_', // _
@@ -939,108 +933,128 @@ static int memory_file_open(const char *filename, size_t size) {
return fd;
}
-// mmap_shared is used for memory mode = map
-static void *memory_file_mmap(const char *filename, size_t size, int flags) {
- // info("memory_file_mmap('%s', %zu", filename, size);
- static int log_madvise = 1;
+static inline int madvise_sequential(void *mem, size_t len) {
+ static int logger = 1;
+ int ret = madvise(mem, len, MADV_SEQUENTIAL);
- int fd = -1;
- if(filename) {
- fd = memory_file_open(filename, size);
- if(fd == -1) return MAP_FAILED;
- }
+ if (ret != 0 && logger-- > 0) error("madvise(MADV_SEQUENTIAL) failed.");
+ return ret;
+}
- void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
- if (mem != MAP_FAILED) {
-#ifdef NETDATA_LOG_ALLOCATIONS
- mmap_accounting(size);
-#endif
- int advise = MADV_SEQUENTIAL | MADV_DONTFORK;
- if (flags & MAP_SHARED) advise |= MADV_WILLNEED;
+static inline int madvise_dontfork(void *mem, size_t len) {
+ static int logger = 1;
+ int ret = madvise(mem, len, MADV_DONTFORK);
- if (madvise(mem, size, advise) != 0 && log_madvise) {
- error("Cannot advise the kernel about shared memory usage.");
- log_madvise--;
- }
- }
+ if (ret != 0 && logger-- > 0) error("madvise(MADV_DONTFORK) failed.");
+ return ret;
+}
- if(fd != -1)
- close(fd);
+static inline int madvise_willneed(void *mem, size_t len) {
+ static int logger = 1;
+ int ret = madvise(mem, len, MADV_WILLNEED);
- return mem;
+ if (ret != 0 && logger-- > 0) error("madvise(MADV_WILLNEED) failed.");
+ return ret;
}
+#if __linux__
+static inline int madvise_dontdump(void *mem, size_t len) {
+ static int logger = 1;
+ int ret = madvise(mem, len, MADV_DONTDUMP);
+
+ if (ret != 0 && logger-- > 0) error("madvise(MADV_DONTDUMP) failed.");
+ return ret;
+}
+#else
+static inline int madvise_dontdump(void *mem, size_t len) {
+ UNUSED(mem);
+ UNUSED(len);
+
+ return 0;
+}
+#endif
+
+static inline int madvise_mergeable(void *mem, size_t len) {
#ifdef MADV_MERGEABLE
-static void *memory_file_mmap_ksm(const char *filename, size_t size, int flags) {
- // info("memory_file_mmap_ksm('%s', %zu", filename, size);
- static int log_madvise_2 = 1, log_madvise_3 = 1;
+ static int logger = 1;
+ int ret = madvise(mem, len, MADV_MERGEABLE);
+
+ if (ret != 0 && logger-- > 0) error("madvise(MADV_MERGEABLE) failed.");
+ return ret;
+#else
+ UNUSED(mem);
+ UNUSED(len);
+
+ return 0;
+#endif
+}
+
+void *netdata_mmap(const char *filename, size_t size, int flags, int ksm) {
+ // info("netdata_mmap('%s', %zu", filename, size);
+
+ // MAP_SHARED is used in memory mode map
+ // MAP_PRIVATE is used in memory mode ram and save
+
+ if(unlikely(!(flags & MAP_SHARED) && !(flags & MAP_PRIVATE)))
+ fatal("Neither MAP_SHARED or MAP_PRIVATE were given to netdata_mmap()");
+
+ if(unlikely((flags & MAP_SHARED) && (flags & MAP_PRIVATE)))
+ fatal("Both MAP_SHARED and MAP_PRIVATE were given to netdata_mmap()");
+
+ if(unlikely((flags & MAP_SHARED) && (!filename || !*filename)))
+ fatal("MAP_SHARED requested, without a filename to netdata_mmap()");
+
+ // don't enable ksm is the global setting is disabled
+ if(unlikely(!enable_ksm)) ksm = 0;
+
+ // KSM only merges anonymous (private) pages, never pagecache (file) pages
+ // but MAP_PRIVATE without MAP_ANONYMOUS it fails too, so we need it always
+ if((flags & MAP_PRIVATE)) flags |= MAP_ANONYMOUS;
int fd = -1;
- if(filename) {
+ void *mem = MAP_FAILED;
+
+ if(filename && *filename) {
+ // open/create the file to be used
fd = memory_file_open(filename, size);
- if(fd == -1) return MAP_FAILED;
+ if(fd == -1) goto cleanup;
+ }
+
+ int fd_for_mmap = fd;
+ if(fd != -1 && (flags & MAP_PRIVATE)) {
+ // this is MAP_PRIVATE allocation
+ // no need for mmap() to use our fd
+ // we will copy the file into the memory allocated
+ fd_for_mmap = -1;
}
- void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0);
+ mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd_for_mmap, 0);
if (mem != MAP_FAILED) {
+
#ifdef NETDATA_LOG_ALLOCATIONS
mmap_accounting(size);
#endif
- if(fd != -1) {
+
+ // if we have a file open, but we didn't give it to mmap(),
+ // we have to read the file into the memory block we allocated
+ if(fd != -1 && fd_for_mmap == -1) {
if (lseek(fd, 0, SEEK_SET) == 0) {
if (read(fd, mem, size) != (ssize_t) size)
- error("Cannot read from file '%s'", filename);
+ info("Cannot read from file '%s'", filename);
}
- else error("Cannot seek to beginning of file '%s'.", filename);
- }
-
- // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE
- if (madvise(mem, size, MADV_SEQUENTIAL | MADV_DONTFORK) != 0 && log_madvise_2) {
- error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.", filename);
- log_madvise_2--;
+ else info("Cannot seek to beginning of file '%s'.", filename);
}
- if (madvise(mem, size, MADV_MERGEABLE) != 0 && log_madvise_3) {
- error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.", filename);
- log_madvise_3--;
- }
+ madvise_sequential(mem, size);
+ madvise_dontfork(mem, size);
+ madvise_dontdump(mem, size);
+ if(flags & MAP_SHARED) madvise_willneed(mem, size);
+ if(ksm) madvise_mergeable(mem, size);
}
- if(fd != -1)
- close(fd);
-
- return mem;
-}
-#else
-static void *memory_file_mmap_ksm(const char *filename, size_t size, int flags) {
- // info("memory_file_mmap_ksm FALLBACK ('%s', %zu", filename, size);
-
- if(filename)
- return memory_file_mmap(filename, size, flags);
-
- // when KSM is not available and no filename is given (memory mode = ram),
- // we just report failure
- return MAP_FAILED;
-}
-#endif
-
-void *mymmap(const char *filename, size_t size, int flags, int ksm) {
- void *mem = NULL;
-
- if (filename && (flags & MAP_SHARED || !enable_ksm || !ksm))
- // memory mode = map | save
- // when KSM is not enabled
- // MAP_SHARED is used for memory mode = map (no KSM possible)
- mem = memory_file_mmap(filename, size, flags);
-
- else
- // memory mode = save | ram
- // when KSM is enabled
- // for memory mode = ram, the filename is NULL
- mem = memory_file_mmap_ksm(filename, size, flags);
-
+cleanup:
+ if(fd != -1) close(fd);
if(mem == MAP_FAILED) return NULL;
-
errno = 0;
return mem;
}
@@ -1097,14 +1111,13 @@ char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) {
}
int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args) {
+ if(unlikely(!n)) return 0;
+
int size = vsnprintf(dst, n, fmt, args);
+ dst[n - 1] = '\0';
- if (unlikely((size_t) size > n)) {
- // truncated
- size = (int)n;
- }
+ if (unlikely((size_t) size > n)) size = (int)n;
- dst[size] = '\0';
return size;
}