@cryptotaxi247 / netdata-1 / commits / 57a55e4f7

fix memory leaks (#20116)

Costa Tsaousis committed Apr 10, 2025 at 21:46 UTC 57a55e4f7a70841f55ccd798355e808c42a756ec
25 files changed +287 -56
src/claim/claim-with-api.c
+8 -1
@@ -421,8 +421,15 @@ bool claim_agent_from_environment(void) {
421 return claim_agent(url, token, rooms, proxy, insecure);
422 }
423
424 +// Static config for claim.conf
425 +static struct config claim_config = APPCONFIG_INITIALIZER;
426 +
427 +// Function to free the static claim_config for shutdown cleanup
428 +void claim_config_free(void) {
429 + inicfg_free(&claim_config);
430 +}
431 +
432 bool claim_agent_from_claim_conf(void) {
425 - static struct config claim_config = APPCONFIG_INITIALIZER;
433 static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
434 bool ret = false;
435
src/collectors/cgroups.plugin/cgroup-discovery.c
+20 -21
@@ -57,6 +57,16 @@ static inline void cgroup_free_network_interfaces(struct cgroup *cg) {
57 static inline void cgroup_free(struct cgroup *cg) {
58 netdata_log_debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
59
60 + if(cg->st_cpu && cg->chart_var_cpu_limit) {
61 + rrdvar_chart_variable_release(cg->st_cpu, cg->chart_var_cpu_limit);
62 + cg->chart_var_cpu_limit = NULL;
63 + }
64 +
65 + if(cg->st_mem_usage && cg->chart_var_memory_limit) {
66 + rrdvar_chart_variable_release(cg->st_mem_usage, cg->chart_var_memory_limit);
67 + cg->chart_var_memory_limit = NULL;
68 + }
69 +
70 cgroup_netdev_delete(cg);
71
72 if(cg->st_cpu) rrdset_is_obsolete___safe_from_collector_thread(cg->st_cpu);
@@ -85,7 +95,6 @@ static inline void cgroup_free(struct cgroup *cg) {
95 freez(cg->filename_cpu_cfs_period);
96 freez(cg->filename_cpu_cfs_quota);
97 freez(cg->filename_memory_limit);
88 - freez(cg->filename_memoryswap_limit);
98
99 cgroup_free_network_interfaces(cg);
100
@@ -541,16 +550,6 @@ static inline void discovery_update_filenames_cgroup_v1(struct cgroup *cg) {
550 }
551 }
552
544 - if (unlikely(!cg->memory.staterr_swap && !cg->memory.filename_msw_usage_in_bytes)) {
545 - snprintfz(filename, FILENAME_MAX, "%s%s/memory.memsw.usage_in_bytes", cgroup_memory_base, cg->id);
546 - if (!(cg->memory.staterr_swap = stat(filename, &buf) != 0)) {
547 - cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
548 -
549 - snprintfz(filename, FILENAME_MAX, "%s%s/memory.memsw.limit_in_bytes", cgroup_memory_base, cg->id);
550 - cg->filename_memoryswap_limit = strdupz(filename);
551 - }
552 - }
553 -
553 if (unlikely(!cg->memory.staterr_failcnt && !cg->memory.filename_failcnt)) {
554 snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
555 if (!(cg->memory.staterr_failcnt = stat(filename, &buf) != 0)) {
@@ -690,16 +689,6 @@ static inline void discovery_update_filenames_cgroup_v2(struct cgroup *cg) {
689 }
690 }
691
693 - if (unlikely(!cg->memory.staterr_swap && !cg->memory.filename_msw_usage_in_bytes)) {
694 - snprintfz(filename, FILENAME_MAX, "%s%s/memory.swap.current", cgroup_unified_base, cg->id);
695 - if (!(cg->memory.staterr_swap = stat(filename, &buf) != 0)) {
696 - cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
697 -
698 - snprintfz(filename, FILENAME_MAX, "%s%s/memory.swap.max", cgroup_unified_base, cg->id);
699 - cg->filename_memoryswap_limit = strdupz(filename);
700 - }
701 - }
702 -
692 // Blkio
693 if (unlikely(!cg->io_service_bytes.staterr && !cg->io_service_bytes.filename)) {
694 snprintfz(filename, FILENAME_MAX, "%s%s/io.stat", cgroup_unified_base, cg->id);
@@ -1338,6 +1327,16 @@ void cgroup_discovery_worker(void *ptr)
1327
1328 discovery_find_all_cgroups();
1329 }
1330 +
1331 + // free all cgroups
1332 + uv_mutex_lock(&cgroup_root_mutex);
1333 + while(cgroup_root) {
1334 + struct cgroup *cg = cgroup_root;
1335 + cgroup_root = cg->next;
1336 + cgroup_free(cg);
1337 + }
1338 + uv_mutex_unlock(&cgroup_root_mutex);
1339 +
1340 collector_info("discovery thread stopped");
1341 cgroup_cleanup_ebpf_integration();
1342 worker_unregister();
src/collectors/cgroups.plugin/cgroup-internals.h
-4
@@ -253,10 +253,6 @@ struct cgroup {
253 unsigned long long memory_limit;
254 const RRDVAR_ACQUIRED *chart_var_memory_limit;
255
256 - char *filename_memoryswap_limit;
257 - unsigned long long memoryswap_limit;
258 - const RRDVAR_ACQUIRED *chart_var_memoryswap_limit;
259 -
256 const DICTIONARY_ITEM *cgroup_netdev_link;
257
258 struct cgroup *next;
src/collectors/cgroups.plugin/sys_fs_cgroup.c
+7 -9
@@ -1021,20 +1021,19 @@ cpu_limits2_err:
1021
1022 static inline int update_memory_limits(struct cgroup *cg) {
1023 char **filename = &cg->filename_memory_limit;
1024 - const RRDVAR_ACQUIRED **chart_var = &cg->chart_var_memory_limit;
1024 unsigned long long *value = &cg->memory_limit;
1025
1026 if(*filename) {
1028 - if(unlikely(!*chart_var)) {
1029 - *chart_var = rrdvar_chart_variable_add_and_acquire(cg->st_mem_usage, "memory_limit");
1030 - if(!*chart_var) {
1027 + if(unlikely(!cg->chart_var_memory_limit)) {
1028 + cg->chart_var_memory_limit = rrdvar_chart_variable_add_and_acquire(cg->st_mem_usage, "memory_limit");
1029 + if(!cg->chart_var_memory_limit) {
1030 collector_error("Cannot create cgroup %s chart variable '%s'. Will not update its limit anymore.", cg->id, "memory_limit");
1031 freez(*filename);
1032 *filename = NULL;
1033 }
1034 }
1035
1037 - if(*filename && *chart_var) {
1036 + if(*filename && cg->chart_var_memory_limit) {
1037 if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
1038 if(read_single_number_file(*filename, value)) {
1039 collector_error("Cannot refresh cgroup %s memory limit by reading '%s'. Will not update its limit anymore.", cg->id, *filename);
@@ -1043,7 +1042,7 @@ static inline int update_memory_limits(struct cgroup *cg) {
1042 }
1043 else {
1044 rrdvar_chart_variable_set(
1046 - cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1045 + cg->st_mem_usage, cg->chart_var_memory_limit, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1046 return 1;
1047 }
1048 } else {
@@ -1058,12 +1057,11 @@ static inline int update_memory_limits(struct cgroup *cg) {
1057 char *s = "max\n\0";
1058 if(strcmp(s, buffer) == 0){
1059 *value = UINT64_MAX;
1061 - rrdvar_chart_variable_set(
1062 - cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1060 + rrdvar_chart_variable_set(cg->st_mem_usage, cg->chart_var_memory_limit, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1061 return 1;
1062 }
1063 *value = str2ull(buffer, NULL);
1066 - rrdvar_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1064 + rrdvar_chart_variable_set(cg->st_mem_usage, cg->chart_var_memory_limit, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1065 return 1;
1066 }
1067 }
src/collectors/proc.plugin/ipc.c
+10 -2
@@ -277,14 +277,22 @@ static int ipc_shm_get_info(const char *shm_filename, struct shm_stats *shm) {
277 return 0;
278 }
279
280 -int do_ipc(int update_every, usec_t dt) {
280 +static const RRDVAR_ACQUIRED *arrays_max = NULL, *semaphores_max = NULL;
281 +void proc_ipc_cleanup(void) {
282 + if(arrays_max)
283 + rrdvar_host_variable_release(localhost, arrays_max);
284 +
285 + if(semaphores_max)
286 + rrdvar_host_variable_release(localhost, semaphores_max);
287 +}
288 +
289 +int do_proc_ipc(int update_every, usec_t dt) {
290 (void)dt;
291
292 static int do_sem = -1, do_msg = -1, do_shm = -1;
293 static int read_limits_next = -1;
294 static struct ipc_limits limits;
295 static struct ipc_status status;
287 - static const RRDVAR_ACQUIRED *arrays_max = NULL, *semaphores_max = NULL;
296 static RRDSET *st_arrays = NULL;
297 static RRDDIM *rd_arrays = NULL;
298 static const char *msg_filename = NULL;
src/collectors/proc.plugin/plugin_proc.c
+15 -7
@@ -9,15 +9,16 @@ static struct proc_module {
9 int enabled;
10
11 int (*func)(int update_every, usec_t dt);
12 + void (*cleanup)(void); // Cleanup function pointer
13
14 RRDDIM *rd;
15
16 } proc_modules[] = {
17
18 // system metrics
18 - {.name = "/proc/stat", .dim = "stat", .func = do_proc_stat},
19 + {.name = "/proc/stat", .dim = "stat", .func = do_proc_stat, .cleanup = proc_stat_plugin_cleanup},
20 {.name = "/proc/uptime", .dim = "uptime", .func = do_proc_uptime},
20 - {.name = "/proc/loadavg", .dim = "loadavg", .func = do_proc_loadavg},
21 + {.name = "/proc/loadavg", .dim = "loadavg", .func = do_proc_loadavg, .cleanup = proc_loadavg_plugin_cleanup},
22 {.name = "/proc/sys/fs/file-nr", .dim = "file-nr", .func = do_proc_sys_fs_file_nr},
23 {.name = "/proc/sys/kernel/random/entropy_avail", .dim = "entropy", .func = do_proc_sys_kernel_random_entropy_avail},
24
@@ -42,16 +43,16 @@ static struct proc_module {
43
44 // network metrics
45 {.name = "/proc/net/wireless", .dim = "netwireless", .func = do_proc_net_wireless},
45 - {.name = "/proc/net/sockstat", .dim = "sockstat", .func = do_proc_net_sockstat},
46 + {.name = "/proc/net/sockstat", .dim = "sockstat", .func = do_proc_net_sockstat, .cleanup = proc_net_sockstat_plugin_cleanup},
47 {.name = "/proc/net/sockstat6", .dim = "sockstat6", .func = do_proc_net_sockstat6},
47 - {.name = "/proc/net/netstat", .dim = "netstat", .func = do_proc_net_netstat},
48 + {.name = "/proc/net/netstat", .dim = "netstat", .func = do_proc_net_netstat, .cleanup = proc_net_netstat_cleanup},
49 {.name = "/proc/net/sctp/snmp", .dim = "sctp", .func = do_proc_net_sctp_snmp},
50 {.name = "/proc/net/softnet_stat", .dim = "softnet", .func = do_proc_net_softnet_stat},
51 {.name = "/proc/net/ip_vs/stats", .dim = "ipvs", .func = do_proc_net_ip_vs_stats},
51 - {.name = "/sys/class/infiniband", .dim = "infiniband", .func = do_sys_class_infiniband},
52 + {.name = "/sys/class/infiniband", .dim = "infiniband", .func = do_sys_class_infiniband, .cleanup = sys_class_infiniband_plugin_cleanup},
53
54 // firewall metrics
54 - {.name = "/proc/net/stat/conntrack", .dim = "conntrack", .func = do_proc_net_stat_conntrack},
55 + {.name = "/proc/net/stat/conntrack", .dim = "conntrack", .func = do_proc_net_stat_conntrack, .cleanup = proc_net_stat_conntrack_cleanup},
56 {.name = "/proc/net/stat/synproxy", .dim = "synproxy", .func = do_proc_net_stat_synproxy},
57
58 // disk metrics
@@ -69,7 +70,7 @@ static struct proc_module {
70 {.name = "/sys/fs/btrfs", .dim = "btrfs", .func = do_sys_fs_btrfs},
71
72 // IPC metrics
72 - {.name = "ipc", .dim = "ipc", .func = do_ipc},
73 + {.name = "ipc", .dim = "ipc", .func = do_proc_ipc, .cleanup = proc_ipc_cleanup},
74
75 // linux power supply metrics
76 {.name = "/sys/class/power_supply", .dim = "power_supply", .func = do_sys_class_power_supply},
@@ -94,6 +95,13 @@ static void proc_main_cleanup(void *pptr)
95
96 static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
97
98 + // Run all module cleanup functions
99 + int i;
100 + for(i = 0; proc_modules[i].name; i++) {
101 + if(proc_modules[i].cleanup)
102 + proc_modules[i].cleanup();
103 + }
104 +
105 nd_thread_join(netdev_thread);
106 worker_unregister();
107
src/collectors/proc.plugin/plugin_proc.h
+10 -1
@@ -41,7 +41,7 @@ int do_sys_fs_btrfs(int update_every, usec_t dt);
41 int do_proc_net_sockstat(int update_every, usec_t dt);
42 int do_proc_net_sockstat6(int update_every, usec_t dt);
43 int do_proc_net_sctp_snmp(int update_every, usec_t dt);
44 -int do_ipc(int update_every, usec_t dt);
44 +int do_proc_ipc(int update_every, usec_t dt);
45 int do_sys_class_power_supply(int update_every, usec_t dt);
46 int do_proc_pagetypeinfo(int update_every, usec_t dt);
47 int do_sys_class_infiniband(int update_every, usec_t dt);
@@ -49,6 +49,15 @@ int do_sys_class_drm(int update_every, usec_t dt);
49 int get_numa_node_count(void);
50 int do_run_reboot_required(int update_every, usec_t dt);
51
52 +// Plugin cleanup functions
53 +void proc_ipc_cleanup(void);
54 +void proc_net_netstat_cleanup(void);
55 +void proc_net_stat_conntrack_cleanup(void);
56 +void proc_stat_plugin_cleanup(void);
57 +void proc_net_sockstat_plugin_cleanup(void);
58 +void proc_loadavg_plugin_cleanup(void);
59 +void sys_class_infiniband_plugin_cleanup(void);
60 +
61 // metrics that need to be shared among data collectors
62 extern unsigned long long zfs_arcstats_shrinkable_cache_size_bytes;
63 extern bool inside_lxc_container;
src/collectors/proc.plugin/proc_loadavg.c
+11 -1
@@ -8,6 +8,17 @@
8 // linux calculates this once every 5 seconds
9 #define MIN_LOADAVG_UPDATE_EVERY 5
10
11 +static const RRDVAR_ACQUIRED *rd_pidmax = NULL;
12 +
13 +void proc_loadavg_plugin_cleanup(void) {
14 + // Cleanup any acquired RRDVARs
15 + RRDSET *st = rrdset_find_localhost("system.active_processes");
16 + if (st && rd_pidmax) {
17 + rrdvar_chart_variable_release(st, rd_pidmax);
18 + rd_pidmax = NULL;
19 + }
20 +}
21 +
22 int do_proc_loadavg(int update_every, usec_t dt) {
23 static procfile *ff = NULL;
24 static int do_loadavg = -1, do_all_processes = -1;
@@ -95,7 +106,6 @@ int do_proc_loadavg(int update_every, usec_t dt) {
106 if(likely(do_all_processes)) {
107 static RRDSET *processes_chart = NULL;
108 static RRDDIM *rd_active = NULL;
98 - static const RRDVAR_ACQUIRED *rd_pidmax;
109
110 if(unlikely(!processes_chart)) {
111 processes_chart = rrdset_create_localhost(
src/collectors/proc.plugin/proc_net_dev.c
+8
@@ -1692,6 +1692,14 @@ static void netdev_main_cleanup(void *pptr) {
1692 if(CLEANUP_FUNCTION_GET_PTR(pptr) != (void *)0x01)
1693 return;
1694
1695 + netdata_mutex_lock(&netdev_mutex);
1696 + while(netdev_root) {
1697 + struct netdev *d = netdev_root;
1698 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(netdev_root, d, prev, next);
1699 + netdev_free(d);
1700 + }
1701 + netdata_mutex_unlock(&netdev_mutex);
1702 +
1703 worker_unregister();
1704 }
1705
src/collectors/proc.plugin/proc_net_netstat.c
+7 -2
@@ -1195,6 +1195,13 @@ static void do_proc_net_snmp6(int update_every) {
1195 }
1196 }
1197
1198 +static const RRDVAR_ACQUIRED *tcp_max_connections_var = NULL;
1199 +
1200 +void proc_net_netstat_cleanup(void) {
1201 + if(tcp_max_connections_var)
1202 + rrdvar_host_variable_release(localhost, tcp_max_connections_var);
1203 +}
1204 +
1205 int do_proc_net_netstat(int update_every, usec_t dt) {
1206 (void)dt;
1207
@@ -1222,8 +1229,6 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
1229 static ARL_BASE *arl_udp = NULL;
1230 static ARL_BASE *arl_udplite = NULL;
1231
1225 - static const RRDVAR_ACQUIRED *tcp_max_connections_var = NULL;
1226 -
1232 // --------------------------------------------------------------------
1233 // IP
1234
src/collectors/proc.plugin/proc_net_sockstat.c
+26 -4
@@ -25,11 +25,34 @@ static struct proc_net_sockstat {
25 } sockstat_root = { 0 };
26
27
28 +static const RRDVAR_ACQUIRED
29 + *tcp_mem_low_threshold = NULL,
30 + *tcp_mem_pressure_threshold = NULL,
31 + *tcp_mem_high_threshold = NULL,
32 + *tcp_max_orphans_var = NULL;
33 +
34 +void proc_net_sockstat_plugin_cleanup(void) {
35 + // Cleanup any acquired RRDVARs
36 + if (tcp_mem_low_threshold) {
37 + rrdvar_host_variable_release(localhost, tcp_mem_low_threshold);
38 + tcp_mem_low_threshold = NULL;
39 + }
40 + if (tcp_mem_pressure_threshold) {
41 + rrdvar_host_variable_release(localhost, tcp_mem_pressure_threshold);
42 + tcp_mem_pressure_threshold = NULL;
43 + }
44 + if (tcp_mem_high_threshold) {
45 + rrdvar_host_variable_release(localhost, tcp_mem_high_threshold);
46 + tcp_mem_high_threshold = NULL;
47 + }
48 + if (tcp_max_orphans_var) {
49 + rrdvar_host_variable_release(localhost, tcp_max_orphans_var);
50 + tcp_max_orphans_var = NULL;
51 + }
52 +}
53 +
54 static int read_tcp_mem(void) {
55 static char *filename = NULL;
30 - static const RRDVAR_ACQUIRED *tcp_mem_low_threshold = NULL,
31 - *tcp_mem_pressure_threshold = NULL,
32 - *tcp_mem_high_threshold = NULL;
56
57 if(unlikely(!tcp_mem_low_threshold)) {
58 tcp_mem_low_threshold = rrdvar_host_variable_add_and_acquire(localhost, "tcp_mem_low");
@@ -69,7 +92,6 @@ static int read_tcp_mem(void) {
92
93 static kernel_uint_t read_tcp_max_orphans(void) {
94 static char *filename = NULL;
72 - static const RRDVAR_ACQUIRED *tcp_max_orphans_var = NULL;
95
96 if(unlikely(!filename)) {
97 char buffer[FILENAME_MAX + 1];
src/collectors/proc.plugin/proc_net_stat_conntrack.c
+7 -1
@@ -6,13 +6,19 @@
6 #define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
7 #define PLUGIN_PROC_MODULE_CONNTRACK_NAME "/proc/net/stat/nf_conntrack"
8
9 +static const RRDVAR_ACQUIRED *rrdvar_max = NULL;
10 +
11 +void proc_net_stat_conntrack_cleanup(void) {
12 + if(rrdvar_max)
13 + rrdvar_host_variable_release(localhost, rrdvar_max);
14 +}
15 +
16 int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
17 static procfile *ff = NULL;
18 static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1;
19 static usec_t get_max_every = 10 * USEC_PER_SEC, usec_since_last_max = 0;
20 static int read_full = 1;
21 static const char *nf_conntrack_filename, *nf_conntrack_count_filename, *nf_conntrack_max_filename;
15 - static const RRDVAR_ACQUIRED *rrdvar_max = NULL;
22
23 unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
24 ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0;
src/collectors/proc.plugin/proc_stat.c
+11 -1
@@ -475,6 +475,16 @@ static int read_cpuidle_states(const char *cpuidle_name_filename, const char *cp
475 return 0;
476 }
477
478 +static const RRDVAR_ACQUIRED *cpus_var = NULL;
479 +
480 +void proc_stat_plugin_cleanup(void) {
481 + // Cleanup any acquired RRDVARs
482 + if (cpus_var) {
483 + rrdvar_host_variable_release(localhost, cpus_var);
484 + cpus_var = NULL;
485 + }
486 +}
487 +
488 int do_proc_stat(int update_every, usec_t dt) {
489 (void)dt;
490
@@ -486,7 +496,7 @@ int do_proc_stat(int update_every, usec_t dt) {
496 static uint32_t hash_intr, hash_ctxt, hash_processes, hash_procs_running, hash_procs_blocked;
497 static const char *core_throttle_count_filename = NULL, *package_throttle_count_filename = NULL, *scaling_cur_freq_filename = NULL,
498 *time_in_state_filename = NULL, *schedstat_filename = NULL, *cpuidle_name_filename = NULL, *cpuidle_time_filename = NULL;
489 - static const RRDVAR_ACQUIRED *cpus_var = NULL;
499 +
500 static int accurate_freq_avail = 0, accurate_freq_is_used = 0;
501 size_t cores_found = (size_t)os_get_system_cpus();
502
src/collectors/proc.plugin/sys_class_infiniband.c
+12
@@ -186,6 +186,7 @@ static struct ibport {
186
187 const RRDVAR_ACQUIRED *stv_speed;
188
189 +
190 usec_t speed_last_collected_usec;
191
192 struct ibport *next;
@@ -216,6 +217,17 @@ void infiniband_hwcounters_parse_mlx(struct ibport *port)
217 if (port->do_hwpackets != CONFIG_BOOLEAN_NO)
218 FOREACH_HWCOUNTER_MLX_PACKETS(GEN_DO_HWCOUNTER_READ, port, port->hwcounters_mlx)
219 }
220 +void sys_class_infiniband_plugin_cleanup(void) {
221 + // Cleanup any acquired RRDVARs
222 + struct ibport *port;
223 + for (port = ibport_root; port; port = port->next) {
224 + if (port->stv_speed) {
225 + rrdvar_chart_variable_release(port->st_bytes, port->stv_speed);
226 + port->stv_speed = NULL;
227 + }
228 + }
229 +}
230 +
231 void infiniband_hwcounters_dorrd_mlx(struct ibport *port)
232 {
233 if (port->do_hwerrors != CONFIG_BOOLEAN_NO) {
src/daemon/daemon-shutdown.c
+19
@@ -13,6 +13,15 @@
13 #include "sentry-native/sentry-native.h"
14 #endif
15
16 +// External configuration structures that need cleanup
17 +extern struct config netdata_config;
18 +extern struct config cloud_config;
19 +extern void inicfg_free(struct config *root);
20 +// Functions to free various configurations
21 +extern void claim_config_free(void);
22 +extern void stream_config_free(void);
23 +extern void exporting_config_free(void);
24 +
25 static bool abort_on_fatal = true;
26
27 void abort_on_fatal_disable(void) {
@@ -373,6 +382,16 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal, bool exi
382 fprintf(stderr, "WARNING: UUIDMAP had %zu UUIDs referenced.\n",
383 uuid_referenced);
384
385 + fprintf(stderr, "Freeing configuration resources...\n");
386 + claim_config_free();
387 + exporting_config_free();
388 + stream_config_free();
389 + inicfg_free(&cloud_config);
390 + inicfg_free(&netdata_config);
391 +
392 + fprintf(stderr, "Cleaning up worker utilization...\n");
393 + worker_utilization_cleanup();
394 +
395 size_t strings_referenced = string_destroy();
396 if(strings_referenced)
397 fprintf(stderr, "WARNING: STRING has %zu strings still allocated.\n",
src/daemon/main.c
+1 -1
@@ -256,7 +256,7 @@ int netdata_main(int argc, char **argv) {
256
257 int i;
258 int config_loaded = 0;
259 - bool close_open_fds = true;
259 + bool close_open_fds = true; (void)close_open_fds;
260 size_t default_stacksize;
261 const char *user = NULL;
262
src/exporting/exporting_engine.h
+8
@@ -299,6 +299,14 @@ void send_internal_metrics(struct instance *instance);
299 void clean_instance(struct instance *ptr);
300 void simple_connector_cleanup(struct instance *instance);
301
302 +/**
303 + * Free exporting configuration
304 + *
305 + * Free all memory associated with the exporting configuration.
306 + * Called during shutdown to prevent memory leaks.
307 + */
308 +void exporting_config_free(void);
309 +
310 static inline void disable_instance(struct instance *instance)
311 {
312 instance->disabled = 1;
src/exporting/read_config.c
+10
@@ -7,6 +7,16 @@ const char *global_exporting_prefix = "netdata";
7
8 struct config exporting_config = APPCONFIG_INITIALIZER;
9
10 +/**
11 + * Free exporting configuration
12 + *
13 + * Free all memory associated with the exporting configuration.
14 + * Called during shutdown to prevent memory leaks.
15 + */
16 +void exporting_config_free(void) {
17 + inicfg_free(&exporting_config);
18 +}
19 +
20 struct instance *prometheus_exporter_instance = NULL;
21
22 static _CONNECTOR_INSTANCE *find_instance(const char *section)
src/libnetdata/inicfg/inicfg.h
+10
@@ -173,6 +173,16 @@ extern struct config netdata_config;
173 bool stream_conf_needs_dbengine(struct config *root);
174 bool stream_conf_has_api_enabled(struct config *root);
175
176 +/**
177 + * Free all configuration resources
178 + *
179 + * This function frees all memory associated with a configuration,
180 + * including all sections and options.
181 + *
182 + * @param root The config structure to free
183 + */
184 +void inicfg_free(struct config *root);
185 +
186 const char *inicfg_get(struct config *root, const char *section, const char *name, const char *default_value);
187 const char *inicfg_set(struct config *root, const char *section, const char *name, const char *value);
188
src/libnetdata/inicfg/inicfg_cleanup.c
+54
@@ -58,3 +58,57 @@ void inicfg_section_option_destroy_non_loaded(struct config *root, const char *s
58 SECTION_UNLOCK(sect);
59 }
60
61 +/**
62 + * Free all config memory
63 + *
64 + * This function frees all memory associated with a config structure,
65 + * including all sections and options.
66 + *
67 + * @param root The config structure to free
68 + */
69 +void inicfg_free(struct config *root) {
70 + if (!root)
71 + return;
72 +
73 + nd_log(NDLS_DAEMON, NDLP_DEBUG, "Freeing config memory");
74 +
75 + // First let's free the linked list (this will properly free all sections and options)
76 + APPCONFIG_LOCK(root);
77 + struct config_section *sect = root->sections;
78 +
79 + while (sect) {
80 + struct config_section *next_sect = sect->next;
81 +
82 + // Remove and free all options in this section
83 + SECTION_LOCK(sect);
84 + struct config_option *opt = sect->values;
85 + while (opt) {
86 + struct config_option *next_opt = opt->next;
87 +
88 + // Remove from index
89 + if(inicfg_option_del(sect, opt)) { ; }
90 +
91 + // Free the option
92 + inicfg_option_free(opt);
93 +
94 + opt = next_opt;
95 + }
96 + SECTION_UNLOCK(sect);
97 +
98 + // Remove from index
99 + if(inicfg_section_del(root, sect)) { ; }
100 +
101 + // Free the section
102 + inicfg_section_free(sect);
103 +
104 + sect = next_sect;
105 + }
106 +
107 + // Reset the sections pointer
108 + root->sections = NULL;
109 + APPCONFIG_UNLOCK(root);
110 +
111 + // Destroy the tree
112 + avl_destroy_lock(&root->index);
113 +}
114 +
src/libnetdata/libjudy/judy-malloc.c
+3
@@ -160,6 +160,9 @@ void libjudy_malloc_init(void) {
160 // IMPORTANT: this is not called on external plugins
161 // the allocator should run even if this is not called
162
163 + (void)jemalloc_initialized;
164 + (void)jemalloc_arena_index;
165 +
166 #ifdef HAVE_JEMALLOC_ARENA_API
167 jemalloc_init();
168 if(!jemalloc_initialized)
src/libnetdata/worker_utilization/worker_utilization.c
+11 -1
@@ -113,6 +113,15 @@ void workers_utilization_enable(void) {
113 workers_globals.enabled = true;
114 }
115
116 +void worker_utilization_cleanup(void) {
117 + if(!workers_globals.enabled)
118 + return;
119 +
120 + spinlock_lock(&workers_globals.spinlock);
121 + JudyHSFreeArray(&workers_globals.worknames_JudyHS, PJE0);
122 + spinlock_unlock(&workers_globals.spinlock);
123 +}
124 +
125 size_t workers_allocated_memory(void) {
126 if(!workers_globals.enabled)
127 return 0;
@@ -213,7 +222,8 @@ void worker_unregister(void) {
222 workers_globals.memory -= sizeof(struct worker) + strlen(worker->tag) + 1 + strlen(worker->workname) + 1;
223 spinlock_unlock(&workers_globals.spinlock);
224
216 - for(int i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
225 + // Free all thread-local resources associated with this worker
226 + for(int i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES; i++) {
227 string_freez(worker->per_job_type[i].name);
228 string_freez(worker->per_job_type[i].units);
229 }
src/libnetdata/worker_utilization/worker_utilization.h
+1
@@ -42,6 +42,7 @@ void worker_register(const char *name);
42 void worker_register_job_name(size_t job_id, const char *name);
43 void worker_register_job_custom_metric(size_t job_id, const char *name, const char *units, WORKER_METRIC_TYPE type);
44 void worker_unregister(void);
45 +void worker_utilization_cleanup(void);
46
47 size_t workers_get_last_job_id();
48
src/streaming/stream-conf.c
+10
@@ -7,6 +7,16 @@
7
8 static struct config stream_config = APPCONFIG_INITIALIZER;
9
10 +/**
11 + * Free stream configuration
12 + *
13 + * Free all memory associated with the stream configuration.
14 + * Called during shutdown to prevent memory leaks.
15 + */
16 +void stream_config_free(void) {
17 + inicfg_free(&stream_config);
18 +}
19 +
20 struct _stream_send stream_send = {
21 .enabled = false,
22 .api_key = NULL,
src/streaming/stream-conf.h
+8
@@ -99,4 +99,12 @@ bool stream_conf_api_key_allows_client(const char *api_key, const char *client_i
99
100 void stream_conf_set_sender_compression_levels(ND_COMPRESSION_PROFILE profile);
101
102 +/**
103 + * Free stream configuration
104 + *
105 + * Free all memory associated with the stream configuration.
106 + * Called during shutdown to prevent memory leaks.
107 + */
108 +void stream_config_free(void);
109 +
110 #endif //NETDATA_STREAM_CONF_H