Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +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 — Smaller ring
## This stage — Origin address

The ring holds four records instead of eight. It is sized in records, so doubling the cap doubled
what it cost — and four is enough to absorb what gets logged while the service task is sending,
because the store rather than the ring is what holds a backlog.

It gives back almost everything the cap rise took. Nothing else moves: the ring bounds how many
records can be in flight, not how large one may be.
`origin` gains the `ip` PARAM, read from the same interface address `HOSTNAME` reports. A relay or
NAT between the device and the collector rewrites what the collector observes; this is what the
device says about itself, and it survives the hop.

<!-- STAGE-COST:START (generated by scripts/gen-cost-table.py — do not edit by hand) -->

**Cost above baseline: Flash +11,980 B, RAM +7,384 B.**
**Cost above baseline: Flash +12,388 B, RAM +7,384 B.**

<!-- STAGE-COST:END -->

Expand Down Expand Up @@ -49,6 +46,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage.
| Origin | the device named in the record itself, not inferred from the source address | +11,972 | +7,136 |
| Larger cap | headroom for the grown record, so full-width counters cannot push it into truncation | +11,980 | +9,440 |
| Smaller ring | most of the cap rise given back, now the store rather than the ring holds a backlog | +11,980 | +7,384 |
| Origin address | the device's own address in the record, which a relay or NAT between it and the collector cannot rewrite | +12,388 | +7,384 |

*Deltas are bytes above the baseline, which is itself Flash 350,308 B, RAM 111,192 B.*

Expand Down
29 changes: 27 additions & 2 deletions app/syslog/Syslog.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
#include "SolidSyslogLwipRawTcpStream.h"
#include "SolidSyslogMetaSd.h"
#include "SolidSyslogOriginSd.h"
#include "SolidSyslogSdValue.h"
#include "SolidSyslogStdAtomicCounter.h"
#include "SolidSyslogStreamSender.h"
#include "SolidSyslogTimeQuality.h"
#include "SolidSyslogTimeQualitySd.h"
#include "SyslogEnterprise.h"
#include "SyslogFields.h"

#include "lwip/ip4_addr.h"
#include "lwip/tcpip.h"

#include "FreeRTOS.h"
Expand Down Expand Up @@ -74,6 +76,29 @@ static void SyslogTimeQuality(struct SolidSyslogTimeQuality* timeQuality)
timeQuality->SyncAccuracyMicroseconds = SOLIDSYSLOG_SYNC_ACCURACY_OMIT;
}

/* The device's own view of its address, which a relay or NAT between it and the
* collector would otherwise replace. */
static size_t SyslogOriginIpCount(void* context)
{
(void) context;

char address[IP4ADDR_STRLEN_MAX] = {0};

SyslogFields_IpAddress(address, sizeof(address));
return (address[0] != '\0') ? 1U : 0U;
}

static void SyslogOriginIpAt(struct SolidSyslogSdValue* value, void* context, size_t index)
{
(void) context;
(void) index;

char address[IP4ADDR_STRLEN_MAX] = {0};

SyslogFields_IpAddress(address, sizeof(address));
SolidSyslogSdValue_String(value, address);
}

/* Bounds the connect spin so it yields instead of busy-waiting. */
static void SyslogSleep(int milliseconds)
{
Expand Down Expand Up @@ -128,12 +153,12 @@ void Syslog_Start(void)
s_sd[0] = SolidSyslogMetaSd_Create(&metaConfig);
s_sd[1] = SolidSyslogTimeQualitySd_Create(SyslogTimeQuality);

/* No ip: the address the collector sees is the one that reached it, until a
* relay makes that untrue. */
struct SolidSyslogOriginSdConfig originConfig = {
.Software = SYSLOG_SOFTWARE,
.SwVersion = SYSLOG_SW_VERSION,
.EnterpriseId = SYSLOG_ENTERPRISE_ID,
.GetIpCount = SyslogOriginIpCount,
.GetIpAt = SyslogOriginIpAt,
};
s_sd[2] = SolidSyslogOriginSd_Create(&originConfig);

Expand Down
19 changes: 13 additions & 6 deletions app/syslog/SyslogFields.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,27 @@ void SyslogFields_Clock(struct SolidSyslogTimestamp* timestamp)
}
}

void SyslogFields_Hostname(struct SolidSyslogHeaderField* field, void* context)
void SyslogFields_IpAddress(char* out, size_t size)
{
(void) context;

char address[IP4ADDR_STRLEN_MAX] = {0};
out[0] = '\0';
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/* netif state belongs to the lwIP core, so read and format under its lock.
* ip4addr_ntoa_r, not ip4addr_ntoa: the latter shares one static buffer. */
LOCK_TCPIP_CORE();
if (netif_default != NULL)
if ((netif_default != NULL) && !ip4_addr_isany_val(*netif_ip4_addr(netif_default)))
{
(void) ip4addr_ntoa_r(netif_ip4_addr(netif_default), address, (int) sizeof(address));
(void) ip4addr_ntoa_r(netif_ip4_addr(netif_default), out, (int) size);
}
UNLOCK_TCPIP_CORE();
}

