@cryptotaxi247 / netdata-1 / commits / 4f072e604

Unified memory API (#19396)

* split memory management into separate files - no functional changes * always use nd_mmap() and unified memory alignment logic * off_t instead of __off_t * added windows version of get_system_pagesize()

Costa Tsaousis committed Jan 14, 2025 at 14:15 UTC 4f072e6040a196519dcac5cda8449a4abc2efee2
19 files changed +870 -795
CMakeLists.txt
+7
@@ -980,6 +980,13 @@ set(LIBNETDATA_FILES
980 src/libnetdata/locks/benchmark-rw.c
981 src/libnetdata/locks/benchmark-rw.h
982 src/libnetdata/log/nd_log-libunwind.c
983 + src/libnetdata/memory/nd-mallocz.c
984 + src/libnetdata/memory/nd-mallocz.h
985 + src/libnetdata/memory/nd-mmap.c
986 + src/libnetdata/memory/nd-mmap.h
987 + src/libnetdata/memory/alignment.h
988 + src/libnetdata/os/get_system_pagesize.c
989 + src/libnetdata/os/get_system_pagesize.h
990 )
991
992 set(LIBH2O_FILES
src/collectors/cgroups.plugin/cgroup-discovery.c
+4 -5
@@ -1070,9 +1070,8 @@ static void netdata_cgroup_ebpf_initialize_shm()
1070 goto end_init_shm;
1071 }
1072
1073 - shm_cgroup_ebpf.header = (netdata_ebpf_cgroup_shm_header_t *) mmap(NULL, length,
1074 - PROT_READ | PROT_WRITE, MAP_SHARED,
1075 - shm_fd_cgroup_ebpf, 0);
1073 + shm_cgroup_ebpf.header = (netdata_ebpf_cgroup_shm_header_t *)
1074 + nd_mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_cgroup_ebpf, 0);
1075
1076 if (unlikely(MAP_FAILED == shm_cgroup_ebpf.header)) {
1077 shm_cgroup_ebpf.header = NULL;
@@ -1091,7 +1090,7 @@ static void netdata_cgroup_ebpf_initialize_shm()
1090 }
1091
1092 collector_error("Cannot create semaphore, integration between eBPF and cgroup won't happen");
1094 - munmap(shm_cgroup_ebpf.header, length);
1093 + nd_munmap(shm_cgroup_ebpf.header, length);
1094 shm_cgroup_ebpf.header = NULL;
1095
1096 end_init_shm:
@@ -1108,7 +1107,7 @@ static void cgroup_cleanup_ebpf_integration()
1107
1108 if (shm_cgroup_ebpf.header) {
1109 shm_cgroup_ebpf.header->cgroup_root_count = 0;
1111 - munmap(shm_cgroup_ebpf.header, shm_cgroup_ebpf.header->body_length);
1110 + nd_munmap(shm_cgroup_ebpf.header, shm_cgroup_ebpf.header->body_length);
1111 }
1112
1113 if (shm_fd_cgroup_ebpf > 0) {
src/collectors/ebpf.plugin/ebpf_cgroup.c
+4 -4
@@ -26,7 +26,7 @@ static inline void *ebpf_cgroup_map_shm_locally(int fd, size_t length)
26 {
27 void *value;
28
29 - value = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
29 + value = nd_mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
30 if (!value) {
31 netdata_log_error("Cannot map shared memory used between eBPF and cgroup, integration between processes won't happen");
32 close(shm_fd_ebpf_cgroup);
@@ -44,7 +44,7 @@ static inline void *ebpf_cgroup_map_shm_locally(int fd, size_t length)
44 */
45 void ebpf_unmap_cgroup_shared_memory()
46 {
47 - munmap(ebpf_mapped_memory, shm_ebpf_cgroup.header->body_length);
47 + nd_munmap(ebpf_mapped_memory, shm_ebpf_cgroup.header->body_length);
48 }
49
50 /**
@@ -87,7 +87,7 @@ void ebpf_map_cgroup_shared_memory()
87
88 size_t length = header->body_length;
89
90 - munmap(header, sizeof(netdata_ebpf_cgroup_shm_header_t));
90 + nd_munmap(header, sizeof(netdata_ebpf_cgroup_shm_header_t));
91
92 if (length <= ((sizeof(netdata_ebpf_cgroup_shm_header_t) + sizeof(netdata_ebpf_cgroup_shm_body_t)))) {
93 return;
@@ -105,7 +105,7 @@ void ebpf_map_cgroup_shared_memory()
105 if (shm_sem_ebpf_cgroup == SEM_FAILED) {
106 netdata_log_error("Cannot create semaphore, integration between eBPF and cgroup won't happen");
107 limit_try = NETDATA_EBPF_CGROUP_MAX_TRIES + 1;
108 - munmap(ebpf_mapped_memory, length);
108 + nd_munmap(ebpf_mapped_memory, length);
109 shm_ebpf_cgroup.header = NULL;
110 shm_ebpf_cgroup.body = NULL;
111 close(shm_fd_ebpf_cgroup);
src/daemon/pulse/pulse-daemon-memory-system.c
+1 -1
@@ -229,7 +229,7 @@ void pulse_daemon_memory_system_do(bool extended) {
229 }
230 #endif // HAVE_C_MALLINFO2
231
232 - size_t netdata_mmaps = __atomic_load_n(&netdata_mmap_count, __ATOMIC_RELAXED);
232 + size_t netdata_mmaps = __atomic_load_n(&nd_mmap_count, __ATOMIC_RELAXED);
233 size_t total_mmaps = netdata_mmaps + glibc_mmaps;
234 {
235 static RRDSET *st_maps = NULL;
src/database/engine/journalfile.c
+7 -7
@@ -196,7 +196,7 @@ static struct journal_v2_header *journalfile_v2_mounted_data_get(struct rrdengin
196 spinlock_lock(&journalfile->mmap.spinlock);
197
198 if(!journalfile->mmap.data) {
199 - journalfile->mmap.data = mmap(NULL, journalfile->mmap.size, PROT_READ, MAP_SHARED, journalfile->mmap.fd, 0);
199 + journalfile->mmap.data = nd_mmap(NULL, journalfile->mmap.size, PROT_READ, MAP_SHARED, journalfile->mmap.fd, 0);
200 if (journalfile->mmap.data == MAP_FAILED) {
201 internal_fatal(true, "DBENGINE: failed to re-mmap() journal file v2");
202 close(journalfile->mmap.fd);
@@ -268,7 +268,7 @@ static bool journalfile_v2_mounted_data_unmount(struct rrdengine_journalfile *jo
268
269 if(!journalfile->v2.refcount) {
270 if(journalfile->mmap.data) {
271 - if (munmap(journalfile->mmap.data, journalfile->mmap.size)) {
271 + if (nd_munmap(journalfile->mmap.data, journalfile->mmap.size)) {
272 char path[RRDENG_PATH_MAX];
273 journalfile_v2_generate_path(journalfile->datafile, path, sizeof(path));
274 netdata_log_error("DBENGINE: failed to unmap index file '%s'", path);
@@ -1087,7 +1087,7 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal
1087 }
1088
1089 usec_t mmap_start_ut = now_monotonic_usec();
1090 - uint8_t *data_start = mmap(NULL, journal_v2_file_size, PROT_READ, MAP_SHARED, fd, 0);
1090 + uint8_t *data_start = nd_mmap(NULL, journal_v2_file_size, PROT_READ, MAP_SHARED, fd, 0);
1091 if (data_start == MAP_FAILED) {
1092 close(fd);
1093 return 1;
@@ -1105,7 +1105,7 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal
1105 else
1106 error_report("File %s is invalid and it will be rebuilt", path_v2);
1107
1108 - if (unlikely(munmap(data_start, journal_v2_file_size)))
1108 + if (unlikely(nd_munmap(data_start, journal_v2_file_size)))
1109 netdata_log_error("DBENGINE: failed to unmap '%s'", path_v2);
1110
1111 close(fd);
@@ -1116,7 +1116,7 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal
1116 uint32_t entries = j2_header->metric_count;
1117
1118 if (unlikely(!entries)) {
1119 - if (unlikely(munmap(data_start, journal_v2_file_size)))
1119 + if (unlikely(nd_munmap(data_start, journal_v2_file_size)))
1120 netdata_log_error("DBENGINE: failed to unmap '%s'", path_v2);
1121
1122 close(fd);
@@ -1338,7 +1338,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1338 total_file_size += sizeof(struct journal_v2_block_trailer);
1339
1340 int fd_v2;
1341 - uint8_t *data_start = netdata_mmap(path, total_file_size, MAP_SHARED, 0, false, true, &fd_v2);
1341 + uint8_t *data_start = nd_mmap_advanced(path, total_file_size, MAP_SHARED, 0, false, true, &fd_v2);
1342 uint8_t *data = data_start;
1343
1344 memset(data_start, 0, extent_offset);
@@ -1494,7 +1494,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1494 resize_file_to = sizeof(j2_header);
1495 }
1496
1497 - netdata_munmap(data_start, total_file_size);
1497 + nd_munmap(data_start, total_file_size);
1498 freez(uuid_list);
1499
1500 if (likely(resize_file_to == total_file_size))
src/database/engine/rrdengineapi.c
+3 -1
@@ -1399,7 +1399,9 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) {
1399 }
1400
1401 // stats.sizeof_metric = 0;
1402 - stats.sizeof_datafile = struct_natural_alignment(sizeof(struct rrdengine_datafile)) + struct_natural_alignment(sizeof(struct rrdengine_journalfile));
1402 + stats.sizeof_datafile =
1403 + natural_alignment(sizeof(struct rrdengine_datafile)) +
1404 + natural_alignment(sizeof(struct rrdengine_journalfile));
1405 stats.sizeof_page_in_cache = 0; // struct_natural_alignment(sizeof(struct page_cache_descr));
1406 stats.sizeof_point_data = page_type_size[ctx->config.page_type];
1407 stats.sizeof_page_data = tier_page_size[ctx->config.tier];
src/database/rrddim.c
+2 -2
@@ -62,7 +62,7 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
62 size_t entries = st->db.entries;
63 if(!entries) entries = 5;
64
65 - rd->db.data = netdata_mmap(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, true, NULL);
65 + rd->db.data = nd_mmap_advanced(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, true, NULL);
66 if(rd->db.data) {
67 rd->db.memsize = entries * sizeof(storage_number);
68 pulse_db_rrd_memory_add(rd->db.memsize);
@@ -228,7 +228,7 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
228 pulse_db_rrd_memory_sub(rd->db.memsize);
229
230 if(rd->rrd_memory_mode == RRD_MEMORY_MODE_RAM)
231 - netdata_munmap(rd->db.data, rd->db.memsize);
231 + nd_munmap(rd->db.data, rd->db.memsize);
232 else
233 freez(rd->db.data);
234 }
src/libnetdata/aral/aral.c
+7 -24
@@ -11,12 +11,6 @@
11 #define TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS
12 #endif
13
14 -#if ENV32BIT
15 -#define SYSTEM_REQUIRED_ALIGNMENT (sizeof(uintptr_t) * 2)
16 -#else
17 -#define SYSTEM_REQUIRED_ALIGNMENT (alignof(uintptr_t))
18 -#endif
19 -
14 // max mapped file size
15 #define ARAL_MAX_PAGE_SIZE_MMAP (1ULL * 1024 * 1024 * 1024)
16
@@ -437,19 +431,6 @@ static inline void aral_free_validate_internal_check(ARAL *ar, ARAL_FREE *fr) {
431 // --------------------------------------------------------------------------------------------------------------------
432 // page size management
433
440 -static inline size_t memory_alignment(size_t size, size_t alignment) {
441 - // return (size + alignment - 1) & ~(alignment - 1); // assumees alignment is power of 2
442 - return ((size + alignment - 1) / alignment) * alignment;
443 -}
444 -
445 -static size_t aral_get_system_page_size(void) {
446 - long int page_size = sysconf(_SC_PAGE_SIZE);
447 - if (unlikely(page_size <= 4096))
448 - return 4096;
449 - else
450 - return page_size;
451 -}
452 -
434 static size_t aral_element_slot_size(size_t requested_element_size, bool usable) {
435 // we need to add a page pointer after the element
436 // so, first align the element size to the pointer size
@@ -525,7 +506,8 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
506 page->filename = strdupz(filename);
507 page->mapped = true;
508
528 - page->data = netdata_mmap(page->filename, size, MAP_SHARED, 0, false, ar->config.options & ARAL_DONT_DUMP, NULL);
509 + page->data =
510 + nd_mmap_advanced(page->filename, size, MAP_SHARED, 0, false, ar->config.options & ARAL_DONT_DUMP, NULL);
511 if (unlikely(!page->data))
512 fatal("ARAL: '%s' cannot allocate aral buffer of size %zu on filename '%s'",
513 ar->config.name, size, page->filename);
@@ -547,7 +529,8 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
529
530 if (size >= ARAL_MALLOC_USE_MMAP_ABOVE) {
531 bool mapped;
550 - uint8_t *ptr = netdata_mmap(NULL, size, MAP_PRIVATE, 1, false, ar->config.options & ARAL_DONT_DUMP, NULL);
532 + uint8_t *ptr =
533 + nd_mmap_advanced(NULL, size, MAP_ANONYMOUS | MAP_PRIVATE, 1, false, ar->config.options & ARAL_DONT_DUMP, NULL);
534 if (ptr) {
535 mapped = true;
536 stats = &ar->stats->mmap;
@@ -614,7 +597,7 @@ void aral_del_page___no_lock_needed(ARAL *ar, ARAL_PAGE *page TRACE_ALLOCATIONS_
597 stats = &ar->stats->mmap;
598 total_size = size + sizeof(ARAL_PAGE);
599
617 - netdata_munmap(page->data, page->size);
600 + nd_munmap(page->data, page->size);
601
602 if (unlikely(unlink(page->filename) == 1))
603 netdata_log_error("Cannot delete file '%s'", page->filename);
@@ -632,7 +615,7 @@ void aral_del_page___no_lock_needed(ARAL *ar, ARAL_PAGE *page TRACE_ALLOCATIONS_
615 #else
616 if(page->mapped) {
617 stats = &ar->stats->mmap;
635 - netdata_munmap(page, page->size);
618 + nd_munmap(page, page->size);
619 }
620 else {
621 stats = &ar->stats->malloc;
@@ -1093,7 +1076,7 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
1076 // ----------------------------------------------------------------------------------------------------------------
1077 // calculate allocation sizes
1078
1096 - ar->config.system_page_size = aral_get_system_page_size();
1079 + ar->config.system_page_size = os_get_system_page_size();
1080
1081 if (ar->config.initial_page_elements < 2)
1082 ar->config.initial_page_elements = 2;
src/libnetdata/libnetdata.c
-645
@@ -6,473 +6,10 @@
6 #define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED)
7 #define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - ((size) % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
8
9 -#if !defined(MADV_DONTFORK)
10 -#define MADV_DONTFORK 0
11 -#endif
12 -
13 -#if !defined(O_NOATIME)
14 -#define O_NOATIME 0
15 -#endif
16 -
9 struct rlimit rlimit_nofile = { .rlim_cur = 1024, .rlim_max = 1024 };
10
19 -#if defined(MADV_MERGEABLE)
20 -int enable_ksm = CONFIG_BOOLEAN_AUTO;
21 -#else
22 -int enable_ksm = 0;
23 -#endif
24 -
11 volatile sig_atomic_t netdata_exit = 0;
12
27 -// ----------------------------------------------------------------------------
28 -// memory allocation functions that handle failures
29 -
30 -// although netdata does not use memory allocations too often (netdata tries to
31 -// maintain its memory footprint stable during runtime, i.e. all buffers are
32 -// allocated during initialization and are adapted to current use throughout
33 -// its lifetime), these can be used to override the default system allocation
34 -// routines.
35 -
36 -#ifdef NETDATA_TRACE_ALLOCATIONS
37 -#warning NETDATA_TRACE_ALLOCATIONS ENABLED
38 -#include "Judy.h"
39 -
40 -#if defined(HAVE_DLSYM) && defined(ENABLE_DLSYM)
41 -#include <dlfcn.h>
42 -
43 -typedef void (*libc_function_t)(void);
44 -
45 -static void *malloc_first_run(size_t size);
46 -static void *(*libc_malloc)(size_t) = malloc_first_run;
47 -
48 -static void *calloc_first_run(size_t n, size_t size);
49 -static void *(*libc_calloc)(size_t, size_t) = calloc_first_run;
50 -
51 -static void *realloc_first_run(void *ptr, size_t size);
52 -static void *(*libc_realloc)(void *, size_t) = realloc_first_run;
53 -
54 -static void free_first_run(void *ptr);
55 -static void (*libc_free)(void *) = free_first_run;
56 -
57 -static char *strdup_first_run(const char *s);
58 -static char *(*libc_strdup)(const char *) = strdup_first_run;
59 -
60 -static char *strndup_first_run(const char *s, size_t len);
61 -static char *(*libc_strndup)(const char *, size_t) = strndup_first_run;
62 -
63 -static size_t malloc_usable_size_first_run(void *ptr);
64 -#ifdef HAVE_MALLOC_USABLE_SIZE
65 -static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size_first_run;
66 -#else
67 -static size_t (*libc_malloc_usable_size)(void *) = NULL;
68 -#endif
69 -
70 -static void link_system_library_function(libc_function_t *func_pptr, const char *name, bool required) {
71 - *func_pptr = dlsym(RTLD_NEXT, name);
72 - if(!*func_pptr && required) {
73 - fprintf(stderr, "FATAL: Cannot find system's %s() function.\n", name);
74 - abort();
75 - }
76 -}
77 -
78 -static void *malloc_first_run(size_t size) {
79 - link_system_library_function((libc_function_t *) &libc_malloc, "malloc", true);
80 - return libc_malloc(size);
81 -}
82 -
83 -static void *calloc_first_run(size_t n, size_t size) {
84 - link_system_library_function((libc_function_t *) &libc_calloc, "calloc", true);
85 - return libc_calloc(n, size);
86 -}
87 -
88 -static void *realloc_first_run(void *ptr, size_t size) {
89 - link_system_library_function((libc_function_t *) &libc_realloc, "realloc", true);
90 - return libc_realloc(ptr, size);
91 -}
92 -
93 -static void free_first_run(void *ptr) {
94 - link_system_library_function((libc_function_t *) &libc_free, "free", true);
95 - libc_free(ptr);
96 -}
97 -
98 -static char *strdup_first_run(const char *s) {
99 - link_system_library_function((libc_function_t *) &libc_strdup, "strdup", true);
100 - return libc_strdup(s);
101 -}
102 -
103 -static char *strndup_first_run(const char *s, size_t len) {
104 - link_system_library_function((libc_function_t *) &libc_strndup, "strndup", true);
105 - return libc_strndup(s, len);
106 -}
107 -
108 -static size_t malloc_usable_size_first_run(void *ptr) {
109 - link_system_library_function((libc_function_t *) &libc_malloc_usable_size, "malloc_usable_size", false);
110 -
111 - if(libc_malloc_usable_size)
112 - return libc_malloc_usable_size(ptr);
113 - else
114 - return 0;
115 -}
116 -
117 -void *malloc(size_t size) {
118 - return mallocz(size);
119 -}
120 -
121 -void *calloc(size_t n, size_t size) {
122 - return callocz(n, size);
123 -}
124 -
125 -void *realloc(void *ptr, size_t size) {
126 - return reallocz(ptr, size);
127 -}
128 -
129 -void *reallocarray(void *ptr, size_t n, size_t size) {
130 - return reallocz(ptr, n * size);
131 -}
132 -
133 -void free(void *ptr) {
134 - freez(ptr);
135 -}
136 -
137 -char *strdup(const char *s) {
138 - return strdupz(s);
139 -}
140 -
141 -char *strndup(const char *s, size_t len) {
142 - return strndupz(s, len);
143 -}
144 -
145 -size_t malloc_usable_size(void *ptr) {
146 - return mallocz_usable_size(ptr);
147 -}
148 -#else // !HAVE_DLSYM
149 -
150 -static void *(*libc_malloc)(size_t) = malloc;
151 -static void *(*libc_calloc)(size_t, size_t) = calloc;
152 -static void *(*libc_realloc)(void *, size_t) = realloc;
153 -static void (*libc_free)(void *) = free;
154 -
155 -#ifdef HAVE_MALLOC_USABLE_SIZE
156 -static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size;
157 -#else
158 -static size_t (*libc_malloc_usable_size)(void *) = NULL;
159 -#endif
160 -
161 -#endif // HAVE_DLSYM
162 -
163 -
164 -void posix_memfree(void *ptr) {
165 - libc_free(ptr);
166 -}
167 -
168 -struct malloc_header_signature {
169 - uint32_t magic;
170 - uint32_t size;
171 - struct malloc_trace *trace;
172 -};
173 -
174 -struct malloc_header {
175 - struct malloc_header_signature signature;
176 - uint8_t padding[(sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) ? MALLOC_ALIGNMENT - (sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) : 0];
177 - uint8_t data[];
178 -};
179 -
180 -static size_t malloc_header_size = sizeof(struct malloc_header);
181 -
182 -int malloc_trace_compare(void *A, void *B) {
183 - struct malloc_trace *a = A;
184 - struct malloc_trace *b = B;
185 - return strcmp(a->function, b->function);
186 -}
187 -
188 -static avl_tree_lock malloc_trace_index = {
189 - .avl_tree = {
190 - .root = NULL,
191 - .compar = malloc_trace_compare},
192 - .rwlock = AVL_LOCK_INITIALIZER
193 -};
194 -
195 -int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data) {
196 - return avl_traverse_lock(&malloc_trace_index, callback, data);
197 -}
198 -
199 -NEVERNULL WARNUNUSED
200 -static struct malloc_trace *malloc_trace_find_or_create(const char *file, const char *function, size_t line) {
201 - struct malloc_trace tmp = {
202 - .line = line,
203 - .function = function,
204 - .file = file,
205 - };
206 -
207 - struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp);
208 - if(!t) {
209 - t = libc_calloc(1, sizeof(struct malloc_trace));
210 - if(!t) fatal("No memory");
211 - t->line = line;
212 - t->function = function;
213 - t->file = file;
214 -
215 - struct malloc_trace *t2 = (struct malloc_trace *)avl_insert_lock(&malloc_trace_index, (avl_t *)t);
216 - if(t2 != t)
217 - free(t);
218 -
219 - t = t2;
220 - }
221 -
222 - if(!t)
223 - fatal("Cannot insert to AVL");
224 -
225 - return t;
226 -}
227 -
228 -void malloc_trace_mmap(size_t size) {
229 - struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
230 - size_t_atomic_count(add, p->mmap_calls, 1);
231 - size_t_atomic_count(add, p->allocations, 1);
232 - size_t_atomic_bytes(add, p->bytes, size);
233 -}
234 -
235 -void malloc_trace_munmap(size_t size) {
236 - struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
237 - size_t_atomic_count(add, p->munmap_calls, 1);
238 - size_t_atomic_count(sub, p->allocations, 1);
239 - size_t_atomic_bytes(sub, p->bytes, size);
240 -}
241 -
242 -void *mallocz_int(size_t size, const char *file, const char *function, size_t line) {
243 - struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
244 -
245 - size_t_atomic_count(add, p->malloc_calls, 1);
246 - size_t_atomic_count(add, p->allocations, 1);
247 - size_t_atomic_bytes(add, p->bytes, size);
248 -
249 - struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
250 - if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
251 - t->signature.magic = 0x0BADCAFE;
252 - t->signature.trace = p;
253 - t->signature.size = size;
254 -
255 -#ifdef NETDATA_INTERNAL_CHECKS
256 - for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
257 - t->padding[i] = 0xFF;
258 -#endif
259 -
260 - return (void *)&t->data;
261 -}
262 -
263 -void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line) {
264 - struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
265 - size = nmemb * size;
266 -
267 - size_t_atomic_count(add, p->calloc_calls, 1);
268 - size_t_atomic_count(add, p->allocations, 1);
269 - size_t_atomic_bytes(add, p->bytes, size);
270 -
271 - struct malloc_header *t = (struct malloc_header *)libc_calloc(1, malloc_header_size + size);
272 - if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
273 - t->signature.magic = 0x0BADCAFE;
274 - t->signature.trace = p;
275 - t->signature.size = size;
276 -
277 -#ifdef NETDATA_INTERNAL_CHECKS
278 - for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
279 - t->padding[i] = 0xFF;
280 -#endif
281 -
282 - return &t->data;
283 -}
284 -
285 -char *strdupz_int(const char *s, const char *file, const char *function, size_t line) {
286 - struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
287 - size_t size = strlen(s) + 1;
288 -
289 - size_t_atomic_count(add, p->strdup_calls, 1);
290 - size_t_atomic_count(add, p->allocations, 1);
291 - size_t_atomic_bytes(add, p->bytes, size);
292 -
293 - struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
294 - if (unlikely(!t)) fatal("strdupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
295 - t->signature.magic = 0x0BADCAFE;
296 - t->signature.trace = p;
297 - t->signature.size = size;
298 -
299 -#ifdef NETDATA_INTERNAL_CHECKS
300 - for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
301 - t->padding[i] = 0xFF;
302 -#endif
303 -
304 - memcpy(&t->data, s, size);
305 - return (char *)&t->data;
306 -}
307 -
308 -char *strndupz_int(const char *s, size_t len, const char *file, const char *function, size_t line) {
309 - struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
310 - size_t size = len + 1;
311 -
312 - size_t_atomic_count(add, p->strdup_calls, 1);
313 - size_t_atomic_count(add, p->allocations, 1);
314 - size_t_atomic_bytes(add, p->bytes, size);
315 -
316 - struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
317 - if (unlikely(!t)) fatal("strndupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
318 - t->signature.magic = 0x0BADCAFE;
319 - t->signature.trace = p;
320 - t->signature.size = size;
321 -
322 -#ifdef NETDATA_INTERNAL_CHECKS
323 - for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
324 - t->padding[i] = 0xFF;
325 -#endif
326 -
327 - memcpy(&t->data, s, size);
328 - t->data[len] = '\0';
329 - return (char *)&t->data;
330 -}
331 -
332 -static struct malloc_header *malloc_get_header(void *ptr, const char *caller, const char *file, const char *function, size_t line) {
333 - uint8_t *ret = (uint8_t *)ptr - malloc_header_size;
334 - struct malloc_header *t = (struct malloc_header *)ret;
335 -
336 - if(t->signature.magic != 0x0BADCAFE) {
337 - netdata_log_error("pointer %p is not our pointer (called %s() from %zu@%s, %s()).", ptr, caller, line, file, function);
338 - return NULL;
339 - }
340 -
341 - return t;
342 -}
343 -
344 -void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line) {
345 - if(!ptr) return mallocz_int(size, file, function, line);
346 -
347 - struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
348 - if(!t)
349 - return libc_realloc(ptr, size);
350 -
351 - if(t->signature.size == size) return ptr;
352 - size_t_atomic_count(add, t->signature.trace->free_calls, 1);
353 - size_t_atomic_count(sub, t->signature.trace->allocations, 1);
354 - size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
355 -
356 - struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
357 - size_t_atomic_count(add, p->realloc_calls, 1);
358 - size_t_atomic_count(add, p->allocations, 1);
359 - size_t_atomic_bytes(add, p->bytes, size);
360 -
361 - t = (struct malloc_header *)libc_realloc(t, malloc_header_size + size);
362 - if (unlikely(!t)) fatal("reallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
363 - t->signature.magic = 0x0BADCAFE;
364 - t->signature.trace = p;
365 - t->signature.size = size;
366 -
367 -#ifdef NETDATA_INTERNAL_CHECKS
368 - for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
369 - t->padding[i] = 0xFF;
370 -#endif
371 -
372 - return (void *)&t->data;
373 -}
374 -
375 -size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line) {
376 - if(unlikely(!ptr)) return 0;
377 -
378 - struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
379 - if(!t) {
380 - if(libc_malloc_usable_size)
381 - return libc_malloc_usable_size(ptr);
382 - else
383 - return 0;
384 - }
385 -
386 - return t->signature.size;
387 -}
388 -
389 -void freez_int(void *ptr, const char *file, const char *function, size_t line) {
390 - if(unlikely(!ptr)) return;
391 -
392 - struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
393 - if(!t) {
394 - libc_free(ptr);
395 - return;
396 - }
397 -
398 - size_t_atomic_count(add, t->signature.trace->free_calls, 1);
399 - size_t_atomic_count(sub, t->signature.trace->allocations, 1);
400 - size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
401 -
402 -#ifdef NETDATA_INTERNAL_CHECKS
403 - // it should crash if it is used after freeing it
404 - memset(t, 0, malloc_header_size + t->signature.size);
405 -#endif
406 -
407 - libc_free(t);
408 -}
409 -#else
410 -
411 -char *strdupz(const char *s) {
412 - char *t = strdup(s);
413 - if (unlikely(!t)) {
414 - OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
415 - fatal("Cannot strdup() string '%s' (system memory available bytes: %lu)", s, sm.ram_available_bytes);
416 - }
417 - return t;
418 -}
419 -
420 -char *strndupz(const char *s, size_t len) {
421 - char *t = strndup(s, len);
422 - if (unlikely(!t)) {
423 - OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
424 - fatal("Cannot strndup() string '%s' of len %zu (system memory available bytes: %lu)", s, len, sm.ram_available_bytes);
425 - }
426 - return t;
427 -}
428 -
429 -// If ptr is NULL, no operation is performed.
430 -void freez(void *ptr) {
431 - if(likely(ptr)) free(ptr);
432 -}
433 -
434 -void *mallocz(size_t size) {
435 - void *p = malloc(size);
436 - if (unlikely(!p)) {
437 - OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
438 - fatal("Cannot allocate %zu bytes of memory (system memory available bytes: %lu)", size, sm.ram_available_bytes);
439 - }
440 - return p;
441 -}
442 -
443 -void *callocz(size_t nmemb, size_t size) {
444 - void *p = calloc(nmemb, size);
445 - if (unlikely(!p)) {
446 - OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
447 - fatal("Cannot allocate %zu bytes of memory (system memory available bytes: %lu)", nmemb * size, sm.ram_available_bytes);
448 - }
449 - return p;
450 -}
451 -
452 -void *reallocz(void *ptr, size_t size) {
453 - void *p = realloc(ptr, size);
454 - if (unlikely(!p)) {
455 - OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
456 - fatal("Cannot re-allocate memory to %zu bytes. (system memory available bytes: %lu)", size, sm.ram_available_bytes);
457 - }
458 - return p;
459 -}
460 -
461 -void posix_memfree(void *ptr) {
462 - free(ptr);
463 -}
464 -#endif
465 -
466 -void mallocz_release_as_much_memory_to_the_system(void) {
467 -#if defined(HAVE_C_MALLOC_TRIM)
468 - static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
469 - if(spinlock_trylock(&spinlock)) {
470 - malloc_trim(0);
471 - spinlock_unlock(&spinlock);
472 - }
473 -#endif
474 -}
475 -
13 // --------------------------------------------------------------------------------------------------------------------
14
15 void json_escape_string(char *dst, const char *src, size_t size) {
@@ -506,188 +43,6 @@ void json_fix_string(char *s) {
43 }
44 }
45
509 -static int memory_file_open(const char *filename, size_t size) {
510 - // netdata_log_info("memory_file_open('%s', %zu", filename, size);
511 -
512 - int fd = open(filename, O_RDWR | O_CREAT | O_NOATIME | O_CLOEXEC, 0664);
513 - if (fd != -1) {
514 - if (lseek(fd, size, SEEK_SET) == (off_t) size) {
515 - if (write(fd, "", 1) == 1) {
516 - if (ftruncate(fd, size))
517 - netdata_log_error("Cannot truncate file '%s' to size %zu. Will use the larger file.", filename, size);
518 - }
519 - else
520 - netdata_log_error("Cannot write to file '%s' at position %zu.", filename, size);
521 - }
522 - else
523 - netdata_log_error("Cannot seek file '%s' to size %zu.", filename, size);
524 - }
525 - else
526 - netdata_log_error("Cannot create/open file '%s'.", filename);
527 -
528 - return fd;
529 -}
530 -
531 -inline int madvise_sequential(void *mem, size_t len) {
532 - static int logger = 1;
533 - int ret = madvise(mem, len, MADV_SEQUENTIAL);
534 -
535 - if (ret != 0 && logger-- > 0)
536 - netdata_log_error("madvise(MADV_SEQUENTIAL) of size %zu, failed.", len);
537 - return ret;
538 -}
539 -
540 -inline int madvise_random(void *mem, size_t len) {
541 - static int logger = 1;
542 - int ret = madvise(mem, len, MADV_RANDOM);
543 -
544 - if (ret != 0 && logger-- > 0)
545 - netdata_log_error("madvise(MADV_RANDOM) of size %zu, failed.", len);
546 - return ret;
547 -}
548 -
549 -inline int madvise_dontfork(void *mem, size_t len) {
550 - static int logger = 1;
551 - int ret = madvise(mem, len, MADV_DONTFORK);
552 -
553 - if (ret != 0 && logger-- > 0)
554 - netdata_log_error("madvise(MADV_DONTFORK) of size %zu, failed.", len);
555 - return ret;
556 -}
557 -
558 -inline int madvise_willneed(void *mem, size_t len) {
559 - static int logger = 1;
560 - int ret = madvise(mem, len, MADV_WILLNEED);
561 -
562 - if (ret != 0 && logger-- > 0)
563 - netdata_log_error("madvise(MADV_WILLNEED) of size %zu, failed.", len);
564 - return ret;
565 -}
566 -
567 -inline int madvise_dontneed(void *mem, size_t len) {
568 - static int logger = 1;
569 - int ret = madvise(mem, len, MADV_DONTNEED);
570 -
571 - if (ret != 0 && logger-- > 0)
572 - netdata_log_error("madvise(MADV_DONTNEED) of size %zu, failed.", len);
573 - return ret;
574 -}
575 -
576 -inline int madvise_dontdump(void *mem __maybe_unused, size_t len __maybe_unused) {
577 -#if __linux__
578 - static int logger = 1;
579 - int ret = madvise(mem, len, MADV_DONTDUMP);
580 -
581 - if (ret != 0 && logger-- > 0)
582 - netdata_log_error("madvise(MADV_DONTDUMP) of size %zu, failed.", len);
583 - return ret;
584 -#else
585 - return 0;
586 -#endif
587 -}
588 -
589 -inline int madvise_mergeable(void *mem __maybe_unused, size_t len __maybe_unused) {
590 -#ifdef MADV_MERGEABLE
591 - static int logger = 1;
592 - int ret = madvise(mem, len, MADV_MERGEABLE);
593 -
594 - if (ret != 0 && logger-- > 0)
595 - netdata_log_error("madvise(MADV_MERGEABLE) of size %zu, failed.", len);
596 - return ret;
597 -#else
598 - return 0;
599 -#endif
600 -}
601 -
602 -size_t netdata_mmap_count = 0;
603 -
604 -void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool read_only, bool dont_dump, int *open_fd)
605 -{
606 - // netdata_log_info("netdata_mmap('%s', %zu", filename, size);
607 -
608 - // MAP_SHARED is used in memory mode map
609 - // MAP_PRIVATE is used in memory mode ram and save
610 -
611 - if(unlikely(!(flags & MAP_SHARED) && !(flags & MAP_PRIVATE)))
612 - fatal("Neither MAP_SHARED or MAP_PRIVATE were given to netdata_mmap()");
613 -
614 - if(unlikely((flags & MAP_SHARED) && (flags & MAP_PRIVATE)))
615 - fatal("Both MAP_SHARED and MAP_PRIVATE were given to netdata_mmap()");
616 -
617 - if(unlikely((flags & MAP_SHARED) && (!filename || !*filename)))
618 - fatal("MAP_SHARED requested, without a filename to netdata_mmap()");
619 -
620 - // don't enable ksm is the global setting is disabled
621 - if(unlikely(!enable_ksm)) ksm = 0;
622 -
623 - // KSM only merges anonymous (private) pages, never pagecache (file) pages
624 - // but MAP_PRIVATE without MAP_ANONYMOUS it fails too, so we need it always
625 - if((flags & MAP_PRIVATE)) flags |= MAP_ANONYMOUS;
626 -
627 - int fd = -1;
628 - void *mem = MAP_FAILED;
629 -
630 - if(filename && *filename) {
631 - // open/create the file to be used
632 - fd = memory_file_open(filename, size);
633 - if(fd == -1) goto cleanup;
634 - }
635 -
636 - int fd_for_mmap = fd;
637 - if(fd != -1 && (flags & MAP_PRIVATE)) {
638 - // this is MAP_PRIVATE allocation
639 - // no need for mmap() to use our fd
640 - // we will copy the file into the memory allocated
641 - fd_for_mmap = -1;
642 - }
643 -
644 - mem = mmap(NULL, size, read_only ? PROT_READ : PROT_READ | PROT_WRITE, flags, fd_for_mmap, 0);
645 - if (mem != MAP_FAILED) {
646 -
647 -#ifdef NETDATA_TRACE_ALLOCATIONS
648 - malloc_trace_mmap(size);
649 -#endif
650 -
651 - // if we have a file open, but we didn't give it to mmap(),
652 - // we have to read the file into the memory block we allocated
653 - if(fd != -1 && fd_for_mmap == -1) {
654 - if (lseek(fd, 0, SEEK_SET) == 0) {
655 - if (read(fd, mem, size) != (ssize_t) size)
656 - netdata_log_info("Cannot read from file '%s'", filename);
657 - }
658 - else netdata_log_info("Cannot seek to beginning of file '%s'.", filename);
659 - }
660 -
661 - // madvise_sequential(mem, size);
662 - // madvise_dontfork(mem, size); // aral is initialized before we daemonize
663 - if(dont_dump) madvise_dontdump(mem, size);
664 - // if(flags & MAP_SHARED) madvise_willneed(mem, size);
665 - if(ksm) madvise_mergeable(mem, size);
666 - }
667 -
668 -cleanup:
669 - if(fd != -1) {
670 - if (open_fd)
671 - *open_fd = fd;
672 - else
673 - close(fd);
674 - }
675 - if(mem == MAP_FAILED) return NULL;
676 - __atomic_add_fetch(&netdata_mmap_count, 1, __ATOMIC_RELAXED);
677 - errno_clear();
678 - return mem;
679 -}
680 -
681 -int netdata_munmap(void *ptr, size_t size) {
682 -#ifdef NETDATA_TRACE_ALLOCATIONS
683 - malloc_trace_munmap(size);
684 -#endif
685 - int rc = munmap(ptr, size);
686 - if(rc == 0)
687 - __atomic_sub_fetch(&netdata_mmap_count, 1, __ATOMIC_RELAXED);
688 - return rc;
689 -}
690 -
46 char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) {
47 char *s = fgets(buf, (int)buf_size, fp);
48 if (!s) return NULL;
src/libnetdata/libnetdata.h
+3 -83
@@ -8,6 +8,9 @@ extern "C" {
8 # endif
9
10 #include "common.h"
11 +#include "memory/alignment.h"
12 +#include "memory/nd-mallocz.h"
13 +#include "memory/nd-mmap.h"
14 #include "log/nd_log-fatal.h"
15 #include "atomics/atomics.h"
16
@@ -23,61 +26,15 @@ extern "C" {
26 #include "storage-point.h"
27 #include "paths/paths.h"
28
26 -int madvise_sequential(void *mem, size_t len);
27 -int madvise_random(void *mem, size_t len);
28 -int madvise_dontfork(void *mem, size_t len);
29 -int madvise_willneed(void *mem, size_t len);
30 -int madvise_dontneed(void *mem, size_t len);
31 -int madvise_dontdump(void *mem, size_t len);
32 -int madvise_mergeable(void *mem, size_t len);
33 -
29 int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args);
30 int snprintfz(char *dst, size_t n, const char *fmt, ...) PRINTFLIKE(3, 4);
31
37 -// memory allocation functions that handle failures
38 -#ifdef NETDATA_TRACE_ALLOCATIONS
39 -int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data);
40 -
41 -#define strdupz(s) strdupz_int(s, __FILE__, __FUNCTION__, __LINE__)
42 -#define strndupz(s, len) strndupz_int(s, len, __FILE__, __FUNCTION__, __LINE__)
43 -#define callocz(nmemb, size) callocz_int(nmemb, size, __FILE__, __FUNCTION__, __LINE__)
44 -#define mallocz(size) mallocz_int(size, __FILE__, __FUNCTION__, __LINE__)
45 -#define reallocz(ptr, size) reallocz_int(ptr, size, __FILE__, __FUNCTION__, __LINE__)
46 -#define freez(ptr) freez_int(ptr, __FILE__, __FUNCTION__, __LINE__)
47 -#define mallocz_usable_size(ptr) mallocz_usable_size_int(ptr, __FILE__, __FUNCTION__, __LINE__)
48 -
49 -char *strdupz_int(const char *s, const char *file, const char *function, size_t line);
50 -char *strndupz_int(const char *s, size_t len, const char *file, const char *function, size_t line);
51 -void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line);
52 -void *mallocz_int(size_t size, const char *file, const char *function, size_t line);
53 -void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line);
54 -void freez_int(void *ptr, const char *file, const char *function, size_t line);
55 -size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line);
56 -
57 -#else // NETDATA_TRACE_ALLOCATIONS
58 -char *strdupz(const char *s) MALLOCLIKE NEVERNULL;
59 -char *strndupz(const char *s, size_t len) MALLOCLIKE NEVERNULL;
60 -void *callocz(size_t nmemb, size_t size) MALLOCLIKE NEVERNULL;
61 -void *mallocz(size_t size) MALLOCLIKE NEVERNULL;
62 -void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL;
63 -void freez(void *ptr);
64 -#endif // NETDATA_TRACE_ALLOCATIONS
65 -
66 -void mallocz_release_as_much_memory_to_the_system(void);
67 -void posix_memfree(void *ptr);
68 -
32 void json_escape_string(char *dst, const char *src, size_t size);
33 void json_fix_string(char *s);
34
72 -extern size_t netdata_mmap_count;
73 -void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool read_only, bool dont_dump, int *open_fd);
74 -int netdata_munmap(void *ptr, size_t size);
75 -int memory_file_save(const char *filename, void *mem, size_t size);
35
36 extern struct rlimit rlimit_nofile;
37
79 -extern int enable_ksm;
80 -
38 char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len);
39
40 int verify_netdata_host_prefix(bool log_msg);
@@ -188,43 +145,6 @@ extern const char *netdata_configured_host_prefix;
145 #include "functions_evloop/functions_evloop.h"
146 #include "query_progress/progress.h"
147
191 -static inline size_t struct_natural_alignment(size_t size) __attribute__((const));
192 -
193 -#define STRUCT_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
194 -static inline size_t struct_natural_alignment(size_t size) {
195 - if(unlikely(size % STRUCT_NATURAL_ALIGNMENT))
196 - size = size + STRUCT_NATURAL_ALIGNMENT - (size % STRUCT_NATURAL_ALIGNMENT);
197 -
198 - return size;
199 -}
200 -
201 -#ifdef NETDATA_TRACE_ALLOCATIONS
202 -struct malloc_trace {
203 - avl_t avl;
204 -
205 - const char *function;
206 - const char *file;
207 - size_t line;
208 -
209 - size_t malloc_calls;
210 - size_t calloc_calls;
211 - size_t realloc_calls;
212 - size_t strdup_calls;
213 - size_t free_calls;
214 -
215 - size_t mmap_calls;
216 - size_t munmap_calls;
217 -
218 - size_t allocations;
219 - size_t bytes;
220 -
221 - struct rrddim *rd_bytes;
222 - struct rrddim *rd_allocations;
223 - struct rrddim *rd_avg_alloc;
224 - struct rrddim *rd_ops;
225 -};
226 -#endif // NETDATA_TRACE_ALLOCATIONS
227 -
148 static inline PPvoid_t JudyLFirstThenNext(Pcvoid_t PArray, Word_t * PIndex, bool *first) {
149 if(unlikely(*first)) {
150 *first = false;
src/libnetdata/memory/alignment.h new
+25
@@ -0,0 +1,25 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ALIGNMENT_H
4 +#define NETDATA_ALIGNMENT_H
5 +
6 +#include "../common.h"
7 +
8 +#if ENV32BIT
9 +#define SYSTEM_REQUIRED_ALIGNMENT (sizeof(uintptr_t) * 2)
10 +#else
11 +#define SYSTEM_REQUIRED_ALIGNMENT (alignof(uintptr_t))
12 +#endif
13 +
14 +static inline size_t memory_alignment(size_t size, size_t alignment) __attribute__((const));
15 +static inline size_t memory_alignment(size_t size, size_t alignment) {
16 + // return (size + alignment - 1) & ~(alignment - 1); // assumes alignment is power of 2
17 + return ((size + alignment - 1) / alignment) * alignment;
18 +}
19 +
20 +static inline size_t natural_alignment(size_t size) __attribute__((const));
21 +static inline size_t natural_alignment(size_t size) {
22 + return memory_alignment(size, SYSTEM_REQUIRED_ALIGNMENT);
23 +}
24 +
25 +#endif //NETDATA_ALIGNMENT_H
src/libnetdata/memory/nd-mallocz.c new
+452
@@ -0,0 +1,452 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "../libnetdata.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// memory allocation functions that handle failures
7 +
8 +// although netdata does not use memory allocations too often (netdata tries to
9 +// maintain its memory footprint stable during runtime, i.e. all buffers are
10 +// allocated during initialization and are adapted to current use throughout
11 +// its lifetime), these can be used to override the default system allocation
12 +// routines.
13 +
14 +#ifdef NETDATA_TRACE_ALLOCATIONS
15 +#warning NETDATA_TRACE_ALLOCATIONS ENABLED
16 +#include "Judy.h"
17 +
18 +#if defined(HAVE_DLSYM) && defined(ENABLE_DLSYM)
19 +#include <dlfcn.h>
20 +
21 +typedef void (*libc_function_t)(void);
22 +
23 +static void *malloc_first_run(size_t size);
24 +static void *(*libc_malloc)(size_t) = malloc_first_run;
25 +
26 +static void *calloc_first_run(size_t n, size_t size);
27 +static void *(*libc_calloc)(size_t, size_t) = calloc_first_run;
28 +
29 +static void *realloc_first_run(void *ptr, size_t size);
30 +static void *(*libc_realloc)(void *, size_t) = realloc_first_run;
31 +
32 +static void free_first_run(void *ptr);
33 +static void (*libc_free)(void *) = free_first_run;
34 +
35 +static char *strdup_first_run(const char *s);
36 +static char *(*libc_strdup)(const char *) = strdup_first_run;
37 +
38 +static char *strndup_first_run(const char *s, size_t len);
39 +static char *(*libc_strndup)(const char *, size_t) = strndup_first_run;
40 +
41 +static size_t malloc_usable_size_first_run(void *ptr);
42 +#ifdef HAVE_MALLOC_USABLE_SIZE
43 +static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size_first_run;
44 +#else
45 +static size_t (*libc_malloc_usable_size)(void *) = NULL;
46 +#endif
47 +
48 +static void link_system_library_function(libc_function_t *func_pptr, const char *name, bool required) {
49 + *func_pptr = dlsym(RTLD_NEXT, name);
50 + if(!*func_pptr && required) {
51 + fprintf(stderr, "FATAL: Cannot find system's %s() function.\n", name);
52 + abort();
53 + }
54 +}
55 +
56 +static void *malloc_first_run(size_t size) {
57 + link_system_library_function((libc_function_t *) &libc_malloc, "malloc", true);
58 + return libc_malloc(size);
59 +}
60 +
61 +static void *calloc_first_run(size_t n, size_t size) {
62 + link_system_library_function((libc_function_t *) &libc_calloc, "calloc", true);
63 + return libc_calloc(n, size);
64 +}
65 +
66 +static void *realloc_first_run(void *ptr, size_t size) {
67 + link_system_library_function((libc_function_t *) &libc_realloc, "realloc", true);
68 + return libc_realloc(ptr, size);
69 +}
70 +
71 +static void free_first_run(void *ptr) {
72 + link_system_library_function((libc_function_t *) &libc_free, "free", true);
73 + libc_free(ptr);
74 +}
75 +
76 +static char *strdup_first_run(const char *s) {
77 + link_system_library_function((libc_function_t *) &libc_strdup, "strdup", true);
78 + return libc_strdup(s);
79 +}
80 +
81 +static char *strndup_first_run(const char *s, size_t len) {
82 + link_system_library_function((libc_function_t *) &libc_strndup, "strndup", true);
83 + return libc_strndup(s, len);
84 +}
85 +
86 +static size_t malloc_usable_size_first_run(void *ptr) {
87 + link_system_library_function((libc_function_t *) &libc_malloc_usable_size, "malloc_usable_size", false);
88 +
89 + if(libc_malloc_usable_size)
90 + return libc_malloc_usable_size(ptr);
91 + else
92 + return 0;
93 +}
94 +
95 +void *malloc(size_t size) {
96 + return mallocz(size);
97 +}
98 +
99 +void *calloc(size_t n, size_t size) {
100 + return callocz(n, size);
101 +}
102 +
103 +void *realloc(void *ptr, size_t size) {
104 + return reallocz(ptr, size);
105 +}
106 +
107 +void *reallocarray(void *ptr, size_t n, size_t size) {
108 + return reallocz(ptr, n * size);
109 +}
110 +
111 +void free(void *ptr) {
112 + freez(ptr);
113 +}
114 +
115 +char *strdup(const char *s) {
116 + return strdupz(s);
117 +}
118 +
119 +char *strndup(const char *s, size_t len) {
120 + return strndupz(s, len);
121 +}
122 +
123 +size_t malloc_usable_size(void *ptr) {
124 + return mallocz_usable_size(ptr);
125 +}
126 +#else // !HAVE_DLSYM
127 +
128 +static void *(*libc_malloc)(size_t) = malloc;
129 +static void *(*libc_calloc)(size_t, size_t) = calloc;
130 +static void *(*libc_realloc)(void *, size_t) = realloc;
131 +static void (*libc_free)(void *) = free;
132 +
133 +#ifdef HAVE_MALLOC_USABLE_SIZE
134 +static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size;
135 +#else
136 +static size_t (*libc_malloc_usable_size)(void *) = NULL;
137 +#endif
138 +
139 +#endif // HAVE_DLSYM
140 +
141 +
142 +void posix_memfree(void *ptr) {
143 + libc_free(ptr);
144 +}
145 +
146 +struct malloc_header_signature {
147 + uint32_t magic;
148 + uint32_t size;
149 + struct malloc_trace *trace;
150 +};
151 +
152 +struct malloc_header {
153 + struct malloc_header_signature signature;
154 + uint8_t padding[(sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) ? MALLOC_ALIGNMENT - (sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) : 0];
155 + uint8_t data[];
156 +};
157 +
158 +static size_t malloc_header_size = sizeof(struct malloc_header);
159 +
160 +int malloc_trace_compare(void *A, void *B) {
161 + struct malloc_trace *a = A;
162 + struct malloc_trace *b = B;
163 + return strcmp(a->function, b->function);
164 +}
165 +
166 +static avl_tree_lock malloc_trace_index = {
167 + .avl_tree = {
168 + .root = NULL,
169 + .compar = malloc_trace_compare},
170 + .rwlock = AVL_LOCK_INITIALIZER
171 +};
172 +
173 +int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data) {
174 + return avl_traverse_lock(&malloc_trace_index, callback, data);
175 +}
176 +
177 +NEVERNULL WARNUNUSED
178 + static struct malloc_trace *malloc_trace_find_or_create(const char *file, const char *function, size_t line) {
179 + struct malloc_trace tmp = {
180 + .line = line,
181 + .function = function,
182 + .file = file,
183 + };
184 +
185 + struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp);
186 + if(!t) {
187 + t = libc_calloc(1, sizeof(struct malloc_trace));
188 + if(!t) fatal("No memory");
189 + t->line = line;
190 + t->function = function;
191 + t->file = file;
192 +
193 + struct malloc_trace *t2 = (struct malloc_trace *)avl_insert_lock(&malloc_trace_index, (avl_t *)t);
194 + if(t2 != t)
195 + free(t);
196 +
197 + t = t2;
198 + }
199 +
200 + if(!t)
201 + fatal("Cannot insert to AVL");
202 +
203 + return t;
204 +}
205 +
206 +void malloc_trace_mmap(size_t size) {
207 + struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
208 + size_t_atomic_count(add, p->mmap_calls, 1);
209 + size_t_atomic_count(add, p->allocations, 1);
210 + size_t_atomic_bytes(add, p->bytes, size);
211 +}
212 +
213 +void malloc_trace_munmap(size_t size) {
214 + struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1);
215 + size_t_atomic_count(add, p->munmap_calls, 1);
216 + size_t_atomic_count(sub, p->allocations, 1);
217 + size_t_atomic_bytes(sub, p->bytes, size);
218 +}
219 +
220 +void *mallocz_int(size_t size, const char *file, const char *function, size_t line) {
221 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
222 +
223 + size_t_atomic_count(add, p->malloc_calls, 1);
224 + size_t_atomic_count(add, p->allocations, 1);
225 + size_t_atomic_bytes(add, p->bytes, size);
226 +
227 + struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
228 + if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
229 + t->signature.magic = 0x0BADCAFE;
230 + t->signature.trace = p;
231 + t->signature.size = size;
232 +
233 +#ifdef NETDATA_INTERNAL_CHECKS
234 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
235 + t->padding[i] = 0xFF;
236 +#endif
237 +
238 + return (void *)&t->data;
239 +}
240 +
241 +void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line) {
242 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
243 + size = nmemb * size;
244 +
245 + size_t_atomic_count(add, p->calloc_calls, 1);
246 + size_t_atomic_count(add, p->allocations, 1);
247 + size_t_atomic_bytes(add, p->bytes, size);
248 +
249 + struct malloc_header *t = (struct malloc_header *)libc_calloc(1, malloc_header_size + size);
250 + if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
251 + t->signature.magic = 0x0BADCAFE;
252 + t->signature.trace = p;
253 + t->signature.size = size;
254 +
255 +#ifdef NETDATA_INTERNAL_CHECKS
256 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
257 + t->padding[i] = 0xFF;
258 +#endif
259 +
260 + return &t->data;
261 +}
262 +
263 +char *strdupz_int(const char *s, const char *file, const char *function, size_t line) {
264 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
265 + size_t size = strlen(s) + 1;
266 +
267 + size_t_atomic_count(add, p->strdup_calls, 1);
268 + size_t_atomic_count(add, p->allocations, 1);
269 + size_t_atomic_bytes(add, p->bytes, size);
270 +
271 + struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
272 + if (unlikely(!t)) fatal("strdupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
273 + t->signature.magic = 0x0BADCAFE;
274 + t->signature.trace = p;
275 + t->signature.size = size;
276 +
277 +#ifdef NETDATA_INTERNAL_CHECKS
278 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
279 + t->padding[i] = 0xFF;
280 +#endif
281 +
282 + memcpy(&t->data, s, size);
283 + return (char *)&t->data;
284 +}
285 +
286 +char *strndupz_int(const char *s, size_t len, const char *file, const char *function, size_t line) {
287 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
288 + size_t size = len + 1;
289 +
290 + size_t_atomic_count(add, p->strdup_calls, 1);
291 + size_t_atomic_count(add, p->allocations, 1);
292 + size_t_atomic_bytes(add, p->bytes, size);
293 +
294 + struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
295 + if (unlikely(!t)) fatal("strndupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
296 + t->signature.magic = 0x0BADCAFE;
297 + t->signature.trace = p;
298 + t->signature.size = size;
299 +
300 +#ifdef NETDATA_INTERNAL_CHECKS
301 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
302 + t->padding[i] = 0xFF;
303 +#endif
304 +
305 + memcpy(&t->data, s, size);
306 + t->data[len] = '\0';
307 + return (char *)&t->data;
308 +}
309 +
310 +static struct malloc_header *malloc_get_header(void *ptr, const char *caller, const char *file, const char *function, size_t line) {
311 + uint8_t *ret = (uint8_t *)ptr - malloc_header_size;
312 + struct malloc_header *t = (struct malloc_header *)ret;
313 +
314 + if(t->signature.magic != 0x0BADCAFE) {
315 + netdata_log_error("pointer %p is not our pointer (called %s() from %zu@%s, %s()).", ptr, caller, line, file, function);
316 + return NULL;
317 + }
318 +
319 + return t;
320 +}
321 +
322 +void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line) {
323 + if(!ptr) return mallocz_int(size, file, function, line);
324 +
325 + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
326 + if(!t)
327 + return libc_realloc(ptr, size);
328 +
329 + if(t->signature.size == size) return ptr;
330 + size_t_atomic_count(add, t->signature.trace->free_calls, 1);
331 + size_t_atomic_count(sub, t->signature.trace->allocations, 1);
332 + size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
333 +
334 + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line);
335 + size_t_atomic_count(add, p->realloc_calls, 1);
336 + size_t_atomic_count(add, p->allocations, 1);
337 + size_t_atomic_bytes(add, p->bytes, size);
338 +
339 + t = (struct malloc_header *)libc_realloc(t, malloc_header_size + size);
340 + if (unlikely(!t)) fatal("reallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
341 + t->signature.magic = 0x0BADCAFE;
342 + t->signature.trace = p;
343 + t->signature.size = size;
344 +
345 +#ifdef NETDATA_INTERNAL_CHECKS
346 + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded
347 + t->padding[i] = 0xFF;
348 +#endif
349 +
350 + return (void *)&t->data;
351 +}
352 +
353 +size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line) {
354 + if(unlikely(!ptr)) return 0;
355 +
356 + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
357 + if(!t) {
358 + if(libc_malloc_usable_size)
359 + return libc_malloc_usable_size(ptr);
360 + else
361 + return 0;
362 + }
363 +
364 + return t->signature.size;
365 +}
366 +
367 +void freez_int(void *ptr, const char *file, const char *function, size_t line) {
368 + if(unlikely(!ptr)) return;
369 +
370 + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
371 + if(!t) {
372 + libc_free(ptr);
373 + return;
374 + }
375 +
376 + size_t_atomic_count(add, t->signature.trace->free_calls, 1);
377 + size_t_atomic_count(sub, t->signature.trace->allocations, 1);
378 + size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size);
379 +
380 +#ifdef NETDATA_INTERNAL_CHECKS
381 + // it should crash if it is used after freeing it
382 + memset(t, 0, malloc_header_size + t->signature.size);
383 +#endif
384 +
385 + libc_free(t);
386 +}
387 +#else
388 +
389 +char *strdupz(const char *s) {
390 + char *t = strdup(s);
391 + if (unlikely(!t)) {
392 + OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
393 + fatal("Cannot strdup() string '%s' (system memory available bytes: %lu)", s, sm.ram_available_bytes);
394 + }
395 + return t;
396 +}
397 +
398 +char *strndupz(const char *s, size_t len) {
399 + char *t = strndup(s, len);
400 + if (unlikely(!t)) {
401 + OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
402 + fatal("Cannot strndup() string '%s' of len %zu (system memory available bytes: %lu)", s, len, sm.ram_available_bytes);
403 + }
404 + return t;
405 +}
406 +
407 +// If ptr is NULL, no operation is performed.
408 +void freez(void *ptr) {
409 + if(likely(ptr)) free(ptr);
410 +}
411 +
412 +void *mallocz(size_t size) {
413 + void *p = malloc(size);
414 + if (unlikely(!p)) {
415 + OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
416 + fatal("Cannot allocate %zu bytes of memory (system memory available bytes: %lu)", size, sm.ram_available_bytes);
417 + }
418 + return p;
419 +}
420 +
421 +void *callocz(size_t nmemb, size_t size) {
422 + void *p = calloc(nmemb, size);
423 + if (unlikely(!p)) {
424 + OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
425 + fatal("Cannot allocate %zu bytes of memory (system memory available bytes: %lu)", nmemb * size, sm.ram_available_bytes);
426 + }
427 + return p;
428 +}
429 +
430 +void *reallocz(void *ptr, size_t size) {
431 + void *p = realloc(ptr, size);
432 + if (unlikely(!p)) {
433 + OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
434 + fatal("Cannot re-allocate memory to %zu bytes. (system memory available bytes: %lu)", size, sm.ram_available_bytes);
435 + }
436 + return p;
437 +}
438 +
439 +void posix_memfree(void *ptr) {
440 + free(ptr);
441 +}
442 +#endif
443 +
444 +void mallocz_release_as_much_memory_to_the_system(void) {
445 +#if defined(HAVE_C_MALLOC_TRIM)
446 + static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
447 + if(spinlock_trylock(&spinlock)) {
448 + malloc_trim(0);
449 + spinlock_unlock(&spinlock);
450 + }
451 +#endif
452 +}
src/libnetdata/memory/nd-mallocz.h new
+65
@@ -0,0 +1,65 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ND_MALLOCZ_H
4 +#define NETDATA_ND_MALLOCZ_H
5 +
6 +#include "../common.h"
7 +
8 +// memory allocation functions that handle failures
9 +#ifdef NETDATA_TRACE_ALLOCATIONS
10 +struct malloc_trace {
11 + avl_t avl;
12 +
13 + const char *function;
14 + const char *file;
15 + size_t line;
16 +
17 + size_t malloc_calls;
18 + size_t calloc_calls;
19 + size_t realloc_calls;
20 + size_t strdup_calls;
21 + size_t free_calls;
22 +
23 + size_t mmap_calls;
24 + size_t munmap_calls;
25 +
26 + size_t allocations;
27 + size_t bytes;
28 +
29 + struct rrddim *rd_bytes;
30 + struct rrddim *rd_allocations;
31 + struct rrddim *rd_avg_alloc;
32 + struct rrddim *rd_ops;
33 +};
34 +
35 +int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data);
36 +
37 +#define strdupz(s) strdupz_int(s, __FILE__, __FUNCTION__, __LINE__)
38 +#define strndupz(s, len) strndupz_int(s, len, __FILE__, __FUNCTION__, __LINE__)
39 +#define callocz(nmemb, size) callocz_int(nmemb, size, __FILE__, __FUNCTION__, __LINE__)
40 +#define mallocz(size) mallocz_int(size, __FILE__, __FUNCTION__, __LINE__)
41 +#define reallocz(ptr, size) reallocz_int(ptr, size, __FILE__, __FUNCTION__, __LINE__)
42 +#define freez(ptr) freez_int(ptr, __FILE__, __FUNCTION__, __LINE__)
43 +#define mallocz_usable_size(ptr) mallocz_usable_size_int(ptr, __FILE__, __FUNCTION__, __LINE__)
44 +
45 +char *strdupz_int(const char *s, const char *file, const char *function, size_t line);
46 +char *strndupz_int(const char *s, size_t len, const char *file, const char *function, size_t line);
47 +void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line);
48 +void *mallocz_int(size_t size, const char *file, const char *function, size_t line);
49 +void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line);
50 +void freez_int(void *ptr, const char *file, const char *function, size_t line);
51 +size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line);
52 +
53 +#else // NETDATA_TRACE_ALLOCATIONS
54 +char *strdupz(const char *s) MALLOCLIKE NEVERNULL;
55 +char *strndupz(const char *s, size_t len) MALLOCLIKE NEVERNULL;
56 +void *callocz(size_t nmemb, size_t size) MALLOCLIKE NEVERNULL;
57 +void *mallocz(size_t size) MALLOCLIKE NEVERNULL;
58 +void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL;
59 +void freez(void *ptr);
60 +#endif // NETDATA_TRACE_ALLOCATIONS
61 +
62 +void mallocz_release_as_much_memory_to_the_system(void);
63 +void posix_memfree(void *ptr);
64 +
65 +#endif //NETDATA_ND_MALLOCZ_H
src/libnetdata/memory/nd-mmap.c new
+216
@@ -0,0 +1,216 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "../libnetdata.h"
4 +
5 +size_t nd_mmap_count = 0;
6 +size_t nd_mmap_size = 0;
7 +
8 +#if !defined(MADV_DONTFORK)
9 +#define MADV_DONTFORK 0
10 +#endif
11 +
12 +#if !defined(O_NOATIME)
13 +#define O_NOATIME 0
14 +#endif
15 +
16 +#if defined(MADV_MERGEABLE)
17 +int enable_ksm = CONFIG_BOOLEAN_AUTO;
18 +#else
19 +int enable_ksm = 0;
20 +#endif
21 +
22 +static int memory_file_open(const char *filename, size_t size) {
23 + // netdata_log_info("memory_file_open('%s', %zu", filename, size);
24 +
25 + int fd = open(filename, O_RDWR | O_CREAT | O_NOATIME | O_CLOEXEC, 0664);
26 + if (fd != -1) {
27 + if (lseek(fd, size, SEEK_SET) == (off_t) size) {
28 + if (write(fd, "", 1) == 1) {
29 + if (ftruncate(fd, size))
30 + netdata_log_error("Cannot truncate file '%s' to size %zu. Will use the larger file.", filename, size);
31 + }
32 + else
33 + netdata_log_error("Cannot write to file '%s' at position %zu.", filename, size);
34 + }
35 + else
36 + netdata_log_error("Cannot seek file '%s' to size %zu.", filename, size);
37 + }
38 + else
39 + netdata_log_error("Cannot create/open file '%s'.", filename);
40 +
41 + return fd;
42 +}
43 +
44 +inline int madvise_sequential(void *mem, size_t len) {
45 + static int logger = 1;
46 + int ret = madvise(mem, len, MADV_SEQUENTIAL);
47 +
48 + if (ret != 0 && logger-- > 0)
49 + netdata_log_error("madvise(MADV_SEQUENTIAL) of size %zu, failed.", len);
50 + return ret;
51 +}
52 +
53 +inline int madvise_random(void *mem, size_t len) {
54 + static int logger = 1;
55 + int ret = madvise(mem, len, MADV_RANDOM);
56 +
57 + if (ret != 0 && logger-- > 0)
58 + netdata_log_error("madvise(MADV_RANDOM) of size %zu, failed.", len);
59 + return ret;
60 +}
61 +
62 +inline int madvise_dontfork(void *mem, size_t len) {
63 + static int logger = 1;
64 + int ret = madvise(mem, len, MADV_DONTFORK);
65 +
66 + if (ret != 0 && logger-- > 0)
67 + netdata_log_error("madvise(MADV_DONTFORK) of size %zu, failed.", len);
68 + return ret;
69 +}
70 +
71 +inline int madvise_willneed(void *mem, size_t len) {
72 + static int logger = 1;
73 + int ret = madvise(mem, len, MADV_WILLNEED);
74 +
75 + if (ret != 0 && logger-- > 0)
76 + netdata_log_error("madvise(MADV_WILLNEED) of size %zu, failed.", len);
77 + return ret;
78 +}
79 +
80 +inline int madvise_dontneed(void *mem, size_t len) {
81 + static int logger = 1;
82 + int ret = madvise(mem, len, MADV_DONTNEED);
83 +
84 + if (ret != 0 && logger-- > 0)
85 + netdata_log_error("madvise(MADV_DONTNEED) of size %zu, failed.", len);
86 + return ret;
87 +}
88 +
89 +inline int madvise_dontdump(void *mem __maybe_unused, size_t len __maybe_unused) {
90 +#if __linux__
91 + static int logger = 1;
92 + int ret = madvise(mem, len, MADV_DONTDUMP);
93 +
94 + if (ret != 0 && logger-- > 0)
95 + netdata_log_error("madvise(MADV_DONTDUMP) of size %zu, failed.", len);
96 + return ret;
97 +#else
98 + return 0;
99 +#endif
100 +}
101 +
102 +inline int madvise_mergeable(void *mem __maybe_unused, size_t len __maybe_unused) {
103 +#ifdef MADV_MERGEABLE
104 + static int logger = 1;
105 + int ret = madvise(mem, len, MADV_MERGEABLE);
106 +
107 + if (ret != 0 && logger-- > 0)
108 + netdata_log_error("madvise(MADV_MERGEABLE) of size %zu, failed.", len);
109 + return ret;
110 +#else
111 + return 0;
112 +#endif
113 +}
114 +
115 +int nd_munmap(void *ptr, size_t size) {
116 +#ifdef NETDATA_TRACE_ALLOCATIONS
117 + malloc_trace_munmap(size);
118 +#endif
119 +
120 + int rc = munmap(ptr, size);
121 +
122 + if(rc == 0) {
123 + __atomic_sub_fetch(&nd_mmap_count, 1, __ATOMIC_RELAXED);
124 + __atomic_sub_fetch(&nd_mmap_size, size, __ATOMIC_RELAXED);
125 + }
126 +
127 + return rc;
128 +}
129 +
130 +void *nd_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) {
131 + void *rc = mmap(addr, len, prot, flags, fd, offset);
132 +
133 + if(rc != MAP_FAILED) {
134 + __atomic_add_fetch(&nd_mmap_count, 1, __ATOMIC_RELAXED);
135 + __atomic_add_fetch(&nd_mmap_size, len, __ATOMIC_RELAXED);
136 +
137 +#ifdef NETDATA_TRACE_ALLOCATIONS
138 + malloc_trace_mmap(size);
139 +#endif
140 + }
141 +
142 + return rc;
143 +}
144 +
145 +void *nd_mmap_advanced(const char *filename, size_t size, int flags, int ksm, bool read_only, bool dont_dump, int *open_fd) {
146 + // netdata_log_info("netdata_mmap('%s', %zu", filename, size);
147 +
148 + // MAP_SHARED is used in memory mode map
149 + // MAP_PRIVATE is used in memory mode ram and save
150 +
151 + if(unlikely(!(flags & MAP_SHARED) && !(flags & MAP_PRIVATE)))
152 + fatal("Neither MAP_SHARED or MAP_PRIVATE were given to nd_mmap_advanced()");
153 +
154 + if(unlikely((flags & MAP_SHARED) && (flags & MAP_PRIVATE)))
155 + fatal("Both MAP_SHARED and MAP_PRIVATE were given to nd_mmap_advanced()");
156 +
157 + if(unlikely((flags & MAP_SHARED) && (!filename || !*filename)))
158 + fatal("MAP_SHARED requested, without a filename to nd_mmap_advanced()");
159 +
160 + // don't enable ksm is the global setting is disabled
161 + if(unlikely(!enable_ksm)) ksm = 0;
162 +
163 + // KSM only merges anonymous (private) pages, never pagecache (file) pages
164 + // but MAP_PRIVATE without MAP_ANONYMOUS it fails too, so we need it always
165 + if((flags & MAP_PRIVATE)) flags |= MAP_ANONYMOUS;
166 +
167 + int fd = -1;
168 + void *mem = MAP_FAILED;
169 +
170 + if(filename && *filename) {
171 + // open/create the file to be used
172 + fd = memory_file_open(filename, size);
173 + if(fd == -1) goto cleanup;
174 + }
175 +
176 + int fd_for_mmap = fd;
177 + if(fd != -1 && (flags & MAP_PRIVATE)) {
178 + // this is MAP_PRIVATE allocation
179 + // no need for mmap() to use our fd
180 + // we will copy the file into the memory allocated
181 + fd_for_mmap = -1;
182 + }
183 +
184 + mem = nd_mmap(NULL, size, read_only ? PROT_READ : PROT_READ | PROT_WRITE, flags, fd_for_mmap, 0);
185 + if (mem != MAP_FAILED) {
186 + // if we have a file open, but we didn't give it to mmap(),
187 + // we have to read the file into the memory block we allocated
188 + if(fd != -1 && fd_for_mmap == -1) {
189 + if (lseek(fd, 0, SEEK_SET) == 0) {
190 + if (read(fd, mem, size) != (ssize_t) size)
191 + netdata_log_info("Cannot read from file '%s'", filename);
192 + }
193 + else netdata_log_info("Cannot seek to beginning of file '%s'.", filename);
194 + }
195 +
196 + // madvise_sequential(mem, size);
197 + // madvise_dontfork(mem, size); // aral is initialized before we daemonize
198 + if(dont_dump) madvise_dontdump(mem, size);
199 + // if(flags & MAP_SHARED) madvise_willneed(mem, size);
200 + if(ksm) madvise_mergeable(mem, size);
201 + }
202 +
203 +cleanup:
204 + if(fd != -1) {
205 + if (open_fd)
206 + *open_fd = fd;
207 + else
208 + close(fd);
209 + }
210 +
211 + if(mem == MAP_FAILED)
212 + return NULL;
213 +
214 + errno_clear();
215 + return mem;
216 +}
src/libnetdata/memory/nd-mmap.h new
+24
@@ -0,0 +1,24 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ND_MMAP_H
4 +#define NETDATA_ND_MMAP_H
5 +
6 +#include "../common.h"
7 +
8 +int madvise_sequential(void *mem, size_t len);
9 +int madvise_random(void *mem, size_t len);
10 +int madvise_dontfork(void *mem, size_t len);
11 +int madvise_willneed(void *mem, size_t len);
12 +int madvise_dontneed(void *mem, size_t len);
13 +int madvise_dontdump(void *mem, size_t len);
14 +int madvise_mergeable(void *mem, size_t len);
15 +
16 +extern size_t nd_mmap_count;
17 +extern size_t nd_mmap_size;
18 +extern int enable_ksm;
19 +
20 +void *nd_mmap_advanced(const char *filename, size_t size, int flags, int ksm, bool read_only, bool dont_dump, int *open_fd);
21 +void *nd_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
22 +int nd_munmap(void *ptr, size_t size);
23 +
24 +#endif //NETDATA_ND_MMAP_H
src/libnetdata/onewayalloc/onewayalloc.c
+3 -23
@@ -1,8 +1,5 @@
1 #include "onewayalloc.h"
2
3 -// https://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
4 -#define OWA_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
5 -
3 typedef struct owa_page {
4 size_t stats_pages;
5 size_t stats_pages_size;
@@ -20,29 +17,12 @@ size_t onewayalloc_allocated_memory(void) {
17 return __atomic_load_n(&onewayalloc_total_memory, __ATOMIC_RELAXED);
18 }
19
23 -// allocations need to be aligned to CPU register width
24 -// https://en.wikipedia.org/wiki/Data_structure_alignment
25 -static inline size_t natural_alignment(size_t size) {
26 - if(unlikely(size % OWA_NATURAL_ALIGNMENT))
27 - size = size + OWA_NATURAL_ALIGNMENT - (size % OWA_NATURAL_ALIGNMENT);
28 -
29 - return size;
30 -}
31 -
20 // Create an OWA
21 // Once it is created, the caller may call the onewayalloc_mallocz()
22 // any number of times, for any amount of memory.
23
24 static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
37 - static size_t OWA_NATURAL_PAGE_SIZE = 0;
38 -
39 - if(unlikely(!OWA_NATURAL_PAGE_SIZE)) {
40 - long int page_size = sysconf(_SC_PAGE_SIZE);
41 - if (unlikely(page_size == -1))
42 - OWA_NATURAL_PAGE_SIZE = 4096;
43 - else
44 - OWA_NATURAL_PAGE_SIZE = page_size;
45 - }
25 + size_t OWA_NATURAL_PAGE_SIZE = os_get_system_page_size();
26
27 // our default page size
28 size_t size = 32768;
@@ -73,7 +53,7 @@ static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
53 size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
54
55 // Use netdata_mmap instead of mallocz
76 - OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0, false, false, NULL);
56 + OWA_PAGE *page = (OWA_PAGE *)nd_mmap_advanced(NULL, size, MAP_ANONYMOUS | MAP_PRIVATE, 0, false, false, NULL);
57 if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
58
59 __atomic_add_fetch(&onewayalloc_total_memory, size, __ATOMIC_RELAXED);
@@ -216,7 +196,7 @@ void onewayalloc_destroy(ONEWAYALLOC *owa) {
196 page = page->next;
197
198 // Use netdata_munmap instead of freez
219 - netdata_munmap(p, p->size);
199 + nd_munmap(p, p->size);
200 }
201
202 __atomic_sub_fetch(&onewayalloc_total_memory, total_size, __ATOMIC_RELAXED);
src/libnetdata/os/get_system_pagesize.c new
+36
@@ -0,0 +1,36 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "get_system_pagesize.h"
4 +
5 +#if defined(OS_WINDOWS)
6 +#include <windows.h>
7 +
8 +size_t os_get_system_page_size(void) {
9 + static size_t page_size = 0;
10 +
11 + if (!page_size) {
12 + SYSTEM_INFO sys_info;
13 + GetSystemInfo(&sys_info);
14 +
15 + // Set the page size, default to 4096 if for some reason it's invalid
16 + page_size = (sys_info.dwPageSize >= 4096) ? sys_info.dwPageSize : 4096;
17 + }
18 +
19 + return page_size;
20 +}
21 +
22 +#else
23 +
24 +size_t os_get_system_page_size(void) {
25 + static long int page_size = 0;
26 +
27 + if(unlikely(!page_size)) {
28 + page_size = sysconf(_SC_PAGE_SIZE);
29 + if (unlikely(page_size <= 4096))
30 + page_size = 4096;
31 + }
32 +
33 + return page_size;
34 +}
35 +
36 +#endif
\ No newline at end of file
src/libnetdata/os/get_system_pagesize.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_GET_SYSTEM_PAGESIZE_H
4 +#define NETDATA_GET_SYSTEM_PAGESIZE_H
5 +
6 +#include "../common.h"
7 +
8 +size_t os_get_system_page_size(void) __attribute__((const));
9 +
10 +#endif //NETDATA_GET_SYSTEM_PAGESIZE_H
src/libnetdata/os/os.h
+1
@@ -19,6 +19,7 @@
19 #include "gettid.h"
20 #include "get_pid_max.h"
21 #include "get_system_cpus.h"
22 +#include "get_system_pagesize.h"
23 #include "sleep.h"
24 #include "uuid_generate.h"
25 #include "setenv.h"