improve status file deduplication (#19726)
* move restarts in agent from dedup - keep reading it for v3 from dedup * use up to 10 hashes for deduplicating crash events * monitor shutdown steps * leftover x-ray vision * fix out of memory * out of memory properly calculates max rss
Costa Tsaousis committed
Feb 27, 2025 at 12:03 UTC
acb738e873011da5f227704189eb8c359147b3da
8 files changed
+242
-55
netdata-installer.sh
+1
-1
@@ -1167,6 +1167,6 @@ else
1167
progress "is installed now!"
1168
fi
1169
1170
-echo >&2 " enjoy real-time performance and health monitoring..."
1170
+echo >&2 " Enjoy X-Ray Vision for your infrastructure..."
1171
echo >&2
1172
exit 0
src/daemon/daemon-shutdown-watcher.c
+4
-3
@@ -1,6 +1,7 @@
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
3
#include "daemon-shutdown-watcher.h"
4
+#include "daemon-status-file.h"
5
6
watcher_step_t *watcher_steps;
7
@@ -33,6 +34,8 @@ static void watcher_wait_for_step(const watcher_step_id_t step_id, usec_t shutdo
34
(int)step_id + 1, (int)WATCHER_STEP_ID_MAX, start_duration_txt,
35
watcher_steps[step_id].msg);
36
37
+ daemon_status_file_shutdown_step(watcher_steps[step_id].msg);
38
+
39
#ifdef ENABLE_SENTRY
40
// Wait with a timeout
41
time_t timeout = 135; // systemd gives us 150, we timeout at 135
@@ -65,6 +68,7 @@ static void watcher_wait_for_step(const watcher_step_id_t step_id, usec_t shutdo
68
(int)step_id + 1, (int)WATCHER_STEP_ID_MAX, start_duration_txt,
69
watcher_steps[step_id].msg, step_duration_txt);
70
71
+ daemon_status_file_shutdown_step("sentry timeout");
72
abort();
73
}
74
}
@@ -104,7 +108,6 @@ void *watcher_main(void *arg)
108
watcher_wait_for_step(WATCHER_STEP_ID_CLOSE_SQL_DATABASES, shutdown_start_time);
109
watcher_wait_for_step(WATCHER_STEP_ID_REMOVE_PID_FILE, shutdown_start_time);
110
watcher_wait_for_step(WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES, shutdown_start_time);
107
- watcher_wait_for_step(WATCHER_STEP_ID_REMOVE_INCOMPLETE_SHUTDOWN_FILE, shutdown_start_time);
111
112
completion_wait_for(&shutdown_end_completion);
113
usec_t shutdown_end_time = now_monotonic_usec();
@@ -165,8 +168,6 @@ void watcher_thread_start() {
168
"remove pid file";
169
watcher_steps[WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES].msg =
170
"free openssl structures";
168
- watcher_steps[WATCHER_STEP_ID_REMOVE_INCOMPLETE_SHUTDOWN_FILE].msg =
169
- "remove incomplete shutdown file";
171
172
for (size_t i = 0; i != WATCHER_STEP_ID_MAX; i++) {
173
completion_init(&watcher_steps[i].p);
src/daemon/daemon-shutdown-watcher.h
-1
@@ -29,7 +29,6 @@ typedef enum {
29
WATCHER_STEP_ID_CLOSE_SQL_DATABASES,
30
WATCHER_STEP_ID_REMOVE_PID_FILE,
31
WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES,
32
- WATCHER_STEP_ID_REMOVE_INCOMPLETE_SHUTDOWN_FILE,
32
33
// Always keep this as the last enum value
34
WATCHER_STEP_ID_MAX
src/daemon/daemon-shutdown.c
+1
-2
@@ -316,12 +316,11 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
316
netdata_ssl_cleanup();
317
watcher_step_complete(WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES);
318
319
- watcher_step_complete(WATCHER_STEP_ID_REMOVE_INCOMPLETE_SHUTDOWN_FILE);
320
-
319
watcher_shutdown_end();
320
watcher_thread_stop();
321
curl_global_cleanup();
322
323
+ daemon_status_file_shutdown_step(NULL);
324
daemon_status_file_update_status(DAEMON_STATUS_EXITED);
325
326
#ifdef OS_WINDOWS
src/daemon/daemon-status-file.c
+135
-32
@@ -9,7 +9,7 @@
9
#include <openssl/pem.h>
10
#include <openssl/err.h>
11
12
-#define STATUS_FILE_VERSION 3
12
+#define STATUS_FILE_VERSION 4
13
14
#define STATUS_FILENAME "status-netdata.json"
15
@@ -81,6 +81,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
81
82
buffer_json_member_add_uuid(wb, "ND_node_id", ds->node_id.uuid); // custom
83
buffer_json_member_add_uuid(wb, "ND_claim_id", ds->claim_id.uuid); // custom
84
+ buffer_json_member_add_uint64(wb, "ND_restarts", ds->restarts); // custom
85
86
ND_PROFILE_2json(wb, "ND_profile", ds->profile); // custom
87
buffer_json_member_add_string(wb, "ND_status", DAEMON_STATUS_2str(ds->status)); // custom
@@ -155,13 +156,21 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
156
}
157
buffer_json_object_close(wb);
158
158
- buffer_json_member_add_object(wb, "dedup"); // custom
159
+ buffer_json_member_add_array(wb, "dedup"); // custom
160
{
160
- buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->dedup.timestamp_ut, true); // custom
161
- buffer_json_member_add_uint64(wb, "hash", ds->dedup.hash); // custom
162
- buffer_json_member_add_uint64(wb, "restarts", ds->dedup.restarts); // custom
161
+ for(size_t i = 0; i < _countof(ds->dedup); i++) {
162
+ if (ds->dedup[i].timestamp_ut == 0)
163
+ continue;
164
+
165
+ buffer_json_add_array_item_object(wb); // custom
166
+ {
167
+ buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->dedup[i].timestamp_ut, true); // custom
168
+ buffer_json_member_add_uint64(wb, "hash", ds->dedup[i].hash); // custom
169
+ }
170
+ buffer_json_object_close(wb);
171
+ }
172
}
164
- buffer_json_object_close(wb);
173
+ buffer_json_array_close(wb);
174
}
175
176
// --------------------------------------------------------------------------------------------------------------------
@@ -180,6 +189,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
189
bool strict = false; // allow missing fields and values
190
bool required_v1 = version >= 1 ? strict : false;
191
bool required_v3 = version >= 3 ? strict : false;
192
+ bool required_v4 = version >= 4 ? strict : false;
193
194
// Parse timestamp
195
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
@@ -203,6 +213,9 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
213
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "init", ds->timings.init, error, required_v1);
214
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "exit", ds->timings.exit, error, required_v1);
215
});
216
+
217
+ if(version >= 4)
218
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "ND_restarts", ds->restarts, error, required_v4);
219
});
220
221
// Parse host object
@@ -257,15 +270,32 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
270
});
271
272
// Parse the last posted object
260
- JSONC_PARSE_SUBOBJECT(jobj, path, "dedup", error, required_v3, {
261
- datetime[0] = '\0';
262
- JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
263
- if(datetime[0])
264
- ds->dedup.timestamp_ut = rfc3339_parse_ut(datetime, NULL);
265
-
266
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "hash", ds->dedup.hash, error, required_v3);
267
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "restarts", ds->dedup.restarts, error, required_v3);
268
- });
273
+ if(version == 3) {
274
+ JSONC_PARSE_SUBOBJECT(jobj, path, "dedup", error, required_v3, {
275
+ datetime[0] = '\0';
276
+ JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v3);
277
+ if (datetime[0])
278
+ ds->dedup[0].timestamp_ut = rfc3339_parse_ut(datetime, NULL);
279
+
280
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "hash", ds->dedup[0].hash, error, required_v3);
281
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "restarts", ds->restarts, error, required_v3);
282
+ });
283
+ }
284
+ else if(version >= 4) {
285
+ JSONC_PARSE_ARRAY(jobj, path, "dedup", error, required_v4, {
286
+ size_t i = 0;
287
+ JSONC_PARSE_ARRAY_ITEM_OBJECT(jobj, path, i, required_v4, {
288
+ if(i >= _countof(ds->dedup))
289
+ break;
290
+
291
+ JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v4);
292
+ if (datetime[0])
293
+ ds->dedup[i].timestamp_ut = rfc3339_parse_ut(datetime, NULL);
294
+
295
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "hash", ds->dedup[i].hash, error, required_v4);
296
+ });
297
+ });
298
+ }
299
300
return true;
301
}
@@ -349,11 +379,11 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
379
session_status.os_id = strdupz(last_session_status.os_id);
380
if(!session_status.os_id_like && last_session_status.os_id_like)
381
session_status.os_id_like = strdupz(last_session_status.os_id_like);
352
- if(!session_status.dedup.restarts)
353
- session_status.dedup.restarts = last_session_status.dedup.restarts + 1;
354
- if(!session_status.dedup.timestamp_ut || !session_status.dedup.hash) {
355
- session_status.dedup.timestamp_ut = last_session_status.dedup.timestamp_ut;
356
- session_status.dedup.hash = last_session_status.dedup.hash;
382
+ if(!session_status.restarts)
383
+ session_status.restarts = last_session_status.restarts + 1;
384
+ if(!session_status.dedup[0].timestamp_ut || !session_status.dedup[0].hash) {
385
+ for (size_t i = 0; i < _countof(session_status.dedup); i++)
386
+ session_status.dedup[i] = last_session_status.dedup[i];
387
}
388
389
if(!session_status.install_type) {
@@ -561,6 +591,65 @@ static void daemon_status_file_out_of_memory(void) {
591
daemon_status_file_exit_reason_save(EXIT_REASON_OUT_OF_MEMORY);
592
}
593
594
+// --------------------------------------------------------------------------------------------------------------------
595
+// deduplication hashes management
596
+
597
+static bool dedup_already_posted(XXH64_hash_t hash) {
598
+ spinlock_lock(&dsf_spinlock);
599
+
600
+ usec_t now_ut = now_realtime_usec();
601
+
602
+ for(size_t i = 0; i < _countof(session_status.dedup); i++) {
603
+ if(session_status.dedup[i].timestamp_ut == 0)
604
+ continue;
605
+
606
+ if(hash == session_status.dedup[i].hash &&
607
+ now_ut - session_status.dedup[i].timestamp_ut < 86400 * USEC_PER_SEC) {
608
+ // we have already posted this crash
609
+ spinlock_unlock(&dsf_spinlock);
610
+ return true;
611
+ }
612
+ }
613
+
614
+ spinlock_unlock(&dsf_spinlock);
615
+ return false;
616
+}
617
+
618
+static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, XXH64_hash_t hash) {
619
+ spinlock_lock(&dsf_spinlock);
620
+
621
+ // find the same hash
622
+ for(size_t i = 0; i < _countof(ds->dedup); i++) {
623
+ if(ds->dedup[i].hash == hash) {
624
+ ds->dedup[i].timestamp_ut = now_realtime_usec();
625
+ spinlock_unlock(&dsf_spinlock);
626
+ return;
627
+ }
628
+ }
629
+
630
+ // find an empty slot
631
+ for(size_t i = 0; i < _countof(ds->dedup); i++) {
632
+ if(!ds->dedup[i].hash) {
633
+ ds->dedup[i].hash = hash;
634
+ ds->dedup[i].timestamp_ut = now_realtime_usec();
635
+ spinlock_unlock(&dsf_spinlock);
636
+ return;
637
+ }
638
+ }
639
+
640
+ // find the oldest slot
641
+ size_t store_at_slot = 0;
642
+ for(size_t i = 1; i < _countof(ds->dedup); i++) {
643
+ if(ds->dedup[i].timestamp_ut < ds->dedup[store_at_slot].timestamp_ut)
644
+ store_at_slot = i;
645
+ }
646
+
647
+ ds->dedup[store_at_slot].timestamp_ut = now_realtime_usec();
648
+ ds->dedup[store_at_slot].hash = hash;
649
+
650
+ spinlock_unlock(&dsf_spinlock);
651
+}
652
+
653
// --------------------------------------------------------------------------------------------------------------------
654
// POST the last status to agent-events
655
@@ -596,10 +685,7 @@ void post_status_file(struct post_status_file_thread_data *d) {
685
CURLcode rc = curl_easy_perform(curl);
686
if(rc == CURLE_OK) {
687
XXH64_hash_t hash = daemon_status_file_hash(&d->status, d->msg, d->cause);
599
- spinlock_lock(&dsf_spinlock);
600
- session_status.dedup.timestamp_ut = now_realtime_usec();
601
- session_status.dedup.hash = hash;
602
- spinlock_unlock(&dsf_spinlock);
688
+ dedup_keep_hash(&session_status, hash);
689
daemon_status_file_save(&session_status);
690
}
691
@@ -797,14 +883,9 @@ void daemon_status_file_check_crash(void) {
883
"Last exit status: %s (%s):\n\n%s",
884
NETDATA_VERSION, msg, cause, buffer_tostring(wb));
885
800
- if(last_session_status.dedup.timestamp_ut && last_session_status.dedup.hash) {
801
- XXH64_hash_t hash = daemon_status_file_hash(&last_session_status, msg, cause);
802
- if(hash == last_session_status.dedup.hash &&
803
- now_realtime_usec() - last_session_status.dedup.timestamp_ut < 86400 * USEC_PER_SEC) {
804
- // we have already posted this crash
805
- disable_crash_report = true;
806
- }
807
- }
886
+ // check if we have already posted this crash in the last 24 hours
887
+ XXH64_hash_t hash = daemon_status_file_hash(&last_session_status, msg, cause);
888
+ disable_crash_report = dedup_already_posted(hash);
889
890
if(!disable_crash_report && (analytics_check_enabled() || post_crash_report)) {
891
netdata_conf_ssl();
@@ -827,12 +908,34 @@ bool daemon_status_file_was_incomplete_shutdown(void) {
908
}
909
910
void daemon_status_file_startup_step(const char *step) {
911
+ if(session_status.fatal.filename)
912
+ // we have a fatal logged
913
+ return;
914
+
915
freez((char *)session_status.fatal.function);
916
session_status.fatal.function = step ? strdupz(step) : NULL;
917
if(step != NULL)
918
daemon_status_file_update_status(DAEMON_STATUS_NONE);
919
}
920
921
+void daemon_status_file_shutdown_step(const char *step) {
922
+ if(session_status.fatal.filename)
923
+ // we have a fatal logged
924
+ return;
925
+
926
+ freez((char *)session_status.fatal.function);
927
+ if(!step)
928
+ session_status.fatal.function = NULL;
929
+
930
+ else {
931
+ char buf[1024];
932
+ snprintfz(buf, sizeof(buf), "shutdown(%s)", step);
933
+ session_status.fatal.function = strdupz(buf);
934
+ }
935
+
936
+ daemon_status_file_update_status(DAEMON_STATUS_NONE);
937
+}
938
+
939
// --------------------------------------------------------------------------------------------------------------------
940
// ng_log() hook for receiving fatal message information
941
src/daemon/daemon-status-file.h
+4
-2
@@ -34,6 +34,7 @@ typedef struct daemon_status_file {
34
time_t boottime; // system boottime
35
time_t uptime; // netdata uptime
36
usec_t timestamp_ut; // the timestamp of the status file
37
+ size_t restarts; // the number of times this agent has restarted
38
39
ND_UUID boot_id; // the boot id of the system
40
ND_UUID invocation; // the netdata invocation id generated the file
@@ -72,8 +73,7 @@ typedef struct daemon_status_file {
73
struct {
74
XXH64_hash_t hash;
75
usec_t timestamp_ut;
75
- size_t restarts;
76
- } dedup;
76
+ } dedup[10];
77
} DAEMON_STATUS_FILE;
78
79
// loads the last status saved
@@ -88,7 +88,9 @@ void daemon_status_file_check_crash(void);
88
89
bool daemon_status_file_has_last_crashed(void);
90
bool daemon_status_file_was_incomplete_shutdown(void);
91
+
92
void daemon_status_file_startup_step(const char *step);
93
+void daemon_status_file_shutdown_step(const char *step);
94
95
void daemon_status_file_register_fatal(const char *filename, const char *function, const char *message, const char *errno_str, const char *stack_trace, long line);
96
src/libnetdata/json/json-c-parser-inline.h
+83
-12
@@ -262,6 +262,17 @@
262
strncpyz(path + len, member, sizeof_path - len); \
263
} while(0)
264
265
+#define JSONC_PATH_CONCAT_INDEX(path, sizeof_path, index, error) do { \
266
+ char _idx_str[32]; \
267
+ snprintfz(_idx_str, sizeof(_idx_str), "[%zu]", index); \
268
+ size_t _path_len = strlen(path); \
269
+ if (_path_len + strlen(_idx_str) >= sizeof_path) { \
270
+ buffer_sprintf(error, "path too long while adding array index"); \
271
+ return false; \
272
+ } \
273
+ strncpyz(path + _path_len, _idx_str, sizeof_path - _path_len); \
274
+} while(0)
275
+
276
#define JSONC_PARSE_SUBOBJECT(jobj, path, member, error, required, block) do { \
277
BUILD_BUG_ON(sizeof(path) < 128); /* ensure path is an array of at least 128 bytes */ \
278
json_object *JSONC_TEMP_VAR(_j, __LINE__); \
@@ -273,19 +284,79 @@
284
} \
285
else { \
286
if (!json_object_is_type(JSONC_TEMP_VAR(_j, __LINE__), json_type_object)) { \
276
- buffer_sprintf(error, "not an object '%s.%s'", *path ? path : "", member); \
277
- return false; \
287
+ if(required) { \
288
+ buffer_sprintf(error, "not an object '%s.%s'", *path ? path : "", member); \
289
+ return false; \
290
+ } \
291
+ } \
292
+ else { \
293
+ json_object *JSONC_TEMP_VAR(saved_jobj, __LINE__) = jobj; \
294
+ jobj = JSONC_TEMP_VAR(_j, __LINE__); \
295
+ char JSONC_TEMP_VAR(saved_path, __LINE__)[strlen(path) + 1]; \
296
+ strncpyz(JSONC_TEMP_VAR(saved_path, __LINE__), path, sizeof(JSONC_TEMP_VAR(saved_path, __LINE__))); \
297
+ JSONC_PATH_CONCAT(path, sizeof(path), path, member, error); \
298
+ /* Run the user's code block */ \
299
+ block \
300
+ /* Restore the previous scope's values */ \
301
+ jobj = JSONC_TEMP_VAR(saved_jobj, __LINE__); \
302
+ strncpyz(path, JSONC_TEMP_VAR(saved_path, __LINE__), sizeof(path)); \
303
+ } \
304
+ } \
305
+} while(0)
306
+
307
+#define JSONC_PARSE_ARRAY(jobj, path, member, error, required, block) do { \
308
+ BUILD_BUG_ON(sizeof(path) < 128); /* ensure path is an array of at least 128 bytes */ \
309
+ json_object *JSONC_TEMP_VAR(_jarray, __LINE__); \
310
+ if (!json_object_object_get_ex(jobj, member, &JSONC_TEMP_VAR(_jarray, __LINE__))) { \
311
+ if (required) { \
312
+ buffer_sprintf(error, "missing '%s.%s' array", *path ? path : "", member); \
313
+ return false; \
314
+ } \
315
+ } \
316
+ else { \
317
+ if (!json_object_is_type(JSONC_TEMP_VAR(_jarray, __LINE__), json_type_array)) { \
318
+ if (required) { \
319
+ buffer_sprintf(error, "not an array '%s.%s'", *path ? path : "", member); \
320
+ return false; \
321
+ } \
322
+ } \
323
+ else { \
324
+ json_object *JSONC_TEMP_VAR(saved_jobj, __LINE__) = jobj; \
325
+ jobj = JSONC_TEMP_VAR(_jarray, __LINE__); \
326
+ char JSONC_TEMP_VAR(saved_path, __LINE__)[strlen(path) + 1]; \
327
+ strncpyz(JSONC_TEMP_VAR(saved_path, __LINE__), path, sizeof(JSONC_TEMP_VAR(saved_path, __LINE__))); \
328
+ JSONC_PATH_CONCAT(path, sizeof(path), path, member, error); \
329
+ /* Run the user's code block */ \
330
+ block \
331
+ /* Restore the previous scope's values */ \
332
+ jobj = JSONC_TEMP_VAR(saved_jobj, __LINE__); \
333
+ strncpyz(path, JSONC_TEMP_VAR(saved_path, __LINE__), sizeof(path)); \
334
+ } \
335
+ } \
336
+} while(0)
337
+
338
+#define JSONC_PARSE_ARRAY_ITEM_OBJECT(jobj, path, index, required, block) do { \
339
+ size_t JSONC_TEMP_VAR(_array_len, __LINE__) = json_object_array_length(jobj); \
340
+ for (index = 0; index < JSONC_TEMP_VAR(_array_len, __LINE__); index++) { \
341
+ json_object *JSONC_TEMP_VAR(_jitem, __LINE__) = json_object_array_get_idx(jobj, index); \
342
+ if (!json_object_is_type(JSONC_TEMP_VAR(_jitem, __LINE__), json_type_object)) { \
343
+ if(required) { \
344
+ buffer_sprintf(error, "not an object '%s[%zu]'", *path ? path : "", index); \
345
+ return false; \
346
+ } \
347
+ } \
348
+ else { \
349
+ json_object *JSONC_TEMP_VAR(saved_jobj, __LINE__) = jobj; \
350
+ jobj = JSONC_TEMP_VAR(_jitem, __LINE__); \
351
+ char JSONC_TEMP_VAR(saved_path, __LINE__)[strlen(path) + 1]; \
352
+ strncpyz(JSONC_TEMP_VAR(saved_path, __LINE__), path, sizeof(JSONC_TEMP_VAR(saved_path, __LINE__))); \
353
+ JSONC_PATH_CONCAT_INDEX(path, sizeof(path), index, error); \
354
+ /* Run the user's code block */ \
355
+ block \
356
+ /* Restore the previous scope's values */ \
357
+ jobj = JSONC_TEMP_VAR(saved_jobj, __LINE__); \
358
+ strncpyz(path, JSONC_TEMP_VAR(saved_path, __LINE__), sizeof(path)); \
359
} \
279
- json_object *JSONC_TEMP_VAR(saved_jobj, __LINE__) = jobj; \
280
- jobj = JSONC_TEMP_VAR(_j, __LINE__); \
281
- char JSONC_TEMP_VAR(saved_path, __LINE__)[strlen(path) + 1]; \
282
- strncpyz(JSONC_TEMP_VAR(saved_path, __LINE__), path, sizeof(JSONC_TEMP_VAR(saved_path, __LINE__))); \
283
- JSONC_PATH_CONCAT(path, sizeof(path), path, member, error); \
284
- /* Run the user's code block */ \
285
- block \
286
- /* Restore the previous scope's values */ \
287
- jobj = JSONC_TEMP_VAR(saved_jobj, __LINE__); \
288
- strncpyz(path, JSONC_TEMP_VAR(saved_path, __LINE__), sizeof(path)); \
360
} \
361
} while(0)
362
src/libnetdata/memory/nd-mallocz.c
+14
-2
@@ -12,16 +12,28 @@ static void out_of_memory(const char *call, size_t size) {
12
if(out_of_memory_callback)
13
out_of_memory_callback();
14
15
+#if defined(OS_LINUX) || defined(OS_WINDOWS)
16
+ int rss_multiplier = 1024;
17
+#else
18
+ int rss_multiplier = 1;
19
+#endif
20
+
21
struct rusage usage = { 0 };
22
if(getrusage(RUSAGE_SELF, &usage) != 0)
23
usage.ru_maxrss = 0;
24
25
+ char mem_available[64];
26
+ char rss_used[64];
27
+
28
OS_SYSTEM_MEMORY sm = os_last_reported_system_memory();
29
+ size_snprintf(mem_available, sizeof(mem_available), sm.ram_available_bytes, "B", false);
30
+ size_snprintf(rss_used, sizeof(rss_used), usage.ru_maxrss * rss_multiplier, "B", false);
31
+
32
fatal("Out of memory on %s(%zu bytes)!\n"
21
- "System memory available: %lu, while our max RSS usage is: %ld\n"
33
+ "System memory available: %s, while our max RSS usage is: %s\n"
34
"O/S mmap limit: %llu, while our mmap count is: %zu",
35
call, size,
24
- sm.ram_available_bytes, usage.ru_maxrss,
36
+ mem_available, rss_used,
37
os_mmap_limit(), __atomic_load_n(&nd_mmap_count, __ATOMIC_RELAXED));
38
}
39