Added esp-idf hook to capture panic log and save it to sd card

This copies (to the RTC memor)y what the panic handler sends over serial.
That way we can save it to crash.log on the SD Card after the restart.

I'm still testing but so far it doesn't seem to interfer with anything
This commit is contained in:
Alex Duchesne 2021-03-01 21:14:08 -05:00
parent 45702937ce
commit 394bd6d259
10 changed files with 145 additions and 94 deletions

View File

@ -107,35 +107,23 @@ static DRAM_ATTR const ili_init_cmd_t ili_init_cmds[] = {
static void
backlight_init()
{
// Initial backlight percent
int percent = backlightLevels[displayConfig.backlight % RG_BACKLIGHT_LEVEL_COUNT];
//configure timer0
ledc_timer_config_t ledc_timer;
memset(&ledc_timer, 0, sizeof(ledc_timer));
ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
};
ledc_timer.duty_resolution = LEDC_TIMER_13_BIT; //set timer counter bit number
ledc_timer.freq_hz = 5000; //set frequency of pwm
ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; //timer mode,
ledc_timer.timer_num = LEDC_TIMER_0; //timer index
//set the configuration
ledc_channel_config_t ledc_channel;
memset(&ledc_channel, 0, sizeof(ledc_channel));
//set LEDC channel 0
ledc_channel.channel = LEDC_CHANNEL_0;
//set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1)
ledc_channel.duty = BACKLIGHT_DUTY_MAX * (percent * 0.01f);
//GPIO number
ledc_channel.gpio_num = RG_GPIO_LCD_BCKL;
//GPIO INTR TYPE, as an example, we enable fade_end interrupt here.
ledc_channel.intr_type = LEDC_INTR_FADE_END;
//set LEDC mode, from ledc_mode_t
ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;
//set LEDC timer source, if different channel use one timer,
//the frequency and bit_num of these channels should be the same
ledc_channel.timer_sel = LEDC_TIMER_0;
ledc_channel_config_t ledc_channel = {
.channel = LEDC_CHANNEL_0,
.duty = BACKLIGHT_DUTY_MAX * (percent * 0.01f),
.gpio_num = RG_GPIO_LCD_BCKL,
.hpoint = 0,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_sel = LEDC_TIMER_0,
};
ledc_timer_config(&ledc_timer);
ledc_channel_config(&ledc_channel);
@ -145,7 +133,7 @@ backlight_init()
static void
backlight_deinit()
{
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0, 100);
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0, 50);
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE);
ledc_fade_func_uninstall();
}
@ -155,7 +143,7 @@ backlight_set_level(int percent)
{
int duty = BACKLIGHT_DUTY_MAX * (RG_MIN(RG_MAX(percent, 5), 100) * 0.01f);
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty, 10);
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty, 50);
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
RG_LOGI("backlight set to %d%%\n", percent);
@ -216,11 +204,10 @@ spi_put_transaction(spi_transaction_t* t)
xSemaphoreGive(spi_count_semaphore);
}
//This function is called (in irq context!) just before a transmission starts. It will
//set the D/C line to the value indicated in the user field.
IRAM_ATTR static void
spi_pre_transfer_callback(spi_transaction_t *t)
{
// Set the data/command line accordingly
gpio_set_level(RG_GPIO_LCD_DC, (int)t->user & 0x01);
}
@ -244,10 +231,7 @@ spi_task(void *arg)
spi_put_buffer((uint16_t*)t->tx_buffer);
}
if (xQueueSend(spi_queue, &t, portMAX_DELAY) != pdPASS)
{
RG_PANIC("display");
}
xQueueSend(spi_queue, &t, portMAX_DELAY);
// if (uxQueueSpacesAvailable(spi_queue) == 0)
if (uxQueueMessagesWaiting(spi_count_semaphore) == 0)
@ -274,24 +258,23 @@ spi_initialize()
xQueueSend(spi_queue, &param, portMAX_DELAY);
}
spi_bus_config_t buscfg;
memset(&buscfg, 0, sizeof(buscfg));
spi_bus_config_t buscfg = {
.miso_io_num = RG_GPIO_LCD_MISO,
.mosi_io_num = RG_GPIO_LCD_MOSI,
.sclk_io_num = RG_GPIO_LCD_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
buscfg.miso_io_num = RG_GPIO_LCD_MISO;
buscfg.mosi_io_num = RG_GPIO_LCD_MOSI;
buscfg.sclk_io_num = RG_GPIO_LCD_CLK;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
spi_device_interface_config_t devcfg;
memset(&devcfg, 0, sizeof(devcfg));
devcfg.clock_speed_hz = SPI_MASTER_FREQ_40M; // 80Mhz causes glitches unfortunately
devcfg.mode = 0; // SPI mode 0
devcfg.spics_io_num = RG_GPIO_LCD_CS; // CS pin
devcfg.queue_size = SPI_TRANSACTION_COUNT; // We want to be able to queue 5 transactions at a time
devcfg.pre_cb = spi_pre_transfer_callback; // Specify pre-transfer callback to handle D/C line
devcfg.flags = SPI_DEVICE_NO_DUMMY; // SPI_DEVICE_HALFDUPLEX;
spi_device_interface_config_t devcfg = {
.clock_speed_hz = SPI_MASTER_FREQ_40M, // 80Mhz causes glitches unfortunately
.mode = 0, // SPI mode 0
.spics_io_num = RG_GPIO_LCD_CS, // CS pin
.queue_size = SPI_TRANSACTION_COUNT, // We want to be able to queue 5 transactions at a time
.pre_cb = spi_pre_transfer_callback, // Specify pre-transfer callback to handle D/C line and SPI lock
// .post_cb = spi_post_transfer_callback, // Specify post-transfer callback to handle SPI lock
.flags = SPI_DEVICE_NO_DUMMY, // SPI_DEVICE_HALFDUPLEX;
};
//Initialize the SPI bus
spi_bus_initialize(HSPI_HOST, &buscfg, 1);
@ -344,8 +327,8 @@ ili9341_init()
{
ili9341_cmd(ili_init_cmds[i].cmd);
ili9341_data(ili_init_cmds[i].data, ili_init_cmds[i].databytes & 0x7F);
if (ili_init_cmds[i].databytes & 0x80)
vTaskDelay(pdMS_TO_TICKS(10));
// if (ili_init_cmds[i].databytes & 0x80)
// vTaskDelay(pdMS_TO_TICKS(10));
}
}
@ -356,8 +339,8 @@ ili9341_deinit()
{
ili9341_cmd(ili_sleep_cmds[i].cmd);
ili9341_data(ili_sleep_cmds[i].data, ili_sleep_cmds[i].databytes & 0x7F);
if (ili_init_cmds[i].databytes & 0x80)
vTaskDelay(pdMS_TO_TICKS(10));
// if (ili_init_cmds[i].databytes & 0x80)
// vTaskDelay(pdMS_TO_TICKS(10));
}
}