void SyslogFields_Hostname(struct SolidSyslogHeaderField* field, void* context)
{
(void) context;

char address[IP4ADDR_STRLEN_MAX] = {0};

SyslogFields_IpAddress(address, sizeof(address));

if (address[0] != '\0')
{
Expand Down
5 changes: 5 additions & 0 deletions app/syslog/SyslogFields.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
#ifndef SYSLOG_FIELDS_H
#define SYSLOG_FIELDS_H

#include <stddef.h>

struct SolidSyslogTimestamp;
struct SolidSyslogHeaderField;

/** SolidSyslogClockFunction. */
void SyslogFields_Clock(struct SolidSyslogTimestamp* timestamp);

/** The default interface's IPv4 address, or an empty string when unavailable. */
void SyslogFields_IpAddress(char* out, size_t size);

/** HOSTNAME as the interface's IPv4 address — RFC 5424 section 6.2.4 allows an
* address where a device has no resolvable name. */
void SyslogFields_Hostname(struct SolidSyslogHeaderField* field, void* context);
Expand Down
13 changes: 13 additions & 0 deletions measurements/origin-ip.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# origin-ip 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,362056
flash_data,640
static_bss,117936
heap_used,4440
mbedtls_peak,21280
mbedtls_free,11488
lwip_mem_free,7576
lwip_pbufs_free,13
stack_log,832
stack_service,1044
stack_harness,2848
1 change: 1 addition & 0 deletions measurements/stages.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ file-store File store records that survive a failed send, spooled to disk with a
origin Origin the device named in the record itself, not inferred from the source address
cap-rise Larger cap headroom for the grown record, so full-width counters cannot push it into truncation
buffer-halve Smaller ring most of the cap rise given back, now the store rather than the ring holds a backlog
origin-ip Origin address the device's own address in the record, which a relay or NAT between it and the collector cannot rewrite
26 changes: 13 additions & 13 deletions run-report.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# solid-syslog-example — run (buffer-halve)
# solid-syslog-example — run (origin-ip)

## Device (self-measured)

Expand All @@ -10,15 +10,15 @@
[device] first record logged: yes
[report] --- SolidSyslog cost above baseline (simulated existing application) ---
[report] key,current,baseline,used_above_baseline
[report] flash_text,361648,349992,11656
[report] flash_text,362056,349992,12064
[report] flash_data,640,316,324
[report] static_bss,117936,110876,7060
[report] heap_used,4440,4440,0
[report] mbedtls_peak,21340,21332,8
[report] mbedtls_free,11428,11436,-8
[report] mbedtls_peak,21280,21332,-52
[report] mbedtls_free,11488,11436,52
[report] lwip_mem_free,7576,7576,0
[report] lwip_pbufs_free,13,14,-1
[report] stack_log,824,120,704
[report] stack_log,832,120,712
[report] stack_service,1044,52,992
[report] stack_harness,2848,2840,8
[report] --- end ---
Expand All @@ -29,7 +29,7 @@

```text
text data bss dec hex filename
361640 648 117936 480224 753e0 /w/build/baseline-cross/baseline.elf
362048 648 117936 480632 75578 /w/build/baseline-cross/baseline.elf
```

## Listeners (proved before the device ran)
Expand All @@ -47,22 +47,22 @@
## Collector (syslog-ng) received

```text
wire <134>1 2026-07-29T08:29:37.410000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="241"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473"] device started
parsed PRIORITY=134 TIMESTAMP=2026-07-29T08:29:37+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="241"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473"] MSG=device started
wire <134>1 2026-07-29T10:10:44.420000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="242"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"] device started
parsed PRIORITY=134 TIMESTAMP=2026-07-29T10:10:44+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="242"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"] MSG=device started
```

## Self-check (vs measurements/buffer-halve.csv)
## Self-check (vs measurements/origin-ip.csv)

```text
OK flash_text: 361648 (expected 361648, Δ0)
OK flash_text: 362056 (expected 362056, Δ0)
OK flash_data: 640 (expected 640, Δ0)
OK static_bss: 117936 (expected 117936, Δ0)
OK heap_used: 4440 (expected 4440, Δ0)
OK mbedtls_peak: 21340 (expected 21340, Δ0)
OK mbedtls_free: 11428 (expected 11428, Δ0)
OK mbedtls_peak: 21280 (expected 21280, Δ0)
OK mbedtls_free: 11488 (expected 11488, Δ0)
OK lwip_mem_free: 7576 (expected 7576, Δ0)
OK lwip_pbufs_free: 13 (expected 13, Δ0)
OK stack_log: 824 (expected 824, Δ0)
OK stack_log: 832 (expected 832, Δ0)
OK stack_service: 1044 (expected 1044, Δ0)
OK stack_harness: 2848 (expected 2848, Δ0)
```
Expand Down
Loading