Skip to content

Add supervisor.get_previous_traceback() function. #5037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/utils/pyexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
result->return_code = ret;
if (ret != 0) {
mp_obj_t return_value = (mp_obj_t)nlr.ret_val;
result->exception_type = mp_obj_get_type(return_value);
result->exception = return_value;
result->exception_line = -1;

if (mp_obj_is_exception_instance(return_value)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/pyexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ typedef enum {

typedef struct {
int return_code;
const mp_obj_type_t *exception_type;
mp_obj_t exception;
int exception_line;
} pyexec_result_t;

Expand Down
52 changes: 43 additions & 9 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "supervisor/shared/safe_mode.h"
#include "supervisor/shared/stack.h"
#include "supervisor/shared/status_leds.h"
#include "supervisor/shared/traceback.h"
#include "supervisor/shared/translate.h"
#include "supervisor/shared/workflow.h"
#include "supervisor/usb.h"
Expand Down Expand Up @@ -227,7 +228,41 @@ STATIC bool maybe_run_list(const char * const * filenames, pyexec_result_t* exec
return true;
}

STATIC void cleanup_after_vm(supervisor_allocation* heap) {
STATIC void count_strn(void *data, const char *str, size_t len) {
*(size_t*)data += len;
}

STATIC void cleanup_after_vm(supervisor_allocation* heap, mp_obj_t exception) {
// Get the traceback of any exception from this run off the heap.
// MP_OBJ_SENTINEL means "this run does not contribute to traceback storage, don't touch it"
// MP_OBJ_NULL (=0) means "this run completed successfully, clear any stored traceback"
if (exception != MP_OBJ_SENTINEL) {
free_memory(prev_traceback_allocation);
// ReloadException is exempt from traceback printing in pyexec_file(), so treat it as "no
// traceback" here too.
if (exception && exception != MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) {
size_t traceback_len = 0;
mp_print_t print_count = {&traceback_len, count_strn};
mp_obj_print_exception(&print_count, exception);
prev_traceback_allocation = allocate_memory(align32_size(traceback_len + 1), false, true);
// Empirically, this never fails in practice - even when the heap is totally filled up
// with single-block-sized objects referenced by a root pointer, exiting the VM frees
// up several hundred bytes, sufficient for the traceback (which tends to be shortened
// because there wasn't memory for the full one). There may be convoluted ways of
// making it fail, but at this point I believe they are not worth spending code on.
if (prev_traceback_allocation != NULL) {
vstr_t vstr;
vstr_init_fixed_buf(&vstr, traceback_len, (char*)prev_traceback_allocation->ptr);
mp_print_t print = {&vstr, (mp_print_strn_t)vstr_add_strn};
mp_obj_print_exception(&print, exception);
((char*)prev_traceback_allocation->ptr)[traceback_len] = '\0';
}
}
else {
prev_traceback_allocation = NULL;
}
}

// Reset port-independent devices, like CIRCUITPY_BLEIO_HCI.
reset_devices();
// Turn off the display and flush the filesystem before the heap disappears.
Expand Down Expand Up @@ -287,7 +322,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
pyexec_result_t result;

result.return_code = 0;
result.exception_type = NULL;
result.exception = MP_OBJ_NULL;
result.exception_line = 0;

bool skip_repl;
Expand Down Expand Up @@ -357,7 +392,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
}

// Finished executing python code. Cleanup includes a board reset.
cleanup_after_vm(heap);
cleanup_after_vm(heap, result.exception);

// If a new next code file was set, that is a reason to keep it (obviously). Stuff this into
// the options because it can be treated like any other reason-for-stickiness bit. The
Expand Down Expand Up @@ -674,8 +709,9 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
usb_set_defaults();
#endif

pyexec_result_t result = {0, MP_OBJ_NULL, 0};
if (ok_to_run) {
bool found_boot = maybe_run_list(boot_py_filenames, NULL);
bool found_boot = maybe_run_list(boot_py_filenames, &result);
(void) found_boot;

#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
Expand All @@ -687,21 +723,18 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
#endif
}


#if CIRCUITPY_USB

// Some data needs to be carried over from the USB settings in boot.py
// to the next VM, while the heap is still available.
// Its size can vary, so save it temporarily on the stack,
// and then when the heap goes away, copy it in into a
// storage_allocation.

size_t size = usb_boot_py_data_size();
uint8_t usb_boot_py_data[size];
usb_get_boot_py_data(usb_boot_py_data, size);
#endif

cleanup_after_vm(heap);
cleanup_after_vm(heap, result.exception);

#if CIRCUITPY_USB
// Now give back the data we saved from the heap going away.
Expand Down Expand Up @@ -736,12 +769,13 @@ STATIC int run_repl(void) {
} else {
exit_code = pyexec_friendly_repl();
}
cleanup_after_vm(heap);
cleanup_after_vm(heap, MP_OBJ_SENTINEL);
#if CIRCUITPY_STATUS_LED
status_led_init();
new_status_color(BLACK);
status_led_deinit();
#endif

autoreload_resume();
return exit_code;
}
Expand Down
6 changes: 3 additions & 3 deletions ports/esp32s2/common-hal/busio/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
typedef struct {
mp_obj_base_t base;

const mcu_pin_obj_t* MOSI;
const mcu_pin_obj_t* MISO;
const mcu_pin_obj_t* clock;
const mcu_pin_obj_t *MOSI;
const mcu_pin_obj_t *MISO;
const mcu_pin_obj_t *clock;

spi_host_device_t host_id;

Expand Down
30 changes: 29 additions & 1 deletion shared-bindings/supervisor/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "supervisor/shared/autoreload.h"
#include "supervisor/shared/status_leds.h"
#include "supervisor/shared/stack.h"
#include "supervisor/shared/traceback.h"
#include "supervisor/shared/translate.h"
#include "supervisor/shared/workflow.h"

Expand Down Expand Up @@ -255,7 +256,33 @@ STATIC mp_obj_t supervisor_ticks_ms(void) {
}
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_ticks_ms_obj, supervisor_ticks_ms);


//| def get_previous_traceback() -> Optional[str]:
//| """If the last vm run ended with an exception (including the KeyboardInterrupt caused by
//| CTRL-C), returns the traceback as a string.
//| Otherwise, returns ``None``.
//|
//| An exception traceback is only preserved over a soft reload, a hard reset clears it.
//|
//| Only code (main or boot) runs are considered, not REPL runs."""
//| ...
//|
STATIC mp_obj_t supervisor_get_previous_traceback(void) {
// TODO is this a safe and proper way of making a heap-allocated string object that points at long-lived (and likely long and unique) data outside the heap?
if (prev_traceback_allocation) {
size_t len = strlen((const char *)prev_traceback_allocation->ptr);
if (len > 0) {
mp_obj_str_t *o = m_new_obj(mp_obj_str_t);
o->base.type = &mp_type_str;
o->len = len;
// TODO is it a good assumption that callers probably aren't going to compare this string, so skip computing the hash?
o->hash = 0;
o->data = (const byte *)prev_traceback_allocation->ptr;
return MP_OBJ_FROM_PTR(o);
}
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_get_previous_traceback_obj, supervisor_get_previous_traceback);

STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) },
Expand All @@ -268,6 +295,7 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_set_next_stack_limit), MP_ROM_PTR(&supervisor_set_next_stack_limit_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) },
{ MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&supervisor_ticks_ms_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) },
};

STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table);
Expand Down
2 changes: 2 additions & 0 deletions supervisor/shared/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ enum {
2
// next_code_allocation
+ 1
// prev_traceback_allocation
+ 1

#if INTERNAL_FLASH_FILESYSTEM == 0
+ 1
Expand Down
29 changes: 29 additions & 0 deletions supervisor/shared/traceback.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Christian Walther
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "traceback.h"

supervisor_allocation *prev_traceback_allocation;
34 changes: 34 additions & 0 deletions supervisor/shared/traceback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Christian Walther
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
#define MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H

#include "supervisor/memory.h"

extern supervisor_allocation *prev_traceback_allocation;

#endif // MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
1 change: 1 addition & 0 deletions supervisor/supervisor.mk
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ SRC_SUPERVISOR = \
supervisor/shared/stack.c \
supervisor/shared/status_leds.c \
supervisor/shared/tick.c \
supervisor/shared/traceback.c \
supervisor/shared/translate.c

NO_USB ?= $(wildcard supervisor/usb.c)
Expand Down