View File

@ -374,22 +374,27 @@ int rg_gui_dialog(const char *header, const dialog_option_t *options_const, int
int last_key = -1;
// We create a copy of options because the callbacks might modify it (ie option->value)
dialog_option_t options[options_count + 1];
for (int i = 0; i <= options_count; i++)
{
options[i] = options_const[i];
char value_buffer[128] = {0xFF, 0};
if (options[i].value)
{
options[i].value = calloc(1, strlen(options[i].value) + 32);
strcpy(options[i].value, options_const[i].value);
}
options[i] = options_const[i];
if (options[i].update_cb)
{
options[i].value = value_buffer;
options[i].update_cb(&options[i], RG_DIALOG_INIT);
if (value_buffer[0] == 0xFF) // Not updated, reset ptr
options[i].value = options_const[i].value;
}
if (options[i].value)
{
char *new_value = malloc(strlen(options[i].value) + 16);
strcpy(new_value, options[i].value);
options[i].value = new_value;
}
}

View File

@ -16,6 +16,7 @@ typedef enum {
RG_DIALOG_IGNORE,
RG_DIALOG_SELECT,
RG_DIALOG_CANCEL,
RG_DIALOG_UPDATE,
} dialog_return_t;
typedef struct {

View File

@ -35,17 +35,23 @@
#define INPUT_TIMEOUT 5000000
#endif
typedef struct
{
uint32_t magicWord;
char output[2048];
size_t cursor;
} panic_console_t;
typedef struct
{
uint32_t magicWord;
char message[256];
char function[64];
char file[256];
char context[128];
} panic_trace_t;
// This is a direct pointer to rtc slow ram which isn't cleared on
// panic. We don't use this region so we can point anywhere in it.
static panic_trace_t *panicTrace = (void *)0x50001000;
// These will survive a software reset
static RTC_NOINIT_ATTR panic_trace_t panicTrace;
static RTC_NOINIT_ATTR panic_console_t panicConsole;
static rg_app_desc_t currentApp;
static runtime_stats_t statistics;
@ -59,6 +65,21 @@ static spi_lock_res_t spiMutexOwner;
#endif
void esp_panic_putchar_hook(char c)
{
if (panicConsole.magicWord != PANIC_TRACE_MAGIC)
{
panicConsole.magicWord = PANIC_TRACE_MAGIC;
panicConsole.cursor = 0;
}
if (panicConsole.cursor < sizeof(panicConsole.output) - 1)
{
panicConsole.output[panicConsole.cursor++] = c;
panicConsole.output[panicConsole.cursor] = 0;
}
}
static void system_monitor_task(void *arg)
{
runtime_counters_t current = {0};
@ -298,36 +319,61 @@ void rg_system_init(int appId, int sampleRate)
currentApp.refreshRate = 1;
currentApp.mainTaskHandle = xTaskGetCurrentTaskHandle();
// sdcard init must be before rg_display_init()
// and rg_settings_init() if JSON is used
// This must be before rg_display_init() and rg_settings_init()
bool sd_init = rg_sdcard_mount();
rg_system_gpio_init();
rg_settings_init();
rg_gui_init();
rg_input_init();
rg_audio_init(sampleRate);
rg_display_init();
rg_display_clear(0);
rg_gui_init();
rg_gui_draw_hourglass();
rg_audio_init(sampleRate);
rg_input_init();
rg_system_time_init();
if (esp_reset_reason() == ESP_RST_PANIC)
{
if (panicTrace->magicWord == PANIC_TRACE_MAGIC)
RG_LOGX(" *** PREVIOUS PANIC: %s *** \n", panicTrace->message);
else // Presumably abort()
strcpy(panicTrace->message, "Application crashed");
panicTrace->magicWord = 0;
rg_audio_deinit();
char message[400] = "Application crashed";
if (panicTrace.magicWord == PANIC_TRACE_MAGIC)
{
RG_LOGX(" *** PANIC TRACE: %s (%s) *** \n", panicTrace.message, panicTrace.context);
strcpy(message, panicTrace.message);
}
if (panicConsole.magicWord == PANIC_TRACE_MAGIC)
{
RG_LOGI("Panic log found, saving to sdcard...\n");
FILE *fp = fopen(RG_BASE_PATH "/crash.log", "w");
if (fp)
{
fprintf(fp, "Application: %s %s\n", app->project_name, app->version);
fprintf(fp, "Build date: %s %s\n", app->date, app->time);
if (panicTrace.magicWord == PANIC_TRACE_MAGIC)
{
fprintf(fp, "Message: %.256s\n", panicTrace.message);
fprintf(fp, "Context: %.256s\n", panicTrace.context);
}
fputs("\nConsole:\n", fp);
fputs(panicConsole.output, fp);
fputs("\n\nEnd of log\n", fp);
fclose(fp);
}
strcat(message, "\n Log saved to SD Card");
}
rg_display_clear(C_BLUE);
rg_gui_set_font_size(12);
rg_gui_alert("System Panic!", panicTrace->message);
rg_gui_alert("System Panic!", message);
rg_sdcard_unmount();
rg_audio_deinit();
rg_system_switch_app(RG_APP_LAUNCHER);
}
if (esp_reset_reason() != ESP_RST_SW)
else
{
rg_display_clear(0);
rg_gui_draw_hourglass();
panicConsole.magicWord = 0;
panicTrace.magicWord = 0;
}
if (!sd_init)
@ -360,7 +406,6 @@ void rg_system_init(int appId, int sampleRate)
xTaskCreate(&system_monitor_task, "sysmon", 2048, NULL, 7, NULL);
panicTrace->magicWord = 0;
initialized = true;
RG_LOGI("Retro-Go init done.\n");
@ -612,15 +657,14 @@ void rg_system_set_boot_app(const char *app)
RG_LOGI("Boot partition set to %d '%s'\n", partition->subtype, partition->label);
}
void rg_system_panic(const char *reason, const char *function, const char *file)
void rg_system_panic(const char *message, const char *context)
{
RG_LOGX("*** PANIC: %s\n *** FUNCTION: %s\n *** FILE: %s\n", reason, function, file);
strcpy(panicTrace.message, message ? message : "");
strcpy(panicTrace.context, context ? context : "");
panicTrace.magicWord = PANIC_TRACE_MAGIC;
strcpy(panicTrace->message, reason);
strcpy(panicTrace->file, file);
strcpy(panicTrace->function, function);
panicTrace->magicWord = PANIC_TRACE_MAGIC;
RG_LOGX("*** PANIC : %s\n", panicTrace.message);
RG_LOGX("*** CONTEXT: %s\n", panicTrace.context);
abort();
}

View File

@ -93,7 +93,7 @@ typedef struct
} runtime_stats_t;
void rg_system_init(int app_id, int sampleRate);
void rg_system_panic(const char *reason, const char *function, const char *file) __attribute__((noreturn));
void rg_system_panic(const char *reason, const char *context) __attribute__((noreturn));
void rg_system_halt() __attribute__((noreturn));
void rg_system_sleep() __attribute__((noreturn));
void rg_system_restart() __attribute__((noreturn));
@ -157,8 +157,8 @@ extern uint32_t crc32_le(uint32_t crc, const uint8_t * buf, uint32_t len);
#define RG_MAX(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b);_a > _b ? _a : _b; })
// This should really support printf format...
#define RG_PANIC(x) rg_system_panic(x, __FUNCTION__, __FILE__)
#define RG_ASSERT(cond, x) do { if (!(cond)) rg_system_panic(x, __FUNCTION__, __FILE__); } while(0);
#define RG_PANIC(x) rg_system_panic(x, __FUNCTION__)
#define RG_ASSERT(cond, x) while (!(cond)) { RG_PANIC(x); }
#define RG_LOGX(x, ...) printf(x, ## __VA_ARGS__)
#define RG_LOGE(x, ...) printf("!! %s: " x, __func__, ## __VA_ARGS__)

View File

@ -0,0 +1,18 @@
--- a/components/esp32/panic.c
+++ b/components/esp32/panic.c
@@ -65,10 +65,15 @@
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
//printf may be broken, so we fix our own printing fns...
+void __attribute__((weak)) esp_panic_putchar_hook(char c)
+{
+ // nothing
+}
static void panicPutChar(char c)
{
while (((READ_PERI_REG(UART_STATUS_REG(CONFIG_ESP_CONSOLE_UART_NUM)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ;
WRITE_PERI_REG(UART_FIFO_REG(CONFIG_ESP_CONSOLE_UART_NUM), c);
+ esp_panic_putchar_hook(c);
}
static void panicPutStr(const char *c)