diff --git a/Makefile b/Makefile index 4d7543e..53a8521 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,8 @@ APP_SRCS := \ $(APP_DIR)/platform/SemihostingIo.c \ $(APP_DIR)/net/EthernetIf.c \ $(APP_DIR)/storage/diskio.c \ - $(APP_DIR)/storage/SemihostingDisk.c + $(APP_DIR)/storage/SemihostingDisk.c \ + $(APP_DIR)/syslog/SyslogErrorHandler.c UPSTREAM_SRCS := \ $(FREERTOS_SRCS) \ @@ -71,6 +72,7 @@ APP_INCLUDES := \ -I$(APP_DIR)/net/smsc9220 \ -I$(APP_DIR)/platform \ -I$(APP_DIR)/storage \ + -I$(APP_DIR)/syslog \ $(FREERTOS_INCLUDES) $(LWIP_INCLUDES) $(FATFS_INCLUDES) $(MBEDTLS_INCLUDES) \ $(SOLIDSYSLOG_INCLUDES) diff --git a/README.md b/README.md index 43166cc..2ed7f21 100644 --- a/README.md +++ b/README.md @@ -10,28 +10,35 @@ 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 linked into the application without any of it being called. The stage is broken out -for clarity: it separates getting the build to accept the library from getting the device to use -it, so anything that goes wrong here is a build problem and nothing else. +Install the handler before any other call into SolidSyslog. -The library ships `solidsyslog.mk`, so `make/solidsyslog.mk` names the platforms and includes it, -and gets the source lists and include sets back. `SOLIDSYSLOG_PLATFORMS` names the platforms rather -than letting the library infer them from the environment — lwIP alone, because nothing at this -stage reaches any other pack. What is left is this build's own architecture: Core compiles against -the library's own headers into an archive, and the platform sources compile with this device's -flags and config headers. +```c +SolidSyslog_SetErrorHandler(OnSyslogError, NULL); +``` -`--gc-sections` discards what nothing calls, so a platform pack that is linked but unused does not -reach the image. +Nothing in the library fails loudly. A `_Create` that cannot succeed substitutes a Null object and +reports it rather than returning `NULL`, so a logger that has silently stopped looks exactly like +one with nothing to say. The handler is what tells the two apart, which is why it goes in before +the first `_Create` and not after something looks wrong. It reports many misconfiguration errors, +and can save significant time while integrating. -For now you need only the core and a network platform. The library has no release tag yet, so the -pin is a commit. +This is not only an integration aid. The handler is the seam into the device's own error and health +reporting, and it stays valuable at run time: later stages raise an edge-triggered warning when the +collector becomes unreachable and a notice when delivery recovers. Route it wherever the device +already routes faults — here, the same console as everything else, so a fault lands in the run +report next to the rest of what the device did. + +The handler names the four lifecycle categories a misconfigured integration raises and prints the +rest numerically. A device reacting to a fault would switch on the category rather than the text. + +**When you need it.** Every device, and first. It is the only thing standing between a +misconfigured logger and a silent one. -**Cost above baseline: Flash +0 B, RAM +0 B.** +**Cost above baseline: Flash +412 B, RAM +8 B.** @@ -46,6 +53,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 | +412 | +8 | *Deltas are bytes above the baseline, which is itself Flash 350,124 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..e07d021 --- /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,350216 +flash_data,320 +static_bss,110880 +heap_used,4440 +mbedtls_peak,21304 +mbedtls_free,11464 +lwip_mem_free,7576 +lwip_pbufs_free,13 +stack_log,120 +stack_service,52 +stack_harness,2840 diff --git a/measurements/stages.tsv b/measurements/stages.tsv index ed70112..7153a7c 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -11,3 +11,4 @@ # after it. 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 1812bdf..46599e8 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,349808,349808,0 -[report] flash_data,316,316,0 -[report] static_bss,110876,110876,0 +[report] flash_text,350216,349808,408 +[report] flash_data,320,316,4 +[report] static_bss,110880,110876,4 [report] heap_used,4440,4440,0 -[report] mbedtls_peak,21244,21328,-84 -[report] mbedtls_free,11524,11440,84 +[report] mbedtls_peak,21220,21328,-108 +[report] mbedtls_free,11548,11440,108 [report] lwip_mem_free,7576,7576,0 -[report] lwip_pbufs_free,13,13,0 +[report] lwip_pbufs_free,14,13,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 - 349800 324 110876 461000 708c8 /w/build/baseline.elf + 350208 328 110880 461416 70a68 /w/build/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: 349808 (expected 349808, Δ0) - OK flash_data: 316 (expected 316, Δ0) - OK static_bss: 110876 (expected 110876, Δ0) + OK flash_text: 350216 (expected 350216, Δ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: 21244 (expected 21284, Δ40) - OK mbedtls_free: 11524 (expected 11484, Δ40) + OK mbedtls_peak: 21220 (expected 21304, Δ84) + OK mbedtls_free: 11548 (expected 11464, Δ84) OK lwip_mem_free: 7576 (expected 7576, Δ0) - OK lwip_pbufs_free: 13 (expected 13, Δ0) + OK lwip_pbufs_free: 14 (expected 13, Δ1) OK stack_log: 120 (expected 120, Δ0) OK stack_service: 52 (expected 52, Δ0) OK stack_harness: 2840 (expected 2840, Δ0)