@cryptotaxi247 / netdata-1 / commits / acb738e87

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