daemon status file 17 (#19869)
* cleanup thread name to avoid reporting duplicate events * ensure there is a stack trace message in all cases * keep separate hashes for sentry and netdata * fixes * signal code now renders signo and si_code independently as decimal numbers; it also supports the common SI_* codes on all signals * prevent sentry from looping signals back to us * allow the chained handler to run and use on_crash to disable the crash * cleanup * fix codacy warnings * fix for missing signals on freebsd * fix for missing signals on macos
Costa Tsaousis committed
Mar 15, 2025 at 14:51 UTC
d686b537218a59d1b2482c7da51ad2056801ee90
11 files changed
+377
-123
src/daemon/daemon-status-file.c
+80
-37
@@ -9,7 +9,7 @@
9
#include <openssl/pem.h>
10
#include <openssl/err.h>
11
12
-#define STATUS_FILE_VERSION 16
12
+#define STATUS_FILE_VERSION 17
13
14
#define STATUS_FILENAME "status-netdata.json"
15
@@ -59,6 +59,30 @@ static void daemon_status_file_out_of_memory(void);
59
#define dsf_acquire(ds) __atomic_load_n(&(ds).v, __ATOMIC_ACQUIRE)
60
#define dsf_release(ds) __atomic_store_n(&(ds).v, (ds).v, __ATOMIC_RELEASE)
61
62
+static void copy_and_clean_thread_name_if_empty(DAEMON_STATUS_FILE *ds, const char *name) {
63
+ if(ds->fatal.thread[0] && strcmp(ds->fatal.thread, "NO_NAME") != 0)
64
+ return;
65
+
66
+ if(!name || !*name) name = "NO_NAME";
67
+
68
+ strncpyz(ds->fatal.thread, name, sizeof(ds->fatal.thread) - 1);
69
+
70
+ // remove the variable part from the thread by removing [XXX] from it
71
+ unsigned char *p = (unsigned char *)strchr(ds->fatal.thread, '[');
72
+ if(p && isdigit(p[1]) && (isdigit(p[2]) || p[2] == ']'))
73
+ *p = '\0';
74
+}
75
+
76
+#define STACK_TRACE_INFO_PREFIX "info: "
77
+static bool stack_trace_is_empty(DAEMON_STATUS_FILE *ds) {
78
+ return !ds->fatal.stack_trace[0] || strncmp(ds->fatal.stack_trace, STACK_TRACE_INFO_PREFIX, strlen(STACK_TRACE_INFO_PREFIX)) == 0;
79
+}
80
+
81
+static void set_stack_trace_message_if_empty(DAEMON_STATUS_FILE *ds, const char *msg) {
82
+ if(stack_trace_is_empty(ds))
83
+ strncpyz(ds->fatal.stack_trace, msg, sizeof(ds->fatal.stack_trace) - 1);
84
+}
85
+
86
// --------------------------------------------------------------------------------------------------------------------
87
// json generation
88
@@ -237,6 +261,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
261
{
262
buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->dedup.slot[i].timestamp_ut, true); // custom
263
buffer_json_member_add_uint64(wb, "hash", ds->dedup.slot[i].hash); // custom
264
+ buffer_json_member_add_boolean(wb, "sentry", ds->dedup.slot[i].sentry); // custom
265
}
266
buffer_json_object_close(wb);
267
}
@@ -268,6 +293,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
293
bool required_v10 = version >= 10 ? strict : false;
294
bool required_v14 = version >= 14 ? strict : false;
295
bool required_v16 = version >= 16 ? strict : false;
296
+ bool required_v17 = version >= 17 ? strict : false;
297
298
// Parse timestamp
299
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
@@ -390,8 +416,8 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
416
if (datetime[0])
417
ds->dedup.slot[i].timestamp_ut = rfc3339_parse_ut(datetime, NULL);
418
393
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(
394
- jobj, path, "hash", ds->dedup.slot[i].hash, error, required_v4);
419
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "hash", ds->dedup.slot[i].hash, error, required_v4);
420
+ JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "sentry", ds->dedup.slot[i].sentry, error, required_v17);
421
}
422
});
423
});
@@ -788,7 +814,7 @@ static void daemon_status_file_save(BUFFER *wb, DAEMON_STATUS_FILE *ds, bool log
814
// --------------------------------------------------------------------------------------------------------------------
815
// deduplication hashes management
816
791
-static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, uint64_t hash) {
817
+static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, uint64_t hash, bool sentry) {
818
usec_t now_ut = now_realtime_usec();
819
820
for(size_t i = 0; i < _countof(ds->dedup.slot); i++) {
@@ -796,6 +822,7 @@ static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, uint64_t hash) {
822
continue;
823
824
if(hash == ds->dedup.slot[i].hash &&
825
+ sentry == ds->dedup.slot[i].sentry &&
826
now_ut - ds->dedup.slot[i].timestamp_ut < 86400 * USEC_PER_SEC) {
827
// we have already posted this crash
828
return true;
@@ -805,11 +832,12 @@ static bool dedup_already_posted(DAEMON_STATUS_FILE *ds, uint64_t hash) {
832
return false;
833
}
834
808
-static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, uint64_t hash) {
835
+static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, uint64_t hash, bool sentry) {
836
// find the same hash
837
for(size_t i = 0; i < _countof(ds->dedup.slot); i++) {
811
- if(ds->dedup.slot[i].hash == hash) {
838
+ if(ds->dedup.slot[i].hash == hash && ds->dedup.slot[i].sentry == sentry) {
839
ds->dedup.slot[i].hash = hash;
840
+ ds->dedup.slot[i].sentry = sentry;
841
ds->dedup.slot[i].timestamp_ut = now_realtime_usec();
842
return;
843
}
@@ -819,6 +847,7 @@ static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, uint64_t hash) {
847
for(size_t i = 0; i < _countof(ds->dedup.slot); i++) {
848
if(!ds->dedup.slot[i].hash) {
849
ds->dedup.slot[i].hash = hash;
850
+ ds->dedup.slot[i].sentry = sentry;
851
ds->dedup.slot[i].timestamp_ut = now_realtime_usec();
852
return;
853
}
@@ -832,6 +861,7 @@ static void dedup_keep_hash(DAEMON_STATUS_FILE *ds, uint64_t hash) {
861
}
862
863
ds->dedup.slot[store_at_slot].hash = hash;
864
+ ds->dedup.slot[store_at_slot].sentry = sentry;
865
ds->dedup.slot[store_at_slot].timestamp_ut = now_realtime_usec();
866
}
867
@@ -872,7 +902,7 @@ void post_status_file(struct post_status_file_thread_data *d) {
902
CURLcode rc = curl_easy_perform(curl);
903
if(rc == CURLE_OK) {
904
uint64_t hash = daemon_status_file_hash(d->status, d->msg, d->cause);
875
- dedup_keep_hash(&session_status, hash);
905
+ dedup_keep_hash(&session_status, hash, false);
906
daemon_status_file_save(wb, &session_status, true);
907
}
908
@@ -1135,7 +1165,7 @@ void daemon_status_file_check_crash(void) {
1165
(last_session_status.restarts >= 10 || !is_ci()) &&
1166
1167
// we have not already reported this
1138
- !dedup_already_posted(&session_status, daemon_status_file_hash(&last_session_status, msg, cause))
1168
+ !dedup_already_posted(&session_status, daemon_status_file_hash(&last_session_status, msg, cause), false)
1169
1170
) {
1171
netdata_conf_ssl();
@@ -1154,19 +1184,28 @@ void daemon_status_file_check_crash(void) {
1184
}
1185
}
1186
1157
-static void daemon_status_file_save_again_if_we_can_get_stack_trace(void) {
1158
- if(!session_status.fatal.stack_trace[0]) {
1159
- buffer_flush(static_save_buffer);
1160
- capture_stack_trace(static_save_buffer);
1187
+static void daemon_status_file_save_twice_if_we_can_get_stack_trace(BUFFER *wb, DAEMON_STATUS_FILE *ds, bool force) {
1188
+ if(capture_stack_trace_available())
1189
+ set_stack_trace_message_if_empty(&session_status, STACK_TRACE_INFO_PREFIX "will now attempt to get stack trace - if you see this message, we couldn't get it.");
1190
+ else
1191
+ set_stack_trace_message_if_empty(&session_status, STACK_TRACE_INFO_PREFIX "no stack trace backend available");
1192
1162
- if(buffer_strlen(static_save_buffer) > 0) {
1163
- strncpyz(
1164
- session_status.fatal.stack_trace,
1165
- buffer_tostring(static_save_buffer),
1166
- sizeof(session_status.fatal.stack_trace) - 1);
1193
+ // save it without a stack trace to be sure we will have the event
1194
+ daemon_status_file_save(wb, ds, false);
1195
1168
- daemon_status_file_save(static_save_buffer, &session_status, false);
1169
- }
1196
+ if(!stack_trace_is_empty(ds) && !force)
1197
+ return;
1198
+
1199
+ buffer_flush(wb);
1200
+ capture_stack_trace(wb);
1201
+
1202
+ if(buffer_strlen(wb) > 0) {
1203
+ strncpyz(
1204
+ ds->fatal.stack_trace,
1205
+ buffer_tostring(wb),
1206
+ sizeof(ds->fatal.stack_trace) - 1);
1207
+
1208
+ daemon_status_file_save(wb, ds, false);
1209
}
1210
}
1211
@@ -1180,7 +1219,8 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
1219
spinlock_lock(&session_status.fatal.spinlock);
1220
1221
exit_initiated_add(EXIT_REASON_FATAL);
1183
- strncpyz(session_status.fatal.thread, nd_thread_tag(), sizeof(session_status.fatal.thread) - 1);
1222
+
1223
+ copy_and_clean_thread_name_if_empty(&session_status, nd_thread_tag());
1224
1225
if(!session_status.fatal.filename[0] && filename)
1226
strncpyz(session_status.fatal.filename, filename, sizeof(session_status.fatal.filename) - 1);
@@ -1194,7 +1234,7 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
1234
if(!session_status.fatal.errno_str[0] && errno_str)
1235
strncpyz(session_status.fatal.errno_str, errno_str, sizeof(session_status.fatal.errno_str) - 1);
1236
1197
- if(!session_status.fatal.stack_trace[0] && stack_trace)
1237
+ if(stack_trace_is_empty(&session_status) && stack_trace)
1238
strncpyz(session_status.fatal.stack_trace, stack_trace, sizeof(session_status.fatal.stack_trace) - 1);
1239
1240
if(!session_status.fatal.line)
@@ -1204,15 +1244,13 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
1244
dsf_release(session_status);
1245
1246
CLEAN_BUFFER *wb = buffer_create(0, NULL);
1207
- daemon_status_file_save(wb, &session_status, false);
1247
+ daemon_status_file_save_twice_if_we_can_get_stack_trace(wb, &session_status, false);
1248
1249
freez((void *)filename);
1250
freez((void *)function);
1251
freez((void *)message);
1252
freez((void *)errno_str);
1253
freez((void *)stack_trace);
1214
-
1215
- daemon_status_file_save_again_if_we_can_get_stack_trace();
1254
}
1255
1256
// --------------------------------------------------------------------------------------------------------------------
@@ -1235,10 +1273,10 @@ static void daemon_status_file_out_of_memory(void) {
1273
1274
dsf_acquire(session_status);
1275
session_status.exit_reason = exit_initiated;
1276
+
1277
dsf_release(session_status);
1278
1240
- daemon_status_file_save(static_save_buffer, &session_status, false);
1241
- daemon_status_file_save_again_if_we_can_get_stack_trace();
1279
+ daemon_status_file_save_twice_if_we_can_get_stack_trace(static_save_buffer, &session_status, true);
1280
}
1281
1282
bool daemon_status_file_deadly_signal_received(EXIT_REASON reason, SIGNAL_CODE code, bool chained_handler) {
@@ -1254,32 +1292,37 @@ bool daemon_status_file_deadly_signal_received(EXIT_REASON reason, SIGNAL_CODE c
1292
if(code)
1293
session_status.fatal.signal_code = code;
1294
1257
- if(!session_status.fatal.thread[0])
1258
- strncpyz(session_status.fatal.thread, nd_thread_tag_async_safe(), sizeof(session_status.fatal.thread) - 1);
1295
+ copy_and_clean_thread_name_if_empty(&session_status, nd_thread_tag_async_safe());
1296
1297
dsf_release(session_status);
1298
1299
// the buffer should already be allocated, so this should normally do nothing
1300
static_save_buffer_init();
1301
1265
- // save what we know already
1266
- daemon_status_file_save(static_save_buffer, &session_status, false);
1267
-
1302
// deduplicate the crash for sentry
1303
bool duplicate = false;
1304
if(chained_handler) {
1305
uint64_t hash = daemon_status_file_hash(&session_status, NULL, NULL);
1272
- duplicate = dedup_already_posted(&session_status, hash);
1306
+ duplicate = dedup_already_posted(&session_status, hash, true);
1307
if (!duplicate) {
1308
// 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);
1309
+ dedup_keep_hash(&session_status, hash, true);
1310
}
1311
}
1312
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();
1313
+ bool safe_to_get_stack_trace = reason != EXIT_REASON_SIGABRT || capture_stack_trace_is_async_signal_safe();
1314
+ bool get_stack_trace = capture_stack_trace_available() && safe_to_get_stack_trace && stack_trace_is_empty(&session_status);
1315
+
1316
+ // save it
1317
+ if(get_stack_trace)
1318
+ daemon_status_file_save_twice_if_we_can_get_stack_trace(static_save_buffer, &session_status, true);
1319
+ else {
1320
+ if (!capture_stack_trace_available())
1321
+ set_stack_trace_message_if_empty(&session_status, STACK_TRACE_INFO_PREFIX "no stack trace backend available");
1322
+ else
1323
+ set_stack_trace_message_if_empty(&session_status, STACK_TRACE_INFO_PREFIX "not safe to get a stack trace for this signal using this backend");
1324
+
1325
+ daemon_status_file_save(static_save_buffer, &session_status, false);
1326
}
1327
1328
return duplicate;
src/daemon/daemon-status-file.h
+2
-1
@@ -86,9 +86,10 @@ typedef struct daemon_status_file {
86
87
struct {
88
struct {
89
+ bool sentry;
90
uint64_t hash;
91
usec_t timestamp_ut;
91
- } slot[10];
92
+ } slot[15];
93
} dedup;
94
} DAEMON_STATUS_FILE;
95
src/daemon/main.c
+1
@@ -989,6 +989,7 @@ int netdata_main(int argc, char **argv) {
989
// ----------------------------------------------------------------------------------------------------------------
990
delta_startup_time("sentry");
991
992
+ nd_cleanup_fatal_signals(); // avoid sentry looping deadly signals back to us
993
nd_sentry_init();
994
nd_initialize_signals(true);
995
#endif
src/daemon/sentry-native/sentry-native.c
+47
@@ -5,6 +5,40 @@
5
6
#include "sentry.h"
7
8
+bool nd_sentry_crash_report_enabled = true;
9
+
10
+void nd_sentry_crash_report(bool enable) {
11
+ nd_sentry_crash_report_enabled = enable;
12
+}
13
+
14
+static sentry_value_t nd_sentry_on_crash(
15
+ const sentry_ucontext_t *uctx __maybe_unused, // provides the user-space context of the crash
16
+ sentry_value_t event, // used the same way as in `before_send`
17
+ void *closure __maybe_unused // user-data that you can provide at configuration time
18
+) {
19
+ // IMPORTANT: this function is called from a signal handler
20
+
21
+ if (!nd_sentry_crash_report_enabled) {
22
+ sentry_value_decref(event);
23
+ return sentry_value_new_null();
24
+ }
25
+
26
+ return event;
27
+}
28
+
29
+static sentry_value_t nd_sentry_before_send(
30
+ sentry_value_t event,
31
+ void *hint __maybe_unused,
32
+ void *closure __maybe_unused) {
33
+
34
+ if (!nd_sentry_crash_report_enabled) {
35
+ sentry_value_decref(event);
36
+ return sentry_value_new_null();
37
+ }
38
+
39
+ return event;
40
+}
41
+
42
void nd_sentry_init(void)
43
{
44
if (!analytics_check_enabled())
@@ -29,6 +63,19 @@ void nd_sentry_init(void)
63
sentry_options_set_debug(options, 1);
64
#endif
65
66
+ CLEAN_BUFFER *profile = buffer_create(0, NULL);
67
+ ND_PROFILE_2buffer(profile, nd_profile_detect_and_configure(false), " ");
68
+ sentry_set_tag("profile", buffer_tostring(profile));
69
+
70
+ sentry_set_tag("db_mode", rrd_memory_mode_name(default_rrd_memory_mode));
71
+
72
+ char tiers[UINT64_MAX_LENGTH];
73
+ print_uint64(tiers, nd_profile.storage_tiers);
74
+ sentry_set_tag("db_tiers", tiers);
75
+
76
+ sentry_options_set_on_crash(options, nd_sentry_on_crash, NULL);
77
+ sentry_options_set_before_send(options, nd_sentry_before_send, NULL);
78
+
79
sentry_init(options);
80
}
81
src/daemon/sentry-native/sentry-native.h
+4
@@ -3,10 +3,14 @@
3
#ifndef ND_SENTRY_H
4
#define ND_SENTRY_H
5
6
+#include "libnetdata/common.h"
7
+
8
void nd_sentry_init(void);
9
void nd_sentry_fini(void);
10
11
void nd_sentry_set_user(const char *guid);
12
void nd_sentry_add_breadcrumb(const char *message);
13
14
+void nd_sentry_crash_report(bool enable);
15
+
16
#endif /* ND_SENTRY_H */
src/daemon/signal-handler.c
+31
-1
@@ -3,6 +3,10 @@
3
#include "common.h"
4
#include "daemon/daemon-status-file.h"
5
6
+#ifdef ENABLE_SENTRY
7
+#include "sentry-native/sentry-native.h"
8
+#endif
9
+
10
typedef enum signal_action {
11
NETDATA_SIGNAL_IGNORE,
12
NETDATA_SIGNAL_EXIT_CLEANLY,
@@ -63,7 +67,11 @@ void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused)
67
SIGNAL_CODE sc = info ? signal_code(signo, info->si_code) : 0;
68
if(daemon_status_file_deadly_signal_received(signals_waiting[i].reason, sc, chained_handler)) {
69
// this is a duplicate event, do not send it to sentry
70
+#ifdef ENABLE_SENTRY
71
+ nd_sentry_crash_report(false);
72
+#else
73
chained_handler = false;
74
+#endif
75
}
76
77
// log it
@@ -71,8 +79,11 @@ void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused)
79
strncpyz(b, "SIGNAL HANDLER: received deadly signal: ", sizeof(b) - 1);
80
strcat(b, signals_waiting[i].name);
81
if(sc) {
82
+ char buf[128];
83
+ SIGNAL_CODE_2str_h(sc, buf, sizeof(buf));
84
+
85
strcat(b, " (");
75
- strcat(b, SIGNAL_CODE_2str(sc));
86
+ strcat(b, buf);
87
strcat(b, ")");
88
}
89
strcat(b, " in thread ");
@@ -127,6 +138,25 @@ static void posix_unmask_my_signals(void) {
138
netdata_log_error("SIGNAL: cannot unmask netdata signals");
139
}
140
141
+void nd_cleanup_fatal_signals(void) {
142
+ struct sigaction act;
143
+ memset(&act, 0, sizeof(struct sigaction));
144
+
145
+ // ignore all signals while we run in a signal handler
146
+ sigfillset(&act.sa_mask);
147
+
148
+ for (size_t i = 0; i < _countof(signals_waiting); i++) {
149
+ if(signals_waiting[i].action != NETDATA_SIGNAL_DEADLY)
150
+ continue;
151
+
152
+ act.sa_flags = 0;
153
+ act.sa_handler = SIG_DFL;
154
+
155
+ if (sigaction(signals_waiting[i].signo, &act, NULL) == -1)
156
+ netdata_log_error("SIGNAL: Failed to cleanup signal handler for: %s", signals_waiting[i].name);
157
+ }
158
+}
159
+
160
void nd_initialize_signals(bool chain_existing) {
161
signals_block_all_except_deadly();
162
src/daemon/signal-handler.h
+1
@@ -3,6 +3,7 @@
3
#ifndef NETDATA_SIGNAL_HANDLER_H
4
#define NETDATA_SIGNAL_HANDLER_H 1
5
6
+void nd_cleanup_fatal_signals(void);
7
void nd_initialize_signals(bool chain_existing);
8
void nd_process_signals(void) NORETURN;
9
src/libnetdata/log/nd_log-stacktrace.c
+12
@@ -28,6 +28,10 @@ bool capture_stack_trace_is_async_signal_safe(void) {
28
#endif
29
}
30
31
+bool capture_stack_trace_available(void) {
32
+ return true;
33
+}
34
+
35
void capture_stack_trace(BUFFER *wb) {
36
// this function is async-signal-safe, if the buffer has enough space to hold the stack trace
37
@@ -76,6 +80,10 @@ void capture_stack_trace(BUFFER *wb) {
80
81
#elif defined(HAVE_BACKTRACE)
82
83
+bool capture_stack_trace_available(void) {
84
+ return true;
85
+}
86
+
87
void capture_stack_trace_init(void) {
88
;
89
}
@@ -143,6 +151,10 @@ void capture_stack_trace(BUFFER *wb) {
151
152
#else
153
154
+bool capture_stack_trace_available(void) {
155
+ return false;
156
+}
157
+
158
void capture_stack_trace_init(void) {
159
;
160
}
src/libnetdata/log/nd_log.h
+1
@@ -36,6 +36,7 @@ ND_UUID nd_log_get_invocation_id(void);
36
void capture_stack_trace(BUFFER *wb);
37
void capture_stack_trace_init(void);
38
void capture_stack_trace_flush(void);
39
+bool capture_stack_trace_available(void);
40
bool capture_stack_trace_is_async_signal_safe(void);
41
42
typedef void (*log_event_t)(const char *filename, const char *function, const char *message, const char *errno_str, const char *stack_trace, long line);
src/libnetdata/signals/signal-code.c
+197
-81
@@ -3,150 +3,266 @@
3
#include "signal-code.h"
4
#include "libnetdata/libnetdata.h"
5
6
+#define SIG_NOT_FOUND 0xFFFFFFF
7
+
8
+// --------------------------------------------------------------------------------------------------------------------
9
+
10
+typedef int SIGNAL_NUM;
11
+
12
+ENUM_STR_MAP_DEFINE(SIGNAL_NUM) = {
13
+ { SIGHUP, "SIGHUP" }, // 1 Hangup.
14
+ { SIGINT, "SIGINT" }, // 2 Interactive attention signal.
15
+ { SIGQUIT, "SIGQUIT" }, // 3 Quit.
16
+ { SIGILL, "SIGILL" }, // 4 Illegal instruction.
17
+ { SIGTRAP, "SIGTRAP" }, // 5 Trace/breakpoint trap.
18
+ { SIGABRT, "SIGABRT" }, // 6 Abnormal termination.
19
+ { SIGBUS, "SIGBUS" }, // 7 Bus error.
20
+ { SIGFPE, "SIGFPE" }, // 8 Erroneous arithmetic operation.
21
+ { SIGKILL, "SIGKILL" }, // 9 Killed.
22
+ { SIGUSR1, "SIGUSR1" }, // 10 User-defined signal 1.
23
+ { SIGSEGV, "SIGSEGV" }, // 11 Invalid access to storage.
24
+ { SIGUSR2, "SIGUSR2" }, // 12 User-defined signal 2.
25
+ { SIGPIPE, "SIGPIPE" }, // 13 Broken pipe.
26
+ { SIGALRM, "SIGALRM" }, // 14 Alarm clock.
27
+ { SIGTERM, "SIGTERM" }, // 15 Termination request.
28
+#ifdef SIGSTKFLT
29
+ { SIGSTKFLT, "SIGSTKFLT" }, // 16 Stack fault (obsolete).
30
+#endif
31
+ { SIGCHLD, "SIGCHLD" }, // 17 Child terminated or stopped.
32
+ { SIGCONT, "SIGCONT" }, // 18 Continue.
33
+ { SIGSTOP, "SIGSTOP" }, // 19 Stop, unblockable.
34
+ { SIGTSTP, "SIGTSTP" }, // 20 Keyboard stop.
35
+ { SIGTTIN, "SIGTTIN" }, // 21 Background read from control terminal.
36
+ { SIGTTOU, "SIGTTOU" }, // 22 Background write to control terminal.
37
+ { SIGURG, "SIGURG" }, // 23 Urgent data is available at a socket.
38
+ { SIGXCPU, "SIGXCPU" }, // 24 CPU time limit exceeded.
39
+ { SIGXFSZ, "SIGXFSZ" }, // 25 File size limit exceeded.
40
+ { SIGVTALRM, "SIGVTALRM" }, // 26 Virtual timer expired.
41
+ { SIGPROF, "SIGPROF" }, // 27 Profiling timer expired.
42
+ { SIGWINCH, "SIGWINCH" }, // 28 Window size change (4.3 BSD, Sun).
43
+#ifdef SIGPOLL
44
+ { SIGPOLL, "SIGPOLL" }, // 29 Pollable event occurred (System V).
45
+#endif
46
+#ifdef SIGPWR
47
+ { SIGPWR, "SIGPWR" }, // 30 Power failure imminent.
48
+#endif
49
+ { SIGSYS, "SIGSYS" }, // 31 Bad system call.
50
+
51
+ // Terminator
52
+ { 0, NULL },
53
+};
54
+
55
+// IMPORTANT: These function return invalid values when the signal is not found - always use a function wrapper
56
+ENUM_STR_DEFINE_FUNCTIONS(SIGNAL_NUM, SIG_NOT_FOUND, NULL);
57
+
58
+// --------------------------------------------------------------------------------------------------------------------
59
+
60
+typedef int SI_CODE;
61
+
62
+ENUM_STR_MAP_DEFINE(SI_CODE) = {
63
+#ifdef SI_ASYNCNL
64
+ { SI_ASYNCNL, "SI_ASYNCNL" },
65
+#endif
66
+#ifdef SI_DETHREAD
67
+ { SI_DETHREAD, "SI_DETHREAD" },
68
+#endif
69
+#ifdef SI_TKILL
70
+ { SI_TKILL, "SI_TKILL" },
71
+#endif
72
+#ifdef SI_SIGIO
73
+ { SI_SIGIO, "SI_SIGIO" },
74
+#endif
75
+ { SI_ASYNCIO, "SI_ASYNCIO" },
76
+ { SI_MESGQ, "SI_MESGQ" },
77
+ { SI_TIMER, "SI_TIMER" },
78
+ { SI_QUEUE, "SI_QUEUE" },
79
+ { SI_USER, "SI_USER" },
80
+#ifdef SI_KERNEL
81
+ { SI_KERNEL, "SI_KERNEL" },
82
+#endif
83
+
84
+ // Terminator
85
+ {0, NULL},
86
+};
87
+
88
+// IMPORTANT: These function return invalid values when the signal is not found - always use a function wrapper
89
+ENUM_STR_DEFINE_FUNCTIONS(SI_CODE, SIG_NOT_FOUND, NULL);
90
+
91
+// --------------------------------------------------------------------------------------------------------------------
92
+
93
// Helper macro to create a SIGNAL_CODE value
94
#define SIGNAL_CODE_CREATE(signo, si_code) (((uint64_t)(signo) << 32) | (uint32_t)(si_code))
95
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
-
96
// Define mapping from SIGNAL_CODE to string representation
97
ENUM_STR_MAP_DEFINE(SIGNAL_CODE) = {
98
// 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
99
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLOPC), "ILL_ILLOPC" }, // Illegal opcode
100
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLOPN), "ILL_ILLOPN" }, // Illegal operand
101
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLADR), "ILL_ILLADR" }, // Illegal addressing mode
102
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_ILLTRP), "ILL_ILLTRP" }, // Illegal trap
103
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_PRVOPC), "ILL_PRVOPC" }, // Privileged opcode
104
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_PRVREG), "ILL_PRVREG" }, // Privileged register
105
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_COPROC), "ILL_COPROC" }, // Coprocessor error
106
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_BADSTK), "ILL_BADSTK" }, // Internal stack error
107
#ifdef ILL_BADIADDR
26
- { SIGNAL_CODE_CREATE(SIGILL, ILL_BADIADDR), "SIGILL/ILL_BADIADDR" }, // Unimplemented instruction address
108
+ { SIGNAL_CODE_CREATE(SIGILL, ILL_BADIADDR), "ILL_BADIADDR" }, // Unimplemented instruction address
109
#endif
110
111
// 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
112
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_INTDIV), "FPE_INTDIV" }, // Integer divide by zero
113
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_INTOVF), "FPE_INTOVF" }, // Integer overflow
114
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTDIV), "FPE_FLTDIV" }, // Floating point divide by zero
115
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTOVF), "FPE_FLTOVF" }, // Floating point overflow
116
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTUND), "FPE_FLTUND" }, // Floating point underflow
117
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTRES), "FPE_FLTRES" }, // Floating point inexact result
118
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTINV), "FPE_FLTINV" }, // Floating point invalid operation
119
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTSUB), "FPE_FLTSUB" }, // Subscript out of range
120
#ifdef FPE_FLTUNK
39
- { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTUNK), "SIGFPE/FPE_FLTUNK" }, // Undiagnosed floating-point exception
121
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_FLTUNK), "FPE_FLTUNK" }, // Undiagnosed floating-point exception
122
#endif
123
#ifdef FPE_CONDTRAP
42
- { SIGNAL_CODE_CREATE(SIGFPE, FPE_CONDTRAP), "SIGFPE/FPE_CONDTRAP" }, // Trap on condition
124
+ { SIGNAL_CODE_CREATE(SIGFPE, FPE_CONDTRAP), "FPE_CONDTRAP" }, // Trap on condition
125
#endif
126
127
// 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
128
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MAPERR), "SEGV_MAPERR" }, // Address not mapped to object
129
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ACCERR), "SEGV_ACCERR" }, // Invalid permissions for mapped object
130
#ifdef SEGV_BNDERR
49
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_BNDERR), "SIGSEGV/SEGV_BNDERR" }, // Bounds checking failure
131
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_BNDERR), "SEGV_BNDERR" }, // Bounds checking failure
132
#endif
133
#ifdef SEGV_PKUERR
52
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_PKUERR), "SIGSEGV/SEGV_PKUERR" }, // Protection key checking failure
134
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_PKUERR), "SEGV_PKUERR" }, // Protection key checking failure
135
#endif
136
#ifdef SEGV_ACCADI
55
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ACCADI), "SIGSEGV/SEGV_ACCADI" }, // ADI not enabled for mapped object
137
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ACCADI), "SEGV_ACCADI" }, // ADI not enabled for mapped object
138
#endif
139
#ifdef SEGV_ADIDERR
58
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ADIDERR), "SIGSEGV/SEGV_ADIDERR" }, // Disrupting MCD error
140
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ADIDERR), "SEGV_ADIDERR" }, // Disrupting MCD error
141
#endif
142
#ifdef SEGV_ADIPERR
61
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ADIPERR), "SIGSEGV/SEGV_ADIPERR" }, // Precise MCD exception
143
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_ADIPERR), "SEGV_ADIPERR" }, // Precise MCD exception
144
#endif
145
#ifdef SEGV_MTEAERR
64
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MTEAERR), "SIGSEGV/SEGV_MTEAERR" }, // Asynchronous ARM MTE error
146
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MTEAERR), "SEGV_MTEAERR" }, // Asynchronous ARM MTE error
147
#endif
148
#ifdef SEGV_MTESERR
67
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MTESERR), "SIGSEGV/SEGV_MTESERR" }, // Synchronous ARM MTE exception
149
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_MTESERR), "SEGV_MTESERR" }, // Synchronous ARM MTE exception
150
#endif
151
#ifdef SEGV_CPERR
70
- { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_CPERR), "SIGSEGV/SEGV_CPERR" }, // Control protection fault
152
+ { SIGNAL_CODE_CREATE(SIGSEGV, SEGV_CPERR), "SEGV_CPERR" }, // Control protection fault
153
#endif
154
155
// 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
156
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_ADRALN), "BUS_ADRALN" }, // Invalid address alignment
157
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_ADRERR), "BUS_ADRERR" }, // Non-existent physical address
158
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_OBJERR), "BUS_OBJERR" }, // Object specific hardware error
159
#ifdef BUS_MCEERR_AR
78
- { SIGNAL_CODE_CREATE(SIGBUS, BUS_MCEERR_AR), "SIGBUS/BUS_MCEERR_AR" }, // Hardware memory error: action required
160
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_MCEERR_AR), "BUS_MCEERR_AR" }, // Hardware memory error: action required
161
#endif
162
#ifdef BUS_MCEERR_AO
81
- { SIGNAL_CODE_CREATE(SIGBUS, BUS_MCEERR_AO), "SIGBUS/BUS_MCEERR_AO" }, // Hardware memory error: action optional
163
+ { SIGNAL_CODE_CREATE(SIGBUS, BUS_MCEERR_AO), "BUS_MCEERR_AO" }, // Hardware memory error: action optional
164
#endif
165
166
// SIGTRAP codes
167
#ifdef TRAP_BRKPT
86
- { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_BRKPT), "SIGTRAP/TRAP_BRKPT" }, // Process breakpoint
168
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_BRKPT), "TRAP_BRKPT" }, // Process breakpoint
169
#endif
170
#ifdef TRAP_TRACE
89
- { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_TRACE), "SIGTRAP/TRAP_TRACE" }, // Process trace trap
171
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_TRACE), "TRAP_TRACE" }, // Process trace trap
172
#endif
173
#ifdef TRAP_BRANCH
92
- { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_BRANCH), "SIGTRAP/TRAP_BRANCH" }, // Process taken branch trap
174
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_BRANCH), "TRAP_BRANCH" }, // Process taken branch trap
175
#endif
176
#ifdef TRAP_HWBKPT
95
- { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_HWBKPT), "SIGTRAP/TRAP_HWBKPT" }, // Hardware breakpoint/watchpoint
177
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_HWBKPT), "TRAP_HWBKPT" }, // Hardware breakpoint/watchpoint
178
#endif
179
#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" },
180
+ { SIGNAL_CODE_CREATE(SIGTRAP, TRAP_UNK), "TRAP_UNK" }, // Undiagnosed trap
181
+#endif
182
183
// Terminator
184
{ 0, NULL },
185
};
186
123
-// Define string conversion functions for SIGNAL_CODE type
124
-ENUM_STR_DEFINE_FUNCTIONS(SIGNAL_CODE, 0, "");
187
+// IMPORTANT: These function return invalid values when the signal is not found - always use a function wrapper
188
+ENUM_STR_DEFINE_FUNCTIONS(SIGNAL_CODE, SIG_NOT_FOUND, NULL);
189
126
-void SIGNAL_CODE_2str_h(SIGNAL_CODE code, char *buf, size_t len) {
127
- if(!buf || !len) return;
128
- if(len < 3) {
190
+// --------------------------------------------------------------------------------------------------------------------
191
+
192
+void SIGNAL_CODE_2str_h(SIGNAL_CODE code, char *buf, size_t size) {
193
+ if(!buf || !size) return;
194
+ if(size < 3 || code == 0) {
195
buf[0] = '\0';
196
return;
197
}
198
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);
199
+ char b1[UINT64_MAX_LENGTH];
200
+ char b2[UINT64_MAX_LENGTH];
201
+
202
+ int signo = SIGNAL_CODE_GET_SIGNO(code);
203
+ int si_code = SIGNAL_CODE_GET_SI_CODE(code);
204
+
205
+ // find the string for the signal
206
+ const char *signo_str = SIGNAL_NUM_2str(signo);
207
+ if(!signo_str || !*signo_str) {
208
+ print_int64(b1, signo);
209
+ signo_str = b1;
210
+ }
211
+
212
+ // find the string for the si_code
213
+ const char *si_code_str = SIGNAL_CODE_2str(code);
214
+ if(!si_code_str || !*si_code_str) {
215
+ si_code_str = SI_CODE_2str(si_code);
216
+ if(!si_code_str || !*si_code_str) {
217
+ print_int64(b2, si_code);
218
+ si_code_str = b2;
219
}
140
- else
141
- strncpyz(buf, s, len - 1);
220
}
143
- else
144
- buf[0] = '\0';
221
+
222
+ // now we have to concatenate the two strings
223
+ // with a slash in between
224
+
225
+ strncpyz(buf, signo_str, size - 1);
226
+ size_t len = strlen(buf);
227
+
228
+ if(size - 1 > len)
229
+ buf[len++] = '/';
230
+
231
+ if(size -1 > len)
232
+ strncpyz(&buf[len], si_code_str, size - 1 - len);
233
+
234
+ buf[size - 1] = '\0';
235
}
236
237
SIGNAL_CODE SIGNAL_CODE_2id_h(const char *str) {
148
- if(strncmp(str, "0x", 2) == 0)
149
- return strtoull(str + 2, NULL, 16);
238
+ if(!str || !*str) return 0;
239
+
240
+ char buf[strlen(str) + 1];
241
+ memcpy(buf, str, strlen(str) + 1);
242
+
243
+ char *si_code_str = strchr(buf, '/');
244
+ if(si_code_str) {
245
+ *si_code_str = '\0';
246
+ si_code_str++;
247
+ }
248
+
249
+ int signo = SIGNAL_NUM_2id(buf);
250
+ if(signo == SIG_NOT_FOUND)
251
+ signo = str2i(buf);
252
+
253
+ SIGNAL_CODE code = si_code_str ? SIGNAL_CODE_2id(si_code_str) : SIG_NOT_FOUND;
254
+ if(code != SIG_NOT_FOUND)
255
+ // this has both signo and si_code in it
256
+ return code;
257
+
258
+ int si_code = si_code_str ? SI_CODE_2id(si_code_str) : SIG_NOT_FOUND;
259
+ if(si_code == SIG_NOT_FOUND)
260
+ si_code = si_code_str ? str2i(si_code_str) : 0;
261
151
- return SIGNAL_CODE_2id(str);
262
+ return SIGNAL_CODE_CREATE(signo, si_code);
263
+}
264
+
265
+// Function to create a SIGNAL_CODE from signal number and signal code
266
+SIGNAL_CODE signal_code(int signo, int si_code) {
267
+ return SIGNAL_CODE_CREATE(signo, si_code);
268
}
src/libnetdata/signals/signal-code.h
+1
-3
@@ -18,10 +18,8 @@ SIGNAL_CODE signal_code(int signo, int si_code);
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
-
21
// 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);
22
+void SIGNAL_CODE_2str_h(SIGNAL_CODE code, char *buf, size_t size);
23
24
// parse a signal code from a string, by name or hex
25
SIGNAL_CODE SIGNAL_CODE_2id_h(const char *str);