Ram Monitor
Source: src/ram_monitor.c · 196 lines · 9 functions.
Module entry points
These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.
How to read these diagrams
Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.
These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.
Functions in this file
captureSnapshot() · source line 38
static void captureSnapshot(RamSnapshot *snapshot)
- Execute
const uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT; - Execute
snapshot->psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); - Execute
snapshot->psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM); - Execute
snapshot->psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM); - Execute
snapshot->heap_free = heap_caps_get_free_size(internal_caps); - Execute
snapshot->heap_min = heap_caps_get_minimum_free_size(internal_caps); - Execute
snapshot->heap_largest = heap_caps_get_largest_free_block(internal_caps); - Execute
snapshot->stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t);
Read original function source
static void captureSnapshot(RamSnapshot *snapshot)
{
const uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
snapshot->psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
snapshot->psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
snapshot->psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
snapshot->heap_free = heap_caps_get_free_size(internal_caps);
snapshot->heap_min = heap_caps_get_minimum_free_size(internal_caps);
snapshot->heap_largest = heap_caps_get_largest_free_block(internal_caps);
snapshot->stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t);
}snapshotIsWarning() · source line 50
static bool snapshotIsWarning(const RamSnapshot *snapshot)
- Exit this function
return snapshot->heap_free < RAM_HEAP_WARNING_BYTES || snapshot->psram_free < RAM_PSRAM_WARNING_BYTES || snapshot->stack_min < RAM_STACK_WARNING_BYTES;
Read original function source
static bool snapshotIsWarning(const RamSnapshot *snapshot)
{
return snapshot->heap_free < RAM_HEAP_WARNING_BYTES ||
snapshot->psram_free < RAM_PSRAM_WARNING_BYTES ||
snapshot->stack_min < RAM_STACK_WARNING_BYTES;
}countRecords() · source line 57
static unsigned countRecords(FILE *file)
- Execute
char line[RAM_LOG_LINE_CHARS]; - Decision
fgets(line, sizeof(line), file) == NULLTrue- Exit this function
return 0U;
FalseContinue after this decision.
- Execute
unsigned records = 0U; - WHILE loop
fgets(line, sizeof(line), file) != NULLRepeat body while the loop condition permits; then continue below.
- Decision
line[0] != '\0' && line[0] != '\n'True- Execute
++records;
FalseContinue after this decision.
- Exit this function
return records;
Read original function source
static unsigned countRecords(FILE *file)
{
char line[RAM_LOG_LINE_CHARS];
if (fgets(line, sizeof(line), file) == NULL) return 0U;
unsigned records = 0U;
while (fgets(line, sizeof(line), file) != NULL) {
if (line[0] != '\0' && line[0] != '\n') ++records;
}
return records;
}ramMonitorRecordCount() · source line 69
unsigned ramMonitorRecordCount(void)
Calls into this documentation:
- Execute
FILE *file = fopen(RAM_LOG_PATH, "rb"); - Decision
file == NULLTrue- Exit this function
return 0U;
FalseContinue after this decision.
- Execute
const unsigned records = countRecords(file); - Execute
fclose(file); - Exit this function
return records;
Read original function source
unsigned ramMonitorRecordCount(void)
{
FILE *file = fopen(RAM_LOG_PATH, "rb");
if (file == NULL) return 0U;
const unsigned records = countRecords(file);
fclose(file);
return records;
}ramMonitorReadRecord() · source line 78
bool ramMonitorReadRecord(unsigned record_index, char *record, size_t record_size)
- Decision
record == NULL || record_size == 0UTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
record[0] = '\0'; - Execute
FILE *file = fopen(RAM_LOG_PATH, "rb"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
char line[RAM_LOG_LINE_CHARS]; - Decision
fgets(line, sizeof(line), file) == NULLTrue- Execute
fclose(file); - Exit this function
return false;
FalseContinue after this decision.
- Execute
unsigned index = 0U; - WHILE loop
fgets(line, sizeof(line), file) != NULLRepeat body while the loop condition permits; then continue below.
- Decision
line[0] == '\0' || line[0] == '\n'True- Next loop iteration
continue;
FalseContinue after this decision.
- Decision
index++ != record_indexTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
size_t length = strlen(line); - WHILE loop
length > 0U && (line[length - 1U] == '\n' || line[length - 1U] == '\r')Repeat body while the loop condition permits; then continue below.
- Execute
--length;
- Decision
length >= record_sizeTrue- Execute
length = record_size - 1U;
FalseContinue after this decision.
- Execute
memcpy(record, line, length); - Execute
record[length] = '\0'; - Execute
fclose(file); - Exit this function
return true;
- Execute
fclose(file); - Exit this function
return false;
Read original function source
bool ramMonitorReadRecord(unsigned record_index, char *record, size_t record_size)
{
if (record == NULL || record_size == 0U) return false;
record[0] = '\0';
FILE *file = fopen(RAM_LOG_PATH, "rb");
if (file == NULL) return false;
char line[RAM_LOG_LINE_CHARS];
if (fgets(line, sizeof(line), file) == NULL) {
fclose(file);
return false;
}
unsigned index = 0U;
while (fgets(line, sizeof(line), file) != NULL) {
if (line[0] == '\0' || line[0] == '\n') continue;
if (index++ != record_index) continue;
size_t length = strlen(line);
while (length > 0U && (line[length - 1U] == '\n' || line[length - 1U] == '\r')) --length;
if (length >= record_size) length = record_size - 1U;
memcpy(record, line, length);
record[length] = '\0';
fclose(file);
return true;
}
fclose(file);
return false;
}appendSnapshot() · source line 106
static bool appendSnapshot(const char *record)
Calls into this documentation:
- Execute
FILE *source = fopen(RAM_LOG_PATH, "rb"); - Decision
source == NULLTrue- Execute
FILE *file = fopen(RAM_LOG_PATH, "wb"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
const bool written = fprintf(file, "# Mini Arcade RAM monitor; F=free M=minimum X=largest S=stack\n%s\n", record) > 0; - Exit this function
return fclose(file) == 0 && written;
FalseContinue after this decision.
- Execute
const unsigned records = countRecords(source); - Execute
fclose(source); - Decision
records < RAM_LOG_MAX_RECORDSTrue- Execute
FILE *file = fopen(RAM_LOG_PATH, "ab"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
const bool written = fprintf(file, "%s\n", record) > 0; - Exit this function
return fclose(file) == 0 && written;
FalseContinue after this decision.
- Execute
source = fopen(RAM_LOG_PATH, "rb"); - Execute
FILE *temporary = fopen(RAM_LOG_TEMP_PATH, "wb"); - Decision
source == NULL || temporary == NULLTrue- Decision
source != NULLTrue- Execute
fclose(source);
FalseContinue after this decision.
- Decision
temporary != NULLTrue- Execute
fclose(temporary);
FalseContinue after this decision.
- Execute
remove(RAM_LOG_TEMP_PATH); - Exit this function
return false;
FalseContinue after this decision.
- Execute
char line[RAM_LOG_LINE_CHARS]; - Execute
bool written = fgets(line, sizeof(line), source) != NULL && fputs(line, temporary) >= 0; - Execute
unsigned skipped = 0U; - WHILE loop
written && skipped <= records - RAM_LOG_MAX_RECORDS && fgets(line, sizeof(line), source) != NULLRepeat body while the loop condition permits; then continue below.
- Execute
++skipped;
- WHILE loop
written && fgets(line, sizeof(line), source) != NULLRepeat body while the loop condition permits; then continue below.
- Execute
written = fputs(line, temporary) >= 0;
- Decision
writtenTrue- Execute
written = fprintf(temporary, "%s\n", record) > 0;
FalseContinue after this decision.
- Decision
fclose(source) != 0True- Execute
written = false;
FalseContinue after this decision.
- Decision
fclose(temporary) != 0True- Execute
written = false;
FalseContinue after this decision.
- Decision
!written || rename(RAM_LOG_TEMP_PATH, RAM_LOG_PATH) != 0True- Execute
remove(RAM_LOG_TEMP_PATH); - Exit this function
return false;
FalseContinue after this decision.
- Exit this function
return true;
Read original function source
static bool appendSnapshot(const char *record)
{
FILE *source = fopen(RAM_LOG_PATH, "rb");
if (source == NULL) {
FILE *file = fopen(RAM_LOG_PATH, "wb");
if (file == NULL) return false;
const bool written = fprintf(file, "# Mini Arcade RAM monitor; F=free M=minimum X=largest S=stack\n%s\n",
record) > 0;
return fclose(file) == 0 && written;
}
const unsigned records = countRecords(source);
fclose(source);
if (records < RAM_LOG_MAX_RECORDS) {
FILE *file = fopen(RAM_LOG_PATH, "ab");
if (file == NULL) return false;
const bool written = fprintf(file, "%s\n", record) > 0;
return fclose(file) == 0 && written;
}
source = fopen(RAM_LOG_PATH, "rb");
FILE *temporary = fopen(RAM_LOG_TEMP_PATH, "wb");
if (source == NULL || temporary == NULL) {
if (source != NULL) fclose(source);
if (temporary != NULL) fclose(temporary);
remove(RAM_LOG_TEMP_PATH);
return false;
}
char line[RAM_LOG_LINE_CHARS];
bool written = fgets(line, sizeof(line), source) != NULL && fputs(line, temporary) >= 0;
unsigned skipped = 0U;
while (written && skipped <= records - RAM_LOG_MAX_RECORDS &&
fgets(line, sizeof(line), source) != NULL) {
++skipped;
}
while (written && fgets(line, sizeof(line), source) != NULL)
written = fputs(line, temporary) >= 0;
if (written) written = fprintf(temporary, "%s\n", record) > 0;
if (fclose(source) != 0) written = false;
if (fclose(temporary) != 0) written = false;
if (!written || rename(RAM_LOG_TEMP_PATH, RAM_LOG_PATH) != 0) {
remove(RAM_LOG_TEMP_PATH);
return false;
}
return true;
}persistSnapshot() · source line 154
static bool persistSnapshot(const RamSnapshot *snapshot, uint32_t now_ms, bool warning)
Calls into this documentation:
- Execute
char record[RAM_LOG_LINE_CHARS]; - Execute
const int length = snprintf(record, sizeof(record), "UP=%lus PF=%uK PM=%uK PX=%uK HF=%uK HM=%uK HX=%uK SM=%uK%s", (unsigned long)(now_ms / 1000U), (unsigned)(snapshot->psram_free / 1024U), (unsigned)(snapshot->psram_min / 1024U), (unsigned)(snapshot->psram_largest / 1024U), (unsigned)(snapshot->heap_free / 1024U), (unsigned)(snapshot->heap_min / 1024U), (unsigned)(snapshot->heap_largest / 1024U), (unsigned)(snapshot->stack_min / 1024U), warning ? " WARN" : ""); - Decision
length < 0 || (size_t)length >= sizeof(record)True- Exit this function
return false;
FalseContinue after this decision.
- Decision
!appendSnapshot(record)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
ESP_LOGI(TAG, "%s", record); - Exit this function
return true;
Read original function source
static bool persistSnapshot(const RamSnapshot *snapshot, uint32_t now_ms, bool warning)
{
char record[RAM_LOG_LINE_CHARS];
const int length = snprintf(record, sizeof(record),
"UP=%lus PF=%uK PM=%uK PX=%uK HF=%uK HM=%uK HX=%uK SM=%uK%s",
(unsigned long)(now_ms / 1000U),
(unsigned)(snapshot->psram_free / 1024U),
(unsigned)(snapshot->psram_min / 1024U),
(unsigned)(snapshot->psram_largest / 1024U),
(unsigned)(snapshot->heap_free / 1024U),
(unsigned)(snapshot->heap_min / 1024U),
(unsigned)(snapshot->heap_largest / 1024U),
(unsigned)(snapshot->stack_min / 1024U), warning ? " WARN" : "");
if (length < 0 || (size_t)length >= sizeof(record)) return false;
if (!appendSnapshot(record)) return false;
ESP_LOGI(TAG, "%s", record);
return true;
}ramMonitorInitialize() · source line 173
void ramMonitorInitialize(void)
- Execute
last_sample_ms = 0U; - Execute
last_persist_ms = 0U; - Execute
warning_active = false; - Execute
ESP_LOGI(TAG, "RAM monitor ready: sample 30s, persist 60s, log %u records", RAM_LOG_MAX_RECORDS);
Read original function source
void ramMonitorInitialize(void)
{
last_sample_ms = 0U;
last_persist_ms = 0U;
warning_active = false;
ESP_LOGI(TAG, "RAM monitor ready: sample 30s, persist 60s, log %u records", RAM_LOG_MAX_RECORDS);
}ramMonitorUpdate() · source line 181
void ramMonitorUpdate(uint32_t now_ms)
Calls into this documentation:
- Decision
(uint32_t)(now_ms - last_sample_ms) < RAM_SAMPLE_INTERVAL_MSTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
last_sample_ms = now_ms; - Execute
RamSnapshot snapshot; - Execute
captureSnapshot(&snapshot); - Execute
const bool warning = snapshotIsWarning(&snapshot); - Execute
const bool warning_started = warning && !warning_active; - Execute
warning_active = warning; - Execute
const bool periodic_due = (uint32_t)(now_ms - last_persist_ms) >= RAM_PERSIST_INTERVAL_MS; - Decision
!periodic_due && !warning_startedTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
persistSnapshot(&snapshot, now_ms, warning)True- Execute
last_persist_ms = now_ms;
False- Execute
ESP_LOGE(TAG, "Could not write %s", RAM_LOG_PATH);
Read original function source
void ramMonitorUpdate(uint32_t now_ms)
{
if ((uint32_t)(now_ms - last_sample_ms) < RAM_SAMPLE_INTERVAL_MS) return;
last_sample_ms = now_ms;
RamSnapshot snapshot;
captureSnapshot(&snapshot);
const bool warning = snapshotIsWarning(&snapshot);
const bool warning_started = warning && !warning_active;
warning_active = warning;
const bool periodic_due = (uint32_t)(now_ms - last_persist_ms) >= RAM_PERSIST_INTERVAL_MS;
if (!periodic_due && !warning_started) return;
if (persistSnapshot(&snapshot, now_ms, warning)) last_persist_ms = now_ms;
else ESP_LOGE(TAG, "Could not write %s", RAM_LOG_PATH);
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "ram_monitor.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define RAM_LOG_PATH "/spiffs/ram_log.txt"
#define RAM_LOG_TEMP_PATH "/spiffs/ram_log.tmp"
#define RAM_SAMPLE_INTERVAL_MS 30000U
#define RAM_PERSIST_INTERVAL_MS 60000U
#define RAM_LOG_MAX_RECORDS 128U
#define RAM_LOG_LINE_CHARS RAM_MONITOR_RECORD_CHARS
#define RAM_HEAP_WARNING_BYTES (32U * 1024U)
#define RAM_PSRAM_WARNING_BYTES (512U * 1024U)
#define RAM_STACK_WARNING_BYTES (2U * 1024U)
static const char *TAG = "ram_monitor";
typedef struct {
size_t psram_free;
size_t psram_min;
size_t psram_largest;
size_t heap_free;
size_t heap_min;
size_t heap_largest;
size_t stack_min;
} RamSnapshot;
static uint32_t last_sample_ms;
static uint32_t last_persist_ms;
static bool warning_active;
static void captureSnapshot(RamSnapshot *snapshot)
{
const uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
snapshot->psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
snapshot->psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
snapshot->psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
snapshot->heap_free = heap_caps_get_free_size(internal_caps);
snapshot->heap_min = heap_caps_get_minimum_free_size(internal_caps);
snapshot->heap_largest = heap_caps_get_largest_free_block(internal_caps);
snapshot->stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t);
}
static bool snapshotIsWarning(const RamSnapshot *snapshot)
{
return snapshot->heap_free < RAM_HEAP_WARNING_BYTES ||
snapshot->psram_free < RAM_PSRAM_WARNING_BYTES ||
snapshot->stack_min < RAM_STACK_WARNING_BYTES;
}
static unsigned countRecords(FILE *file)
{
char line[RAM_LOG_LINE_CHARS];
if (fgets(line, sizeof(line), file) == NULL) return 0U;
unsigned records = 0U;
while (fgets(line, sizeof(line), file) != NULL) {
if (line[0] != '\0' && line[0] != '\n') ++records;
}
return records;
}
unsigned ramMonitorRecordCount(void)
{
FILE *file = fopen(RAM_LOG_PATH, "rb");
if (file == NULL) return 0U;
const unsigned records = countRecords(file);
fclose(file);
return records;
}
bool ramMonitorReadRecord(unsigned record_index, char *record, size_t record_size)
{
if (record == NULL || record_size == 0U) return false;
record[0] = '\0';
FILE *file = fopen(RAM_LOG_PATH, "rb");
if (file == NULL) return false;
char line[RAM_LOG_LINE_CHARS];
if (fgets(line, sizeof(line), file) == NULL) {
fclose(file);
return false;
}
unsigned index = 0U;
while (fgets(line, sizeof(line), file) != NULL) {
if (line[0] == '\0' || line[0] == '\n') continue;
if (index++ != record_index) continue;
size_t length = strlen(line);
while (length > 0U && (line[length - 1U] == '\n' || line[length - 1U] == '\r')) --length;
if (length >= record_size) length = record_size - 1U;
memcpy(record, line, length);
record[length] = '\0';
fclose(file);
return true;
}
fclose(file);
return false;
}
static bool appendSnapshot(const char *record)
{
FILE *source = fopen(RAM_LOG_PATH, "rb");
if (source == NULL) {
FILE *file = fopen(RAM_LOG_PATH, "wb");
if (file == NULL) return false;
const bool written = fprintf(file, "# Mini Arcade RAM monitor; F=free M=minimum X=largest S=stack\n%s\n",
record) > 0;
return fclose(file) == 0 && written;
}
const unsigned records = countRecords(source);
fclose(source);
if (records < RAM_LOG_MAX_RECORDS) {
FILE *file = fopen(RAM_LOG_PATH, "ab");
if (file == NULL) return false;
const bool written = fprintf(file, "%s\n", record) > 0;
return fclose(file) == 0 && written;
}
source = fopen(RAM_LOG_PATH, "rb");
FILE *temporary = fopen(RAM_LOG_TEMP_PATH, "wb");
if (source == NULL || temporary == NULL) {
if (source != NULL) fclose(source);
if (temporary != NULL) fclose(temporary);
remove(RAM_LOG_TEMP_PATH);
return false;
}
char line[RAM_LOG_LINE_CHARS];
bool written = fgets(line, sizeof(line), source) != NULL && fputs(line, temporary) >= 0;
unsigned skipped = 0U;
while (written && skipped <= records - RAM_LOG_MAX_RECORDS &&
fgets(line, sizeof(line), source) != NULL) {
++skipped;
}
while (written && fgets(line, sizeof(line), source) != NULL)
written = fputs(line, temporary) >= 0;
if (written) written = fprintf(temporary, "%s\n", record) > 0;
if (fclose(source) != 0) written = false;
if (fclose(temporary) != 0) written = false;
if (!written || rename(RAM_LOG_TEMP_PATH, RAM_LOG_PATH) != 0) {
remove(RAM_LOG_TEMP_PATH);
return false;
}
return true;
}
static bool persistSnapshot(const RamSnapshot *snapshot, uint32_t now_ms, bool warning)
{
char record[RAM_LOG_LINE_CHARS];
const int length = snprintf(record, sizeof(record),
"UP=%lus PF=%uK PM=%uK PX=%uK HF=%uK HM=%uK HX=%uK SM=%uK%s",
(unsigned long)(now_ms / 1000U),
(unsigned)(snapshot->psram_free / 1024U),
(unsigned)(snapshot->psram_min / 1024U),
(unsigned)(snapshot->psram_largest / 1024U),
(unsigned)(snapshot->heap_free / 1024U),
(unsigned)(snapshot->heap_min / 1024U),
(unsigned)(snapshot->heap_largest / 1024U),
(unsigned)(snapshot->stack_min / 1024U), warning ? " WARN" : "");
if (length < 0 || (size_t)length >= sizeof(record)) return false;
if (!appendSnapshot(record)) return false;
ESP_LOGI(TAG, "%s", record);
return true;
}
void ramMonitorInitialize(void)
{
last_sample_ms = 0U;
last_persist_ms = 0U;
warning_active = false;
ESP_LOGI(TAG, "RAM monitor ready: sample 30s, persist 60s, log %u records", RAM_LOG_MAX_RECORDS);
}
void ramMonitorUpdate(uint32_t now_ms)
{
if ((uint32_t)(now_ms - last_sample_ms) < RAM_SAMPLE_INTERVAL_MS) return;
last_sample_ms = now_ms;
RamSnapshot snapshot;
captureSnapshot(&snapshot);
const bool warning = snapshotIsWarning(&snapshot);
const bool warning_started = warning && !warning_active;
warning_active = warning;
const bool periodic_due = (uint32_t)(now_ms - last_persist_ms) >= RAM_PERSIST_INTERVAL_MS;
if (!periodic_due && !warning_started) return;
if (persistSnapshot(&snapshot, now_ms, warning)) last_persist_ms = now_ms;
else ESP_LOGE(TAG, "Could not write %s", RAM_LOG_PATH);
}