@cryptotaxi247 / netdata-1 / commits / 7ebb0a4da

Fix memory leak when archived data is requested (#10837)

Stelios Fragkakis committed Mar 23, 2021 at 18:09 UTC 7ebb0a4da20465016a32a3c0b28635277926b815
4 files changed +88 -36
daemon/main.c
+1
@@ -50,6 +50,7 @@ void netdata_cleanup_and_exit(int ret) {
50 rrdeng_exit(&multidb_ctx);
51 #endif
52 }
53 + sql_close_database();
54
55 // unlink the pid
56 if(pidfile[0]) {
database/sqlite/sqlite_functions.c
+53 -12
@@ -48,6 +48,31 @@ static int execute_insert(sqlite3_stmt *res)
48 return rc;
49 }
50
51 +#define MAX_OPEN_STATEMENTS (512)
52 +
53 +static void add_stmt_to_list(sqlite3_stmt *res)
54 +{
55 + static int idx = 0;
56 + static sqlite3_stmt *statements[MAX_OPEN_STATEMENTS];
57 +
58 + if (unlikely(!res)) {
59 + while (idx > 0)
60 + sqlite3_finalize(statements[--idx]);
61 + return;
62 + }
63 +
64 + if (unlikely(idx == MAX_OPEN_STATEMENTS))
65 + return;
66 + statements[idx++] = res;
67 +}
68 +
69 +static int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement) {
70 + int rc = sqlite3_prepare_v2(database, query, -1, statement, 0);
71 + if (likely(rc == SQLITE_OK))
72 + add_stmt_to_list(*statement);
73 + return rc;
74 +}
75 +
76 /*
77 * Store a chart or dimension UUID in chart_active or dimension_active
78 * The statement that will be prepared determines that
@@ -178,9 +203,12 @@ void sql_close_database(void)
203 return;
204
205 info("Closing SQLite database");
181 - rc = sqlite3_close(db_meta);
206 +
207 + add_stmt_to_list(NULL);
208 +
209 + rc = sqlite3_close_v2(db_meta);
210 if (unlikely(rc != SQLITE_OK))
183 - error_report("Error %d while closing the SQLite database", rc);
211 + error_report("Error %d while closing the SQLite database, %s", rc, sqlite3_errstr(rc));
212 return;
213 }
214
@@ -193,7 +221,7 @@ int find_uuid_type(uuid_t *uuid)
221 int uuid_type = 3;
222
223 if (unlikely(!res)) {
196 - rc = sqlite3_prepare_v2(db_meta, FIND_UUID_TYPE, -1, &res, 0);
224 + rc = prepare_statement(db_meta, FIND_UUID_TYPE, &res);
225 if (rc != SQLITE_OK) {
226 error_report("Failed to bind prepare statement to find UUID type in the database");
227 return 0;
@@ -228,7 +256,7 @@ uuid_t *find_dimension_uuid(RRDSET *st, RRDDIM *rd)
256 return NULL;
257
258 if (unlikely(!res)) {
231 - rc = sqlite3_prepare_v2(db_meta, SQL_FIND_DIMENSION_UUID, -1, &res, 0);
259 + rc = prepare_statement(db_meta, SQL_FIND_DIMENSION_UUID, &res);
260 if (rc != SQLITE_OK) {
261 error_report("Failed to bind prepare statement to lookup dimension UUID in the database");
262 return NULL;
@@ -308,7 +336,7 @@ void delete_dimension_uuid(uuid_t *dimension_uuid)
336 #endif
337
338 if (unlikely(!res)) {
311 - rc = sqlite3_prepare_v2(db_meta, DELETE_DIMENSION_UUID, -1, &res, 0);
339 + rc = prepare_statement(db_meta, DELETE_DIMENSION_UUID, &res);
340 if (rc != SQLITE_OK) {
341 error_report("Failed to prepare statement to delete a dimension uuid");
342 return;
@@ -344,7 +372,7 @@ uuid_t *find_chart_uuid(RRDHOST *host, const char *type, const char *id, const c
372 return NULL;
373
374 if (unlikely(!res)) {
347 - rc = sqlite3_prepare_v2(db_meta, SQL_FIND_CHART_UUID, -1, &res, 0);
375 + rc = prepare_statement(db_meta, SQL_FIND_CHART_UUID, &res);
376 if (rc != SQLITE_OK) {
377 error_report("Failed to prepare statement to lookup chart UUID in the database");
378 return NULL;
@@ -449,7 +477,7 @@ int sql_store_host(
477 }
478
479 if (unlikely((!res))) {
452 - rc = sqlite3_prepare_v2(db_meta, SQL_STORE_HOST, -1, &res, 0);
480 + rc = prepare_statement(db_meta, SQL_STORE_HOST, &res);
481 if (unlikely(rc != SQLITE_OK)) {
482 error_report("Failed to prepare statement to store host, rc = %d", rc);
483 return 1;
@@ -510,7 +538,7 @@ int sql_store_chart(
538 const char *context, const char *title, const char *units, const char *plugin, const char *module, long priority,
539 int update_every, int chart_type, int memory_mode, long history_entries)
540 {
513 - static __thread sqlite3_stmt *res;
541 + static __thread sqlite3_stmt *res = NULL;
542 int rc, param = 0;
543
544 if (unlikely(!db_meta)) {
@@ -521,7 +549,7 @@ int sql_store_chart(
549 }
550
551 if (unlikely(!res)) {
524 - rc = sqlite3_prepare_v2(db_meta, SQL_STORE_CHART, -1, &res, 0);
552 + rc = prepare_statement(db_meta, SQL_STORE_CHART, &res);
553 if (unlikely(rc != SQLITE_OK)) {
554 error_report("Failed to prepare statement to store chart, rc = %d", rc);
555 return 1;
@@ -647,7 +675,7 @@ int sql_store_dimension(
675 }
676
677 if (unlikely(!res)) {
650 - rc = sqlite3_prepare_v2(db_meta, SQL_STORE_DIMENSION, -1, &res, 0);
678 + rc = prepare_statement(db_meta, SQL_STORE_DIMENSION, &res);
679 if (unlikely(rc != SQLITE_OK)) {
680 error_report("Failed to prepare statement to store dimension, rc = %d", rc);
681 return 1;
@@ -1252,6 +1280,11 @@ void sql_build_context_param_list(struct context_param **param_list, RRDHOST *ho
1280 sprintf(id, "%s.%s", sqlite3_column_text(res, 3), sqlite3_column_text(res, 1));
1281
1282 if (!st || uuid_compare(*(uuid_t *)sqlite3_column_blob(res, 7), chart_id)) {
1283 + if (unlikely(st && !st->counter)) {
1284 + freez(st->context);
1285 + freez((char *) st->name);
1286 + freez(st);
1287 + }
1288 st = callocz(1, sizeof(*st));
1289 char n[RRD_ID_LENGTH_MAX + 1];
1290
@@ -1282,8 +1315,16 @@ void sql_build_context_param_list(struct context_param **param_list, RRDHOST *ho
1315 rd->next = (*param_list)->rd;
1316 (*param_list)->rd = rd;
1317 }
1285 - if (likely(st && context && !st->context))
1286 - st->context = strdupz(context);
1318 + if (st) {
1319 + if (!st->counter) {
1320 + freez(st->context);
1321 + freez((char *)st->name);
1322 + freez(st);
1323 + }
1324 + else
1325 + if (!st->context && context)
1326 + st->context = strdupz(context);
1327 + }
1328
1329 failed:
1330 rc = sqlite3_finalize(res);
web/api/formatters/json_wrapper.c
+2 -2
@@ -48,8 +48,8 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
48 , kq, kq, sq, context_mode && temp_rd?r->st->context:r->st->name, sq
49 , kq, kq, r->update_every
50 , kq, kq, r->st->update_every
51 - , kq, kq, (uint32_t) (context_param_list ? context_param_list->first_entry_t : rrdset_first_entry_t(r->st))
52 - , kq, kq, (uint32_t) (context_param_list ? context_param_list->last_entry_t : rrdset_last_entry_t(r->st))
51 + , kq, kq, (uint32_t) (context_param_list ? context_param_list->first_entry_t : rrdset_first_entry_t_nolock(r->st))
52 + , kq, kq, (uint32_t) (context_param_list ? context_param_list->last_entry_t : rrdset_last_entry_t_nolock(r->st))
53 , kq, kq, (uint32_t)r->before
54 , kq, kq, (uint32_t)r->after
55 , kq, kq);
web/api/formatters/rrd2json.c
+32 -22
@@ -2,7 +2,28 @@
2
3 #include "web/api/web_api_v1.h"
4
5 -static inline void free_temp_rrddim(RRDDIM *temp_rd)
5 +static inline void free_single_rrdrim(RRDDIM *temp_rd, int archive_mode)
6 +{
7 + if (unlikely(!temp_rd))
8 + return;
9 +
10 + freez((char *)temp_rd->id);
11 + freez((char *)temp_rd->name);
12 +
13 + if (unlikely(archive_mode)) {
14 + temp_rd->rrdset->counter--;
15 + if (!temp_rd->rrdset->counter) {
16 + freez((char *)temp_rd->rrdset->name);
17 + freez(temp_rd->rrdset->context);
18 + freez(temp_rd->rrdset);
19 + }
20 + }
21 + freez(temp_rd->state->metric_uuid);
22 + freez(temp_rd->state);
23 + freez(temp_rd);
24 +}
25 +
26 +static inline void free_rrddim_list(RRDDIM *temp_rd, int archive_mode)
27 {
28 if (unlikely(!temp_rd))
29 return;
@@ -10,14 +31,7 @@ static inline void free_temp_rrddim(RRDDIM *temp_rd)
31 RRDDIM *t;
32 while (temp_rd) {
33 t = temp_rd->next;
13 - freez((char *)temp_rd->id);
14 - freez((char *)temp_rd->name);
15 -#ifdef ENABLE_DBENGINE
16 - if (temp_rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
17 - freez(temp_rd->state->metric_uuid);
18 -#endif
19 - freez(temp_rd->state);
20 - freez(temp_rd);
34 + free_single_rrdrim(temp_rd, archive_mode);
35 temp_rd = t;
36 }
37 }
@@ -27,7 +41,7 @@ void free_context_param_list(struct context_param **param_list)
41 if (unlikely(!param_list || !*param_list))
42 return;
43
30 - free_temp_rrddim(((*param_list)->rd));
44 + free_rrddim_list(((*param_list)->rd), (*param_list)->flags & CONTEXT_FLAGS_ARCHIVE);
45 freez((*param_list));
46 *param_list = NULL;
47 }
@@ -36,21 +50,17 @@ void rebuild_context_param_list(struct context_param *context_param_list, time_t
50 {
51 RRDDIM *temp_rd = context_param_list->rd;
52 RRDDIM *new_rd_list = NULL, *t;
53 + int is_archived = (context_param_list->flags & CONTEXT_FLAGS_ARCHIVE);
54 while (temp_rd) {
55 t = temp_rd->next;
41 - if (rrdset_last_entry_t(temp_rd->rrdset) >= after_requested) {
56 + RRDSET *st = temp_rd->rrdset;
57 + time_t last_entry_t = is_archived ? st->last_entry_t : rrdset_last_entry_t(st);
58 +
59 + if (last_entry_t >= after_requested) {
60 temp_rd->next = new_rd_list;
61 new_rd_list = temp_rd;
44 - } else {
45 - freez((char *)temp_rd->id);
46 - freez((char *)temp_rd->name);
47 -#ifdef ENABLE_DBENGINE
48 - if (temp_rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
49 - freez(temp_rd->state->metric_uuid);
50 -#endif
51 - freez(temp_rd->state);
52 - freez(temp_rd);
53 - }
62 + } else
63 + free_single_rrdrim(temp_rd, is_archived);
64 temp_rd = t;
65 }
66 context_param_list->rd = new_rd_list;
@@ -65,7 +75,7 @@ void build_context_param_list(struct context_param **param_list, RRDSET *st)
75 *param_list = mallocz(sizeof(struct context_param));
76 (*param_list)->first_entry_t = LONG_MAX;
77 (*param_list)->last_entry_t = 0;
68 - (*param_list)->flags = 0;
78 + (*param_list)->flags = CONTEXT_FLAGS_CONTEXT;
79 (*param_list)->rd = NULL;
80 }
81