@cryptotaxi247 / netdata-1 / commits / cfd98f4f9

Fix memory leaks (#20062)

* feat(dbengine): add ARAL memory accounting for EPDL_EXTENT structures Fixes memory leaks detected by address and leak sanitizers in the database engine's EPDL_EXTENT structures. - Create an ARAL for EPDL_EXTENT with init/get/release functions in pdc.c - Add the structure to the global initialization in rrdengine.c - Add a common cleanup function for both datafile deletion and finalization - Add RRDENG_MEM_EPDL_EXTENT to the enum in rrdengineapi.h - Integrate with Pulse for proper memory monitoring in netdata.dbengine_buffers This improves memory management by properly tracking and accounting for all EPDL_EXTENT allocations, ensuring consistent cleanup in all code paths. fix(dbengine): properly free EPDL_EXTENT structures during shutdown This change ensures proper cleanup of EPDL_EXTENT structures during database shutdown when FSANITIZE_ADDRESS is enabled. Although this wasn't causing actual memory leaks during normal operation, it was reported as leaks by the address and leak sanitizers. The fix extracts the EPDL_EXTENT cleanup code into a common function that's called from both datafile deletion and finalization paths, ensuring consistent memory management across all shutdown scenarios. * Fix memory leak in health alarm entries with ARAL-based management This commit addresses a memory leak in health alarm entries by: 1. Implementing ARAL memory management for ALARM_ENTRY structures - Added ARAL initialization in health_plugin_init() - Created health_alarm_entry_get() and health_alarm_entry_release() functions - Modified health_create_alarm_entry() to use ARAL allocation 2. Adding automatic cleanup of old alarm entries based on retention settings - Implemented health_alarm_log_cleanup() to remove old in-memory entries - Integrated with sql_health_alarm_log_cleanup() to ensure both database and memory entries are cleaned up together 3. Adding Pulse monitoring for health alarm entries - Added health log memory tracking to netdata.memory chart This fix ensures that alarm entries are properly tracked and freed when they become older than the configured retention period. * Fix memory leak in worker spinlock contention tracking This commit fixes a memory leak in the pulse worker monitoring system where memory allocated for spinlock contention tracking wasn't properly freed. The leak occurred in worker_utilization_charts_callback where spinlock structures were allocated with callocz() but never freed when the thread exited. Added a proper cleanup mechanism in pulse_workers_cleanup() with a helper function that: 1. Iterates through each spinlock structure in the Judy arrays 2. Frees individual spinlock entries via callback 3. Properly cleans up both per-worker and global spinlock collections This ensures all memory is properly freed when the pulse thread terminates. * Fix memory leak in health alert prototypes pattern matching This commit addresses a memory leak in the health alerting system where pattern arrays containing SIMPLE_PATTERN structures were not being properly freed when the health plugin was destroyed. The memory leak occurred when these structures were created during the health_plugin_init() function but never cleaned up. The fix implements proper cleanup in health_plugin_destroy() by: 1. Destroying the health_globals.prototypes.dict dictionary 2. Setting the initialization state to false 3. Ensuring proper thread safety with spinlocks This addresses the 1248 bytes memory leak in 39 objects allocated during pattern matching for health alert prototypes. * add basic aral accounting under FSANITIZE_ADDRESS * cleanup popen_instance from alarm entry and freeing it * minor cleanup to ensure memory is zeroed * rework the destructor of pattern-arrays and split them to a separate file * increase the vendor name field * do not match computer when searching for compute * fix rrdlabels memory accounting * make remove_this_page_from_index_unsafe() log pointers in hex * the shutdown watcher should wait any amount of time with FSANITIZE_ADDRESS * use void pointers when printing cache indexes * remove no_status from fatal function * make alarm entries use double linked list * use double linked list for alarm entries in progress * assertion when trying add in progress an alert twice * fix sensors message * set journal file unmount inactivity to 10 minutes * prevent the dbengine unittest from exiting prematurely

Costa Tsaousis committed Apr 5, 2025 at 22:51 UTC cfd98f4f93f0dc280aeab3e581431362c9f01617
35 files changed +508 -331
CMakeLists.txt
+2
@@ -1650,6 +1650,8 @@ set(RRD_PLUGIN_FILES
1650 src/database/rrdhost-labels.h
1651 src/database/rrdhost-collection.c
1652 src/database/rrdhost-collection.h
1653 + src/database/pattern-array.c
1654 + src/database/pattern-array.h
1655 )
1656
1657 if(ENABLE_DBENGINE)
src/collectors/debugfs.plugin/module-libsensors.c
+6
@@ -1256,6 +1256,12 @@ void *libsensors_thread(void *ptr __maybe_unused) {
1256 usec_t ended_ut = now_monotonic_usec();
1257 usec_t average_ut = (ended_ut - started_ut) / samples;
1258
1259 + if(average_ut < 1)
1260 + average_ut = 1;
1261 +
1262 + if(max_ut < 1)
1263 + max_ut = 1;
1264 +
1265 // List of valid intervals in seconds (divisors and multiples of 60)
1266 static const int valid_update_every_intervals[] = {1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60, 120, 180, 240, 300, 600, 900, 1200, 1800, 3600};
1267
src/daemon/config/netdata-conf-profile.c
+1 -1
@@ -123,7 +123,7 @@ void nd_profile_setup(void) {
123 nd_profile.malloc_arenas = 4;
124 nd_profile.malloc_trim = 128 * 1024;
125 nd_profile.stream_sender_compression = ND_COMPRESSION_FASTEST;
126 - nd_profile.dbengine_journal_v2_unmount_time = 0;
126 + nd_profile.dbengine_journal_v2_unmount_time = 600;
127 nd_profile.max_page_size = 2 * 1024 * 1024; // 2MB for THP
128 nd_profile.ml_enabled = CONFIG_BOOLEAN_AUTO;
129 // web server threads = dynamic
src/daemon/daemon-shutdown-watcher.c
+5
@@ -70,7 +70,12 @@ static void watcher_wait_for_step(const watcher_step_id_t step_id, usec_t shutdo
70 if(remaining_seconds < 0)
71 remaining_seconds = 0;
72
73 +#if defined(FSANITIZE_ADDRESS)
74 + completion_wait_for(&watcher_steps[step_id].p);
75 + bool ok = true;
76 +#else
77 bool ok = completion_timedwait_for(&watcher_steps[step_id].p, remaining_seconds);
78 +#endif
79
80 usec_t step_duration = now_monotonic_usec() - step_start_time;
81
src/daemon/daemon-shutdown.c
+9 -1
@@ -360,7 +360,15 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal, bool exi
360 if(metrics_referenced)
361 fprintf(stderr, "WARNING: MRG had %zu metrics referenced.\n",
362 metrics_referenced);
363 -#endif
363 +
364 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
365 + if(multidb_ctx[tier]) {
366 + fprintf(stderr, "Finalizing data files for tier %zu...\n", tier);
367 + finalize_rrd_files(multidb_ctx[tier]);
368 + memset(multidb_ctx[tier], 0, sizeof(*multidb_ctx[tier]));
369 + }
370 + }
371 +#endif
372
373 fprintf(stderr, "Destroying UUIDMap...\n");
374 size_t uuid_referenced = uuidmap_destroy();
src/daemon/pulse/pulse-daemon-memory.c
+9
@@ -3,6 +3,7 @@
3 #define PULSE_INTERNALS 1
4 #include "pulse-daemon-memory.h"
5 #include "streaming/stream-replication-sender.h"
6 +#include "health/health.h"
7
8 static size_t rrd_slot_memory = 0;
9
@@ -49,6 +50,7 @@ void pulse_daemon_memory_do(bool extended __maybe_unused) {
50 static RRDDIM *rd_aral = NULL;
51 static RRDDIM *rd_judy = NULL;
52 static RRDDIM *rd_slots = NULL;
53 + static RRDDIM *rd_health_log = NULL;
54 static RRDDIM *rd_other = NULL;
55
56 if (unlikely(!st_memory)) {
@@ -93,6 +95,7 @@ void pulse_daemon_memory_do(bool extended __maybe_unused) {
95 rd_judy = rrddim_add(st_memory, "judy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
96 rd_slots = rrddim_add(st_memory, "slots", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
97 rd_other = rrddim_add(st_memory, "other", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
98 + rd_health_log = rrddim_add(st_memory, "health log", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
99 }
100
101 // each of these should also be analyzed below at the buffers chart
@@ -118,6 +121,8 @@ void pulse_daemon_memory_do(bool extended __maybe_unused) {
121
122 size_t strings_memory = 0, strings_index = 0;
123 string_statistics(NULL, NULL, NULL, NULL, NULL, &strings_memory, &strings_index, NULL, NULL);
124 +
125 + size_t health_log_memory = aral_used_bytes_from_stats(health_alarm_entry_aral_stats());
126
127 rrddim_set_by_pointer(st_memory, rd_db_dbengine, (collected_number)pulse_dbengine_total_memory);
128 rrddim_set_by_pointer(st_memory, rd_db_rrd, (collected_number)pulse_rrd_memory_size);
@@ -194,6 +199,10 @@ void pulse_daemon_memory_do(bool extended __maybe_unused) {
199 rrddim_set_by_pointer(st_memory, rd_slots,
200 (collected_number)__atomic_load_n(&rrd_slot_memory, __ATOMIC_RELAXED));
201
202 +
203 + rrddim_set_by_pointer(st_memory, rd_health_log,
204 + (collected_number)health_log_memory);
205 +
206 rrddim_set_by_pointer(st_memory, rd_other,
207 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_other));
208
src/daemon/pulse/pulse-db-dbengine.c
+3
@@ -752,6 +752,7 @@ void pulse_dbengine_do(bool extended) {
752 static RRDDIM *rd_pgc_buffers_epdl = NULL;
753 static RRDDIM *rd_pgc_buffers_deol = NULL;
754 static RRDDIM *rd_pgc_buffers_pd = NULL;
755 + static RRDDIM *rd_pgc_buffers_epdl_extent = NULL;
756
757 if (unlikely(!st_pgc_buffers)) {
758 st_pgc_buffers = rrdset_create_localhost(
@@ -782,6 +783,7 @@ void pulse_dbengine_do(bool extended) {
783 rd_pgc_buffers_xt_buf = rrddim_add(st_pgc_buffers, "extent buffers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
784 rd_pgc_buffers_epdl = rrddim_add(st_pgc_buffers, "epdl", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
785 rd_pgc_buffers_deol = rrddim_add(st_pgc_buffers, "deol", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
786 + rd_pgc_buffers_epdl_extent = rrddim_add(st_pgc_buffers, "epdl extent", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
787 }
788 priority++;
789
@@ -799,6 +801,7 @@ void pulse_dbengine_do(bool extended) {
801 rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_xt_buf, (collected_number)dbmem.xt_buf);
802 rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_epdl, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_EPDL]));
803 rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_deol, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_DEOL]));
804 + rrddim_set_by_pointer(st_pgc_buffers, rd_pgc_buffers_epdl_extent, (collected_number)aral_free_bytes_from_stats(dbmem.as[RRDENG_MEM_EPDL_EXTENT]));
805
806 rrdset_done(st_pgc_buffers);
807 }
src/daemon/pulse/pulse-workers.c
+10
@@ -1117,6 +1117,10 @@ static void worker_utilization_charts_callback(void *ptr
1117 wu->memory_calls[i] += memory_calls[i];
1118 }
1119
1120 +static void spinlocks_free_callback(Word_t key __maybe_unused, struct worker_spinlocks *wusp, void *data __maybe_unused) {
1121 + freez(wusp);
1122 +}
1123 +
1124 void pulse_workers_cleanup(void) {
1125 int i, j;
1126 for(i = 0; all_workers_utilization[i].name ;i++) {
@@ -1135,6 +1139,9 @@ void pulse_workers_cleanup(void) {
1139 wu->per_job_type[j].units = NULL;
1140 }
1141
1142 + // Free the spinlocks Judy array for this worker
1143 + SPINLOCKS_FREE(&wu->spinlocks, spinlocks_free_callback, NULL);
1144 +
1145 // mark all threads as not enabled
1146 struct worker_thread *t;
1147 for(t = wu->threads; t ; t = t->next)
@@ -1143,6 +1150,9 @@ void pulse_workers_cleanup(void) {
1150 // let the cleanup job free them
1151 workers_threads_cleanup(wu);
1152 }
1153 +
1154 + // Clean up the global spinlocks array
1155 + SPINLOCKS_FREE(&ALL_SPINLOCKS, spinlocks_free_callback, NULL);
1156 }
1157
1158 void pulse_workers_do(bool extended) {
src/daemon/status-file-product.c
+6 -2
@@ -57,6 +57,7 @@ static void dmi_normalize_vendor_field(char *buf, size_t buf_size) {
57 {"DELL", "Dell"},
58 {"Dell Computer Corporation", "Dell"},
59 {"Dell Inc.", "Dell"},
60 + {"Dell EMC", "Dell"},
61
62 {"FUJITSU", "Fujitsu"},
63 {"FUJITSU CLIENT COMPUTING LIMITED", "Fujitsu"},
@@ -87,6 +88,8 @@ static void dmi_normalize_vendor_field(char *buf, size_t buf_size) {
88
89 {"IBM Corp.", "IBM"},
90
91 + {"IceWhale Technology Co.,Ltd.", "IceWhale"},
92 +
93 {"INSYDE", "Insyde"},
94 {"INSYDE Corp.", "Insyde"},
95 {"Insyde Corp.", "Insyde"},
@@ -174,7 +177,8 @@ static bool dmi_is_virtual_machine(const DMI_INFO *dmi) {
177 if(!dmi) return false;
178
179 const char *vm_indicators[] = {
177 - "Virt", "KVM", "vServer", "Cloud", "Hyper", "Droplet", "Compute",
180 + "Virt", "KVM", "vServer", "Cloud", "Hyper", "Droplet",
181 + "Compute ", // with a space to not match "Computer"
182 "HVM domU", "Parallels", "(i440FX", "(q35", "OpenStack", "QEMU",
183 "VMWare", "DigitalOcean", "Oracle", "Linode", "Amazon EC2"
184 };
@@ -289,7 +293,7 @@ void product_name_vendor_type(DAEMON_STATUS_FILE *ds) {
293 else if(strcasestr(ds->hw.product.name, "NVIDIA") != NULL &&
294 strcasestr(ds->hw.product.name, "Kit") != NULL) {
295 safecpy(ds->product.vendor, "NVIDIA");
292 - force_type = "vm";
296 + force_type = "mini-pc";
297 }
298 else if(strcasestr(ds->hw.product.name, "Raspberry") != NULL) {
299 safecpy(ds->product.vendor, "Raspberry");
src/daemon/status-file.c
-1
@@ -1177,7 +1177,6 @@ void daemon_status_file_check_crash(void) {
1177 last_session_status = session_status;
1178 last_session_status.status = DAEMON_STATUS_NONE;
1179 last_session_status.exit_reason = 0;
1180 - safecpy(last_session_status.fatal.function, "no_status");
1180 }
1181
1182 struct post_status_file_thread_data d = {
src/daemon/status-file.h
+1 -1
@@ -93,7 +93,7 @@ typedef struct daemon_status_file {
93
94 struct {
95 // normalized information from cloud provider and h/w information
96 - char vendor[32];
96 + char vendor[64];
97 char name[96];
98 char type[16];
99 } product;
src/database/engine/cache.c
+12 -12
@@ -1004,37 +1004,37 @@ static void remove_this_page_from_index_unsafe(PGC *cache, PGC_PAGE *page, size_
1004
1005 Pvoid_t *metrics_judy_pptr = JudyLGet(cache->index[partition].sections_judy, page->section, PJE0);
1006 if(unlikely(!metrics_judy_pptr))
1007 - fatal("DBENGINE CACHE: section '%lu' should exist, but it does not.", page->section);
1007 + fatal("DBENGINE CACHE: section '%p' should exist, but it does not.", (void *)page->section);
1008
1009 Pvoid_t *pages_judy_pptr = JudyLGet(*metrics_judy_pptr, page->metric_id, PJE0);
1010 if(unlikely(!pages_judy_pptr))
1011 - fatal("DBENGINE CACHE: metric '%lu' in section '%lu' should exist, but it does not.",
1012 - page->metric_id, page->section);
1011 + fatal("DBENGINE CACHE: metric '%p' in section '%p' should exist, but it does not.",
1012 + (void *)page->metric_id, (void *)page->section);
1013
1014 Pvoid_t *page_ptr = JudyLGet(*pages_judy_pptr, page->start_time_s, PJE0);
1015 if(unlikely(!page_ptr))
1016 - fatal("DBENGINE CACHE: page with start time '%ld' of metric '%lu' in section '%lu' should exist, but it does not.",
1017 - page->start_time_s, page->metric_id, page->section);
1016 + fatal("DBENGINE CACHE: page with start time '%ld' of metric '%p' in section '%p' should exist, but it does not.",
1017 + page->start_time_s, (void *)page->metric_id, (void *)page->section);
1018
1019 PGC_PAGE *found_page = *page_ptr;
1020 if(unlikely(found_page != page))
1021 - fatal("DBENGINE CACHE: page with start time '%ld' of metric '%lu' in section '%lu' should exist, "
1021 + fatal("DBENGINE CACHE: page with start time '%ld' of metric '%p' in section '%p' should exist, "
1022 "but the index returned a different address (expected %p, got %p).",
1023 - page->start_time_s, page->metric_id, page->section,
1023 + page->start_time_s, (void *)page->metric_id, (void *)page->section,
1024 page, found_page);
1025
1026 JudyAllocThreadPulseReset();
1027
1028 if(unlikely(!JudyLDel(pages_judy_pptr, page->start_time_s, PJE0)))
1029 - fatal("DBENGINE CACHE: page with start time '%ld' of metric '%lu' in section '%lu' exists, but cannot be deleted.",
1030 - page->start_time_s, page->metric_id, page->section);
1029 + fatal("DBENGINE CACHE: page with start time '%ld' of metric '%p' in section '%p' exists, but cannot be deleted.",
1030 + page->start_time_s, (void *)page->metric_id, (void *)page->section);
1031
1032 if(!*pages_judy_pptr && !JudyLDel(metrics_judy_pptr, page->metric_id, PJE0))
1033 - fatal("DBENGINE CACHE: metric '%lu' in section '%lu' exists and is empty, but cannot be deleted.",
1034 - page->metric_id, page->section);
1033 + fatal("DBENGINE CACHE: metric '%p' in section '%p' exists and is empty, but cannot be deleted.",
1034 + (void *)page->metric_id, (void *)page->section);
1035
1036 if(!*metrics_judy_pptr && !JudyLDel(&cache->index[partition].sections_judy, page->section, PJE0))
1037 - fatal("DBENGINE CACHE: section '%lu' exists and is empty, but cannot be deleted.", page->section);
1037 + fatal("DBENGINE CACHE: section '%p' exists and is empty, but cannot be deleted.", (void *)page->section);
1038
1039 pgc_stats_index_judy_change(cache, JudyAllocThreadPulseGetAndReset());
1040
src/database/engine/datafile.c
+19
@@ -549,6 +549,22 @@ int init_data_files(struct rrdengine_instance *ctx)
549 return 0;
550 }
551
552 +void cleanup_datafile_epdl_structures(struct rrdengine_datafile *datafile)
553 +{
554 + rw_spinlock_write_lock(&datafile->extent_epdl.spinlock);
555 + bool first = true;
556 + Word_t idx = 0;
557 + Pvoid_t *PValue;
558 + while ((PValue = JudyLFirstThenNext(datafile->extent_epdl.epdl_per_extent, &idx, &first))) {
559 + EPDL_EXTENT *e = *PValue;
560 + internal_error(e->base, "DBENGINE: unexpected active EPDLs during datafile cleanup");
561 + epdl_extent_release(e);
562 + *PValue = NULL;
563 + }
564 + JudyLFreeArray(&datafile->extent_epdl.epdl_per_extent, PJE0);
565 + rw_spinlock_write_unlock(&datafile->extent_epdl.spinlock);
566 +}
567 +
568 void finalize_data_files(struct rrdengine_instance *ctx)
569 {
570 bool logged = false;
@@ -602,6 +618,9 @@ void finalize_data_files(struct rrdengine_instance *ctx)
618 spinlock_unlock(&datafile->writers.spinlock);
619 uv_rwlock_wrunlock(&ctx->datafiles.rwlock);
620
621 + // Clean up EPDL_EXTENT structures
622 + cleanup_datafile_epdl_structures(datafile);
623 +
624 memset(journalfile, 0, sizeof(*journalfile));
625 memset(datafile, 0, sizeof(*datafile));
626
src/database/engine/datafile.h
+2
@@ -43,6 +43,7 @@ typedef struct {
43 SPINLOCK spinlock;
44 struct extent_page_details_list *base;
45 } EPDL_EXTENT;
46 +void epdl_extent_release(EPDL_EXTENT *e);
47
48 #define DATAFILE_MAGIC 0xDA7AF11E
49
@@ -102,6 +103,7 @@ int create_data_file(struct rrdengine_datafile *datafile);
103 int create_new_datafile_pair(struct rrdengine_instance *ctx, bool having_lock);
104 int init_data_files(struct rrdengine_instance *ctx);
105 void finalize_data_files(struct rrdengine_instance *ctx);
106 +void cleanup_datafile_epdl_structures(struct rrdengine_datafile *datafile);
107
108 NEVERNULL ALWAYS_INLINE
109 static struct rrdengine_instance *datafile_ctx(struct rrdengine_datafile *datafile) {
src/database/engine/dbengine-unittest.c
+3
@@ -407,6 +407,9 @@ int test_dbengine(void) {
407 errors += dbengine_test_rrdr_single_region(st, rd, current_region, time_start[current_region], time_end[current_region]);
408 }
409
410 + // prevent closing the database before the test is finished
411 + sleep(5);
412 +
413 rrd_wrlock();
414 rrdeng_quiesce((struct rrdengine_instance *)host->db[0].si, false);
415 rrdeng_exit((struct rrdengine_instance *)host->db[0].si);
src/database/engine/pdc.c
+36 -2
@@ -46,6 +46,10 @@ static struct {
46 struct {
47 ARAL *ar;
48 } deol;
49 +
50 + struct {
51 + ARAL *ar;
52 + } epdl_extent;
53 } pdc_globals = {};
54
55 void pdc_init(void) {
@@ -163,6 +167,36 @@ struct aral_statistics *deol_aral_stats(void) {
167 return aral_get_statistics(pdc_globals.deol.ar);
168 }
169
170 +// ----------------------------------------------------------------------------
171 +// epdl_extent cache
172 +
173 +void epdl_extent_init(void) {
174 + pdc_globals.epdl_extent.ar = aral_create(
175 + "dbengine-epdl-extent",
176 + sizeof(EPDL_EXTENT),
177 + 0,
178 + 0,
179 + NULL,
180 + NULL, NULL, false, false, true
181 + );
182 +
183 + pulse_aral_register(pdc_globals.epdl_extent.ar, "epdl_extent");
184 +}
185 +
186 +static ALWAYS_INLINE EPDL_EXTENT *epdl_extent_get(void) {
187 + EPDL_EXTENT *e = aral_mallocz(pdc_globals.epdl_extent.ar);
188 + memset(e, 0, sizeof(EPDL_EXTENT));
189 + return e;
190 +}
191 +
192 +ALWAYS_INLINE void epdl_extent_release(EPDL_EXTENT *e) {
193 + aral_freez(pdc_globals.epdl_extent.ar, e);
194 +}
195 +
196 +struct aral_statistics *epdl_extent_aral_stats(void) {
197 + return aral_get_statistics(pdc_globals.epdl_extent.ar);
198 +}
199 +
200 // ----------------------------------------------------------------------------
201 // extent with buffer cache
202
@@ -471,7 +505,7 @@ static ALWAYS_INLINE EPDL_EXTENT *epdl_find_extent_base(EPDL *epdl) {
505
506 if(!e) {
507 EPDL_EXTENT *e_to_free = NULL;
474 - e = callocz(1, sizeof(*e));
508 + e = epdl_extent_get();
509
510 rw_spinlock_write_lock(&epdl->datafile->extent_epdl.spinlock);
511 PValue = JudyLIns(&epdl->datafile->extent_epdl.epdl_per_extent, epdl->extent_offset, PJE0);
@@ -486,7 +520,7 @@ static ALWAYS_INLINE EPDL_EXTENT *epdl_find_extent_base(EPDL *epdl) {
520 }
521 rw_spinlock_write_unlock(&epdl->datafile->extent_epdl.spinlock);
522
489 - freez(e_to_free);
523 + epdl_extent_release(e_to_free);
524 }
525
526 return e;
src/database/engine/pdc.h
+2
@@ -26,6 +26,7 @@ struct aral_statistics *pdc_aral_stats(void);
26 struct aral_statistics *pd_aral_stats(void);
27 struct aral_statistics *epdl_aral_stats(void);
28 struct aral_statistics *deol_aral_stats(void);
29 +struct aral_statistics *epdl_extent_aral_stats(void);
30
31 size_t extent_buffer_cache_size(void);
32
@@ -33,6 +34,7 @@ void pdc_init(void);
34 void page_details_init(void);
35 void epdl_init(void);
36 void deol_init(void);
37 +void epdl_extent_init(void);
38 void extent_buffer_cleanup1(void);
39
40 void epdl_cmd_dequeued(void *epdl_ptr);
src/database/engine/rrdengine.c
+3 -14
@@ -1278,20 +1278,7 @@ void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *
1278 deleted_bytes += datafile_bytes;
1279 }
1280
1281 - {
1282 - rw_spinlock_write_lock(&datafile->extent_epdl.spinlock);
1283 - bool first = true;
1284 - Word_t idx = 0;
1285 - Pvoid_t *PValue;
1286 - while ((PValue = JudyLFirstThenNext(datafile->extent_epdl.epdl_per_extent, &idx, &first))) {
1287 - EPDL_EXTENT *e = *PValue;
1288 - internal_fatal(e->base, "The should not be any EPDLs ");
1289 - freez(e);
1290 - *PValue = NULL;
1291 - }
1292 - JudyLFreeArray(&datafile->extent_epdl.epdl_per_extent, PJE0);
1293 - rw_spinlock_write_unlock(&datafile->extent_epdl.spinlock);
1294 - }
1281 + cleanup_datafile_epdl_structures(datafile);
1282
1283 memset(journal_file, 0, sizeof(*journal_file));
1284 memset(datafile, 0, sizeof(*datafile));
@@ -1622,6 +1609,7 @@ struct rrdeng_buffer_sizes rrdeng_pulse_memory_sizes(void) {
1609 [RRDENG_MEM_EPDL] = epdl_aral_stats(),
1610 [RRDENG_MEM_DEOL] = deol_aral_stats(),
1611 [RRDENG_MEM_PD] = pd_aral_stats(),
1612 + [RRDENG_MEM_EPDL_EXTENT] = epdl_extent_aral_stats(),
1613 [RRDENG_MEM_OPCODES] = aral_get_statistics(rrdeng_main.cmd_queue.ar),
1614 [RRDENG_MEM_HANDLES] = aral_get_statistics(rrdeng_main.handles.ar),
1615 [RRDENG_MEM_DESCRIPTORS] = aral_get_statistics(rrdeng_main.descriptors.ar),
@@ -1765,6 +1753,7 @@ static void dbengine_initialize_structures(void) {
1753 page_details_init();
1754 epdl_init();
1755 deol_init();
1756 + epdl_extent_init();
1757 rrdeng_cmd_queue_init();
1758 work_request_init();
1759 rrdeng_query_handle_init();
src/database/engine/rrdengineapi.c
+1 -4
@@ -1282,10 +1282,7 @@ int rrdeng_exit(struct rrdengine_instance *ctx) {
1282 completion_wait_for(&completion);
1283 completion_destroy(&completion);
1284
1285 - // No need to release the datafiles list
1286 - //finalize_rrd_files(ctx);
1287 -
1288 - if (unittest_running) //(ctx->config.unittest)
1285 + if(unittest_running)
1286 freez(ctx);
1287
1288 rrd_stat_atomic_add(&global_stats.rrdeng_reserved_file_descriptors, -RRDENG_FD_BUDGET_PER_INSTANCE);
src/database/engine/rrdengineapi.h
+1
@@ -233,6 +233,7 @@ typedef enum rrdeng_mem {
233 RRDENG_MEM_EPDL,
234 RRDENG_MEM_DEOL,
235 RRDENG_MEM_PD,
236 + RRDENG_MEM_EPDL_EXTENT,
237
238 // terminator
239 RRDENG_MEM_MAX,
src/database/pattern-array.c new
+154
@@ -0,0 +1,154 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "pattern-array.h"
4 +
5 +struct pattern_array *pattern_array_allocate()
6 +{
7 + struct pattern_array *pa = callocz(1, sizeof(*pa));
8 + return pa;
9 +}
10 +
11 +void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp)
12 +{
13 + if (!pa || !key || !sp)
14 + return;
15 +
16 + STRING *string_key = string_strdupz(key);
17 + Pvoid_t *Pvalue = JudyLIns(&pa->JudyL, (Word_t) string_key, PJE0);
18 + if (!Pvalue) {
19 + string_freez(string_key);
20 + simple_pattern_free(sp);
21 + return;
22 + }
23 +
24 + struct pattern_array_item *pai;
25 + if (*Pvalue) {
26 + pai = *Pvalue;
27 + } else {
28 + *Pvalue = pai = callocz(1, sizeof(*pai));
29 + pa->key_count++;
30 + }
31 +
32 + pai->size++;
33 + Pvalue = JudyLIns(&pai->JudyL, (Word_t) pai->size, PJE0);
34 + if (!Pvalue) {
35 + simple_pattern_free(sp);
36 + return;
37 + }
38 +
39 + *Pvalue = sp;
40 +}
41 +
42 +bool pattern_array_label_match(
43 + struct pattern_array *pa,
44 + RRDLABELS *labels,
45 + char eq,
46 + size_t *searches)
47 +{
48 + if (!pa || !labels)
49 + return true;
50 +
51 + Pvoid_t *Pvalue;
52 + Word_t Index = 0;
53 + bool first_then_next = true;
54 + while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) {
55 + // for each label key in the patterns array
56 +
57 + struct pattern_array_item *pai = *Pvalue;
58 + SIMPLE_PATTERN_RESULT match = SP_NOT_MATCHED ;
59 + for (Word_t i = 1; i <= pai->size; i++) {
60 + // for each pattern in the label key pattern list
61 +
62 + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)) || !*Pvalue)
63 + continue;
64 +
65 + match = rrdlabels_match_simple_pattern_parsed(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches);
66 +
67 + if(match != SP_NOT_MATCHED)
68 + break;
69 + }
70 +
71 + if (match != SP_MATCHED_POSITIVE)
72 + return false;
73 + }
74 + return true;
75 +}
76 +
77 +struct pattern_array *pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern)
78 +{
79 + if (unlikely(!pattern || !key))
80 + return pa;
81 +
82 + if (!pa)
83 + pa = pattern_array_allocate();
84 +
85 + pattern_array_add_lblkey_with_sp(pa, key, pattern);
86 + return pa;
87 +}
88 +
89 +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep)
90 +{
91 + if (unlikely(!pattern))
92 + return pa;
93 +
94 + if (!pa)
95 + pa = pattern_array_allocate();
96 +
97 + char *label_key;
98 + while (pattern && (label_key = simple_pattern_iterate(&pattern))) {
99 + char key[RRDLABELS_MAX_NAME_LENGTH + 1], *key_sep;
100 +
101 + if (unlikely(!label_key || !(key_sep = strchr(label_key, sep))))
102 + return pa;
103 +
104 + *key_sep = '\0';
105 + strncpyz(key, label_key, RRDLABELS_MAX_NAME_LENGTH);
106 + *key_sep = sep;
107 +
108 + pattern_array_add_lblkey_with_sp(pa, key, string_to_simple_pattern(label_key));
109 + }
110 + return pa;
111 +}
112 +
113 +struct pattern_array *pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep)
114 +{
115 + if (unlikely(!key || !value))
116 + return pa;
117 +
118 + if (!pa)
119 + pa = pattern_array_allocate();
120 +
121 + char label_key[RRDLABELS_MAX_NAME_LENGTH + RRDLABELS_MAX_VALUE_LENGTH + 2];
122 + snprintfz(label_key, sizeof(label_key) - 1, "%s%c%s", key, sep, value);
123 + pattern_array_add_lblkey_with_sp(
124 + pa, key, simple_pattern_create(label_key, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true));
125 + return pa;
126 +}
127 +
128 +void pattern_array_free(struct pattern_array *pa)
129 +{
130 + if (!pa)
131 + return;
132 +
133 + Pvoid_t *Pvalue;
134 + Word_t Index = 0;
135 + bool first = true;
136 + while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first))) {
137 + struct pattern_array_item *pai = *Pvalue;
138 +
139 + Word_t Index2 = 0;
140 + bool first2 = true;
141 + while ((Pvalue = JudyLFirstThenNext(pai->JudyL, &Index2, &first2))) {
142 + SIMPLE_PATTERN *sp = (SIMPLE_PATTERN *)*Pvalue;
143 + simple_pattern_free(sp);
144 + }
145 +
146 + JudyLFreeArray(&(pai->JudyL), PJE0);
147 + string_freez((STRING *)Index);
148 + freez(pai);
149 + }
150 +
151 + JudyLFreeArray(&(pa->JudyL), PJE0);
152 + freez(pa);
153 +}
154 +
src/database/pattern-array.h new
+32
@@ -0,0 +1,32 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PATTERN_ARRAY_H
4 +#define NETDATA_PATTERN_ARRAY_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +#include "rrdlabels.h"
8 +
9 +struct pattern_array_item {
10 + Word_t size;
11 + Pvoid_t JudyL;
12 +};
13 +
14 +struct pattern_array {
15 + Word_t key_count;
16 + Pvoid_t JudyL;
17 +};
18 +
19 +struct pattern_array *pattern_array_allocate();
20 +struct pattern_array *
21 +pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep);
22 +bool pattern_array_label_match(
23 + struct pattern_array *pa,
24 + RRDLABELS *labels,
25 + char eq,
26 + size_t *searches);
27 +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep);
28 +struct pattern_array *
29 +pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern);
30 +void pattern_array_free(struct pattern_array *pa);
31 +
32 +#endif //NETDATA_PATTERN_ARRAY_H
src/database/rrd.h
+1
@@ -105,6 +105,7 @@ static inline uint32_t get_uint32_id() {
105 #include "health/rrdvar.h"
106 #include "health/rrdcalc.h"
107 #include "rrdlabels.h"
108 +#include "pattern-array.h"
109 #include "streaming/stream-capabilities.h"
110 #include "streaming/stream-path.h"
111 #include "streaming/stream.h"
src/database/rrdlabels.c
+26 -189
@@ -64,31 +64,11 @@ typedef struct rrdlabels {
64 } \
65 while (0)
66
67 -static inline void STATS_PLUS_MEMORY(struct dictionary_stats *stats, int64_t judy_mem, size_t item_size, size_t value_size) {
68 - if(judy_mem)
69 - __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
70 -
71 - if(item_size)
72 - __atomic_fetch_add(&stats->memory.dict, (ssize_t)item_size, __ATOMIC_RELAXED);
73 -
74 - if(value_size)
75 - __atomic_fetch_add(&stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
76 -}
77 -
78 -static inline void STATS_MINUS_MEMORY(struct dictionary_stats *stats, int64_t judy_mem, size_t item_size, size_t value_size) {
79 - if(judy_mem)
80 - __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
81 -
82 - if(item_size)
83 - __atomic_fetch_sub(&stats->memory.dict, (ssize_t)item_size, __ATOMIC_RELAXED);
84 -
85 - if(value_size)
86 - __atomic_fetch_sub(&stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
67 +static inline void RRDLABELS_MEMORY_DELTA(struct dictionary_stats *stats, int64_t judy_mem, int64_t item_size) {
68 + __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
69 + __atomic_fetch_add(&stats->memory.dict, item_size, __ATOMIC_RELAXED);
70 }
71
89 -#define RRDLABELS_MAX_NAME_LENGTH 200
90 -#define RRDLABELS_MAX_VALUE_LENGTH 800 // 800 in bytes, up to 200 UTF-8 characters
91 -
72 __attribute__((constructor)) void initialize_label_stats(void) {
73 dictionary_stats_category_rrdlabels.memory.dict = 0;
74 dictionary_stats_category_rrdlabels.memory.index = 0;
@@ -101,7 +81,7 @@ __attribute__((constructor)) void initialize_label_stats(void) {
81 RRDLABELS *rrdlabels_create(void)
82 {
83 RRDLABELS *labels = callocz(1, sizeof(*labels));
104 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, sizeof(RRDLABELS), 0);
84 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, 0, sizeof(RRDLABELS));
85 return labels;
86 }
87
@@ -143,12 +123,12 @@ static RRDLABEL *add_label_name_value(const char *name, const char *value)
123 rrdlabel = *PValue;
124 string_freez(label_index.key);
125 string_freez(label_index.value);
146 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, 0, 0);
126 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
127 } else {
128 rrdlabel = callocz(1, sizeof(*rrdlabel));
129 rrdlabel->label.index = label_index;
130 *PValue = rrdlabel;
151 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(RRDLABEL_IDX), 0);
131 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(RRDLABEL_IDX));
132 }
133 __atomic_add_fetch(&rrdlabel->refcount, 1, __ATOMIC_RELAXED);
134
@@ -171,7 +151,7 @@ static void delete_label(RRDLABEL *label)
151
152 int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
153
174 - STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(*rrdlabel), 0);
154 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, -(int64_t)sizeof(*rrdlabel));
155 string_freez(label->index.key);
156 string_freez(label->index.value);
157 freez(rrdlabel);
@@ -195,8 +175,10 @@ void rrdlabels_flush(RRDLABELS *labels) {
175 while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) {
176 delete_label((RRDLABEL *)Index);
177 }
198 - size_t memory_freed = JudyLFreeArray(&labels->JudyL, PJE0);
199 - STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, memory_freed + sizeof(RRDLABELS), 0);
178 + JudyAllocThreadPulseReset();
179 + JudyLFreeArray(&labels->JudyL, PJE0);
180 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
181 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, -(int64_t)sizeof(RRDLABELS));
182 spinlock_unlock(&labels->spinlock);
183 }
184
@@ -243,7 +225,7 @@ static void labels_add_already_sanitized(RRDLABELS *labels, const char *key, con
225
226 RRDLABEL_SRC new_ls = (ls & ~(RRDLABEL_FLAG_NEW | RRDLABEL_FLAG_OLD));
227
246 - size_t mem_before_judyl = JudyLMemUsed(labels->JudyL);
228 + JudyAllocThreadPulseReset();
229
230 Pvoid_t *PValue = JudyLIns(&labels->JudyL, (Word_t)new_label, PJE0);
231 if (!PValue || PValue == PJERR)
@@ -268,8 +250,8 @@ static void labels_add_already_sanitized(RRDLABELS *labels, const char *key, con
250
251 labels->version++;
252
271 - size_t mem_after_judyl = JudyLMemUsed(labels->JudyL);
272 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, mem_after_judyl - mem_before_judyl, 0);
253 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
254 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
255
256 spinlock_unlock(&labels->spinlock);
257 }
@@ -504,11 +486,11 @@ static void rrdlabels_remove_all_unmarked_unsafe(RRDLABELS *labels)
486 while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) {
487 if (!((*((RRDLABEL_SRC *)PValue)) & (RRDLABEL_FLAG_INTERNAL))) {
488
507 - size_t mem_before_judyl = JudyLMemUsed(labels->JudyL);
489 + JudyAllocThreadPulseReset();
490 (void)JudyLDel(&labels->JudyL, Index, PJE0);
509 - size_t mem_after_judyl = JudyLMemUsed(labels->JudyL);
491 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
492
511 - STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, mem_before_judyl - mem_after_judyl, 0);
493 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
494
495 delete_label((RRDLABEL *)Index);
496 if (labels->JudyL != (Pvoid_t) NULL) {
@@ -586,7 +568,8 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) {
568 RRDLABEL_SRC ls;
569 lfe_start_nolock(src, label, ls)
570 {
589 - size_t mem_before_judyl = JudyLMemUsed(dst->JudyL);
571 + JudyAllocThreadPulseGetAndReset();
572 +
573 PValue = JudyLIns(&dst->JudyL, (Word_t)label, PJE0);
574 if(unlikely(!PValue || PValue == PJERR))
575 fatal("RRDLABELS migrate: corrupted labels array");
@@ -595,8 +578,8 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) {
578 if (!*PValue) {
579 flag = (ls & ~(RRDLABEL_FLAG_OLD | RRDLABEL_FLAG_NEW)) | RRDLABEL_FLAG_NEW;
580 dup_label(label);
598 - size_t mem_after_judyl = JudyLMemUsed(dst->JudyL);
599 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, mem_after_judyl - mem_before_judyl, 0);
581 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
582 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
583 }
584 else
585 flag = RRDLABEL_FLAG_OLD;
@@ -656,7 +639,8 @@ void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src)
639 spinlock_lock(&dst->spinlock);
640 spinlock_lock(&src->spinlock);
641
659 - size_t mem_before_judyl = JudyLMemUsed(dst->JudyL);
642 + JudyAllocThreadPulseReset();
643 +
644 bool update_statistics = false;
645 lfe_start_nolock(src, label, ls)
646 {
@@ -682,8 +666,8 @@ void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src)
666 }
667 lfe_done_nolock();
668 if (update_statistics) {
685 - size_t mem_after_judyl = JudyLMemUsed(dst->JudyL);
686 - STATS_PLUS_MEMORY(&dictionary_stats_category_rrdlabels, 0, mem_after_judyl - mem_before_judyl, 0);
669 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
670 + RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
671 }
672
673 spinlock_unlock(&src->spinlock);
@@ -927,154 +911,6 @@ void rrdset_update_rrdlabels(RRDSET *st, RRDLABELS *new_rrdlabels) {
911 rrdset_metadata_updated(st);
912 }
913
930 -struct pattern_array *pattern_array_allocate()
931 -{
932 - struct pattern_array *pa = callocz(1, sizeof(*pa));
933 - return pa;
934 -}
935 -
936 -static void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp)
937 -{
938 - if (!pa || !key || !sp)
939 - return;
940 -
941 - STRING *string_key = string_strdupz(key);
942 - Pvoid_t *Pvalue = JudyLIns(&pa->JudyL, (Word_t) string_key, PJE0);
943 - if (!Pvalue) {
944 - string_freez(string_key);
945 - simple_pattern_free(sp);
946 - return;
947 - }
948 -
949 - struct pattern_array_item *pai;
950 - if (*Pvalue) {
951 - pai = *Pvalue;
952 - } else {
953 - *Pvalue = pai = callocz(1, sizeof(*pai));
954 - pa->key_count++;
955 - }
956 -
957 - pai->size++;
958 - Pvalue = JudyLIns(&pai->JudyL, (Word_t) pai->size, PJE0);
959 - if (!Pvalue) {
960 - simple_pattern_free(sp);
961 - return;
962 - }
963 -
964 - *Pvalue = sp;
965 -}
966 -
967 -bool pattern_array_label_match(
968 - struct pattern_array *pa,
969 - RRDLABELS *labels,
970 - char eq,
971 - size_t *searches)
972 -{
973 - if (!pa || !labels)
974 - return true;
975 -
976 - Pvoid_t *Pvalue;
977 - Word_t Index = 0;
978 - bool first_then_next = true;
979 - while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) {
980 - // for each label key in the patterns array
981 -
982 - struct pattern_array_item *pai = *Pvalue;
983 - SIMPLE_PATTERN_RESULT match = SP_NOT_MATCHED ;
984 - for (Word_t i = 1; i <= pai->size; i++) {
985 - // for each pattern in the label key pattern list
986 -
987 - if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)) || !*Pvalue)
988 - continue;
989 -
990 - match = rrdlabels_match_simple_pattern_parsed(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches);
991 -
992 - if(match != SP_NOT_MATCHED)
993 - break;
994 - }
995 -
996 - if (match != SP_MATCHED_POSITIVE)
997 - return false;
998 - }
999 - return true;
1000 -}
1001 -
1002 -struct pattern_array *pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern)
1003 -{
1004 - if (unlikely(!pattern || !key))
1005 - return pa;
1006 -
1007 - if (!pa)
1008 - pa = pattern_array_allocate();
1009 -
1010 - pattern_array_add_lblkey_with_sp(pa, key, pattern);
1011 - return pa;
1012 -}
1013 -
1014 -struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep)
1015 -{
1016 - if (unlikely(!pattern))
1017 - return pa;
1018 -
1019 - if (!pa)
1020 - pa = pattern_array_allocate();
1021 -
1022 - char *label_key;
1023 - while (pattern && (label_key = simple_pattern_iterate(&pattern))) {
1024 - char key[RRDLABELS_MAX_NAME_LENGTH + 1], *key_sep;
1025 -
1026 - if (unlikely(!label_key || !(key_sep = strchr(label_key, sep))))
1027 - return pa;
1028 -
1029 - *key_sep = '\0';
1030 - strncpyz(key, label_key, RRDLABELS_MAX_NAME_LENGTH);
1031 - *key_sep = sep;
1032 -
1033 - pattern_array_add_lblkey_with_sp(pa, key, string_to_simple_pattern(label_key));
1034 - }
1035 - return pa;
1036 -}
1037 -
1038 -struct pattern_array *pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep)
1039 -{
1040 - if (unlikely(!key || !value))
1041 - return pa;
1042 -
1043 - if (!pa)
1044 - pa = pattern_array_allocate();
1045 -
1046 - char label_key[RRDLABELS_MAX_NAME_LENGTH + RRDLABELS_MAX_VALUE_LENGTH + 2];
1047 - snprintfz(label_key, sizeof(label_key) - 1, "%s%c%s", key, sep, value);
1048 - pattern_array_add_lblkey_with_sp(
1049 - pa, key, simple_pattern_create(label_key, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true));
1050 - return pa;
1051 -}
1052 -
1053 -void pattern_array_free(struct pattern_array *pa)
1054 -{
1055 - if (!pa)
1056 - return;
1057 -
1058 - Pvoid_t *Pvalue;
1059 - Word_t Index = 0;
1060 - while ((Pvalue = JudyLFirst(pa->JudyL, &Index, PJE0))) {
1061 - struct pattern_array_item *pai = *Pvalue;
1062 -
1063 - for (Word_t i = 1; i <= pai->size; i++) {
1064 - if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)))
1065 - continue;
1066 - simple_pattern_free((SIMPLE_PATTERN *) (*Pvalue));
1067 - }
1068 - JudyLFreeArray(&(pai->JudyL), PJE0);
1069 -
1070 - string_freez((STRING *)Index);
1071 - (void) JudyLDel(&(pa->JudyL), Index, PJE0);
1072 - freez(pai);
1073 - Index = 0;
1074 - }
1075 - freez(pa);
1076 -}
1077 -
914 // ----------------------------------------------------------------------------
915 // rrdlabels unit test
916
@@ -1280,6 +1116,7 @@ static int unittest_dump_labels(const char *name, const char *value, RRDLABEL_SR
1116 return 1;
1117 }
1118
1119 +void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp);
1120 static int rrdlabels_unittest_pattern_check()
1121 {
1122 fprintf(stderr, "\n%s() tests\n", __FUNCTION__);
src/database/rrdlabels.h
+3 -23
@@ -5,16 +5,6 @@
5
6 #include "libnetdata/libnetdata.h"
7
8 -struct pattern_array_item {
9 - Word_t size;
10 - Pvoid_t JudyL;
11 -};
12 -
13 -struct pattern_array {
14 - Word_t key_count;
15 - Pvoid_t JudyL;
16 -};
17 -
8 typedef enum __attribute__ ((__packed__)) rrdlabel_source {
9 RRDLABEL_SRC_AUTO = (1 << 0), // set when Netdata found the label by some automation
10 RRDLABEL_SRC_CONFIG = (1 << 1), // set when the user configured the label
@@ -30,6 +20,9 @@ typedef enum __attribute__ ((__packed__)) rrdlabel_source {
20
21 #define RRDLABEL_FLAG_INTERNAL (RRDLABEL_FLAG_OLD | RRDLABEL_FLAG_NEW | RRDLABEL_FLAG_DONT_DELETE)
22
23 +#define RRDLABELS_MAX_NAME_LENGTH 200
24 +#define RRDLABELS_MAX_VALUE_LENGTH 800 // 800 in bytes, up to 200 UTF-8 characters
25 +
26 struct rrdlabels;
27 typedef struct rrdlabels RRDLABELS;
28
@@ -65,19 +58,6 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src);
58 void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src);
59 size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2);
60
68 -struct pattern_array *pattern_array_allocate();
69 -struct pattern_array *
70 -pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep);
71 -bool pattern_array_label_match(
72 - struct pattern_array *pa,
73 - RRDLABELS *labels,
74 - char eq,
75 - size_t *searches);
76 -struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep);
77 -struct pattern_array *
78 -pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern);
79 -void pattern_array_free(struct pattern_array *pa);
80 -
61 int rrdlabels_unittest(void);
62 size_t rrdlabels_sanitize_name(char *dst, const char *src, size_t dst_size);
63
src/database/sqlite/sqlite_health.c
+8 -3
@@ -375,6 +375,9 @@ void sql_health_alarm_log_cleanup(RRDHOST *host)
375 done:
376 REPORT_BIND_FAIL(res, param);
377 SQLITE_FINALIZE(res);
378 +
379 + // After cleaning up SQLite entries, also clean up in-memory entries
380 + health_alarm_log_cleanup(host);
381 }
382
383 #define SQL_UPDATE_TRANSITION_IN_HEALTH_LOG \
@@ -706,7 +709,7 @@ void sql_health_alarm_log_load(RRDHOST *host)
709 }
710 }
711
709 - ae = callocz(1, sizeof(ALARM_ENTRY));
712 + ae = health_alarm_entry_create();
713
714 ae->unique_id = unique_id;
715 ae->alarm_id = alarm_id;
@@ -765,8 +768,7 @@ void sql_health_alarm_log_load(RRDHOST *host)
768 ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1));
769 ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1));
770
768 - ae->next = host->health_log.alarms;
769 - host->health_log.alarms = ae;
771 + DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(host->health_log.alarms, ae, prev, next);
772
773 if(unlikely(ae->unique_id > host->health_max_unique_id))
774 host->health_max_unique_id = ae->unique_id;
@@ -793,6 +795,9 @@ void sql_health_alarm_log_load(RRDHOST *host)
795 nd_log(NDLS_DAEMON, errored ? NDLP_WARNING : NDLP_DEBUG,
796 "[%s]: Table health_log, loaded %zd alarm entries, errors in %zd entries.",
797 rrdhost_hostname(host), loaded, errored);
798 +
799 + // Clean up old entries based on retention settings
800 + health_alarm_log_cleanup(host);
801 done:
802 REPORT_BIND_FAIL(res, param);
803 SQLITE_FINALIZE(res);
src/health/health-alert-entry.h
+4 -1
@@ -59,7 +59,7 @@ struct alarm_entry {
59
60 POPEN_INSTANCE *popen_instance;
61
62 - struct alarm_entry *next;
62 + struct alarm_entry *next, *prev;
63 struct alarm_entry *next_in_progress;
64 struct alarm_entry *prev_in_progress;
65 };
@@ -79,4 +79,7 @@ struct alarm_entry {
79 #define ae_old_value_string(ae) string2str((ae)->old_value_string)
80 #define ae_new_value_string(ae) string2str((ae)->new_value_string)
81
82 +// Function to clean up old alarm entries based on retention settings
83 +void health_alarm_log_cleanup(RRDHOST *host);
84 +
85 #endif //NETDATA_HEALTH_ALERT_ENTRY_H
src/health/health-alert-log.h
-1
@@ -8,7 +8,6 @@
8 typedef struct alarm_log {
9 uint32_t next_log_id;
10 uint32_t next_alarm_id;
11 - unsigned int count;
11 unsigned int max;
12 uint32_t health_log_retention_s; // the health log retention in seconds to be kept in db
13 struct alarm_entry *alarms;
src/health/health.c
+15 -1
@@ -152,6 +152,7 @@ void health_plugin_init(void) {
152
153 health_globals.initialization.done = true;
154
155 + health_alarm_entry_aral_init();
156 health_init_prototypes();
157 health_load_config_defaults();
158
@@ -166,7 +167,20 @@ cleanup:
167 }
168
169 void health_plugin_destroy(void) {
169 - ;
170 + if(!health_globals.initialization.done)
171 + return;
172 +
173 + spinlock_lock(&health_globals.initialization.spinlock);
174 +
175 + // Clean up health prototypes dictionary
176 + if(health_globals.prototypes.dict) {
177 + dictionary_destroy(health_globals.prototypes.dict);
178 + health_globals.prototypes.dict = NULL;
179 + }
180 +
181 + health_globals.initialization.done = false;
182 +
183 + spinlock_unlock(&health_globals.initialization.spinlock);
184 }
185
186 void health_plugin_reload(void) {
src/health/health.h
+6
@@ -81,6 +81,12 @@ void health_alarm_log_free(RRDHOST *host);
81
82 void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae);
83
84 +// ARAL management functions for ALARM_ENTRY
85 +void health_alarm_entry_aral_init(void);
86 +struct aral_statistics *health_alarm_entry_aral_stats(void);
87 +ALARM_ENTRY *health_alarm_entry_create(void);
88 +void health_alarm_entry_destroy(ALARM_ENTRY *ae);
89 +
90 void *health_cmdapi_thread(void *ptr);
91
92 char *health_edit_command_from_source(const char *source);
src/health/health_log.c
+89 -10
@@ -3,6 +3,42 @@
3 #include "health.h"
4 #include "health-alert-entry.h"
5
6 +// ----------------------------------------------------------------------------
7 +// ARAL memory management for ALARM_ENTRY structures
8 +
9 +static struct {
10 + ARAL *ar;
11 +} health_alarm_entry_globals = {
12 + .ar = NULL,
13 +};
14 +
15 +void health_alarm_entry_aral_init(void) {
16 + health_alarm_entry_globals.ar = aral_create(
17 + "health-alarm-entry",
18 + sizeof(ALARM_ENTRY),
19 + 0,
20 + 0,
21 + NULL,
22 + NULL, NULL, false, false, true
23 + );
24 +
25 + pulse_aral_register(health_alarm_entry_globals.ar, "health_log");
26 +}
27 +
28 +struct aral_statistics *health_alarm_entry_aral_stats(void) {
29 + return aral_get_statistics(health_alarm_entry_globals.ar);
30 +}
31 +
32 +ALARM_ENTRY *health_alarm_entry_create(void) {
33 + ALARM_ENTRY *ae = aral_mallocz(health_alarm_entry_globals.ar);
34 + memset(ae, 0, sizeof(ALARM_ENTRY));
35 + return ae;
36 +}
37 +
38 +void health_alarm_entry_destroy(ALARM_ENTRY *ae) {
39 + aral_freez(health_alarm_entry_globals.ar, ae);
40 +}
41 +
42 // ----------------------------------------------------------------------------
43
44 inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae, bool async)
@@ -153,7 +189,7 @@ inline ALARM_ENTRY* health_create_alarm_entry(
189
190 netdata_log_debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
191
156 - ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
192 + ALARM_ENTRY *ae = health_alarm_entry_create();
193 ae->name = string_dup(name);
194 ae->chart = string_dup(chart);
195 ae->chart_context = string_dup(chart_context);
@@ -209,15 +245,12 @@ inline void health_alarm_log_add_entry(RRDHOST *host, ALARM_ENTRY *ae, bool asyn
245
246 // link it
247 rw_spinlock_write_lock(&host->health_log.spinlock);
212 - ae->next = host->health_log.alarms;
213 - host->health_log.alarms = ae;
214 - host->health_log.count++;
248 + DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(host->health_log.alarms, ae, prev, next);
249 rw_spinlock_write_unlock(&host->health_log.spinlock);
250
251 // match previous alarms
252 rw_spinlock_read_lock(&host->health_log.spinlock);
219 - ALARM_ENTRY *t;
220 - for(t = host->health_log.alarms ; t ; t = t->next) {
253 + for(ALARM_ENTRY *t = host->health_log.alarms ; t ; t = t->next) {
254 if(t != ae && t->alarm_id == ae->alarm_id) {
255 if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) {
256 t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
@@ -259,18 +292,64 @@ inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) {
292 string_freez(ae->old_value_string);
293 string_freez(ae->new_value_string);
294 string_freez(ae->summary);
262 - freez(ae);
295 +
296 + if(ae->popen_instance) {
297 + spawn_popen_kill(ae->popen_instance, 0);
298 + ae->popen_instance = NULL;
299 + }
300 +
301 + if(ae->next || ae->prev)
302 + fatal("HEALTH: alarm entry to delete is still linked!");
303 +
304 +// if(ae->prev_in_progress || ae->next_in_progress)
305 +// fatal("HEALTH: alarm entry to be delete is linked in progress!");
306 +
307 + health_alarm_entry_destroy(ae);
308 }
309 }
310
311 inline void health_alarm_log_free(RRDHOST *host) {
312 rw_spinlock_write_lock(&host->health_log.spinlock);
313
269 - ALARM_ENTRY *ae;
270 - while((ae = host->health_log.alarms)) {
271 - host->health_log.alarms = ae->next;
314 + while(host->health_log.alarms) {
315 + ALARM_ENTRY *ae = host->health_log.alarms;
316 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(host->health_log.alarms, ae, prev, next);
317 health_alarm_log_free_one_nochecks_nounlink(ae);
318 }
319
320 rw_spinlock_write_unlock(&host->health_log.spinlock);
321 }
322 +
323 +// Clean up old alarm entries from memory based on retention settings
324 +void health_alarm_log_cleanup(RRDHOST *host) {
325 + if(!host->health_log.alarms)
326 + return;
327 +
328 + time_t now = now_realtime_sec();
329 + time_t retention = host->health_log.health_log_retention_s;
330 +
331 + rw_spinlock_write_lock(&host->health_log.spinlock);
332 +
333 + ALARM_ENTRY *ae = host->health_log.alarms;
334 + while(ae) {
335 + // Check if entry is old enough to be deleted
336 + if(ae->when < now - retention &&
337 + (ae->flags & HEALTH_ENTRY_FLAG_UPDATED) && // Only remove entries that have been processed/updated
338 + __atomic_load_n(&ae->pending_save_count, __ATOMIC_RELAXED) == 0) { // Only remove entries not pending save
339 +
340 + // Remove from linked list
341 + ALARM_ENTRY *next = ae->next;
342 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(host->health_log.alarms, ae, prev, next);
343 +
344 + ALARM_ENTRY *to_free = ae;
345 + ae = next;
346 +
347 + // Free memory
348 + health_alarm_log_free_one_nochecks_nounlink(to_free);
349 + }
350 + else
351 + ae = ae->next;
352 + }
353 +
354 + rw_spinlock_write_unlock(&host->health_log.spinlock);
355 +}
src/health/health_notifications.c
+14 -49
@@ -4,10 +4,7 @@
4 #include "health-alert-entry.h"
5
6 // the queue of executed alarm notifications that haven't been waited for yet
7 -static struct {
8 - ALARM_ENTRY *head; // oldest
9 - ALARM_ENTRY *tail; // latest
10 -} alarm_notifications_in_progress = {NULL, NULL};
7 +static ALARM_ENTRY *alarm_notifications_in_progress = NULL;
8
9 struct health_raised_summary {
10 RRDHOST *host;
@@ -38,6 +35,7 @@ void health_alarm_wait_for_execution(ALARM_ENTRY *ae) {
35 }
36
37 code = spawn_popen_wait(ae->popen_instance);
38 + ae->popen_instance = NULL;
39 netdata_log_debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
40
41 cleanup:
@@ -52,7 +50,7 @@ cleanup:
50
51 void wait_for_all_notifications_to_finish_before_allowing_health_to_be_cleaned_up(void) {
52 ALARM_ENTRY *ae;
55 - while (NULL != (ae = alarm_notifications_in_progress.head)) {
53 + while (NULL != (ae = alarm_notifications_in_progress)) {
54 if(unlikely(!service_running(SERVICE_HEALTH)))
55 break;
56
@@ -62,36 +60,14 @@ void wait_for_all_notifications_to_finish_before_allowing_health_to_be_cleaned_u
60
61 void unlink_alarm_notify_in_progress(ALARM_ENTRY *ae)
62 {
65 - struct alarm_entry *prev = ae->prev_in_progress;
66 - struct alarm_entry *next = ae->next_in_progress;
67 -
68 - if (NULL != prev) {
69 - prev->next_in_progress = next;
70 - }
71 - if (NULL != next) {
72 - next->prev_in_progress = prev;
73 - }
74 - if (ae == alarm_notifications_in_progress.head) {
75 - alarm_notifications_in_progress.head = next;
76 - }
77 - if (ae == alarm_notifications_in_progress.tail) {
78 - alarm_notifications_in_progress.tail = prev;
79 - }
63 + fatal_assert(ae->prev_in_progress || ae->next_in_progress);
64 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(alarm_notifications_in_progress, ae, prev_in_progress, next_in_progress);
65 }
66
67 static inline void enqueue_alarm_notify_in_progress(ALARM_ENTRY *ae)
68 {
84 - ae->prev_in_progress = NULL;
85 - ae->next_in_progress = NULL;
86 -
87 - if (NULL != alarm_notifications_in_progress.tail) {
88 - ae->prev_in_progress = alarm_notifications_in_progress.tail;
89 - alarm_notifications_in_progress.tail->next_in_progress = ae;
90 - }
91 - if (NULL == alarm_notifications_in_progress.head) {
92 - alarm_notifications_in_progress.head = ae;
93 - }
94 - alarm_notifications_in_progress.tail = ae;
69 + fatal_assert(!ae->prev_in_progress && !ae->next_in_progress);
70 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(alarm_notifications_in_progress, ae, prev_in_progress, next_in_progress);
71 }
72
73 static bool prepare_command(BUFFER *wb,
@@ -534,8 +510,7 @@ void health_alarm_log_process_to_send_notifications(RRDHOST *host, struct health
510
511 rw_spinlock_read_lock(&host->health_log.spinlock);
512
537 - ALARM_ENTRY *ae;
538 - for(ae = host->health_log.alarms; ae && ae->unique_id >= host->health_last_processed_id; ae = ae->next) {
513 + for(ALARM_ENTRY *ae = host->health_log.alarms; ae && ae->unique_id >= host->health_last_processed_id; ae = ae->next) {
514 if(unlikely(
515 !(ae->flags & HEALTH_ENTRY_FLAG_PROCESSED) &&
516 !(ae->flags & HEALTH_ENTRY_FLAG_UPDATED)
@@ -556,9 +531,9 @@ void health_alarm_log_process_to_send_notifications(RRDHOST *host, struct health
531 //delete those that are updated, no in progress execution, and is not repeating
532 rw_spinlock_write_lock(&host->health_log.spinlock);
533
559 - ALARM_ENTRY *prev = NULL, *next = NULL;
560 - for(ae = host->health_log.alarms; ae ; ae = next) {
561 - next = ae->next; // set it here, for the next iteration
534 + ALARM_ENTRY *ae = host->health_log.alarms;
535 + while(ae) {
536 + ALARM_ENTRY *next = ae->next; // set it here, for the next iteration
537
538 if((likely(!(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING)) &&
539 (ae->flags & HEALTH_ENTRY_FLAG_UPDATED) &&
@@ -569,21 +544,11 @@ void health_alarm_log_process_to_send_notifications(RRDHOST *host, struct health
544 (ae->flags & HEALTH_ENTRY_FLAG_SAVED) &&
545 (ae->when + 86400 < now_realtime_sec())))
546 {
572 -
573 - if(host->health_log.alarms == ae) {
574 - host->health_log.alarms = next;
575 - // prev is also NULL here
576 - }
577 - else {
578 - prev->next = next;
579 - // prev should not be touched here - we need it for the next iteration
580 - // because we may have to also remove the next item
581 - }
582 -
547 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(host->health_log.alarms, ae, prev, next);
548 health_alarm_log_free_one_nochecks_nounlink(ae);
549 }
585 - else
586 - prev = ae;
550 +
551 + ae = next;
552 }
553
554 rw_spinlock_write_unlock(&host->health_log.spinlock);
src/health/health_prototypes.c
+1 -2
@@ -203,6 +203,7 @@ static void alert_action_options_init(void) {
203 static void health_prototype_cleanup_one_unsafe(RRD_ALERT_PROTOTYPE *ap) {
204 rrd_alert_match_cleanup(&ap->match);
205 rrd_alert_config_cleanup(&ap->config);
206 + memset(ap, 0, sizeof(*ap));
207 }
208
209 void health_prototype_cleanup(RRD_ALERT_PROTOTYPE *ap) {
@@ -246,7 +247,6 @@ bool health_prototype_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, vo
247 if(ap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG) {
248 // the existing is a dyncfg and the new one is read from the config
249 health_prototype_cleanup(nap);
249 - memset(nap, 0, sizeof(*nap));
250 }
251 else {
252 // alerts with the same name are appended to the existing one
@@ -272,7 +272,6 @@ bool health_prototype_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, vo
272 rw_spinlock_write_unlock(&nap->_internal.rw_spinlock);
273
274 health_prototype_cleanup(nap);
275 - memset(nap, 0, sizeof(*nap));
275 }
276
277 return true;
src/health/rrdcalc.c
+14 -14
@@ -364,19 +364,6 @@ static void rrdcalc_rrdhost_react_callback(const DICTIONARY_ITEM *item __maybe_u
364 // ----------------------------------------------------------------------------
365 // RRDCALC rrdhost index management - destructor
366
367 -static void rrdcalc_free_internals(RRDCALC *rc) {
368 - if(unlikely(!rc)) return;
369 -
370 - rrd_alert_match_cleanup(&rc->match);
371 - rrd_alert_config_cleanup(&rc->config);
372 -
373 - string_freez(rc->key);
374 - string_freez(rc->chart);
375 -
376 - string_freez(rc->info);
377 - string_freez(rc->summary);
378 -}
379 -
367 static __thread bool thread_having_ll_wrlock = false;
368
369 static void rrdcalc_rrdhost_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdhost __maybe_unused) {
@@ -388,7 +375,16 @@ static void rrdcalc_rrdhost_delete_callback(const DICTIONARY_ITEM *item __maybe_
375 // any destruction actions that require other locks
376 // have to be placed in rrdcalc_del(), because the object is actually locked for deletion
377
391 - rrdcalc_free_internals(rc);
378 + rrd_alert_match_cleanup(&rc->match);
379 + rrd_alert_config_cleanup(&rc->config);
380 +
381 + string_freez(rc->key);
382 + string_freez(rc->chart);
383 +
384 + string_freez(rc->info);
385 + string_freez(rc->summary);
386 +
387 + memset(rc, 0, sizeof(*rc));
388 }
389
390 // ----------------------------------------------------------------------------
@@ -491,6 +487,8 @@ void rrd_alert_match_cleanup(struct rrd_alert_match *am) {
487
488 string_freez(am->chart_labels);
489 pattern_array_free(am->chart_labels_pattern);
490 +
491 + memset(am, 0, sizeof(*am));
492 }
493
494 void rrd_alert_config_cleanup(struct rrd_alert_config *ac) {
@@ -513,4 +511,6 @@ void rrd_alert_config_cleanup(struct rrd_alert_config *ac) {
511 expression_free(ac->calculation);
512 expression_free(ac->warning);
513 expression_free(ac->critical);
514 +
515 + memset(ac, 0, sizeof(*ac));
516 }
src/libnetdata/aral/aral.c
+10
@@ -921,6 +921,11 @@ ALWAYS_INLINE void *aral_callocz_internal(ARAL *ar, bool marked TRACE_ALLOCATION
921
922 void *aral_mallocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
923 #if defined(FSANITIZE_ADDRESS)
924 + if(ar->stats) {
925 + __atomic_add_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
926 + __atomic_add_fetch(&ar->stats->malloc.allocated_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
927 + __atomic_add_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
928 + }
929 return mallocz(ar->config.requested_element_size);
930 #endif
931
@@ -978,6 +983,11 @@ void aral_unmark_allocation(ARAL *ar, void *ptr) {
983
984 void aral_freez_internal(ARAL *ar, void *ptr TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
985 #if defined(FSANITIZE_ADDRESS)
986 + if(ptr && ar->stats) {
987 + __atomic_sub_fetch(&ar->stats->malloc.allocations, 1, __ATOMIC_RELAXED);
988 + __atomic_sub_fetch(&ar->stats->malloc.allocated_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
989 + __atomic_sub_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
990 + }
991 freez(ptr);
992 return;
993 #endif