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