diff --git a/README.md b/README.md index 4be7d09..0ff8562 100644 --- a/README.md +++ b/README.md @@ -10,19 +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 — Buffered +## This stage — TCP -A circular buffer in front of the sender, and a service task that drains it. `Log` now enqueues and -returns instead of sending, so the logging task is no longer held up by the network, and a mutex -makes the two sides safe on different tasks — which means logging can now happen from any task, not -just this one. - -Most of the cost is the ring and the service seam's stack — the send moved onto that task, so that -is where the depth moved too. +The sender is a TCP stream to the collector on 5601 instead of a UDP datagram, with records framed +by octet count as a receiver on a stream expects. A record the network drops is retransmitted +rather than lost, and a send to a collector that is not there fails instead of quietly succeeding. -**Cost above baseline: Flash +6,788 B, RAM +5,676 B.** +**Cost above baseline: Flash +7,320 B, RAM +5,856 B.** @@ -44,6 +40,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage. | Sequence numbers | every record numbered, so a gap in the sequence is visible | +6,032 | +436 | | Message cap | a bounded record size, so a long message truncates instead of being dropped | +6,032 | +1,956 | | Buffered | logging that returns immediately, with the send moved off the logging task | +6,788 | +5,676 | +| TCP | records the network retransmits instead of dropping, and a send that fails when the collector is gone | +7,320 | +5,856 | *Deltas are bytes above the baseline, which is itself Flash 350,308 B, RAM 111,192 B.* diff --git a/app/syslog/Syslog.c b/app/syslog/Syslog.c index 9753b82..c571c98 100644 --- a/app/syslog/Syslog.c +++ b/app/syslog/Syslog.c @@ -1,6 +1,6 @@ /* See Syslog.h. * - * A UDP sender over lwIP behind a circular buffer: Log enqueues and returns, and + * A TCP stream over lwIP behind a circular buffer: Log enqueues and returns, and * the service task drains and sends. The mutex is what makes those two sides * safe on different tasks. * @@ -15,17 +15,20 @@ #include "SolidSyslogEndpointHost.h" #include "SolidSyslogFreeRtosMutex.h" #include "SolidSyslogLwipRawAddress.h" -#include "SolidSyslogLwipRawDatagram.h" #include "SolidSyslogLwipRawMarshal.h" #include "SolidSyslogLwipRawResolver.h" +#include "SolidSyslogLwipRawTcpStream.h" #include "SolidSyslogMetaSd.h" #include "SolidSyslogNullStore.h" #include "SolidSyslogStdAtomicCounter.h" -#include "SolidSyslogUdpSender.h" +#include "SolidSyslogStreamSender.h" #include "SyslogFields.h" #include "lwip/tcpip.h" +#include "FreeRTOS.h" +#include "task.h" + #include #include #include @@ -34,7 +37,7 @@ * the resolver numeric-only — no DNS, so no LWIP_DNS and no DNS resolver * component to compile. */ #define SYSLOG_COLLECTOR_HOST "10.0.2.2" -#define SYSLOG_COLLECTOR_PORT ((uint16_t) 5514U) +#define SYSLOG_COLLECTOR_PORT ((uint16_t) 5601U) /* Depth enough to absorb a burst while the sender is busy, without sizing for a * backlog the store is there to hold. */ @@ -46,7 +49,13 @@ static uint8_t s_ring[SOLIDSYSLOG_CIRCULAR_BUFFER_RING_BYTES(SYSLOG_BUFFER_RECOR /* The logger reads these on every record, so they outlive Syslog_Start. */ static struct SolidSyslogStructuredData* s_sd[1]; -/* Every lwIP Raw call the datagram makes has to happen on the thread that owns +/* Bounds the connect spin so it yields instead of busy-waiting. */ +static void SyslogSleep(int milliseconds) +{ + vTaskDelay(pdMS_TO_TICKS(milliseconds)); +} + +/* Every lwIP Raw call the stream makes has to happen on the thread that owns * the lwIP core. lwipopts.h sets LWIP_TCPIP_CORE_LOCKING, so taking the core * lock in the caller's own task is simpler and cheaper than posting to the tcpip * mailbox — and unconditionally synchronous, which the marshal contract @@ -73,16 +82,17 @@ void Syslog_Start(void) { SolidSyslogLwipRaw_SetMarshal(LwipCoreLockMarshal); - /* A numeric resolver to parse the literal, a datagram for the socket, and an - * address slot for the resolver to write into. No EndpointVersion — this - * collector never moves, so the sender resolves once and pins it. */ - struct SolidSyslogUdpSenderConfig senderConfig = { + struct SolidSyslogLwipRawTcpStreamConfig tcpConfig = {.Sleep = SyslogSleep}; + + /* No EndpointVersion — this collector never moves, so the sender resolves + * once and pins it. */ + struct SolidSyslogStreamSenderConfig senderConfig = { .Resolver = SolidSyslogLwipRawResolver_Create(), - .Datagram = SolidSyslogLwipRawDatagram_Create(), + .Stream = SolidSyslogLwipRawTcpStream_Create(&tcpConfig), .Address = SolidSyslogLwipRawAddress_Create(), .Endpoint = CollectorEndpoint, }; - struct SolidSyslogSender* sender = SolidSyslogUdpSender_Create(&senderConfig); + struct SolidSyslogSender* sender = SolidSyslogStreamSender_Create(&senderConfig); /* One counter Increment per record formatted, so a record that never reaches * the collector leaves a gap in the sequence rather than no trace at all. */ diff --git a/measurements/stages.tsv b/measurements/stages.tsv index 34c934c..3e57072 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -18,3 +18,4 @@ header-fields Header fields a timestamped record naming the device, instead of n sequence-id Sequence numbers every record numbered, so a gap in the sequence is visible message-cap Message cap a bounded record size, so a long message truncates instead of being dropped buffered Buffered logging that returns immediately, with the send moved off the logging task +tcp TCP records the network retransmits instead of dropping, and a send that fails when the collector is gone diff --git a/measurements/tcp.csv b/measurements/tcp.csv new file mode 100644 index 0000000..75601c8 --- /dev/null +++ b/measurements/tcp.csv @@ -0,0 +1,13 @@ +# tcp 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,357140 +flash_data,488 +static_bss,116560 +heap_used,4440 +mbedtls_peak,21328 +mbedtls_free,11440 +lwip_mem_free,7576 +lwip_pbufs_free,13 +stack_log,568 +stack_service,724 +stack_harness,2848 diff --git a/run-report.md b/run-report.md index 9ba08ba..d9c97ab 100644 --- a/run-report.md +++ b/run-report.md @@ -1,4 +1,4 @@ -# solid-syslog-example — run (buffered) +# solid-syslog-example — run (tcp) ## Device (self-measured) @@ -10,16 +10,16 @@ [device] first record logged: yes [report] --- SolidSyslog cost above baseline (simulated existing application) --- [report] key,current,baseline,used_above_baseline -[report] flash_text,356608,349992,6616 +[report] flash_text,357140,349992,7148 [report] flash_data,488,316,172 -[report] static_bss,116380,110876,5504 +[report] static_bss,116560,110876,5684 [report] heap_used,4440,4440,0 -[report] mbedtls_peak,21200,21332,-132 -[report] mbedtls_free,11568,11436,132 +[report] mbedtls_peak,21328,21332,-4 +[report] mbedtls_free,11440,11436,4 [report] lwip_mem_free,7576,7576,0 [report] lwip_pbufs_free,13,14,-1 [report] stack_log,568,120,448 -[report] stack_service,780,52,728 +[report] stack_service,724,52,672 [report] stack_harness,2848,2840,8 [report] --- end --- [device] ready @@ -29,7 +29,7 @@ ```text text data bss dec hex filename - 356600 496 116380 473476 73984 /w/build/baseline-cross/baseline.elf + 357132 496 116560 474188 73c4c /w/build/baseline-cross/baseline.elf ``` ## Listeners (proved before the device ran) @@ -47,23 +47,23 @@ ## Collector (syslog-ng) received ```text -wire <134>1 2026-07-29T07:17:47.410000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1"] device started -parsed PRIORITY=134 TIMESTAMP=2026-07-29T07:17:47+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1"] MSG=device started +wire <134>1 2026-07-29T07:30:26.440000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1"] device started +parsed PRIORITY=134 TIMESTAMP=2026-07-29T07:30:26+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1"] MSG=device started ``` -## Self-check (vs measurements/buffered.csv) +## Self-check (vs measurements/tcp.csv) ```text - OK flash_text: 356608 (expected 356608, Δ0) + OK flash_text: 357140 (expected 357140, Δ0) OK flash_data: 488 (expected 488, Δ0) - OK static_bss: 116380 (expected 116380, Δ0) + OK static_bss: 116560 (expected 116560, Δ0) OK heap_used: 4440 (expected 4440, Δ0) - OK mbedtls_peak: 21200 (expected 21316, Δ116) - OK mbedtls_free: 11568 (expected 11452, Δ116) + OK mbedtls_peak: 21328 (expected 21328, Δ0) + OK mbedtls_free: 11440 (expected 11440, Δ0) OK lwip_mem_free: 7576 (expected 7576, Δ0) OK lwip_pbufs_free: 13 (expected 13, Δ0) OK stack_log: 568 (expected 568, Δ0) - OK stack_service: 780 (expected 780, Δ0) + OK stack_service: 724 (expected 724, Δ0) OK stack_harness: 2848 (expected 2848, Δ0) ```