/
ellersseer
/
MyCheat
Обзор
Документация
Войти
/
ellersseer
/
MyCheat
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
2
CI/CD
Аналитика
Безопасность
master
src/debug_log.cpp
433 строки
15 KB
Dmitry Zhiltsov
feat(logs): Добавить логи в WEB
20 фев 2026, 20:39
20 фев 2026, 20:39
dae6bcf
Код
Авторство
О чём код?
#include "debug_log.h" #include "log_buffer.h" #include <ArduinoJson.h> #include <stdarg.h> // ============================================ // RTC Memory Storage // ============================================ // RTC_NOINIT_ATTR - NOT initialized on reset, preserves data RTC_NOINIT_ATTR static DebugLogData rtcDebugData; // Global instance DebugLogManager debugLog; // ============================================ // Implementation // ============================================ void DebugLogManager::begin() { logCapture.println("\n[DebugLog] ========== BOOT DIAGNOSTICS =========="); // Check if RTC data is valid bool dataValid = (rtcDebugData.magic == DEBUG_LOG_MAGIC); if (!dataValid) { logCapture.println("[DebugLog] First boot or power loss - initializing RTC data"); initializeData(); } else { logCapture.printf("[DebugLog] RTC data valid, boot #%lu\n", rtcDebugData.bootCount + 1); // Copy previous boot info before overwriting copyPreviousBoot(); } // Increment counters based on reset reason incrementCounters(); // Print reset reason esp_reset_reason_t reason = esp_reset_reason(); RESET_REASON cpu0_reason = rtc_get_reset_reason(0); RESET_REASON cpu1_reason = rtc_get_reset_reason(1); logCapture.printf("[DebugLog] Reset reason: %s (%d)\n", getResetReasonString(reason), (int)reason); logCapture.printf("[DebugLog] CPU0 reset: %s (%d)\n", getCpuResetReasonString(cpu0_reason), (int)cpu0_reason); logCapture.printf("[DebugLog] CPU1 reset: %s (%d)\n", getCpuResetReasonString(cpu1_reason), (int)cpu1_reason); // Store current reset reason rtcDebugData.currentBoot.lastResetReason = (uint8_t)reason; rtcDebugData.currentBoot.lastResetReasonCpu0 = (uint8_t)cpu0_reason; rtcDebugData.currentBoot.lastResetReasonCpu1 = (uint8_t)cpu1_reason; rtcDebugData.currentBoot.bootTime = 0; rtcDebugData.currentBoot.lastAliveTime = millis(); rtcDebugData.currentBoot.freeHeap = ESP.getFreeHeap(); rtcDebugData.currentBoot.minFreeHeap = ESP.getFreeHeap(); // Print statistics logCapture.printf("[DebugLog] Statistics: boots=%lu, crashes=%lu, panics=%lu, wdt=%lu, brownout=%lu\n", rtcDebugData.bootCount, rtcDebugData.crashCount, rtcDebugData.panicCount, rtcDebugData.wdtCount, rtcDebugData.brownoutCount); // Print previous boot info if available if (dataValid && rtcDebugData.previousBoot.lastAliveTime > 0) { logCapture.println("[DebugLog] Previous boot info:"); logCapture.printf(" - Uptime: %lu ms\n", rtcDebugData.previousBoot.lastAliveTime); logCapture.printf(" - Free heap: %lu bytes\n", rtcDebugData.previousBoot.freeHeap); logCapture.printf(" - Min heap: %lu bytes\n", rtcDebugData.previousBoot.minFreeHeap); logCapture.printf(" - Stack HWM OneWire: %u\n", rtcDebugData.previousBoot.stackHwmOnewire); logCapture.printf(" - Stack HWM MQTT: %u\n", rtcDebugData.previousBoot.stackHwmMqtt); } // Check for core dump if (hasCoreDump()) { logCapture.println("[DebugLog] *** CORE DUMP AVAILABLE ***"); logCapture.println(getCoreDumpSummary()); } // Log boot event logEventF(EVT_BOOT, "Boot #%lu, reason=%d", rtcDebugData.bootCount, (int)reason); logCapture.println("[DebugLog] ========================================\n"); } void DebugLogManager::initializeData() { memset(&rtcDebugData, 0, sizeof(rtcDebugData)); rtcDebugData.magic = DEBUG_LOG_MAGIC; rtcDebugData.bootCount = 0; rtcDebugData.eventHead = 0; rtcDebugData.eventCount = 0; } void DebugLogManager::copyPreviousBoot() { // Save current boot data as previous memcpy(&rtcDebugData.previousBoot, &rtcDebugData.currentBoot, sizeof(BootRecord)); } void DebugLogManager::incrementCounters() { rtcDebugData.bootCount++; esp_reset_reason_t reason = esp_reset_reason(); switch (reason) { case ESP_RST_PANIC: rtcDebugData.crashCount++; rtcDebugData.panicCount++; logCapture.println("[DebugLog] !!! PANIC DETECTED !!!"); break; case ESP_RST_TASK_WDT: case ESP_RST_INT_WDT: case ESP_RST_WDT: rtcDebugData.crashCount++; rtcDebugData.wdtCount++; logCapture.println("[DebugLog] !!! WATCHDOG RESET DETECTED !!!"); break; case ESP_RST_BROWNOUT: rtcDebugData.crashCount++; rtcDebugData.brownoutCount++; logCapture.println("[DebugLog] !!! BROWNOUT DETECTED - CHECK POWER SUPPLY !!!"); break; case ESP_RST_POWERON: case ESP_RST_SW: case ESP_RST_DEEPSLEEP: // Normal resets break; default: // Unknown - count as potential crash if (reason != ESP_RST_UNKNOWN) { rtcDebugData.crashCount++; } break; } } void DebugLogManager::logEvent(DebugEventType type, const char* message, uint8_t data1, uint16_t data2) { DebugEvent& evt = rtcDebugData.events[rtcDebugData.eventHead]; evt.timestamp = millis(); evt.type = type; evt.data1 = data1; evt.data2 = data2; if (message) { strncpy(evt.message, message, DEBUG_LOG_MSG_SIZE - 1); evt.message[DEBUG_LOG_MSG_SIZE - 1] = '\0'; } else { evt.message[0] = '\0'; } rtcDebugData.eventHead = (rtcDebugData.eventHead + 1) % DEBUG_LOG_MAX_EVENTS; if (rtcDebugData.eventCount < DEBUG_LOG_MAX_EVENTS) { rtcDebugData.eventCount++; } } void DebugLogManager::logEventF(DebugEventType type, const char* format, ...) { char buffer[DEBUG_LOG_MSG_SIZE]; va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); logEvent(type, buffer); } void DebugLogManager::updateAliveStatus(uint16_t stackHwmOnewire, uint16_t stackHwmMqtt) { rtcDebugData.currentBoot.lastAliveTime = millis(); rtcDebugData.currentBoot.freeHeap = ESP.getFreeHeap(); uint32_t minHeap = ESP.getMinFreeHeap(); if (minHeap < rtcDebugData.currentBoot.minFreeHeap || rtcDebugData.currentBoot.minFreeHeap == 0) { rtcDebugData.currentBoot.minFreeHeap = minHeap; } if (stackHwmOnewire > 0) { rtcDebugData.currentBoot.stackHwmOnewire = stackHwmOnewire; } if (stackHwmMqtt > 0) { rtcDebugData.currentBoot.stackHwmMqtt = stackHwmMqtt; } // Log warning if heap is getting low if (minHeap < 10000 && minHeap > 0) { static uint32_t lastLowMemWarning = 0; if (millis() - lastLowMemWarning > 60000) { // Max once per minute logEventF(EVT_LOW_MEMORY, "Low heap: %lu bytes", minHeap); lastLowMemWarning = millis(); logCapture.printf("[DebugLog] WARNING: Low memory! Free: %lu, Min: %lu\n", ESP.getFreeHeap(), minHeap); } } } uint32_t DebugLogManager::getBootCount() const { return rtcDebugData.bootCount; } uint32_t DebugLogManager::getCrashCount() const { return rtcDebugData.crashCount; } const BootRecord& DebugLogManager::getPreviousBoot() const { return rtcDebugData.previousBoot; } const BootRecord& DebugLogManager::getCurrentBoot() const { return rtcDebugData.currentBoot; } esp_reset_reason_t DebugLogManager::getLastResetReason() const { return esp_reset_reason(); } bool DebugLogManager::wasAbnormalReset() const { esp_reset_reason_t reason = esp_reset_reason(); return (reason == ESP_RST_PANIC || reason == ESP_RST_TASK_WDT || reason == ESP_RST_INT_WDT || reason == ESP_RST_WDT || reason == ESP_RST_BROWNOUT); } const char* DebugLogManager::getResetReasonString(esp_reset_reason_t reason) const { switch (reason) { case ESP_RST_UNKNOWN: return "Unknown"; case ESP_RST_POWERON: return "Power-on"; case ESP_RST_EXT: return "External reset"; case ESP_RST_SW: return "Software reset"; case ESP_RST_PANIC: return "PANIC/Exception"; case ESP_RST_INT_WDT: return "Interrupt watchdog"; case ESP_RST_TASK_WDT: return "Task watchdog"; case ESP_RST_WDT: return "Other watchdog"; case ESP_RST_DEEPSLEEP: return "Deep sleep wake"; case ESP_RST_BROWNOUT: return "Brownout"; case ESP_RST_SDIO: return "SDIO reset"; default: return "Unknown"; } } const char* DebugLogManager::getCpuResetReasonString(RESET_REASON reason) const { switch (reason) { case NO_MEAN: return "No meaning"; case POWERON_RESET: return "Power on"; case SW_RESET: return "Software reset (legacy)"; case OWDT_RESET: return "RTC WDT reset"; case DEEPSLEEP_RESET: return "Deep sleep"; case SDIO_RESET: return "SDIO reset"; case TG0WDT_SYS_RESET: return "Timer Group 0 WDT system"; case TG1WDT_SYS_RESET: return "Timer Group 1 WDT system"; case RTCWDT_SYS_RESET: return "RTC WDT system"; case INTRUSION_RESET: return "Intrusion"; case TGWDT_CPU_RESET: return "Timer Group WDT CPU"; case SW_CPU_RESET: return "Software CPU reset"; case RTCWDT_CPU_RESET: return "RTC WDT CPU"; case EXT_CPU_RESET: return "External CPU reset"; case RTCWDT_BROWN_OUT_RESET: return "Brownout"; case RTCWDT_RTC_RESET: return "RTC WDT RTC"; default: return "Unknown"; } } const char* DebugLogManager::eventTypeToString(DebugEventType type) const { switch (type) { case EVT_BOOT: return "BOOT"; case EVT_WIFI_CONNECT: return "WIFI_OK"; case EVT_WIFI_DISCONNECT: return "WIFI_LOST"; case EVT_MQTT_CONNECT: return "MQTT_OK"; case EVT_MQTT_DISCONNECT: return "MQTT_LOST"; case EVT_MQTT_ERROR: return "MQTT_ERR"; case EVT_ONEWIRE_ERROR: return "1WIRE_ERR"; case EVT_LOW_MEMORY: return "LOW_MEM"; case EVT_STACK_WARNING: return "STACK_WARN"; case EVT_TASK_WDT_WARNING:return "WDT_WARN"; case EVT_WEB_REQUEST: return "WEB_REQ"; case EVT_TEMP_UPDATE: return "TEMP_UPD"; case EVT_ERROR: return "ERROR"; case EVT_WARNING: return "WARNING"; case EVT_INFO: return "INFO"; case EVT_CUSTOM: return "CUSTOM"; default: return "UNKNOWN"; } } String DebugLogManager::getDebugJson() const { JsonDocument doc; // Current session doc["uptime_ms"] = millis(); doc["free_heap"] = ESP.getFreeHeap(); doc["min_free_heap"] = ESP.getMinFreeHeap(); // Reset info esp_reset_reason_t reason = esp_reset_reason(); JsonObject reset = doc["reset"].to<JsonObject>(); reset["reason"] = (int)reason; reset["reason_str"] = getResetReasonString(reason); reset["cpu0_reason"] = (int)rtc_get_reset_reason(0); reset["cpu0_reason_str"] = getCpuResetReasonString(rtc_get_reset_reason(0)); reset["cpu1_reason"] = (int)rtc_get_reset_reason(1); reset["cpu1_reason_str"] = getCpuResetReasonString(rtc_get_reset_reason(1)); reset["was_abnormal"] = wasAbnormalReset(); // Category for UI styling: normal, warning, danger if (wasAbnormalReset()) { reset["category"] = "danger"; } else if (reason == ESP_RST_SW) { reset["category"] = "warning"; } else { reset["category"] = "normal"; } // Statistics JsonObject stats = doc["stats"].to<JsonObject>(); stats["boot_count"] = rtcDebugData.bootCount; stats["crash_count"] = rtcDebugData.crashCount; stats["panic_count"] = rtcDebugData.panicCount; stats["wdt_count"] = rtcDebugData.wdtCount; stats["brownout_count"] = rtcDebugData.brownoutCount; // Previous boot info if (rtcDebugData.previousBoot.lastAliveTime > 0) { JsonObject prev = doc["previous_boot"].to<JsonObject>(); prev["uptime_ms"] = rtcDebugData.previousBoot.lastAliveTime; prev["free_heap"] = rtcDebugData.previousBoot.freeHeap; prev["min_free_heap"] = rtcDebugData.previousBoot.minFreeHeap; prev["stack_hwm_onewire"] = rtcDebugData.previousBoot.stackHwmOnewire; prev["stack_hwm_mqtt"] = rtcDebugData.previousBoot.stackHwmMqtt; prev["reset_reason"] = rtcDebugData.previousBoot.lastResetReason; prev["reset_reason_str"] = getResetReasonString( (esp_reset_reason_t)rtcDebugData.previousBoot.lastResetReason); } // Core dump status doc["has_coredump"] = hasCoreDump(); String output; serializeJson(doc, output); return output; } String DebugLogManager::getEventsJson() const { JsonDocument doc; JsonArray events = doc["events"].to<JsonArray>(); // Output events in chronological order (oldest first) if (rtcDebugData.eventCount > 0) { uint8_t start; uint8_t count = rtcDebugData.eventCount; if (count >= DEBUG_LOG_MAX_EVENTS) { start = rtcDebugData.eventHead; // Oldest event } else { start = 0; } for (uint8_t i = 0; i < count; i++) { uint8_t idx = (start + i) % DEBUG_LOG_MAX_EVENTS; const DebugEvent& evt = rtcDebugData.events[idx]; JsonObject obj = events.add<JsonObject>(); obj["ts"] = evt.timestamp; obj["type"] = eventTypeToString(evt.type); obj["type_id"] = (int)evt.type; if (evt.message[0] != '\0') { obj["msg"] = evt.message; } if (evt.data1 != 0 || evt.data2 != 0) { obj["d1"] = evt.data1; obj["d2"] = evt.data2; } } } doc["count"] = rtcDebugData.eventCount; doc["max"] = DEBUG_LOG_MAX_EVENTS; String output; serializeJson(doc, output); return output; } bool DebugLogManager::hasCoreDump() const { esp_core_dump_summary_t summary; esp_err_t err = esp_core_dump_get_summary(&summary); return (err == ESP_OK); } String DebugLogManager::getCoreDumpSummary() const { esp_core_dump_summary_t summary; esp_err_t err = esp_core_dump_get_summary(&summary); if (err != ESP_OK) { return "No core dump available"; } String result = "Core dump found:\n"; result += " Task: " + String(summary.exc_task) + "\n"; result += " PC: 0x" + String(summary.exc_pc, HEX) + "\n"; // Print backtrace result += " Backtrace: "; for (int i = 0; i < summary.exc_bt_info.depth && i < 16; i++) { result += "0x" + String(summary.exc_bt_info.bt[i], HEX) + " "; } result += "\n"; return result; } void DebugLogManager::eraseCoreDump() { esp_core_dump_image_erase(); logCapture.println("[DebugLog] Core dump erased"); } void DebugLogManager::resetStats() { rtcDebugData.bootCount = 0; rtcDebugData.crashCount = 0; rtcDebugData.panicCount = 0; rtcDebugData.wdtCount = 0; rtcDebugData.brownoutCount = 0; rtcDebugData.eventHead = 0; rtcDebugData.eventCount = 0; memset(&rtcDebugData.previousBoot, 0, sizeof(BootRecord)); logCapture.println("[DebugLog] Statistics reset"); }