From 4aa5e5e22f7dcee520544eb7b519228be627e2eb Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 28 Jul 2026 23:13:04 +0100 Subject: [PATCH] feat: install the SolidSyslog error handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing in the library fails loudly. A _Create that cannot succeed returns a Null object and carries on, so a logger that has silently stopped looks exactly like one with nothing to say. The handler is what tells them apart, and it goes in before the first _Create rather than after something looks wrong. Flash +404 B RAM +8 B The handler prints to the same console as everything else, so a fault lands in the run report next to the rest of what the device did. It names the four lifecycle categories a misconfigured integration raises and prints the rest numerically — a device reacting to those would switch on the range, not the text. That is 404 bytes for something this device may never use. It buys the difference between a silent failure and a sentence, which is worth more than the bytes on anything that has to be trusted to report. Co-Authored-By: Claude Opus 5 (1M context) --- CMakeLists.txt | 2 + README.md | 10 ++-- app/main.c | 4 ++ app/syslog/SyslogErrorHandler.c | 89 +++++++++++++++++++++++++++++++++ app/syslog/SyslogErrorHandler.h | 15 ++++++ measurements/error-handler.csv | 13 +++++ measurements/stages.tsv | 1 + run-report.md | 30 +++++------ 8 files changed, 145 insertions(+), 19 deletions(-) create mode 100644 app/syslog/SyslogErrorHandler.c create mode 100644 app/syslog/SyslogErrorHandler.h create mode 100644 measurements/error-handler.csv diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c79ec1..e1088bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ add_executable(baseline ${APP_DIR}/net/EthernetIf.c ${APP_DIR}/storage/diskio.c ${APP_DIR}/storage/SemihostingDisk.c + ${APP_DIR}/syslog/SyslogErrorHandler.c $ ) @@ -150,6 +151,7 @@ target_include_directories(baseline PRIVATE ${APP_DIR}/net/smsc9220 ${APP_DIR}/platform # CmsdkUart.h, SemihostingExit.h, SemihostingIo.h ${APP_DIR}/storage # SemihostingDisk.h + ${APP_DIR}/syslog # SyslogErrorHandler.h ${FREERTOS_KERNEL_PATH}/include ${FREERTOS_PORT_DIR} ${LWIP_DIR}/src/include diff --git a/README.md b/README.md index 99cfbe3..e18c78e 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,15 @@ It builds on a baseline that simulates the sort of device you might be adding th measures itself: see [docs/baseline.md](docs/baseline.md) for what the baseline is, how the figures are made, and how to run it. -## This stage — Linked +## This stage — Error handler -SolidSyslog is in the build with the lwIP platform selected: the core library, plus the components -that integrate lwIP in raw mode. Nothing calls it yet, so the linker strips it all back out. +The device installs its own handler on SolidSyslog's error slot, before creating anything. A +`_Create` that cannot succeed returns a Null object rather than failing, so without this a +mis-wired logger and a quiet one look identical. -**Cost above baseline: Flash +0 B, RAM +0 B.** +**Cost above baseline: Flash +404 B, RAM +8 B.** @@ -32,6 +33,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage. |---|---|---|---| | Baseline | a device that already networks, stores, and holds an mTLS session — before any syslog | — | — | | Linked | the core library and lwIP raw-mode networking, linked but not yet called | +0 | +0 | +| Error handler | a fault inside the logger reaches the console instead of being silent | +404 | +8 | *Deltas are bytes above the baseline, which is itself Flash 350,308 B, RAM 111,192 B.* diff --git a/app/main.c b/app/main.c index db87cfc..b5a7b11 100644 --- a/app/main.c +++ b/app/main.c @@ -9,6 +9,7 @@ #include "SemihostingExit.h" #include "ServiceTask.h" #include "SimulatedExistingApp.h" +#include "SyslogErrorHandler.h" #include "lwip/tcpip.h" @@ -93,6 +94,9 @@ int main(void) CmsdkUart_Init(&UART_ACCESS, DEVICE_UART0_BASE); (void) printf("[device] solid-syslog-example (FreeRTOS + lwIP + mbedTLS + FatFs)\n"); + /* Before the first _Create — see SyslogErrorHandler.h for why that matters. */ + SyslogErrorHandler_Install(); + /* lwIP tcpip thread + core-lock mutex + mbox. Pre-scheduler safe. */ tcpip_init(NULL, NULL); diff --git a/app/syslog/SyslogErrorHandler.c b/app/syslog/SyslogErrorHandler.c new file mode 100644 index 0000000..4c6f76a --- /dev/null +++ b/app/syslog/SyslogErrorHandler.c @@ -0,0 +1,89 @@ +/* See SyslogErrorHandler.h. Prints to the same console as the rest of the + * device, so a fault shows up in the run report next to everything else. */ + +#include "SyslogErrorHandler.h" + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorCategory.h" +#include "SolidSyslogPrival.h" + +#include +#include + +static const char* SeverityName(enum SolidSyslogSeverity severity) +{ + switch (severity) + { + case SOLIDSYSLOG_SEVERITY_EMERGENCY: + return "EMERGENCY"; + case SOLIDSYSLOG_SEVERITY_ALERT: + return "ALERT"; + case SOLIDSYSLOG_SEVERITY_CRITICAL: + return "CRITICAL"; + case SOLIDSYSLOG_SEVERITY_ERROR: + return "ERROR"; + case SOLIDSYSLOG_SEVERITY_WARNING: + return "WARNING"; + case SOLIDSYSLOG_SEVERITY_NOTICE: + return "NOTICE"; + case SOLIDSYSLOG_SEVERITY_INFORMATIONAL: + return "INFO"; + case SOLIDSYSLOG_SEVERITY_DEBUG: + return "DEBUG"; + } + return "?"; +} + +/* The four a misconfigured integration raises. Role-specific categories sit in + * ranges above 0x0100 and print numerically. */ +static const char* CategoryName(uint16_t category) +{ + switch (category) + { + case SOLIDSYSLOG_CAT_BAD_CONFIG: + return "bad-config"; + case SOLIDSYSLOG_CAT_BAD_ARGUMENT: + return "bad-argument"; + case SOLIDSYSLOG_CAT_POOL_EXHAUSTED: + return "pool-exhausted"; + case SOLIDSYSLOG_CAT_UNKNOWN_DESTROY: + return "unknown-destroy"; + default: + return NULL; + } +} + +static void OnSyslogError(void* context, const struct SolidSyslogErrorEvent* event) +{ + (void) context; + + if (event == NULL) + { + return; + } + + /* Sources are matched by pointer identity — Name is only ever a label, which + * is all it is used for here. */ + const char* source = ((event->Source != NULL) && (event->Source->Name != NULL)) ? event->Source->Name : "?"; + const char* category = CategoryName(event->Category); + + if (category != NULL) + { + (void) printf("[syslog] %s %s %s (detail %ld)\n", SeverityName(event->Severity), source, category, (long) event->Detail); + } + else + { + (void) printf( + "[syslog] %s %s category 0x%04X (detail %ld)\n", + SeverityName(event->Severity), + source, + (unsigned int) event->Category, + (long) event->Detail + ); + } +} + +void SyslogErrorHandler_Install(void) +{ + SolidSyslog_SetErrorHandler(OnSyslogError, NULL); +} diff --git a/app/syslog/SyslogErrorHandler.h b/app/syslog/SyslogErrorHandler.h new file mode 100644 index 0000000..089865a --- /dev/null +++ b/app/syslog/SyslogErrorHandler.h @@ -0,0 +1,15 @@ +/* The device's reaction to a SolidSyslog internal fault. + * + * Nothing in the library fails loudly — a _Create that cannot succeed returns a + * Null object instead — so a logger that has silently stopped looks exactly like + * one with nothing to say. This is the only thing that tells them apart, which + * is why it goes in before the first _Create and not after something looks + * wrong. */ +#ifndef SYSLOG_ERROR_HANDLER_H +#define SYSLOG_ERROR_HANDLER_H + +/** Install the handler on the library's single global slot. Call once, at + * startup, before any SolidSyslog object is created. */ +void SyslogErrorHandler_Install(void); + +#endif /* SYSLOG_ERROR_HANDLER_H */ diff --git a/measurements/error-handler.csv b/measurements/error-handler.csv new file mode 100644 index 0000000..221cc45 --- /dev/null +++ b/measurements/error-handler.csv @@ -0,0 +1,13 @@ +# error-handler figures (bytes) — captured by scripts/run.sh (CAPTURE=1). +# The device reads measurements/Baseline.csv as its frozen baseline and reports current-minus-Baseline. +flash_text,350392 +flash_data,320 +static_bss,110880 +heap_used,4440 +mbedtls_peak,21336 +mbedtls_free,11432 +lwip_mem_free,7576 +lwip_pbufs_free,14 +stack_log,120 +stack_service,52 +stack_harness,2840 diff --git a/measurements/stages.tsv b/measurements/stages.tsv index a09dee6..2999673 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -11,3 +11,4 @@ # after it, and this history has already had one inserted. Baseline Baseline a device that already networks, stores, and holds an mTLS session — before any syslog linked Linked the core library and lwIP raw-mode networking, linked but not yet called +error-handler Error handler a fault inside the logger reaches the console instead of being silent diff --git a/run-report.md b/run-report.md index 19a1519..3d8b6a0 100644 --- a/run-report.md +++ b/run-report.md @@ -1,4 +1,4 @@ -# solid-syslog-example — run (linked) +# solid-syslog-example — run (error-handler) ## Device (self-measured) @@ -9,14 +9,14 @@ [device] sim app (lwIP up, FatFs mounted, broker session held over mTLS): ready [report] --- SolidSyslog cost above baseline (simulated existing application) --- [report] key,current,baseline,used_above_baseline -[report] flash_text,349992,349992,0 -[report] flash_data,316,316,0 -[report] static_bss,110876,110876,0 +[report] flash_text,350392,349992,400 +[report] flash_data,320,316,4 +[report] static_bss,110880,110876,4 [report] heap_used,4440,4440,0 -[report] mbedtls_peak,21328,21332,-4 -[report] mbedtls_free,11440,11436,4 +[report] mbedtls_peak,21208,21332,-124 +[report] mbedtls_free,11560,11436,124 [report] lwip_mem_free,7576,7576,0 -[report] lwip_pbufs_free,14,14,0 +[report] lwip_pbufs_free,13,14,-1 [report] stack_log,120,120,0 [report] stack_service,52,52,0 [report] stack_harness,2840,2840,0 @@ -28,7 +28,7 @@ ```text text data bss dec hex filename - 349984 324 110876 461184 70980 /w/build/baseline-cross/baseline.elf + 350384 328 110880 461592 70b18 /w/build/baseline-cross/baseline.elf ``` ## Listeners (proved before the device ran) @@ -49,17 +49,17 @@ (nothing — this device sends no records yet) ``` -## Self-check (vs measurements/linked.csv) +## Self-check (vs measurements/error-handler.csv) ```text - OK flash_text: 349992 (expected 349992, Δ0) - OK flash_data: 316 (expected 316, Δ0) - OK static_bss: 110876 (expected 110876, Δ0) + OK flash_text: 350392 (expected 350392, Δ0) + OK flash_data: 320 (expected 320, Δ0) + OK static_bss: 110880 (expected 110880, Δ0) OK heap_used: 4440 (expected 4440, Δ0) - OK mbedtls_peak: 21328 (expected 21332, Δ4) - OK mbedtls_free: 11440 (expected 11436, Δ4) + OK mbedtls_peak: 21208 (expected 21336, Δ128) + OK mbedtls_free: 11560 (expected 11432, Δ128) OK lwip_mem_free: 7576 (expected 7576, Δ0) - OK lwip_pbufs_free: 14 (expected 14, Δ0) + OK lwip_pbufs_free: 13 (expected 14, Δ1) OK stack_log: 120 (expected 120, Δ0) OK stack_service: 52 (expected 52, Δ0) OK stack_harness: 2840 (expected 2840, Δ0)