debug mode added for memory testing

This commit is contained in:
cottongin 2026-01-26 13:56:36 -05:00
parent d8bee1d21f
commit a707cc6da2
No known key found for this signature in database
GPG Key ID: 0ECC91FE4655C262
25 changed files with 89 additions and 9 deletions

View File

@ -63,3 +63,11 @@ extends = base
build_flags =
${base.build_flags}
-DCROSSPOINT_VERSION=\"${crosspoint.version}\"
[env:debug_memory]
extends = base
build_flags =
${base.build_flags}
-DCROSSPOINT_VERSION=\"${crosspoint.version}-dev\"
-DDEBUG_MEMORY=1
-DCORE_DEBUG_LEVEL=4

View File

@ -60,6 +60,7 @@ void OpdsBookBrowserActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -45,6 +45,7 @@ void DictionaryMenuActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -40,6 +40,7 @@ void DictionaryResultActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -88,6 +88,7 @@ void DictionarySearchActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -42,6 +42,7 @@ void EpubWordSelectionActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -39,6 +39,10 @@ int HomeActivity::getMenuItemCount() const {
void HomeActivity::onEnter() {
Activity::onEnter();
Serial.printf("[%lu] [HOME] [MEM] onEnter - Free: %d, Largest: %d, CoverBuffer: %s (%d bytes)\n", millis(),
ESP.getFreeHeap(), ESP.getMaxAllocHeap(), coverBufferStored ? "allocated" : "null",
coverBufferStored ? static_cast<int>(GfxRenderer::getBufferSize()) : 0);
renderingMutex = xSemaphoreCreateMutex();
// Check if we have a book to continue reading
@ -153,17 +157,24 @@ void HomeActivity::onEnter() {
void HomeActivity::onExit() {
Activity::onExit();
Serial.printf("[%lu] [HOME] [MEM] onExit start - Free: %d, Largest: %d, CoverBuffer: %s\n", millis(),
ESP.getFreeHeap(), ESP.getMaxAllocHeap(), coverBufferStored ? "allocated" : "null");
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
// NOTE: Do NOT free cover buffer here - keep it cached for fast re-entry
// The buffer will be freed/replaced when the book changes
Serial.printf("[%lu] [HOME] [MEM] onExit end - Free: %d, Largest: %d\n", millis(), ESP.getFreeHeap(),
ESP.getMaxAllocHeap());
}
bool HomeActivity::storeCoverBuffer() {
@ -202,8 +213,11 @@ bool HomeActivity::restoreCoverBuffer() {
void HomeActivity::freeCoverBuffer() {
if (coverBuffer) {
Serial.printf("[%lu] [HOME] [MEM] Freeing cover buffer (%d bytes), heap before: %d\n", millis(),
static_cast<int>(GfxRenderer::getBufferSize()), ESP.getFreeHeap());
free(coverBuffer);
coverBuffer = nullptr;
Serial.printf("[%lu] [HOME] [MEM] Cover buffer freed, heap after: %d\n", millis(), ESP.getFreeHeap());
}
coverBufferStored = false;
}

View File

@ -96,6 +96,7 @@ void ListViewActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -188,6 +188,7 @@ void MyLibraryActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -82,6 +82,7 @@ void CalibreWirelessActivity::onExit() {
if (networkTaskHandle) {
vTaskDelete(networkTaskHandle);
networkTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
xSemaphoreGive(stateMutex);
@ -90,6 +91,7 @@ void CalibreWirelessActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -5,6 +5,7 @@
#include <GfxRenderer.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include <esp_wifi.h>
#include <qrcode.h>
#include <cstddef>
@ -94,15 +95,18 @@ void CrossPointWebServerActivity::onEnter() {
void CrossPointWebServerActivity::onExit() {
ActivityWithSubactivity::onExit();
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d, Largest: %d\n", millis(), ESP.getFreeHeap(),
ESP.getMaxAllocHeap());
state = WebServerActivityState::SHUTTING_DOWN;
// Stop the web server first (before disconnecting WiFi)
stopWebServer();
Serial.printf("[%lu] [WEBACT] [MEM] After stopWebServer: %d bytes\n", millis(), ESP.getFreeHeap());
// Stop mDNS
MDNS.end();
Serial.printf("[%lu] [WEBACT] [MEM] After MDNS.end: %d bytes\n", millis(), ESP.getFreeHeap());
// Stop DNS server if running (AP mode)
if (dnsServer) {
@ -110,6 +114,7 @@ void CrossPointWebServerActivity::onExit() {
dnsServer->stop();
delete dnsServer;
dnsServer = nullptr;
Serial.printf("[%lu] [WEBACT] [MEM] After DNS server cleanup: %d bytes\n", millis(), ESP.getFreeHeap());
}
// Brief wait for LWIP stack to flush pending packets
@ -120,16 +125,24 @@ void CrossPointWebServerActivity::onExit() {
Serial.printf("[%lu] [WEBACT] Stopping WiFi AP...\n", millis());
WiFi.softAPdisconnect(true);
} else {
Serial.printf("[%lu] [WEBACT] Disconnecting WiFi (graceful)...\n", millis());
WiFi.disconnect(false); // false = don't erase credentials, send disconnect frame
Serial.printf("[%lu] [WEBACT] Disconnecting WiFi...\n", millis());
WiFi.disconnect(false); // false = don't erase credentials, just send disconnect frame
}
delay(30); // Allow disconnect frame to be sent
delay(100); // Allow disconnect frame to be sent and event callbacks to complete
Serial.printf("[%lu] [WEBACT] [MEM] After WiFi disconnect: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] Setting WiFi mode OFF...\n", millis());
WiFi.mode(WIFI_OFF);
delay(30); // Allow WiFi hardware to power down
delay(50);
Serial.printf("[%lu] [WEBACT] [MEM] After WiFi.mode(OFF): %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap after WiFi disconnect: %d bytes\n", millis(), ESP.getFreeHeap());
// Aggressive WiFi cleanup: stop the WiFi driver to recover more memory
// WiFi is on-demand for File Transfer only, so full cleanup is safe
Serial.printf("[%lu] [WEBACT] Stopping WiFi driver (esp_wifi_stop)...\n", millis());
esp_wifi_stop();
delay(100); // Allow LWIP stack to fully release
Serial.printf("[%lu] [WEBACT] [MEM] After esp_wifi_stop: %d, Largest: %d\n", millis(), ESP.getFreeHeap(),
ESP.getMaxAllocHeap());
// Acquire mutex before deleting task
Serial.printf("[%lu] [WEBACT] Acquiring rendering mutex before task deletion...\n", millis());
@ -140,16 +153,18 @@ void CrossPointWebServerActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
Serial.printf("[%lu] [WEBACT] Display task deleted\n", millis());
// Allow idle task to free the task stack
vTaskDelay(10 / portTICK_PERIOD_MS);
Serial.printf("[%lu] [WEBACT] [MEM] After task delete + yield: %d bytes\n", millis(), ESP.getFreeHeap());
}
// Delete the mutex
Serial.printf("[%lu] [WEBACT] Deleting mutex...\n", millis());
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
Serial.printf("[%lu] [WEBACT] Mutex deleted\n", millis());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d, Largest: %d, MinFree: %d\n", millis(),
ESP.getFreeHeap(), ESP.getMaxAllocHeap(), ESP.getMinFreeHeap());
}
void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode) {

View File

@ -44,6 +44,7 @@ void NetworkModeSelectionActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -82,6 +82,7 @@ void WifiSelectionActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
Serial.printf("[%lu] [WIFI] Display task deleted\n", millis());
}

View File

@ -192,6 +192,7 @@ void EpubReaderActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -88,6 +88,7 @@ void EpubReaderChapterSelectionActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -131,6 +131,7 @@ void TxtReaderActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -109,6 +109,7 @@ void XtcReaderActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -69,6 +69,7 @@ void XtcReaderChapterSelectionActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -44,6 +44,7 @@ void CalibreSettingsActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -63,6 +63,7 @@ void CategorySettingsActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -35,6 +35,7 @@ void ClearCacheActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -90,6 +90,7 @@ void OtaUpdateActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -115,6 +115,7 @@ void SettingsActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -55,6 +55,7 @@ void KeyboardEntryActivity::onExit() {
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;

View File

@ -114,17 +114,38 @@ EpdFontFamily ui12FontFamily(&ui12RegularFont, &ui12BoldFont);
unsigned long t1 = 0;
unsigned long t2 = 0;
// Memory debugging helper - logs heap state for tracking leaks
#ifdef DEBUG_MEMORY
void logMemoryState(const char* tag, const char* context) {
Serial.printf("[%lu] [%s] [MEM] %s - Free: %d, Largest: %d, MinFree: %d\n",
millis(), tag, context,
ESP.getFreeHeap(),
ESP.getMaxAllocHeap(),
ESP.getMinFreeHeap());
}
#else
// No-op when not in debug mode
#define logMemoryState(tag, context) ((void)0)
#endif
void exitActivity() {
if (currentActivity) {
logMemoryState("MAIN", "Before onExit");
currentActivity->onExit();
logMemoryState("MAIN", "After onExit, before delete");
delete currentActivity;
currentActivity = nullptr;
// Allow idle task to free FreeRTOS task stacks
delay(50);
logMemoryState("MAIN", "After delete + delay");
}
}
void enterNewActivity(Activity* activity) {
logMemoryState("MAIN", "Before enterNewActivity");
currentActivity = activity;
currentActivity->onEnter();
logMemoryState("MAIN", "After onEnter");
}
// Verify long press on wake-up from deep sleep