DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
guardrails.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file guardrails.cpp
6 * @brief Heap/stack guardrail evaluator + JSON (pure) and the ESP32 sampler.
7 *
8 * The evaluator and serializer are host-tested; the sample reads the live esp_* /
9 * FreeRTOS counters on ESP32 and returns zeros on host.
10 */
11
13
14#if DETWS_ENABLE_GUARDRAILS
15
16#include <stdio.h>
17
18uint8_t detws_guardrail_eval(const DetwsHealth *h, uint32_t heap_min, uint32_t frag_min_block, uint32_t stack_min)
19{
20 uint8_t b = DetwsBreach::DETWS_BREACH_NONE;
21 if (!h)
22 return b;
23 if (h->free_heap < heap_min)
24 b |= DetwsBreach::DETWS_BREACH_HEAP;
25 if (h->largest_free_block < frag_min_block)
26 b |= DetwsBreach::DETWS_BREACH_FRAG;
27 if (h->stack_free < stack_min)
28 b |= DetwsBreach::DETWS_BREACH_STACK;
29 return b;
30}
31
32int detws_health_json(const DetwsHealth *h, char *out, size_t cap)
33{
34 if (!out || cap == 0)
35 return 0;
36 out[0] = '\0';
37 if (!h)
38 return 0;
39 int w = snprintf(out, cap, "{\"free_heap\":%u,\"min_free_heap\":%u,\"largest_free_block\":%u,\"stack_free\":%u}",
40 (unsigned)h->free_heap, (unsigned)h->min_free_heap, (unsigned)h->largest_free_block,
41 (unsigned)h->stack_free);
42 if (w < 0 || (size_t)w >= cap)
43 {
44 out[0] = '\0';
45 return 0;
46 }
47 return w;
48}
49
50#ifdef ARDUINO
51
52#include "esp_heap_caps.h"
53#include "esp_system.h"
54#include "freertos/FreeRTOS.h"
55#include "freertos/task.h"
56
57namespace
58{
59// All guardrails sampler state, owned by one instance (internal linkage): the breach
60// callback, so it is one named owner, unreachable from any other translation unit.
61struct GuardrailsCtx
62{
63 detws_breach_fn cb = nullptr;
64};
65GuardrailsCtx s_gr;
66} // namespace
67
68void detws_guardrails_sample(DetwsHealth *h)
69{
70 if (!h)
71 return;
72 h->free_heap = esp_get_free_heap_size();
73 h->min_free_heap = esp_get_minimum_free_heap_size();
74 h->largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
75 h->stack_free = (uint32_t)uxTaskGetStackHighWaterMark(nullptr) * sizeof(StackType_t);
76}
77
78void detws_guardrails_begin(detws_breach_fn cb)
79{
80 s_gr.cb = cb;
81}
82
83uint8_t detws_guardrails_check(void)
84{
85 DetwsHealth h;
86 detws_guardrails_sample(&h);
87 uint8_t b =
89 if (b != DetwsBreach::DETWS_BREACH_NONE && s_gr.cb)
90 s_gr.cb(b, &h);
91 return b;
92}
93
94#else // host build - no live counters
95
96void detws_guardrails_sample(DetwsHealth *h)
97{
98 if (h)
99 *h = DetwsHealth{};
100}
101void detws_guardrails_begin(detws_breach_fn)
102{
103}
104uint8_t detws_guardrails_check(void)
105{
106 return DetwsBreach::DETWS_BREACH_NONE;
107}
108
109#endif // ARDUINO
110
111#endif // DETWS_ENABLE_GUARDRAILS
#define DETWS_GUARDRAIL_STACK_MIN
Task remaining-stack floor (bytes); below this trips the stack guardrail.
#define DETWS_GUARDRAIL_HEAP_MIN
Free-heap floor (bytes); below this trips the heap guardrail.
#define DETWS_GUARDRAIL_FRAG_MIN_BLOCK
Largest-free-block floor (bytes); below this trips the fragmentation guardrail.
Runtime heap/stack guardrails (DETWS_ENABLE_GUARDRAILS).