#include "DebugTrace.h" #if defined(RESTART_DEBUG_INFO) && defined(ESP32) && !defined(esp32c3m_4mb) //#ifdef RESTART_DEBUG_INFO __NOINIT_ATTR static re_restart_debug_t _debug_info; #include "esp_debug_helpers.h" #include "esp_types.h" #include "esp_attr.h" #include "esp_err.h" #include "soc/soc_memory_layout.h" #include "soc/cpu.h" // RU: Размер буфера для конвертации даты и времeни в строку #define CONFIG_FORMAT_STRFTIME_BUFFER_SIZE 32 #define CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE 20 // YYYY.MM.DD HH:NN:SS + \n // RU: Форматы даты и времени #define CONFIG_FORMAT_DTS "%d.%m.%Y %H:%M:%S" void IRAM_ATTR debugHeapUpdate() { _debug_info.heap_total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT); _debug_info.heap_free = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); size_t _new_free_min = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT); if ((_debug_info.heap_free_min == 0) || (_new_free_min < _debug_info.heap_free_min)) { _debug_info.heap_free_min = _new_free_min; _debug_info.heap_min_time = time(nullptr); }; } void IRAM_ATTR debugBacktraceUpdate() { esp_backtrace_frame_t stk_frame; esp_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp), &(stk_frame.next_pc)); _debug_info.backtrace[0] = esp_cpu_process_stack_pc(stk_frame.pc); bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) && esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc))) ? false : true; uint8_t i = CONFIG_RESTART_DEBUG_STACK_DEPTH; while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) { if (!esp_backtrace_get_next_frame(&stk_frame)) { corrupted = true; }; _debug_info.backtrace[CONFIG_RESTART_DEBUG_STACK_DEPTH - i] = esp_cpu_process_stack_pc(stk_frame.pc); }; } void IRAM_ATTR debugUpdate() { debugHeapUpdate(); debugBacktraceUpdate(); } extern "C" void __wrap_esp_panic_handler(void *info) { debugHeapUpdate(); debugBacktraceUpdate(); bootloop_panic_count += 1; // Call the original panic handler function to finish processing this error (creating a core dump for example...) __real_esp_panic_handler(info); } re_restart_debug_t debugGet() { re_restart_debug_t ret; memset(&ret, 0, sizeof(re_restart_debug_t)); esp_reset_reason_t esp_reason = esp_reset_reason(); if ((esp_reason != ESP_RST_UNKNOWN) && (esp_reason != ESP_RST_POWERON)) { uint8_t i = CONFIG_RESTART_DEBUG_STACK_DEPTH; ret = _debug_info; if (_debug_info.heap_total > heap_caps_get_total_size(MALLOC_CAP_DEFAULT)) { memset(&ret, 0, sizeof(re_restart_debug_t)); }; }; memset(&_debug_info, 0, sizeof(re_restart_debug_t)); return ret; } #define CONFIG_MESSAGE_TG_VERSION_DEF "! Устройство запущено\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s" #define CONFIG_MESSAGE_TG_VERSION_HEAP "! Устройство аварийно перезапущено !\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s\nHEAP: %s" #define CONFIG_MESSAGE_TG_VERSION_TRACE "! Устройство аварийно перезапущено !\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s\nHEAP: %s\nTRACE: %s" char *malloc_stringf(const char *format, ...) { char *ret = nullptr; if (format != nullptr) { // get the list of arguments va_list args1, args2; va_start(args1, format); va_copy(args2, args1); // calculate length of resulting string int len = vsnprintf(nullptr, 0, format, args1); va_end(args1); // allocate memory for string if (len > 0) { #if USE_ESP_MALLOC ret = (char *)esp_malloc(len + 1); #else ret = (char *)malloc(len + 1); #endif if (ret != nullptr) { memset(ret, 0, len + 1); vsnprintf(ret, len + 1, format, args2); } else { // rlog_e(tagHEAP, "Failed to format string: out of memory!"); }; }; va_end(args2); }; return ret; } static char *statesGetDebugHeap(re_restart_debug_t *debug) { if ((debug->heap_total > 0) && (debug->heap_total > debug->heap_free)) { struct tm timeinfo; localtime_r(&debug->heap_min_time, &timeinfo); char time_buffer[CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE]; memset(&time_buffer, 0, CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE); strftime(time_buffer, CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE, CONFIG_FORMAT_DTS, &timeinfo); double heapTotal = (double)debug->heap_total / 1024; double heapFree = (double)debug->heap_free / 1024; double heapFreeMin = (double)debug->heap_free_min / 1024; return malloc_stringf("Total %.1fkB ; Free %.1fkB (%.1f%%) ; FreeMin %.1fkB (%.1f%%) %s", heapTotal, heapFree, 100.0 * (heapFree / heapTotal), heapFreeMin, 100.0 * (heapFreeMin / heapTotal), time_buffer); }; return nullptr; } static char *statesGetDebugTrace(re_restart_debug_t *debug) { char *backtrace = nullptr; char *item = nullptr; char *temp = nullptr; for (uint8_t i = 0; i < CONFIG_RESTART_DEBUG_STACK_DEPTH; i++) { if (debug->backtrace[i] != 0) { item = malloc_stringf("0x%08x", debug->backtrace[i]); if (item) { if (backtrace) { temp = backtrace; backtrace = malloc_stringf("%s %s", temp, item); free(item); free(temp); } else { backtrace = item; }; item = nullptr; }; } else { break; } }; return backtrace; } void printDebugTrace() { // esp_register_shutdown_handler(debugUpdate); re_restart_debug_t debug = debugGet(); char *debug_heap = statesGetDebugHeap(&debug); char *debug_trace = nullptr; if (debug_heap) { debug_trace = statesGetDebugTrace(&debug); if (debug_trace) { Serial.printf(CONFIG_MESSAGE_TG_VERSION_TRACE, jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), debug_heap, debug_trace); // free(debug_trace); } else { Serial.printf(CONFIG_MESSAGE_TG_VERSION_HEAP, jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), debug_heap); }; // free(debug_heap); } else { // Serial.println("DEVICE START"); Serial.printf(CONFIG_MESSAGE_TG_VERSION_DEF, jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str()); } } void sendDebugTraceAndFreeMemory( bool postMsg) { // esp_register_shutdown_handler(debugUpdate); re_restart_debug_t debug = debugGet(); char *debug_heap = statesGetDebugHeap(&debug); char *debug_trace = nullptr; if (debug_heap) { if (isNetworkActive() && postMsg) { debug_trace = statesGetDebugTrace(&debug); if (debug_trace) { if (tlgrmItem) { char *msg; msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_TRACE, jsonReadStr(settingsFlashJson, F("name")).c_str(), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), debug_heap, debug_trace); tlgrmItem->sendTelegramMsg(false, String(msg)); tlgrmItem->sendTelegramMsg(false, String("Подробности /helpDebug в Telegram_v2")); free(msg); } free(debug_trace); } else { /* Serial.printf(CONFIG_MESSAGE_TG_VERSION_HEAP, jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), debug_heap); */ if (tlgrmItem) { char *msg; msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_HEAP, jsonReadStr(settingsFlashJson, F("name")).c_str(), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), debug_heap); tlgrmItem->sendTelegramMsg(false, String(msg)); tlgrmItem->sendTelegramMsg(false, String("Подробности /helpDebug в Telegram_v2")); free(msg); } } } free(debug_heap); } /* else { // Serial.println("DEVICE START"); // Serial.printf(CONFIG_MESSAGE_TG_VERSION_DEF, // FIRMWARE_VERSION, ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str()); if (tlgrmItem && isNetworkActive()) { char *msg; msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_DEF, WiFi.localIP().toString(), FIRMWARE_VERSION, ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str()); tlgrmItem->sendTelegramMsg(false, String(msg)); free(msg); } };*/ } #else void printDebugTrace() {} void sendDebugTraceAndFreeMemory(bool) {} void IRAM_ATTR debugUpdate() {} extern "C" void __wrap_esp_panic_handler(void *info) { // Call the original panic handler function to finish processing this error (creating a core dump for example...) __real_esp_panic_handler(info); } #endif // RESTART_DEBUG_INFO extern "C" bool verifyRollbackLater(){ Serial.printf("verifyRollbackLater OVERRIDDEN FUNCTION!"); return true; } void verifyFirmware(){ Serial.printf("[SYSTEM] - Checking firmware...\n"); const esp_partition_t *running = esp_ota_get_running_partition(); esp_ota_img_states_t ota_state; if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { const char* otaState = ota_state == ESP_OTA_IMG_NEW ? "ESP_OTA_IMG_NEW" : ota_state == ESP_OTA_IMG_PENDING_VERIFY ? "ESP_OTA_IMG_PENDING_VERIFY" : ota_state == ESP_OTA_IMG_VALID ? "ESP_OTA_IMG_VALID" : ota_state == ESP_OTA_IMG_INVALID ? "ESP_OTA_IMG_INVALID" : ota_state == ESP_OTA_IMG_ABORTED ? "ESP_OTA_IMG_ABORTED" : "ESP_OTA_IMG_UNDEFINED"; Serial.printf( "[System] - Ota state: %s\n",otaState); if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { if (esp_ota_mark_app_valid_cancel_rollback() == ESP_OK) { Serial.printf( "[System] - App is valid, rollback cancelled successfully\n"); } else { Serial.printf("[System] - Failed to cancel rollback\n"); } } }else{ Serial.printf("[System] - OTA partition has no record in OTA data\n"); } }