From d8930db8af66f4647d3cccee9a5f752bf43a41c8 Mon Sep 17 00:00:00 2001 From: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com> Date: Sun, 27 Aug 2023 00:33:27 -0500 Subject: [PATCH] Add Stack Dump option to debug menu and to GURU meditation fault (#1414) * Stack dump * Stack dump * Stack dump * Stack dump * Stack dump * Stack dump * Update debug.cpp * Clang * Update debug.cpp * Skip dumping unused bytes of stack --- firmware/application/apps/ui_debug.cpp | 2 + firmware/application/debug.cpp | 85 ++++++++++++++++++++++---- firmware/application/debug.hpp | 2 + firmware/application/irq_controls.cpp | 6 +- firmware/application/irq_controls.hpp | 1 + 5 files changed, 81 insertions(+), 15 deletions(-) diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 8be14402..5fce87d5 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -20,6 +20,7 @@ */ #include "ui_debug.hpp" +#include "debug.hpp" #include "ch.h" @@ -406,6 +407,7 @@ DebugMenuView::DebugMenuView(NavigationView& nav) { {"Touch Test", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push(); }}, {"P.Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push(); }}, {"Debug Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { portapack::persistent_memory::debug_dump(); }}, + {"M0 Stack Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { stack_dump(); }}, //{"Fonts Viewer", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push(); }}, // temporarily disabled to conserve ROM space }); set_max_rows(2); // allow wider buttons diff --git a/firmware/application/debug.cpp b/firmware/application/debug.cpp index 2df10015..f51f1480 100644 --- a/firmware/application/debug.cpp +++ b/firmware/application/debug.cpp @@ -30,6 +30,7 @@ #include "portapack.hpp" #include "string_format.hpp" #include "ui_styles.hpp" +#include "irq_controls.hpp" using namespace ui; @@ -46,7 +47,7 @@ void __debug_log(const std::string& msg) { pg_debug_log->write_entry(msg); } -void runtime_error(LED); +void runtime_error(uint8_t source); std::string number_to_hex_string(uint32_t); void draw_line(int32_t, const char*, regarm_t); static bool error_shown = false; @@ -67,8 +68,10 @@ void draw_guru_meditation_header(uint8_t source, const char* hint) { painter.draw_string({48, 24}, Styles::white, "M? Guru"); painter.draw_string({48 + 8 * 8, 24}, Styles::white, "Meditation"); - if (source == CORTEX_M0) + if (source == CORTEX_M0) { painter.draw_string({48 + 8, 24}, Styles::white, "0"); + painter.draw_string({12, 320 - 32}, Styles::white, "Press DFU to try Stack Dump"); + } if (source == CORTEX_M4) painter.draw_string({48 + 8, 24}, Styles::white, "4"); @@ -83,11 +86,7 @@ void draw_guru_meditation(uint8_t source, const char* hint) { draw_guru_meditation_header(source, hint); } - if (source == CORTEX_M0) - runtime_error(hackrf::one::led_rx); - - if (source == CORTEX_M4) - runtime_error(hackrf::one::led_tx); + runtime_error(source); } void draw_guru_meditation(uint8_t source, const char* hint, struct extctx* ctxp, uint32_t cfsr = 0) { @@ -117,11 +116,7 @@ void draw_guru_meditation(uint8_t source, const char* hint, struct extctx* ctxp, } } - if (source == CORTEX_M0) - runtime_error(hackrf::one::led_rx); - - if (source == CORTEX_M4) - runtime_error(hackrf::one::led_tx); + runtime_error(source); } void draw_line(int32_t y_offset, const char* label, regarm_t value) { @@ -131,7 +126,10 @@ void draw_line(int32_t y_offset, const char* label, regarm_t value) { painter.draw_string({15 + 8 * 8, y_offset}, Styles::white, to_string_hex((uint32_t)value, 8)); } -void runtime_error(LED led) { +void runtime_error(uint8_t source) { + bool dumped{false}; + LED led = (source == CORTEX_M0) ? hackrf::one::led_rx : hackrf::one::led_tx; + led.off(); while (true) { @@ -139,9 +137,70 @@ void runtime_error(LED led) { while (n--) ; led.toggle(); + + // Stack dump is not guaranteed to work in this state, so wait for DFU button press to attempt it + if (!dumped && (swizzled_switches() & (1 << (int)Switch::Dfu))) { + dumped = true; + stack_dump(); + } } } +// TODO: Fix this function to work after a fault; might need to write to screen instead or to Flash memory. +// Using the stack while dumping the stack isn't ideal, but hopefully anything imporant is still on the call stack. +bool stack_dump() { + ui::Painter painter{}; + std::string debug_dir = "DEBUG"; + std::filesystem::path filename{}; + File stack_dump_file{}; + bool error; + std::string str; + uint32_t* p; + int n; + bool data_found; + + make_new_directory(debug_dir); + filename = next_filename_matching_pattern(debug_dir + "/STACK_DUMP_????.TXT"); + error = filename.empty(); + if (!error) + error = stack_dump_file.create(filename) != 0; + if (error) { + painter.draw_string({16, 320 - 32}, ui::Styles::red, "ERROR DUMPING " + filename.filename().string() + "!"); + return false; + } + + for (p = &__process_stack_base__, n = 0, data_found = false; p < &__process_stack_end__; p++) { + if (!data_found) { + if (*p == CRT0_STACKS_FILL_PATTERN) + continue; + else { + data_found = true; + auto stack_space_left = p - &__process_stack_base__; + stack_dump_file.write_line(to_string_hex((uint32_t)&__process_stack_base__, 8) + ": Unused bytes " + to_string_dec_uint(stack_space_left * sizeof(uint32_t))); + + // align subsequent lines to start on 16-byte boundaries + p -= (stack_space_left & 3); + } + } + + if (n++ == 0) { + str = to_string_hex((uint32_t)p, 8) + ":"; + stack_dump_file.write(str.data(), 9); + } + + str = " " + to_string_hex(*p, 8); + stack_dump_file.write(str.data(), 9); + + if (n == 4) { + stack_dump_file.write("\r\n", 2); + n = 0; + } + } + + painter.draw_string({16, 320 - 32}, ui::Styles::green, filename.filename().string() + " dumped!"); + return true; +} + extern "C" { #if CH_DBG_ENABLED void port_halt(void) { diff --git a/firmware/application/debug.hpp b/firmware/application/debug.hpp index ce265a18..4e0ad8e6 100644 --- a/firmware/application/debug.hpp +++ b/firmware/application/debug.hpp @@ -47,4 +47,6 @@ inline uint32_t get_free_stack_space() { return stack_space_left; } +bool stack_dump(); + #endif /*__DEBUG_H__*/ diff --git a/firmware/application/irq_controls.cpp b/firmware/application/irq_controls.cpp index 8fa7f30c..cd1e7f24 100644 --- a/firmware/application/irq_controls.cpp +++ b/firmware/application/irq_controls.cpp @@ -139,7 +139,9 @@ static uint8_t switches_raw = 0; // uint8_t rot_b() const { return (raw_ >> 6) & 1; } // uint8_t dfu() const { return (raw_ >> 7) & 1; }}; -static uint8_t swizzle_raw(uint8_t raw) { +uint8_t swizzled_switches() { + uint8_t raw = io.io_update(touch_pins_configs[touch_phase]); + return (raw & 0x1F) | // Keep the bottom 5 bits the same. ((raw >> 2) & 0x20) | // Shift the DFU bit down to bit 6. ((raw << 1) & 0xC0); // Shift the encoder bits up to be 7 & 8. @@ -183,7 +185,7 @@ void timer0_callback(GPTDriver* const) { eventmask_t event_mask = 0; if (touch_update()) event_mask |= EVT_MASK_TOUCH; - switches_raw = swizzle_raw(io.io_update(touch_pins_configs[touch_phase])); + switches_raw = swizzled_switches(); if (switches_update(switches_raw)) event_mask |= EVT_MASK_SWITCHES; diff --git a/firmware/application/irq_controls.hpp b/firmware/application/irq_controls.hpp index e45dedc4..a158b138 100644 --- a/firmware/application/irq_controls.hpp +++ b/firmware/application/irq_controls.hpp @@ -43,6 +43,7 @@ using SwitchesState = std::bitset<6>; using EncoderPosition = uint32_t; void controls_init(); +uint8_t swizzled_switches(); SwitchesState get_switches_state(); EncoderPosition get_encoder_position(); touch::Frame get_touch_frame();