Skip to content

Commit beda1f7

Browse files
authored
Merge pull request #5037 from hierophect/getprevtraceback
Add supervisor.get_previous_traceback() function.
2 parents 779b5c1 + ace04ef commit beda1f7

File tree

9 files changed

+143
-15
lines changed

9 files changed

+143
-15
lines changed

lib/utils/pyexec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
167167
result->return_code = ret;
168168
if (ret != 0) {
169169
mp_obj_t return_value = (mp_obj_t)nlr.ret_val;
170-
result->exception_type = mp_obj_get_type(return_value);
170+
result->exception = return_value;
171171
result->exception_line = -1;
172172

173173
if (mp_obj_is_exception_instance(return_value)) {

lib/utils/pyexec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ typedef enum {
3535

3636
typedef struct {
3737
int return_code;
38-
const mp_obj_type_t *exception_type;
38+
mp_obj_t exception;
3939
int exception_line;
4040
} pyexec_result_t;
4141

main.c

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
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
}

ports/esp32s2/common-hal/busio/SPI.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333
typedef struct {
3434
mp_obj_base_t base;
3535

36-
const mcu_pin_obj_t* MOSI;
37-
const mcu_pin_obj_t* MISO;
38-
const mcu_pin_obj_t* clock;
36+
const mcu_pin_obj_t *MOSI;
37+
const mcu_pin_obj_t *MISO;
38+
const mcu_pin_obj_t *clock;
3939

4040
spi_host_device_t host_id;
4141

shared-bindings/supervisor/__init__.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "supervisor/shared/autoreload.h"
3535
#include "supervisor/shared/status_leds.h"
3636
#include "supervisor/shared/stack.h"
37+
#include "supervisor/shared/traceback.h"
3738
#include "supervisor/shared/translate.h"
3839
#include "supervisor/shared/workflow.h"
3940

@@ -255,7 +256,33 @@ STATIC mp_obj_t supervisor_ticks_ms(void) {
255256
}
256257
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_ticks_ms_obj, supervisor_ticks_ms);
257258

258-
259+
//| def get_previous_traceback() -> Optional[str]:
260+
//| """If the last vm run ended with an exception (including the KeyboardInterrupt caused by
261+
//| CTRL-C), returns the traceback as a string.
262+
//| Otherwise, returns ``None``.
263+
//|
264+
//| An exception traceback is only preserved over a soft reload, a hard reset clears it.
265+
//|
266+
//| Only code (main or boot) runs are considered, not REPL runs."""
267+
//| ...
268+
//|
269+
STATIC mp_obj_t supervisor_get_previous_traceback(void) {
270+
// 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?
271+
if (prev_traceback_allocation) {
272+
size_t len = strlen((const char *)prev_traceback_allocation->ptr);
273+
if (len > 0) {
274+
mp_obj_str_t *o = m_new_obj(mp_obj_str_t);
275+
o->base.type = &mp_type_str;
276+
o->len = len;
277+
// TODO is it a good assumption that callers probably aren't going to compare this string, so skip computing the hash?
278+
o->hash = 0;
279+
o->data = (const byte *)prev_traceback_allocation->ptr;
280+
return MP_OBJ_FROM_PTR(o);
281+
}
282+
}
283+
return mp_const_none;
284+
}
285+
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_get_previous_traceback_obj, supervisor_get_previous_traceback);
259286

260287
STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
261288
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) },
@@ -268,6 +295,7 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
268295
{ MP_ROM_QSTR(MP_QSTR_set_next_stack_limit), MP_ROM_PTR(&supervisor_set_next_stack_limit_obj) },
269296
{ MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) },
270297
{ MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&supervisor_ticks_ms_obj) },
298+
{ MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) },
271299
};
272300

273301
STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table);

supervisor/shared/memory.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ enum {
3838
2
3939
// next_code_allocation
4040
+ 1
41+
// prev_traceback_allocation
42+
+ 1
4143

4244
#if INTERNAL_FLASH_FILESYSTEM == 0
4345
+ 1

supervisor/shared/traceback.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Christian Walther
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "traceback.h"
28+
29+
supervisor_allocation *prev_traceback_allocation;

supervisor/shared/traceback.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Christian Walther
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#ifndef MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
28+
#define MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
29+
30+
#include "supervisor/memory.h"
31+
32+
extern supervisor_allocation *prev_traceback_allocation;
33+
34+
#endif // MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H

supervisor/supervisor.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ SRC_SUPERVISOR = \
1414
supervisor/shared/stack.c \
1515
supervisor/shared/status_leds.c \
1616
supervisor/shared/tick.c \
17+
supervisor/shared/traceback.c \
1718
supervisor/shared/translate.c
1819

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

0 commit comments

Comments
 (0)