5656#include "supervisor/shared/safe_mode.h"
5757#include "supervisor/shared/stack.h"
5858#include "supervisor/shared/status_leds.h"
59+ #include "supervisor/shared/traceback.h"
5960#include "supervisor/shared/translate.h"
6061#include "supervisor/shared/workflow.h"
6162#include "supervisor/usb.h"
@@ -227,7 +228,41 @@ STATIC bool maybe_run_list(const char * const * filenames, pyexec_result_t* exec
227228 return true;
228229}
229230
230- STATIC void cleanup_after_vm (supervisor_allocation * heap ) {
231+ STATIC void count_strn (void * data , const char * str , size_t len ) {
232+ * (size_t * )data += len ;
233+ }
234+
235+ STATIC void cleanup_after_vm (supervisor_allocation * heap , mp_obj_t exception ) {
236+ // Get the traceback of any exception from this run off the heap.
237+ // MP_OBJ_SENTINEL means "this run does not contribute to traceback storage, don't touch it"
238+ // MP_OBJ_NULL (=0) means "this run completed successfully, clear any stored traceback"
239+ if (exception != MP_OBJ_SENTINEL ) {
240+ free_memory (prev_traceback_allocation );
241+ // ReloadException is exempt from traceback printing in pyexec_file(), so treat it as "no
242+ // traceback" here too.
243+ if (exception && exception != MP_OBJ_FROM_PTR (& MP_STATE_VM (mp_reload_exception ))) {
244+ size_t traceback_len = 0 ;
245+ mp_print_t print_count = {& traceback_len , count_strn };
246+ mp_obj_print_exception (& print_count , exception );
247+ prev_traceback_allocation = allocate_memory (align32_size (traceback_len + 1 ), false, true);
248+ // Empirically, this never fails in practice - even when the heap is totally filled up
249+ // with single-block-sized objects referenced by a root pointer, exiting the VM frees
250+ // up several hundred bytes, sufficient for the traceback (which tends to be shortened
251+ // because there wasn't memory for the full one). There may be convoluted ways of
252+ // making it fail, but at this point I believe they are not worth spending code on.
253+ if (prev_traceback_allocation != NULL ) {
254+ vstr_t vstr ;
255+ vstr_init_fixed_buf (& vstr , traceback_len , (char * )prev_traceback_allocation -> ptr );
256+ mp_print_t print = {& vstr , (mp_print_strn_t )vstr_add_strn };
257+ mp_obj_print_exception (& print , exception );
258+ ((char * )prev_traceback_allocation -> ptr )[traceback_len ] = '\0' ;
259+ }
260+ }
261+ else {
262+ prev_traceback_allocation = NULL ;
263+ }
264+ }
265+
231266 // Reset port-independent devices, like CIRCUITPY_BLEIO_HCI.
232267 reset_devices ();
233268 // Turn off the display and flush the filesystem before the heap disappears.
@@ -287,7 +322,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
287322 pyexec_result_t result ;
288323
289324 result .return_code = 0 ;
290- result .exception_type = NULL ;
325+ result .exception = MP_OBJ_NULL ;
291326 result .exception_line = 0 ;
292327
293328 bool skip_repl ;
@@ -357,7 +392,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
357392 }
358393
359394 // Finished executing python code. Cleanup includes a board reset.
360- cleanup_after_vm (heap );
395+ cleanup_after_vm (heap , result . exception );
361396
362397 // If a new next code file was set, that is a reason to keep it (obviously). Stuff this into
363398 // the options because it can be treated like any other reason-for-stickiness bit. The
@@ -674,8 +709,9 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
674709 usb_set_defaults ();
675710#endif
676711
712+ pyexec_result_t result = {0 , MP_OBJ_NULL , 0 };
677713 if (ok_to_run ) {
678- bool found_boot = maybe_run_list (boot_py_filenames , NULL );
714+ bool found_boot = maybe_run_list (boot_py_filenames , & result );
679715 (void ) found_boot ;
680716
681717 #ifdef CIRCUITPY_BOOT_OUTPUT_FILE
@@ -687,21 +723,18 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
687723 #endif
688724 }
689725
690-
691726#if CIRCUITPY_USB
692-
693727 // Some data needs to be carried over from the USB settings in boot.py
694728 // to the next VM, while the heap is still available.
695729 // Its size can vary, so save it temporarily on the stack,
696730 // and then when the heap goes away, copy it in into a
697731 // storage_allocation.
698-
699732 size_t size = usb_boot_py_data_size ();
700733 uint8_t usb_boot_py_data [size ];
701734 usb_get_boot_py_data (usb_boot_py_data , size );
702735#endif
703736
704- cleanup_after_vm (heap );
737+ cleanup_after_vm (heap , result . exception );
705738
706739#if CIRCUITPY_USB
707740 // Now give back the data we saved from the heap going away.
@@ -736,12 +769,13 @@ STATIC int run_repl(void) {
736769 } else {
737770 exit_code = pyexec_friendly_repl ();
738771 }
739- cleanup_after_vm (heap );
772+ cleanup_after_vm (heap , MP_OBJ_SENTINEL );
740773 #if CIRCUITPY_STATUS_LED
741774 status_led_init ();
742775 new_status_color (BLACK );
743776 status_led_deinit ();
744777 #endif
778+
745779 autoreload_resume ();
746780 return exit_code ;
747781}
0 commit comments