daemon status file 16 (#19863)
* store sentry status into the status file * handle more deadly signals: SIGSYS, SIGXCPU, SIGXFSZ, SIGEMT * unblock deadly signals on every thread * add aral max allocation size to nd_profile, to dynamically select the max allocation size depending on profile * disable machine learning by default on iot profile * added signal si_code to status file * chain sentry handlers after our own * deduplicate sentry events * fixes * removed SIGEMT
Costa Tsaousis committed
Mar 14, 2025 at 19:37 UTC
17a53bceb8a9ed69406e62d1993d286a7c0ccea1
22 files changed
+530
-131
CMakeLists.txt
+2
@@ -1071,6 +1071,8 @@ set(LIBNETDATA_FILES
1071
src/libnetdata/signals/signals.h
1072
src/libnetdata/os/machine_id.c
1073
src/libnetdata/os/machine_id.h
1074
+ src/libnetdata/signals/signal-code.c
1075
+ src/libnetdata/signals/signal-code.h
1076
)
1077
1078
list(APPEND LIBNETDATA_FILES ${INICFG_FILES})
src/daemon/config/README.md
+25
-2
@@ -37,10 +37,11 @@ After `netdata.conf` has been modified, Netdata needs to be [restarted](/docs/ne
37
38
| setting | default | info |
39
|:----------------------------------:|:--------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
40
+| profile | auto-detected | Can be `iot`, `child`, `parent`, `standalone`. Depending on the profile detected, Netdata changes various internal settings (like the number of allocation arenas, the max allocation size, streaming compression levels, shared memory cleanup frequency, etc) to optimize performance and balance resources usage. Especially for `iot`, it disables machine learning based anomaly detection. See below for more information. |
41
| process scheduling policy | `keep` | See [Netdata process scheduling policy](/src/daemon/README.md#process-scheduling-policy-unix-only) |
42
| OOM score | `0` | |
42
-| glibc malloc arena max for plugins | `1` | |
43
-| glibc malloc arena max for Netdata | `1` | |
43
+| glibc malloc arena max for plugins | auto-detected | This settings affects memory allocations performance and fragmentation. More arenas give better performance, but they introduce more fragmentation. |
44
+| glibc malloc arena max for Netdata | auto-detected | This settings affects memory allocations performance and fragmentation. More arenas give better performance, but they introduce more fragmentation. |
45
| hostname | auto-detected | The hostname of the computer running Netdata. |
46
| host access prefix | empty | This is used in Docker environments where /proc, /sys, etc have to be accessed via another path. You may also have to set SYS_PTRACE capability on the docker for this work. Check [issue 43](https://github.com/netdata/netdata/issues/43). |
47
| timezone | auto-detected | The timezone retrieved from the environment variable |
@@ -48,6 +49,28 @@ After `netdata.conf` has been modified, Netdata needs to be [restarted](/docs/ne
49
| pthread stack size | auto-detected | |
50
| crash reports | `all` or `off` | `all` when anonymous telemetry is enabled, or the agent is claimed or connected to Netdata Cloud (directly or via a Netdata Parent). When it is `all` Netdata reports restarts and crashes. It can also be `crashes` to report only crashes. When it is `off` nothing is reported. Each kind of event is deduplicated and reported at most once per day. [Read more at this blog post](https://www.netdata.cloud/blog/2025-03-06-monitoring-netdata-restarts/). |
51
52
+#### Profiles
53
+
54
+The profiles are detected in this order:
55
+
56
+1. `iot` is used when the system has 1 CPU core and/or less than 1GiB of RAM. It has the highest priority among all the profiles, so that if this is detected, it will be used instead of any of the others.
57
+2. `parent` is detected when `stream.conf` has configuration for receiving data from child nodes and the system is not `iot`.
58
+3. `child` is detected when `stream.conf` has configuration for sending data to a parent node, does not have configuration for receiving data from other nodes, and the system is not `iot`.
59
+4. `standalone` is the fallback profile when none of the above are detected.
60
+
61
+The following are the parameters affected by the profile:
62
+
63
+| Feature | iot | parent | child | standalone |
64
+|:----------------------------------------:|:-------:|:----------:|:--------:|:----------:|
65
+| libc allocation arenas | 1 | 4 | 1 | 1 |
66
+| libc memory reclaiming | 16KiB | 128KiB | 32KiB | 64KiB |
67
+| outbound streaming compression level | fastest | fastest | balanced | balanced |
68
+| max batch allocation size | 16KiB | 2MiB (THP) | 32KiB | 64KiB |
69
+| machine learning training | off | auto | auto | auto |
70
+| dbengine journal files unmapping timeout | 2m | off | 2m | 2m |
71
+
72
+A few of these settings can be individually configured in `netdata.conf`, like the libc allocation arenas and machine learning. The rest are automatically set based on the profile.
73
+
74
### `db` section options
75
76
| setting | default | info |
src/daemon/config/netdata-conf-profile.c
+10
-1
@@ -102,9 +102,11 @@ void nd_profile_setup(void) {
102
nd_profile.storage_tiers = 3; // MUST BE 1
103
nd_profile.update_every = 1; // MUST BE 2
104
nd_profile.malloc_arenas = 1;
105
- nd_profile.malloc_trim = 32 * 1024;
105
+ nd_profile.malloc_trim = 16 * 1024;
106
nd_profile.stream_sender_compression = ND_COMPRESSION_FASTEST;
107
nd_profile.dbengine_journal_v2_unmount_time = 120;
108
+ nd_profile.max_page_size = 16 * 1024;
109
+ nd_profile.ml_enabled = CONFIG_BOOLEAN_NO;
110
// web server threads = 6
111
// aclk query threads = 6
112
// backfill threads = 0
@@ -122,6 +124,8 @@ void nd_profile_setup(void) {
124
nd_profile.malloc_trim = 128 * 1024;
125
nd_profile.stream_sender_compression = ND_COMPRESSION_FASTEST;
126
nd_profile.dbengine_journal_v2_unmount_time = 0;
127
+ nd_profile.max_page_size = 2 * 1024 * 1024; // 2MB for THP
128
+ nd_profile.ml_enabled = CONFIG_BOOLEAN_AUTO;
129
// web server threads = dynamic
130
// aclk query threads = dynamic
131
// backfill threads = dynamic
@@ -136,6 +140,8 @@ void nd_profile_setup(void) {
140
nd_profile.malloc_trim = 32 * 1024;
141
nd_profile.stream_sender_compression = ND_COMPRESSION_DEFAULT;
142
nd_profile.dbengine_journal_v2_unmount_time = 120;
143
+ nd_profile.max_page_size = 32 * 1024;
144
+ nd_profile.ml_enabled = CONFIG_BOOLEAN_AUTO;
145
// web server threads = 6
146
// aclk query threads = 6
147
// backfill threads = 0
@@ -150,6 +156,8 @@ void nd_profile_setup(void) {
156
nd_profile.malloc_trim = 64 * 1024;
157
nd_profile.stream_sender_compression = ND_COMPRESSION_DEFAULT;
158
nd_profile.dbengine_journal_v2_unmount_time = 120;
159
+ nd_profile.max_page_size = 64 * 1024;
160
+ nd_profile.ml_enabled = CONFIG_BOOLEAN_AUTO;
161
// web server threads = 6
162
// aclk query threads = 6
163
// backfill threads = 0
@@ -158,6 +166,7 @@ void nd_profile_setup(void) {
166
// health enabled = true
167
}
168
169
+ aral_optimal_malloc_page_size_set(nd_profile.max_page_size);
170
netdata_conf_glibc_malloc_initialize(nd_profile.malloc_arenas, nd_profile.malloc_trim);
171
stream_conf_set_sender_compression_levels(nd_profile.stream_sender_compression);
172
}
src/daemon/config/netdata-conf-profile.h
+2
@@ -52,8 +52,10 @@ struct nd_profile_t {
52
time_t update_every;
53
size_t malloc_arenas;
54
size_t malloc_trim;
55
+ size_t max_page_size;
56
time_t dbengine_journal_v2_unmount_time;
57
ND_COMPRESSION_PROFILE stream_sender_compression;
58
+ int ml_enabled;
59
};
60
61
extern struct nd_profile_t nd_profile;
src/daemon/daemon-status-file.c
+104
-44
@@ -9,7 +9,7 @@
9
#include <openssl/pem.h>
10
#include <openssl/err.h>
11
12
-#define STATUS_FILE_VERSION 15
12
+#define STATUS_FILE_VERSION 16
13
14
#define STATUS_FILENAME "status-netdata.json"
15
@@ -43,9 +43,6 @@ static DAEMON_STATUS_FILE last_session_status = {
43
.fatal = {
44
.spinlock = SPINLOCK_INITIALIZER,
45
},
46
- .dedup = {
47
- .spinlock = SPINLOCK_INITIALIZER,
48
- },
46
};
47
48
static DAEMON_STATUS_FILE session_status = {
@@ -54,9 +51,6 @@ static DAEMON_STATUS_FILE session_status = {
51
.fatal = {
52
.spinlock = SPINLOCK_INITIALIZER,
53
},
57
- .dedup = {
58
- .spinlock = SPINLOCK_INITIALIZER,
59
- },
54
};
55
56
static void daemon_status_file_out_of_memory(void);
@@ -68,29 +62,59 @@ static void daemon_status_file_out_of_memory(void);
62
// --------------------------------------------------------------------------------------------------------------------
63
// json generation
64
71
-static XXH64_hash_t daemon_status_file_hash(DAEMON_STATUS_FILE *ds, const char *msg, const char *cause) {
65
+static uint64_t daemon_status_file_hash(DAEMON_STATUS_FILE *ds, const char *msg, const char *cause) {
66
dsf_acquire(*ds);
73
- CLEAN_BUFFER *wb = buffer_create(0, NULL);
74
- buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
75
- buffer_json_member_add_uint64(wb, "version", STATUS_FILE_VERSION);
76
- buffer_json_member_add_uint64(wb, "version_saved", ds->v);
77
- buffer_json_member_add_uuid(wb, "host_id", ds->host_id.uuid);
78
- buffer_json_member_add_uuid(wb, "node_id", ds->node_id.uuid);
79
- buffer_json_member_add_uuid(wb, "claim_id", ds->claim_id.uuid);
80
- buffer_json_member_add_string(wb, "agent_version", ds->version);
81
- buffer_json_member_add_uint64(wb, "fatal_line", ds->fatal.line);
82
- buffer_json_member_add_string_or_empty(wb, "fatal_filename", ds->fatal.filename);
83
- buffer_json_member_add_string_or_empty(wb, "fatal_errno", ds->fatal.errno_str);
84
- buffer_json_member_add_string_or_empty(wb, "fatal_function", ds->fatal.function);
85
- buffer_json_member_add_string_or_empty(wb, "fatal_stack_trace", ds->fatal.stack_trace);
86
- buffer_json_member_add_string(wb, "message", msg);
87
- buffer_json_member_add_string(wb, "cause", cause);
88
- buffer_json_member_add_string(wb, "status", DAEMON_STATUS_2str(ds->status));
89
- EXIT_REASON_2json(wb, "exit_reason", ds->exit_reason);
90
- ND_PROFILE_2json(wb, "profile", ds->profile);
67
+
68
+ struct {
69
+ uint32_t v;
70
+ DAEMON_STATUS status;
71
+ EXIT_REASON exit_reason;
72
+ SIGNAL_CODE signal_code;
73
+ ND_PROFILE profile;
74
+ RRD_DB_MODE db_mode;
75
+ uint8_t db_tiers;
76
+ bool kubernetes;
77
+ bool sentry;
78
+ ND_UUID host_id;
79
+ ND_UUID machine_id;
80
+ long line;
81
+ char version[sizeof(ds->version)];
82
+ char filename[sizeof(ds->fatal.filename)];
83
+ char function[sizeof(ds->fatal.function)];
84
+ char errno_str[sizeof(ds->fatal.errno_str)];
85
+ char stack_trace[sizeof(ds->fatal.stack_trace)];
86
+ char thread[sizeof(ds->fatal.thread)];
87
+ char msg[128];
88
+ char cause[32];
89
+ } to_hash = {
90
+ .v = ds->v,
91
+ .status = ds->status,
92
+ .signal_code = ds->fatal.signal_code,
93
+ .exit_reason = ds->exit_reason,
94
+ .profile = ds->profile,
95
+ .db_mode = ds->db_mode,
96
+ .db_tiers = ds->db_tiers,
97
+ .kubernetes = ds->kubernetes,
98
+ .sentry = ds->sentry,
99
+ .host_id = ds->host_id,
100
+ .machine_id = ds->machine_id,
101
+ };
102
+ memcpy(to_hash.version, ds->version, sizeof(ds->version));
103
+ memcpy(to_hash.filename, ds->fatal.filename, sizeof(ds->fatal.filename));
104
+ memcpy(to_hash.filename, ds->fatal.function, sizeof(ds->fatal.function));
105
+ memcpy(to_hash.errno_str, ds->fatal.errno_str, sizeof(ds->fatal.errno_str));
106
+ memcpy(to_hash.stack_trace, ds->fatal.stack_trace, sizeof(ds->fatal.stack_trace));
107
+ memcpy(to_hash.thread, ds->fatal.thread, sizeof(ds->fatal.thread));
108
+
109
+ if(msg)
110
+ strncpyz(to_hash.msg, msg, sizeof(to_hash.msg) - 1);
111
+
112
+ if(cause)
113
+ strncpyz(to_hash.cause, cause, sizeof(to_hash.cause) - 1);
114
+
115
+ uint64_t hash = fnv1a_hash_bin64(&to_hash, sizeof(to_hash));
116
+
117
dsf_release(*ds);
92
- buffer_json_finalize(wb);
93
- XXH64_hash_t hash = XXH3_64bits((const void *)buffer_tostring(wb), buffer_strlen(wb));
118
return hash;
119
}
120
@@ -124,6 +148,10 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
148
buffer_json_member_add_boolean(wb, "ND_kubernetes", ds->kubernetes); // custom
149
}
150
151
+ if(ds->v >= 16) {
152
+ buffer_json_member_add_boolean(wb, "ND_sentry", ds->sentry); // custom
153
+ }
154
+
155
buffer_json_member_add_object(wb, "ND_timings"); // custom
156
{
157
buffer_json_member_add_time_t(wb, "init", ds->timings.init);
@@ -190,6 +218,12 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
218
buffer_json_member_add_string_or_empty(wb, "errno", ds->fatal.errno_str);
219
buffer_json_member_add_string_or_empty(wb, "thread", ds->fatal.thread);
220
buffer_json_member_add_string_or_empty(wb, "stack_trace", ds->fatal.stack_trace);
221
+
222
+ if(ds->v >= 16) {
223
+ char signal_code[UINT64_MAX_LENGTH];
224
+ SIGNAL_CODE_2str_h(ds->fatal.signal_code, signal_code, sizeof(signal_code));
225
+ buffer_json_member_add_string_or_empty(wb, "signal_code", signal_code);
226
+ }
227
}
228
buffer_json_object_close(wb);
229
@@ -233,6 +267,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
267
bool required_v5 = version >= 5 ? strict : false;
268
bool required_v10 = version >= 10 ? strict : false;
269
bool required_v14 = version >= 14 ? strict : false;
270
+ bool required_v16 = version >= 16 ? strict : false;
271
272
// Parse timestamp
273
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
@@ -270,6 +305,10 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
305
ds->db_tiers = nd_profile.storage_tiers;
306
ds->kubernetes = false;
307
}
308
+
309
+ if(version >= 16) {
310
+ JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "ND_sentry", ds->sentry, error, required_v16);
311
+ }
312
});
313
314
// Parse host object
@@ -323,6 +362,10 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
362
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "line", ds->fatal.line, error, required_v1);
363
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "errno", ds->fatal.errno_str, error, required_v3);
364
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "thread", ds->fatal.thread, error, required_v5);
365
+
366
+ if(version >= 16) {
367
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "signal_code", SIGNAL_CODE_2id_h, ds->fatal.signal_code, error, required_v16);
368
+ }
369
});
370
371
// Parse the last posted object
@@ -404,6 +447,12 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
447
session_status.db_mode = default_rrd_memory_mode;
448
session_status.db_tiers = nd_profile.storage_tiers;
449
450
+#if defined(ENABLE_SENTRY)
451
+ session_status.sentry = true;
452
+#else
453
+ session_status.sentry = false;
454
+#endif
455
+
456
session_status.claim_id = claim_id_get_uuid();
457
458
if(localhost) {
@@ -739,9 +788,7 @@ static void daemon_status_file_save(BUFFER *wb, DAEMON_STATUS_FILE *ds, bool log
788
// --------------------------------------------------------------------------------------------------------------------
789
// deduplication hashes management
790
742
-static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, XXH64_hash_t hash) {
743
- spinlock_lock(&ds->dedup.spinlock);
744
-
791
+static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, uint64_t hash) {
792
usec_t now_ut = now_realtime_usec();
793
794
for(size_t i = 0; i < _countof(ds->dedup.slot); i++) {
@@ -751,24 +798,19 @@ static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, XXH64_hash_t hash) {
798
if(hash == ds->dedup.slot[i].hash &&
799
now_ut - ds->dedup.slot[i].timestamp_ut < 86400 * USEC_PER_SEC) {
800
// we have already posted this crash
754
- spinlock_unlock(&ds->dedup.spinlock);
801
return true;
802
}
803
}
804
759
- spinlock_unlock(&ds->dedup.spinlock);
805
return false;
806
}
807
763
-static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, XXH64_hash_t hash) {
764
- spinlock_lock(&ds->dedup.spinlock);
765
-
808
+static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, uint64_t hash) {
809
// find the same hash
810
for(size_t i = 0; i < _countof(ds->dedup.slot); i++) {
811
if(ds->dedup.slot[i].hash == hash) {
812
ds->dedup.slot[i].hash = hash;
813
ds->dedup.slot[i].timestamp_ut = now_realtime_usec();
771
- spinlock_unlock(&ds->dedup.spinlock);
814
return;
815
}
816
}
@@ -778,7 +820,6 @@ static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, XXH64_hash_t hash) {
820
if(!ds->dedup.slot[i].hash) {
821
ds->dedup.slot[i].hash = hash;
822
ds->dedup.slot[i].timestamp_ut = now_realtime_usec();
781
- spinlock_unlock(&ds->dedup.spinlock);
823
return;
824
}
825
}
@@ -792,8 +833,6 @@ static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, XXH64_hash_t hash) {
833
834
ds->dedup.slot[store_at_slot].hash = hash;
835
ds->dedup.slot[store_at_slot].timestamp_ut = now_realtime_usec();
795
-
796
- spinlock_unlock(&ds->dedup.spinlock);
836
}
837
838
// --------------------------------------------------------------------------------------------------------------------
@@ -832,7 +871,7 @@ void post_status_file(struct post_status_file_thread_data *d) {
871
872
CURLcode rc = curl_easy_perform(curl);
873
if(rc == CURLE_OK) {
835
- XXH64_hash_t hash = daemon_status_file_hash(d->status, d->msg, d->cause);
874
+ uint64_t hash = daemon_status_file_hash(d->status, d->msg, d->cause);
875
dedup_keep_hash(&session_status, hash);
876
daemon_status_file_save(wb, &session_status, true);
877
}
@@ -1202,14 +1241,19 @@ static void daemon_status_file_out_of_memory(void) {
1241
daemon_status_file_save_again_if_we_can_get_stack_trace();
1242
}
1243
1205
-void daemon_status_file_deadly_signal_received(EXIT_REASON reason) {
1206
- FUNCTION_RUN_ONCE();
1244
+bool daemon_status_file_deadly_signal_received(EXIT_REASON reason, SIGNAL_CODE code, bool chained_handler) {
1245
+ FUNCTION_RUN_ONCE_RET(true);
1246
1247
// DO NOT LOCK OR ALLOCATE IN THIS FUNCTION - WE CRASHED ALREADY AND WE ARE INSIDE THE SIGNAL HANDLER!
1248
1249
dsf_acquire(session_status);
1250
1251
session_status.exit_reason |= reason;
1252
+ session_status.sentry = chained_handler;
1253
+
1254
+ if(code)
1255
+ session_status.fatal.signal_code = code;
1256
+
1257
if(!session_status.fatal.thread[0])
1258
strncpyz(session_status.fatal.thread, nd_thread_tag_async_safe(), sizeof(session_status.fatal.thread) - 1);
1259
@@ -1221,8 +1265,24 @@ void daemon_status_file_deadly_signal_received(EXIT_REASON reason) {
1265
// save what we know already
1266
daemon_status_file_save(static_save_buffer, &session_status, false);
1267
1224
- if(reason != EXIT_REASON_SIGABRT || capture_stack_trace_is_async_signal_safe())
1268
+ // deduplicate the crash for sentry
1269
+ bool duplicate = false;
1270
+ if(chained_handler) {
1271
+ uint64_t hash = daemon_status_file_hash(&session_status, NULL, NULL);
1272
+ duplicate = !dedup_already_posted(&session_status, hash);
1273
+ if (!duplicate) {
1274
+ // save this hash, so that we won't post it again to sentry
1275
+ dedup_keep_hash(&session_status, hash);
1276
+ daemon_status_file_save(static_save_buffer, &session_status, false);
1277
+ }
1278
+ }
1279
+
1280
+ if((!chained_handler || duplicate) && (reason != EXIT_REASON_SIGABRT || capture_stack_trace_is_async_signal_safe())) {
1281
+ // no sentry, try to get the stack trace
1282
daemon_status_file_save_again_if_we_can_get_stack_trace();
1283
+ }
1284
+
1285
+ return duplicate;
1286
}
1287
1288
bool daemon_status_file_has_last_crashed(void) {
src/daemon/daemon-status-file.h
+6
-3
@@ -37,6 +37,7 @@ typedef struct daemon_status_file {
37
RRD_DB_MODE db_mode;
38
uint8_t db_tiers;
39
bool kubernetes;
40
+ bool sentry;
41
42
time_t boottime; // system boottime
43
time_t uptime; // netdata uptime
@@ -80,12 +81,12 @@ typedef struct daemon_status_file {
81
char message[512];
82
char stack_trace[2048];
83
char thread[ND_THREAD_TAG_MAX + 1];
84
+ SIGNAL_CODE signal_code;
85
} fatal;
86
87
struct {
86
- SPINLOCK spinlock;
88
struct {
88
- XXH64_hash_t hash;
89
+ uint64_t hash;
90
usec_t timestamp_ut;
91
} slot[10];
92
} dedup;
@@ -93,7 +94,9 @@ typedef struct daemon_status_file {
94
95
// saves the current status
96
void daemon_status_file_update_status(DAEMON_STATUS status);
96
-void daemon_status_file_deadly_signal_received(EXIT_REASON reason);
97
+
98
+// returns true when the event is duplicate and should not be reported again
99
+bool daemon_status_file_deadly_signal_received(EXIT_REASON reason, SIGNAL_CODE code, bool chained_handler);
100
101
// check for a crash
102
void daemon_status_file_check_crash(void);
src/daemon/daemon.c
+2
-2
@@ -425,7 +425,7 @@ int become_daemon(int dont_fork, const char *user) {
425
426
// the child
427
gettid_uncached();
428
- nd_initialize_signals();
428
+ nd_initialize_signals(false);
429
capture_stack_trace_flush();
430
431
// become session leader
@@ -448,7 +448,7 @@ int become_daemon(int dont_fork, const char *user) {
448
449
// the child
450
gettid_uncached();
451
- nd_initialize_signals();
451
+ nd_initialize_signals(false);
452
capture_stack_trace_flush();
453
}
454
src/daemon/main.c
+2
-1
@@ -788,7 +788,7 @@ int netdata_main(int argc, char **argv) {
788
delta_startup_time("signals");
789
790
signals_block_all_except_deadly();
791
- nd_initialize_signals(); // catches deadly signals and stores them in the status file
791
+ nd_initialize_signals(false); // catches deadly signals and stores them in the status file
792
793
// ----------------------------------------------------------------------------------------------------------------
794
@@ -990,6 +990,7 @@ int netdata_main(int argc, char **argv) {
990
delta_startup_time("sentry");
991
992
nd_sentry_init();
993
+ nd_initialize_signals(true);
994
#endif
995
996
// ----------------------------------------------------------------------------------------------------------------
src/daemon/signal-handler.c
+61
-19
@@ -35,9 +35,16 @@ static struct {
35
{ SIGFPE, "SIGFPE", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGFPE },
36
{ SIGILL, "SIGILL", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGILL },
37
{ SIGABRT, "SIGABRT", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGABRT },
38
+ { SIGSYS, "SIGSYS", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGSYS },
39
+ { SIGXCPU, "SIGXCPU", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGXCPU },
40
+ { SIGXFSZ, "SIGXFSZ", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGXFSZ },
41
};
42
40
-static void signal_handler(int signo) {
43
+static void (*original_handlers[NSIG])(int) = {0};
44
+static void (*original_sigactions[NSIG])(int, siginfo_t *, void *) = {0};
45
+
46
+void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused) {
47
+
48
for(size_t i = 0; i < _countof(signals_waiting) ; i++) {
49
if(signals_waiting[i].signo != signo)
50
continue;
@@ -50,13 +57,23 @@ static void signal_handler(int signo) {
57
#endif
58
59
if(signals_waiting[i].action == NETDATA_SIGNAL_DEADLY) {
60
+ bool chained_handler = original_sigactions[signo] || (original_handlers[signo] && original_handlers[signo] != SIG_IGN && original_handlers[signo] != SIG_DFL);
61
+
62
// Update the status file
54
- daemon_status_file_deadly_signal_received(signals_waiting[i].reason);
63
+ SIGNAL_CODE sc = info ? signal_code(signo, info->si_code) : 0;
64
+ if(daemon_status_file_deadly_signal_received(signals_waiting[i].reason, sc, chained_handler))
65
+ // this is a duplicate event, do not send it to sentry
66
+ chained_handler = false;
67
68
// log it
57
- char b[512];
69
+ char b[1024];
70
strncpyz(b, "SIGNAL HANDLER: received deadly signal: ", sizeof(b) - 1);
71
strcat(b, signals_waiting[i].name);
72
+ if(sc) {
73
+ strcat(b, " (");
74
+ strcat(b, SIGNAL_CODE_2str(sc));
75
+ strcat(b, ")");
76
+ }
77
strcat(b, " in thread ");
78
print_uint64(&b[strlen(b)], gettid_cached());
79
strcat(b, " ");
@@ -68,8 +85,20 @@ static void signal_handler(int signo) {
85
;
86
}
87
71
- // Reset the signal's disposition to the default handler.
88
+ // Chain to the original handler if it exists
89
+ if(chained_handler) {
90
+ if (original_sigactions[signo]) {
91
+ original_sigactions[signo](signo, info, context);
92
+ return; // Original handler should handle the signal
93
+ }
94
+
95
+ if (original_handlers[signo]) {
96
+ original_handlers[signo](signo);
97
+ return; // Original handler should handle the signal
98
+ }
99
+ }
100
101
+ // If there's no original handler or we can't chain, reset to default and re-raise
102
struct sigaction sa;
103
sa.sa_handler = SIG_DFL;
104
sigemptyset(&sa.sa_mask);
@@ -97,27 +126,42 @@ static void posix_unmask_my_signals(void) {
126
netdata_log_error("SIGNAL: cannot unmask netdata signals");
127
}
128
100
-void nd_initialize_signals(void) {
129
+void nd_initialize_signals(bool chain_existing) {
130
signals_block_all_except_deadly();
131
103
- // Catch signals which we want to use
104
- struct sigaction sa;
105
- sa.sa_flags = 0;
132
+ struct sigaction act;
133
+ memset(&act, 0, sizeof(struct sigaction));
134
135
// ignore all signals while we run in a signal handler
108
- sigfillset(&sa.sa_mask);
136
+ sigfillset(&act.sa_mask);
137
+
138
+ for (size_t i = 0; i < _countof(signals_waiting); i++) {
139
+ int signo = signals_waiting[i].signo;
140
+
141
+ // If chaining is requested, get the current handler first
142
+ struct sigaction old_act;
143
+ if (chain_existing &&
144
+ sigaction(signo, NULL, &old_act) == 0 &&
145
+ (uintptr_t)old_act.sa_handler != (uintptr_t)nd_signal_handler) {
146
+ // Save the original handlers for chaining
147
+ if (old_act.sa_flags & SA_SIGINFO)
148
+ original_sigactions[signo] = old_act.sa_sigaction;
149
+ else
150
+ original_handlers[signo] = old_act.sa_handler;
151
+ }
152
110
- for (size_t i = 0; i < _countof(signals_waiting) ; i++) {
153
switch (signals_waiting[i].action) {
112
- case NETDATA_SIGNAL_IGNORE:
113
- sa.sa_handler = SIG_IGN;
114
- break;
115
- default:
116
- sa.sa_handler = signal_handler;
117
- break;
154
+ case NETDATA_SIGNAL_IGNORE:
155
+ act.sa_flags = 0;
156
+ act.sa_handler = SIG_IGN;
157
+ break;
158
+ default:
159
+ act.sa_flags = SA_SIGINFO;
160
+ act.sa_sigaction = nd_signal_handler;
161
+ break;
162
}
163
120
- if(sigaction(signals_waiting[i].signo, &sa, NULL) == -1)
164
+ if (sigaction(signals_waiting[i].signo, &act, NULL) == -1)
165
netdata_log_error("SIGNAL: Failed to change signal handler for: %s", signals_waiting[i].name);
166
}
167
}
@@ -158,8 +202,6 @@ static void process_triggered_signals(void) {
202
break;
203
204
case NETDATA_SIGNAL_DEADLY:
161
- nd_log_limits_unlimited();
162
- daemon_status_file_deadly_signal_received(signals_waiting[i].reason);
205
_exit(1);
206
break;
207
src/daemon/signal-handler.h
+1
-1
@@ -3,7 +3,7 @@
3
#ifndef NETDATA_SIGNAL_HANDLER_H
4
#define NETDATA_SIGNAL_HANDLER_H 1
5
6
-void nd_initialize_signals(void);
6
+void nd_initialize_signals(bool chain_existing);
7
void nd_process_signals(void) NORETURN;
8
9
#endif //NETDATA_SIGNAL_HANDLER_H
src/libnetdata/aral/aral.c
+44
-30
@@ -17,14 +17,14 @@
17
// max malloc size
18
// optimal at current versions of libc is up to 256k
19
// ideal to have the same overhead as libc is 4k
20
-#define ARAL_MAX_PAGE_SIZE_MALLOC (2ULL * 1024 * 1024) // 2MiB to use THP
20
+#define ARAL_MAX_PAGE_SIZE_MALLOC (64ULL * 1024)
21
22
// in malloc mode, when the page is bigger than this
23
// use anonymous private mmap pages
24
-#define ARAL_MALLOC_USE_MMAP_ABOVE (4096ULL * 4)
24
+#define ARAL_MALLOC_USE_MMAP_ABOVE (16ULL * 1024)
25
26
// do not allocate pages smaller than this
27
-#define ARAL_MIN_PAGE_SIZE (4096ULL * 4)
27
+#define ARAL_MIN_PAGE_SIZE (16ULL * 1024)
28
29
#define ARAL_PAGE_INCOMING_PARTITIONS 4 // up to 32 (32-bits bitmap)
30
@@ -114,7 +114,6 @@ struct aral {
114
ARAL_OPTIONS options;
115
116
size_t element_size; // calculated to take into account ARAL overheads
117
- size_t max_allocation_size; // calculated in bytes
117
size_t element_ptr_offset; // calculated
118
size_t system_page_size; // calculated
119
@@ -122,6 +121,8 @@ struct aral {
121
size_t requested_element_size;
122
size_t requested_max_page_size;
123
124
+ size_t min_required_page_size;
125
+
126
struct {
127
bool enabled;
128
const char *filename;
@@ -143,6 +144,8 @@ struct aral {
144
#define aral_pages_head_free(ar, marked) (marked ? &ar->aral_lock.pages_marked_free : &ar->aral_lock.pages_free)
145
#define aral_pages_head_full(ar, marked) (marked ? &ar->aral_lock.pages_marked_full : &ar->aral_lock.pages_full)
146
147
+static size_t aral_max_allocation_size(ARAL *ar);
148
+
149
static inline bool aral_malloc_use_mmap(ARAL *ar __maybe_unused, size_t size) {
150
unsigned long long mmap_limit = os_mmap_limit();
151
@@ -335,7 +338,7 @@ struct free_space {
338
static inline struct free_space check_free_space___aral_lock_needed(ARAL *ar, ARAL_PAGE *my_page, bool marked) {
339
struct free_space f = { 0 };
340
338
- f.max_page_elements = ar->config.max_allocation_size / ar->config.element_size;
341
+ f.max_page_elements = aral_max_allocation_size(ar) / ar->config.element_size;
342
for(f.p = *aral_pages_head_free(ar, marked); f.p ; f.lp = f.p, f.p = f.p->aral_lock.next) {
343
f.pages++;
344
internal_fatal(!f.p->aral_lock.free_elements, "page is in the free list, but does not have any elements free");
@@ -485,10 +488,6 @@ static ALWAYS_INLINE size_t aral_element_slot_size(size_t requested_element_size
488
return element_size;
489
}
490
488
-size_t aral_optimal_malloc_page_size(void) {
489
- return ARAL_MAX_PAGE_SIZE_MALLOC;
490
-}
491
-
491
static ALWAYS_INLINE size_t aral_elements_in_page_size(ARAL *ar, size_t page_size) {
492
if(ar->config.mmap.enabled)
493
return page_size / ar->config.element_size;
@@ -507,8 +506,10 @@ static ALWAYS_INLINE size_t aral_next_allocation_size___adders_lock_needed(ARAL
506
// we are growing, double the size
507
508
size *= 2;
510
- if(size > ar->config.max_allocation_size)
511
- size = ar->config.max_allocation_size;
509
+
510
+ size_t max = aral_max_allocation_size(ar);
511
+ if(size > max)
512
+ size = max;
513
ar->ops[idx].adders.allocation_size = size;
514
}
515
@@ -1133,6 +1134,30 @@ size_t aral_actual_element_size(ARAL *ar) {
1134
return ar->config.element_size;
1135
}
1136
1137
+static size_t aral_max_page_size_malloc = ARAL_MAX_PAGE_SIZE_MALLOC;
1138
+size_t aral_optimal_malloc_page_size(void) {
1139
+ return aral_max_page_size_malloc;
1140
+}
1141
+
1142
+void aral_optimal_malloc_page_size_set(size_t size) {
1143
+ aral_max_page_size_malloc = size < ARAL_MIN_PAGE_SIZE ? ARAL_MIN_PAGE_SIZE : size;
1144
+}
1145
+
1146
+static size_t aral_requested_max_page_size(ARAL *ar) {
1147
+ if(!ar->config.requested_max_page_size)
1148
+ return ar->config.mmap.enabled ? ARAL_MAX_PAGE_SIZE_MMAP : aral_optimal_malloc_page_size();
1149
+ else
1150
+ return ar->config.requested_max_page_size;
1151
+}
1152
+
1153
+static size_t aral_max_allocation_size(ARAL *ar) {
1154
+ size_t size = memory_alignment(aral_requested_max_page_size(ar), ar->config.system_page_size);
1155
+ if(size < ar->config.min_required_page_size)
1156
+ size = ar->config.min_required_page_size;
1157
+
1158
+ return size;
1159
+}
1160
+
1161
ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_elements, size_t max_page_size,
1162
struct aral_statistics *stats, const char *filename, const char **cache_dir,
1163
bool mmap, bool lockless, bool dont_dump) {
@@ -1191,27 +1216,16 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
1216
if (ar->config.initial_page_elements < 2)
1217
ar->config.initial_page_elements = 2;
1218
1194
- if(!ar->config.requested_max_page_size)
1195
- ar->config.requested_max_page_size = ar->config.mmap.enabled ? ARAL_MAX_PAGE_SIZE_MMAP : ARAL_MAX_PAGE_SIZE_MALLOC;
1196
-
1197
- // calculate the maximum allocation size we will do
1198
- ar->config.max_allocation_size =
1199
- memory_alignment(ar->config.requested_max_page_size, ar->config.system_page_size);
1200
-
1219
// find the minimum page size we will use
1202
- size_t min_required_page_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT) + 2 * ar->config.element_size;
1203
-
1204
- if(min_required_page_size < ARAL_MIN_PAGE_SIZE)
1205
- min_required_page_size = ARAL_MIN_PAGE_SIZE;
1220
+ ar->config.min_required_page_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT) + 2 * ar->config.element_size;
1221
1207
- min_required_page_size = memory_alignment(min_required_page_size, ar->config.system_page_size);
1222
+ if(ar->config.min_required_page_size < ARAL_MIN_PAGE_SIZE)
1223
+ ar->config.min_required_page_size = ARAL_MIN_PAGE_SIZE;
1224
1209
- // make sure the maximum is enough
1210
- if(ar->config.max_allocation_size < min_required_page_size)
1211
- ar->config.max_allocation_size = min_required_page_size;
1225
+ ar->config.min_required_page_size = memory_alignment(ar->config.min_required_page_size, ar->config.system_page_size);
1226
1227
// set the starting allocation size for both marked and unmarked partitions
1214
- ar->ops[0].adders.allocation_size = ar->ops[1].adders.allocation_size = min_required_page_size;
1228
+ ar->ops[0].adders.allocation_size = ar->ops[1].adders.allocation_size = ar->config.min_required_page_size;
1229
1230
// ----------------------------------------------------------------------------------------------------------------
1231
@@ -1243,8 +1257,8 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
1257
, ar->config.name
1258
, ar->config.element_size, ar->config.requested_element_size
1259
, ar->ops[0].adders.allocation_size / ar->config.element_size, ar->config.initial_page_elements
1246
- , ar->config.max_allocation_size / ar->config.element_size
1247
- , ar->config.max_allocation_size, ar->config.requested_max_page_size
1260
+ , aral_max_allocation_size(ar) / ar->config.element_size
1261
+ , aral_max_allocation_size(ar), ar->config.requested_max_page_size
1262
);
1263
1264
__atomic_add_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
@@ -1443,7 +1457,7 @@ static void *aral_test_thread(void *ptr) {
1457
pointers[i] = unittest_aral_malloc(ar, marked);
1458
}
1459
1446
- size_t max_page_elements = aral_elements_in_page_size(ar, ar->config.max_allocation_size);
1460
+ size_t max_page_elements = aral_elements_in_page_size(ar, aral_max_allocation_size(ar));
1461
size_t increment = elements / max_page_elements;
1462
for (size_t all = increment; all <= elements / 2; all += increment) {
1463
src/libnetdata/aral/aral.h
+1
@@ -44,6 +44,7 @@ size_t aral_actual_element_size(ARAL *ar);
44
// --------------------------------------------------------------------------------------------------------------------
45
46
size_t aral_optimal_malloc_page_size(void);
47
+void aral_optimal_malloc_page_size_set(size_t size);
48
49
// --------------------------------------------------------------------------------------------------------------------
50
src/libnetdata/exit/exit_initiated.c
+4
@@ -10,6 +10,10 @@ ENUM_STR_MAP_DEFINE(EXIT_REASON) = {
10
{ EXIT_REASON_SIGFPE, "signal-floating-point-exception"},
11
{ EXIT_REASON_SIGILL, "signal-illegal-instruction"},
12
{ EXIT_REASON_SIGABRT, "signal-abort"},
13
+ { EXIT_REASON_SIGSYS, "signal-bad-system-call"},
14
+ { EXIT_REASON_SIGXCPU, "signal-cpu-time-limit-exceeded"},
15
+ { EXIT_REASON_SIGXFSZ, "signal-file-size-limit-exceeded"},
16
+
17
{ EXIT_REASON_OUT_OF_MEMORY, "out-of-memory"},
18
{ EXIT_REASON_ALREADY_RUNNING, "already-running"},
19
src/libnetdata/exit/exit_initiated.h
+46
-15
@@ -15,36 +15,67 @@ typedef enum {
15
EXIT_REASON_SIGFPE = (1 << 2),
16
EXIT_REASON_SIGILL = (1 << 3),
17
EXIT_REASON_SIGABRT = (1 << 4),
18
- EXIT_REASON_OUT_OF_MEMORY = (1 << 5),
19
- EXIT_REASON_ALREADY_RUNNING = (1 << 6),
18
+ EXIT_REASON_SIGSYS = (1 << 5), // Bad system call
19
+ EXIT_REASON_SIGXCPU = (1 << 6), // CPU time limit exceeded
20
+ EXIT_REASON_SIGXFSZ = (1 << 7), // File size limit exceeded
21
+ EXIT_REASON_OUT_OF_MEMORY = (1 << 8),
22
+ EXIT_REASON_ALREADY_RUNNING = (1 << 9),
23
24
// abnormal termination via a fatal message
22
- EXIT_REASON_FATAL = (1 << 7), // a fatal message
25
+ EXIT_REASON_FATAL = (1 << 10), // a fatal message
26
27
// normal termination via APIs
25
- EXIT_REASON_API_QUIT = (1 << 8), // developer only
26
- EXIT_REASON_CMD_EXIT = (1 << 9), // netdatacli
28
+ EXIT_REASON_API_QUIT = (1 << 11), // developer only
29
+ EXIT_REASON_CMD_EXIT = (1 << 12), // netdatacli
30
31
// signals - normal termination
29
- EXIT_REASON_SIGQUIT = (1 << 10), // rare, but graceful
30
- EXIT_REASON_SIGTERM = (1 << 11), // received on Linux, FreeBSD, MacOS
31
- EXIT_REASON_SIGINT = (1 << 12), // received on Windows on normal termination
32
+ EXIT_REASON_SIGQUIT = (1 << 13), // rare, but graceful
33
+ EXIT_REASON_SIGTERM = (1 << 14), // received on Linux, FreeBSD, MacOS
34
+ EXIT_REASON_SIGINT = (1 << 15), // received on Windows on normal termination
35
36
// windows specific, service stop
34
- EXIT_REASON_SERVICE_STOP = (1 << 13),
37
+ EXIT_REASON_SERVICE_STOP = (1 << 16),
38
39
// automatically detect when exit_initiated_set() is called
40
// supports Linux, FreeBSD, MacOS, Windows
38
- EXIT_REASON_SYSTEM_SHUTDOWN = (1 << 14),
41
+ EXIT_REASON_SYSTEM_SHUTDOWN = (1 << 17),
42
43
// netdata update
41
- EXIT_REASON_UPDATE = (1 << 15),
44
+ EXIT_REASON_UPDATE = (1 << 18),
45
} EXIT_REASON;
46
44
-#define EXIT_REASON_NORMAL (EXIT_REASON_SIGINT|EXIT_REASON_SIGTERM|EXIT_REASON_SIGQUIT|EXIT_REASON_API_QUIT|EXIT_REASON_CMD_EXIT|EXIT_REASON_SERVICE_STOP|EXIT_REASON_SYSTEM_SHUTDOWN|EXIT_REASON_UPDATE)
45
-#define EXIT_REASON_ABNORMAL (EXIT_REASON_SIGBUS|EXIT_REASON_SIGSEGV|EXIT_REASON_SIGFPE|EXIT_REASON_SIGILL|EXIT_REASON_SIGABRT|EXIT_REASON_FATAL|EXIT_REASON_OUT_OF_MEMORY)
46
-
47
-#define is_deadly_signal(reason) ((reason) & (EXIT_REASON_SIGBUS|EXIT_REASON_SIGSEGV|EXIT_REASON_SIGFPE|EXIT_REASON_SIGILL|EXIT_REASON_SIGABRT))
47
+#define EXIT_REASON_NORMAL \
48
+ ( \
49
+ EXIT_REASON_SIGINT \
50
+ | EXIT_REASON_SIGTERM \
51
+ | EXIT_REASON_SIGQUIT \
52
+ | EXIT_REASON_API_QUIT \
53
+ | EXIT_REASON_CMD_EXIT \
54
+ | EXIT_REASON_SERVICE_STOP \
55
+ | EXIT_REASON_SYSTEM_SHUTDOWN \
56
+ | EXIT_REASON_UPDATE \
57
+ )
58
+
59
+#define EXIT_REASON_DEADLY_SIGNAL \
60
+ ( \
61
+ EXIT_REASON_SIGBUS \
62
+ | EXIT_REASON_SIGSEGV \
63
+ | EXIT_REASON_SIGFPE \
64
+ | EXIT_REASON_SIGILL \
65
+ | EXIT_REASON_SIGABRT \
66
+ | EXIT_REASON_SIGSYS \
67
+ | EXIT_REASON_SIGXCPU \
68
+ | EXIT_REASON_SIGXFSZ \
69
+ )
70
+
71
+#define EXIT_REASON_ABNORMAL \
72
+ ( \
73
+ EXIT_REASON_DEADLY_SIGNAL \
74
+ | EXIT_REASON_FATAL \
75
+ | EXIT_REASON_OUT_OF_MEMORY \
76
+ )
77
+
78
+#define is_deadly_signal(reason) ((reason) & (EXIT_REASON_DEADLY_SIGNAL))
79
#define is_exit_reason_normal(reason) (((reason) & EXIT_REASON_NORMAL) && !((reason) & EXIT_REASON_ABNORMAL))
80
81
typedef struct web_buffer BUFFER;
src/libnetdata/inlined.h
+10
@@ -84,6 +84,16 @@ static inline uint32_t fnv1a_uhash32(const char *name) {
84
#define simple_hash(s) fnv1a_hash32(s)
85
#define simple_uhash(s) fnv1a_uhash32(s)
86
87
+static inline uint64_t fnv1a_hash_bin64(const void *data, size_t len) {
88
+ const uint8_t *bytes = (const uint8_t *)data;
89
+ uint64_t hash = 14695981039346656037ULL; // FNV offset basis for 64-bit
90
+ for (size_t i = 0; i < len; i++) {
91
+ hash ^= (uint64_t)bytes[i];
92
+ hash *= 1099511628211ULL; // FNV prime for 64-bit
93
+ }
94
+ return hash;
95
+}
96
+
97
static uint32_t murmur32(uint32_t k) __attribute__((const));
98
static inline uint32_t murmur32(uint32_t k) {
99
k ^= k >> 16;
src/libnetdata/signals/signal-code.c
new
+152
@@ -0,0 +1,152 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "signal-code.h"
4
+#include "libnetdata/libnetdata.h"
5
+
6
+// Helper macro to create a SIGNAL_CODE value
7
+#define SIGNAL_CODE_CREATE(signo, si_code) (((uint64_t)(signo) << 32) | (uint32_t)(si_code))
8
+
9
+// Function to create a SIGNAL_CODE from signal number and signal code
10
+SIGNAL_CODE signal_code(int signo, int si_code) {
11
+ return SIGNAL_CODE_CREATE(signo, si_code);
12
+}
13
+
14
+// Define mapping from SIGNAL_CODE to string representation
15
+ENUM_STR_MAP_DEFINE(SIGNAL_CODE) = {
16
+ // SIGILL codes
17
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLOPC), "SIGILL/ILL_ILLOPC" }, // Illegal opcode
18
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLOPN), "SIGILL/ILL_ILLOPN" }, // Illegal operand
19
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLADR), "SIGILL/ILL_ILLADR" }, // Illegal addressing mode
20
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLTRP), "SIGILL/ILL_ILLTRP" }, // Illegal trap
21
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_PRVOPC), "SIGILL/ILL_PRVOPC" }, // Privileged opcode
22
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_PRVREG), "SIGILL/ILL_PRVREG" }, // Privileged register
23
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_COPROC), "SIGILL/ILL_COPROC" }, // Coprocessor error
24
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_BADSTK), "SIGILL/ILL_BADSTK" }, // Internal stack error
25
+#ifdef ILL_BADIADDR
26
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_BADIADDR), "SIGILL/ILL_BADIADDR" }, // Unimplemented instruction address
27
+#endif
28
+
29
+ // SIGFPE codes
30
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_INTDIV), "SIGFPE/FPE_INTDIV" }, // Integer divide by zero
31
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_INTOVF), "SIGFPE/FPE_INTOVF" }, // Integer overflow
32
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTDIV), "SIGFPE/FPE_FLTDIV" }, // Floating point divide by zero
33
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTOVF), "SIGFPE/FPE_FLTOVF" }, // Floating point overflow
34
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTUND), "SIGFPE/FPE_FLTUND" }, // Floating point underflow
35
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTRES), "SIGFPE/FPE_FLTRES" }, // Floating point inexact result
36
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTINV), "SIGFPE/FPE_FLTINV" }, // Floating point invalid operation
37
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTSUB), "SIGFPE/FPE_FLTSUB" }, // Subscript out of range
38
+#ifdef FPE_FLTUNK
39
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTUNK), "SIGFPE/FPE_FLTUNK" }, // Undiagnosed floating-point exception
40
+#endif
41
+#ifdef FPE_CONDTRAP
42
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_CONDTRAP), "SIGFPE/FPE_CONDTRAP" }, // Trap on condition
43
+#endif
44
+
45
+ // SIGSEGV codes
46
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MAPERR), "SIGSEGV/SEGV_MAPERR" }, // Address not mapped to object
47
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ACCERR), "SIGSEGV/SEGV_ACCERR" }, // Invalid permissions for mapped object
48
+#ifdef SEGV_BNDERR
49
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_BNDERR), "SIGSEGV/SEGV_BNDERR" }, // Bounds checking failure
50
+#endif
51
+#ifdef SEGV_PKUERR
52
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_PKUERR), "SIGSEGV/SEGV_PKUERR" }, // Protection key checking failure
53
+#endif
54
+#ifdef SEGV_ACCADI
55
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ACCADI), "SIGSEGV/SEGV_ACCADI" }, // ADI not enabled for mapped object
56
+#endif
57
+#ifdef SEGV_ADIDERR
58
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ADIDERR), "SIGSEGV/SEGV_ADIDERR" }, // Disrupting MCD error
59
+#endif
60
+#ifdef SEGV_ADIPERR
61
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ADIPERR), "SIGSEGV/SEGV_ADIPERR" }, // Precise MCD exception
62
+#endif
63
+#ifdef SEGV_MTEAERR
64
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MTEAERR), "SIGSEGV/SEGV_MTEAERR" }, // Asynchronous ARM MTE error
65
+#endif
66
+#ifdef SEGV_MTESERR
67
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MTESERR), "SIGSEGV/SEGV_MTESERR" }, // Synchronous ARM MTE exception
68
+#endif
69
+#ifdef SEGV_CPERR
70
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_CPERR), "SIGSEGV/SEGV_CPERR" }, // Control protection fault
71
+#endif
72
+
73
+ // SIGBUS codes
74
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_ADRALN), "SIGBUS/BUS_ADRALN" }, // Invalid address alignment
75
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_ADRERR), "SIGBUS/BUS_ADRERR" }, // Non-existent physical address
76
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_OBJERR), "SIGBUS/BUS_OBJERR" }, // Object specific hardware error
77
+#ifdef BUS_MCEERR_AR
78
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_MCEERR_AR), "SIGBUS/BUS_MCEERR_AR" }, // Hardware memory error: action required
79
+#endif
80
+#ifdef BUS_MCEERR_AO
81
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_MCEERR_AO), "SIGBUS/BUS_MCEERR_AO" }, // Hardware memory error: action optional
82
+#endif
83
+
84
+ // SIGTRAP codes
85
+#ifdef TRAP_BRKPT
86
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_BRKPT), "SIGTRAP/TRAP_BRKPT" }, // Process breakpoint
87
+#endif
88
+#ifdef TRAP_TRACE
89
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_TRACE), "SIGTRAP/TRAP_TRACE" }, // Process trace trap
90
+#endif
91
+#ifdef TRAP_BRANCH
92
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_BRANCH), "SIGTRAP/TRAP_BRANCH" }, // Process taken branch trap
93
+#endif
94
+#ifdef TRAP_HWBKPT
95
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_HWBKPT), "SIGTRAP/TRAP_HWBKPT" }, // Hardware breakpoint/watchpoint
96
+#endif
97
+#ifdef TRAP_UNK
98
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_UNK), "SIGTRAP/TRAP_UNK" }, // Undiagnosed trap
99
+#endif
100
+
101
+ // Add entries for standard signals
102
+ { SIGNAL_CODE_CREATE(SIGINT, 0), "SIGINT" },
103
+ { SIGNAL_CODE_CREATE(SIGILL, 0), "SIGILL" },
104
+ { SIGNAL_CODE_CREATE(SIGABRT, 0), "SIGABRT" },
105
+ { SIGNAL_CODE_CREATE(SIGFPE, 0), "SIGFPE" },
106
+ { SIGNAL_CODE_CREATE(SIGSEGV, 0), "SIGSEGV" },
107
+ { SIGNAL_CODE_CREATE(SIGTERM, 0), "SIGTERM" },
108
+ { SIGNAL_CODE_CREATE(SIGHUP, 0), "SIGHUP" },
109
+ { SIGNAL_CODE_CREATE(SIGQUIT, 0), "SIGQUIT" },
110
+ { SIGNAL_CODE_CREATE(SIGTRAP, 0), "SIGTRAP" },
111
+ { SIGNAL_CODE_CREATE(SIGKILL, 0), "SIGKILL" },
112
+ { SIGNAL_CODE_CREATE(SIGPIPE, 0), "SIGPIPE" },
113
+ { SIGNAL_CODE_CREATE(SIGALRM, 0), "SIGALRM" },
114
+ { SIGNAL_CODE_CREATE(SIGCHLD, 0), "SIGCHLD" },
115
+
116
+ { SIGNAL_CODE_CREATE(SIGUSR1, 0), "SIGUSR1" },
117
+ { SIGNAL_CODE_CREATE(SIGUSR2, 0), "SIGUSR2" },
118
+
119
+ // Terminator
120
+ { 0, NULL },
121
+};
122
+
123
+// Define string conversion functions for SIGNAL_CODE type
124
+ENUM_STR_DEFINE_FUNCTIONS(SIGNAL_CODE, 0, "");
125
+
126
+void SIGNAL_CODE_2str_h(SIGNAL_CODE code, char *buf, size_t len) {
127
+ if(!buf || !len) return;
128
+ if(len < 3) {
129
+ buf[0] = '\0';
130
+ return;
131
+ }
132
+
133
+ if(code) {
134
+ const char *s = SIGNAL_CODE_2str(code);
135
+ if (!*s) {
136
+ char b[UINT64_MAX_LENGTH + 2];
137
+ print_uint64_hex(b, code);
138
+ strncpyz(buf, b, len - 1);
139
+ }
140
+ else
141
+ strncpyz(buf, s, len - 1);
142
+ }
143
+ else
144
+ buf[0] = '\0';
145
+}
146
+
147
+SIGNAL_CODE SIGNAL_CODE_2id_h(const char *str) {
148
+ if(strncmp(str, "0x", 2) == 0)
149
+ return strtoull(str + 2, NULL, 16);
150
+
151
+ return SIGNAL_CODE_2id(str);
152
+}
src/libnetdata/signals/signal-code.h
new
+29
@@ -0,0 +1,29 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#ifndef NETDATA_SIGNAL_CODE_H
4
+#define NETDATA_SIGNAL_CODE_H
5
+
6
+#include "libnetdata/common.h"
7
+#include "../template-enum.h"
8
+
9
+// SIGNAL_CODE combines signal number and signal code into a single 64-bit identifier
10
+typedef uint64_t SIGNAL_CODE;
11
+
12
+// Create a SIGNAL_CODE from signal number and signal code
13
+SIGNAL_CODE signal_code(int signo, int si_code);
14
+
15
+// Extract the signal number from a SIGNAL_CODE
16
+#define SIGNAL_CODE_GET_SIGNO(code) ((int)((code) >> 32))
17
+
18
+// Extract the signal code from a SIGNAL_CODE
19
+#define SIGNAL_CODE_GET_SI_CODE(code) ((int)((code) & 0xFFFFFFFF))
20
+
21
+ENUM_STR_DEFINE_FUNCTIONS_EXTERN(SIGNAL_CODE);
22
+
23
+// convert a signal code to string, by name or hex (if no name is available)
24
+void SIGNAL_CODE_2str_h(SIGNAL_CODE code, char *buf, size_t len);
25
+
26
+// parse a signal code from a string, by name or hex
27
+SIGNAL_CODE SIGNAL_CODE_2id_h(const char *str);
28
+
29
+#endif /* NETDATA_SIGNAL_CODE_H */
\ No newline at end of file
src/libnetdata/signals/signals.c
+10
-1
@@ -35,7 +35,16 @@ void signals_unblock(int signals[], size_t count) {
35
}
36
37
void signals_unblock_deadly(void) {
38
- int deadly_signals[] = {SIGBUS, SIGSEGV, SIGFPE, SIGILL, SIGABRT};
38
+ int deadly_signals[] = {
39
+ SIGBUS,
40
+ SIGSEGV,
41
+ SIGFPE,
42
+ SIGILL,
43
+ SIGABRT,
44
+ SIGSYS,
45
+ SIGXCPU,
46
+ SIGXFSZ,
47
+ };
48
signals_unblock(deadly_signals, _countof(deadly_signals));
49
}
50
src/libnetdata/signals/signals.h
+2
@@ -12,4 +12,6 @@ void signals_unblock_one(int signo);
12
void signals_unblock(int signals[], size_t count);
13
void signals_unblock_deadly(void);
14
15
+#include "signal-code.h"
16
+
17
#endif //NETDATA_SIGNALS_H
src/libnetdata/template-enum.h
+14
-11
@@ -51,9 +51,9 @@
51
if (!str || !*str) \
52
return def; \
53
\
54
- for (size_t i = 0; type ## _names[i].name; i++) { \
55
- if (strcmp(type ## _names[i].name, str) == 0) \
56
- return type ## _names[i].id; \
54
+ for (size_t i = 0; type##_names[i].name; i++) { \
55
+ if (strcmp(type##_names[i].name, str) == 0) \
56
+ return type##_names[i].id; \
57
} \
58
\
59
return def; \
@@ -61,9 +61,9 @@
61
\
62
const char *type##_2str_one(type id) \
63
{ \
64
- for (size_t i = 0; type ## _names[i].name; i++) { \
65
- if (id == type ## _names[i].id) \
66
- return type ## _names[i].name; \
64
+ for (size_t i = 0; type##_names[i].name; i++) { \
65
+ if (id == type##_names[i].id) \
66
+ return type##_names[i].name; \
67
} \
68
\
69
return def_str; \
@@ -72,9 +72,11 @@
72
void type##_2json(BUFFER *wb, const char *key, type id) \
73
{ \
74
buffer_json_member_add_array(wb, key); \
75
- for (size_t i = 0; type ## _names[i].name; i++) { \
76
- if ((id & type ## _names[i].id) == type ## _names[i].id) \
77
- buffer_json_add_array_item_string(wb, type ## _names[i].name); \
75
+ for (size_t i = 0; id && type##_names[i].name; i++) { \
76
+ if ((id & type##_names[i].id) == type##_names[i].id) { \
77
+ buffer_json_add_array_item_string(wb, type##_names[i].name); \
78
+ id &= ~(type##_names[i].id); \
79
+ } \
80
} \
81
buffer_json_array_close(wb); \
82
} \
@@ -82,10 +84,11 @@
84
void type##_2buffer(BUFFER *wb, type id, const char *separator) \
85
{ \
86
size_t added = 0; \
85
- for (size_t i = 0; type ## _names[i].name; i++) { \
86
- if ((id & type ## _names[i].id) == type ## _names[i].id) { \
87
+ for (size_t i = 0; id && type##_names[i].name; i++) { \
88
+ if ((id & type##_names[i].id) == type##_names[i].id) { \
89
if(added++) buffer_strcat(wb, separator); \
90
buffer_strcat(wb, type##_names[i].name); \
91
+ id &= ~(type##_names[i].id); \
92
} \
93
} \
94
}
src/libnetdata/threads/threads.c
+2
@@ -354,6 +354,8 @@ static void *nd_thread_starting_point(void *ptr) {
354
355
CLEANUP_FUNCTION_REGISTER(nd_thread_exit) cleanup_ptr = nti;
356
357
+ signals_block_all_except_deadly();
358
+
359
// run the thread code
360
nti->ret = nti->start_routine(nti->arg);
361
src/ml/ml_config.cc
+1
-1
@@ -19,7 +19,7 @@ static T clamp(const T& Value, const T& Min, const T& Max) {
19
void ml_config_load(ml_config_t *cfg) {
20
const char *config_section_ml = CONFIG_SECTION_ML;
21
22
- int enable_anomaly_detection = inicfg_get_boolean_ondemand(&netdata_config, config_section_ml, "enabled", CONFIG_BOOLEAN_AUTO);
22
+ int enable_anomaly_detection = inicfg_get_boolean_ondemand(&netdata_config, config_section_ml, "enabled", nd_profile.ml_enabled);
23
24
/*
25
* Read values