mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Support for displaying stack on screen after a fault (for debug) (#1416)
* Support for displaying stack on screen after a fault * Update debug.cpp * Highlight possible addresses on stack & support Up key * Add small background white style * Add small background white style * Update debug.cpp * Highlight fault PC in yellow * Add bg_yellow_small style * Add bg_yellow_small style * Clang - trailing space
This commit is contained in:
parent
de81156223
commit
4774b3097f
@ -50,7 +50,9 @@ void __debug_log(const std::string& msg) {
|
|||||||
void runtime_error(uint8_t source);
|
void runtime_error(uint8_t source);
|
||||||
std::string number_to_hex_string(uint32_t);
|
std::string number_to_hex_string(uint32_t);
|
||||||
void draw_line(int32_t, const char*, regarm_t);
|
void draw_line(int32_t, const char*, regarm_t);
|
||||||
|
void draw_stack_dump();
|
||||||
static bool error_shown = false;
|
static bool error_shown = false;
|
||||||
|
uint32_t fault_address{0};
|
||||||
|
|
||||||
void draw_guru_meditation_header(uint8_t source, const char* hint) {
|
void draw_guru_meditation_header(uint8_t source, const char* hint) {
|
||||||
Painter painter;
|
Painter painter;
|
||||||
@ -70,7 +72,7 @@ void draw_guru_meditation_header(uint8_t source, const char* hint) {
|
|||||||
|
|
||||||
if (source == CORTEX_M0) {
|
if (source == CORTEX_M0) {
|
||||||
painter.draw_string({48 + 8, 24}, Styles::white, "0");
|
painter.draw_string({48 + 8, 24}, Styles::white, "0");
|
||||||
painter.draw_string({12, 320 - 32}, Styles::white, "Press DFU to try Stack Dump");
|
painter.draw_string({24, 320 - 32}, Styles::white, "Press DFU for Stack Dump");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source == CORTEX_M4)
|
if (source == CORTEX_M4)
|
||||||
@ -109,6 +111,7 @@ void draw_guru_meditation(uint8_t source, const char* hint, struct extctx* ctxp,
|
|||||||
// to see whats causing the fault.
|
// to see whats causing the fault.
|
||||||
draw_line(80 + i++ * 20, "lr:", ctxp->lr_thd);
|
draw_line(80 + i++ * 20, "lr:", ctxp->lr_thd);
|
||||||
draw_line(80 + i++ * 20, "pc:", ctxp->pc);
|
draw_line(80 + i++ * 20, "pc:", ctxp->pc);
|
||||||
|
fault_address = (uint32_t)ctxp->pc;
|
||||||
|
|
||||||
// see SCB_CFSR_* in libopencm3/cm3/scb.h for details
|
// see SCB_CFSR_* in libopencm3/cm3/scb.h for details
|
||||||
if (cfsr != 0)
|
if (cfsr != 0)
|
||||||
@ -127,7 +130,6 @@ void draw_line(int32_t y_offset, const char* label, regarm_t value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void runtime_error(uint8_t source) {
|
void runtime_error(uint8_t source) {
|
||||||
bool dumped{false};
|
|
||||||
LED led = (source == CORTEX_M0) ? hackrf::one::led_rx : hackrf::one::led_tx;
|
LED led = (source == CORTEX_M0) ? hackrf::one::led_rx : hackrf::one::led_tx;
|
||||||
|
|
||||||
led.off();
|
led.off();
|
||||||
@ -138,26 +140,133 @@ void runtime_error(uint8_t source) {
|
|||||||
;
|
;
|
||||||
led.toggle();
|
led.toggle();
|
||||||
|
|
||||||
// Stack dump is not guaranteed to work in this state, so wait for DFU button press to attempt it
|
// Stack dump will cover entire screen, so wait for DFU button press to attempt it
|
||||||
if (!dumped && (swizzled_switches() & (1 << (int)Switch::Dfu))) {
|
if (swizzled_switches() & (1 << (int)Switch::Dfu)) {
|
||||||
dumped = true;
|
draw_stack_dump();
|
||||||
stack_dump();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fix this function to work after a fault; might need to write to screen instead or to Flash memory.
|
// This function should only be called with interrupts disabled due to reading swizzled_switches()
|
||||||
|
// Using the stack while dumping the stack isn't ideal, but hopefully anything imporant is still on the call stack.
|
||||||
|
void draw_stack_dump() {
|
||||||
|
Painter painter;
|
||||||
|
uint32_t* p{&__process_stack_base__};
|
||||||
|
constexpr int border{8};
|
||||||
|
int x, y;
|
||||||
|
int n{0};
|
||||||
|
bool clear_rect{true};
|
||||||
|
bool first_pass{true};
|
||||||
|
|
||||||
|
// NOTE: Stays in this loop forever unless LEFT button pressed
|
||||||
|
while (1) {
|
||||||
|
if (clear_rect) {
|
||||||
|
painter.fill_rectangle(
|
||||||
|
{border, border, portapack::display.width() - (border * 2), portapack::display.height() - (border * 2)},
|
||||||
|
Color::black());
|
||||||
|
x = y = border;
|
||||||
|
clear_rect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip past unused stack words on 1st pass
|
||||||
|
if (first_pass) {
|
||||||
|
first_pass = false;
|
||||||
|
while ((*p == CRT0_STACKS_FILL_PATTERN) && (p < &__process_stack_end__))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
auto stack_space_left = p - &__process_stack_base__;
|
||||||
|
|
||||||
|
// NOTE: in situations like a hard fault it seems not possible to write strings longer than 16 characters.
|
||||||
|
painter.draw_string({x, y}, Styles::white_small, to_string_hex((uint32_t)&__process_stack_base__, 8));
|
||||||
|
x += 8 * 5;
|
||||||
|
painter.draw_string({x, y}, Styles::white_small, " M0 STACK free=");
|
||||||
|
x += 15 * 5;
|
||||||
|
painter.draw_string({x, y}, Styles::white_small, to_string_dec_uint(stack_space_left));
|
||||||
|
x = border;
|
||||||
|
y += 8;
|
||||||
|
|
||||||
|
// align subsequent lines to start on 16-byte boundaries
|
||||||
|
p -= (stack_space_left & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print one page of stack dump
|
||||||
|
while ((p < &__process_stack_end__) && (y < portapack::display.height() - border - 8)) {
|
||||||
|
// show address if start of line
|
||||||
|
if (n++ == 0) {
|
||||||
|
painter.draw_string({x, y}, Styles::white_small, to_string_hex((uint32_t)p, 8) + ":");
|
||||||
|
x += 9 * 5 - 2; // note: saving 2 pixels here to prevent reaching right border
|
||||||
|
}
|
||||||
|
|
||||||
|
// show stack word -- highlight if a possible code address (low bit will be set too for !thumb) or actual fault address
|
||||||
|
bool code_addr = (*p > 0x1400) && (*p < 0x80000) && (((*p) & 0x1) == 0x1); // approximate address range of code .text region in ROM
|
||||||
|
Style style = (fault_address && *p == fault_address) ? Styles::bg_yellow_small : (code_addr ? Styles::bg_white_small : Styles::white_small);
|
||||||
|
painter.draw_string({x, y}, style, " " + to_string_hex(*p, 8));
|
||||||
|
x += 9 * 5;
|
||||||
|
|
||||||
|
// new line?
|
||||||
|
if (n == 4) {
|
||||||
|
n = 0;
|
||||||
|
x = border;
|
||||||
|
y += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// point to next stack word
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out of room on the screen or end of stack - allow Up/Down paging.
|
||||||
|
// First wait for button release from previous press.
|
||||||
|
// NOTE: can't call swizzle_switches() with interrupted enabled!
|
||||||
|
while (swizzled_switches() & ((1 << (int)Switch::Right) | (1 << (int)Switch::Left) | (1 << (int)Switch::Down) | (1 << (int)Switch::Up) | (1 << (int)Switch::Sel) | (1 << (int)Switch::Dfu)))
|
||||||
|
;
|
||||||
|
|
||||||
|
painter.draw_string({border, portapack::display.height() - border - 8}, Styles::white_small, "Use UP/DOWN key");
|
||||||
|
|
||||||
|
// Wait for button press.
|
||||||
|
while (1) {
|
||||||
|
auto switches = swizzled_switches();
|
||||||
|
|
||||||
|
// If LEFT button pressed, exit
|
||||||
|
if (switches & (1 << (int)Switch::Left))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If DOWN pressed; continue to show next page
|
||||||
|
if (switches & (1 << (int)Switch::Down))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If UP pressed, scroll back by one page
|
||||||
|
if (switches & (1 << (int)Switch::Up)) {
|
||||||
|
// Back up pointer by offset on this screen plus one full screen of 8 pixels per line & 4 words per line
|
||||||
|
// (underflow can't happen here due to stack address range in RAM)
|
||||||
|
p -= ((y - border) + (portapack::display.height() - (border * 2 + 1 * 8))) / (8 / 4);
|
||||||
|
if (p < &__process_stack_base__)
|
||||||
|
p = &__process_stack_base__;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If SEL button pressed, try writing to file (likely to hang in fault condition but heh)
|
||||||
|
if (switches & (1 << (int)Switch::Sel))
|
||||||
|
stack_dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear screen to allow new range to be displayed, if we're not at the end
|
||||||
|
if (p < &__process_stack_end__)
|
||||||
|
clear_rect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disk I/O in this function doesn't work after a fault
|
||||||
// Using the stack while dumping the stack isn't ideal, but hopefully anything imporant is still on the call stack.
|
// Using the stack while dumping the stack isn't ideal, but hopefully anything imporant is still on the call stack.
|
||||||
bool stack_dump() {
|
bool stack_dump() {
|
||||||
ui::Painter painter{};
|
Painter painter;
|
||||||
std::string debug_dir = "DEBUG";
|
std::string debug_dir = "DEBUG";
|
||||||
std::filesystem::path filename{};
|
std::filesystem::path filename{};
|
||||||
File stack_dump_file{};
|
File stack_dump_file{};
|
||||||
bool error;
|
bool error;
|
||||||
std::string str;
|
std::string str;
|
||||||
uint32_t* p;
|
uint32_t* p;
|
||||||
int n;
|
int n{0};
|
||||||
bool data_found;
|
bool data_found{false};
|
||||||
|
|
||||||
make_new_directory(debug_dir);
|
make_new_directory(debug_dir);
|
||||||
filename = next_filename_matching_pattern(debug_dir + "/STACK_DUMP_????.TXT");
|
filename = next_filename_matching_pattern(debug_dir + "/STACK_DUMP_????.TXT");
|
||||||
@ -169,25 +278,28 @@ bool stack_dump() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (p = &__process_stack_base__, n = 0, data_found = false; p < &__process_stack_end__; p++) {
|
for (p = &__process_stack_base__; p < &__process_stack_end__; p++) {
|
||||||
|
// skip past unused stack words
|
||||||
if (!data_found) {
|
if (!data_found) {
|
||||||
if (*p == CRT0_STACKS_FILL_PATTERN)
|
if (*p == CRT0_STACKS_FILL_PATTERN)
|
||||||
continue;
|
continue;
|
||||||
else {
|
else {
|
||||||
data_found = true;
|
data_found = true;
|
||||||
auto stack_space_left = p - &__process_stack_base__;
|
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)));
|
stack_dump_file.write_line(to_string_hex((uint32_t)&__process_stack_base__, 8) + ": Unused words " + to_string_dec_uint(stack_space_left));
|
||||||
|
|
||||||
// align subsequent lines to start on 16-byte boundaries
|
// align subsequent lines to start on 16-byte boundaries
|
||||||
p -= (stack_space_left & 3);
|
p -= (stack_space_left & 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write address
|
||||||
if (n++ == 0) {
|
if (n++ == 0) {
|
||||||
str = to_string_hex((uint32_t)p, 8) + ":";
|
str = to_string_hex((uint32_t)p, 8) + ":";
|
||||||
stack_dump_file.write(str.data(), 9);
|
stack_dump_file.write(str.data(), 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write stack dword
|
||||||
str = " " + to_string_hex(*p, 8);
|
str = " " + to_string_hex(*p, 8);
|
||||||
stack_dump_file.write(str.data(), 9);
|
stack_dump_file.write(str.data(), 9);
|
||||||
|
|
||||||
|
@ -44,6 +44,18 @@ const Style Styles::white_small{
|
|||||||
.foreground = Color::white(),
|
.foreground = Color::white(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Style Styles::bg_white_small{
|
||||||
|
.font = ui::font::fixed_5x8,
|
||||||
|
.background = ui::Color::white(),
|
||||||
|
.foreground = ui::Color::black(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const Style Styles::bg_yellow_small{
|
||||||
|
.font = ui::font::fixed_5x8,
|
||||||
|
.background = ui::Color::yellow(),
|
||||||
|
.foreground = ui::Color::black(),
|
||||||
|
};
|
||||||
|
|
||||||
const Style Styles::yellow{
|
const Style Styles::yellow{
|
||||||
.font = font::fixed_8x16,
|
.font = font::fixed_8x16,
|
||||||
.background = Color::black(),
|
.background = Color::black(),
|
||||||
@ -102,4 +114,4 @@ const Style Styles::orange{
|
|||||||
.font = font::fixed_8x16,
|
.font = font::fixed_8x16,
|
||||||
.background = Color::black(),
|
.background = Color::black(),
|
||||||
.foreground = Color::orange(),
|
.foreground = Color::orange(),
|
||||||
};
|
};
|
||||||
|
@ -37,6 +37,12 @@ class Styles {
|
|||||||
/* White foreground, small font. */
|
/* White foreground, small font. */
|
||||||
static const Style white_small;
|
static const Style white_small;
|
||||||
|
|
||||||
|
/* White background, small font. */
|
||||||
|
static const Style bg_white_small;
|
||||||
|
|
||||||
|
/* Red background, small font. */
|
||||||
|
static const Style bg_yellow_small;
|
||||||
|
|
||||||
/* Yellow foreground. */
|
/* Yellow foreground. */
|
||||||
static const Style yellow;
|
static const Style yellow;
|
||||||
|
|
||||||
@ -70,4 +76,4 @@ class Styles {
|
|||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|
||||||
#endif /*__UI_STYLES_H__*/
|
#endif /*__UI_STYLES_H__*/
|
||||||
|
Loading…
Reference in New Issue
Block a user