From f371c0a60906026a032be6466448c00fc31b85d3 Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Wed, 28 Jul 2021 00:00:01 +0530 Subject: [PATCH 1/4] add traceback module --- locale/circuitpython.pot | 40 ++-- .../mpconfigboard.mk | 1 + py/circuitpy_defns.mk | 4 + py/circuitpy_mpconfig.h | 8 + py/circuitpy_mpconfig.mk | 3 + py/modsys.c | 19 -- shared-bindings/traceback/__init__.c | 171 ++++++++++++++++++ shared-module/traceback/__init__.c | 1 + 8 files changed, 214 insertions(+), 33 deletions(-) create mode 100644 shared-bindings/traceback/__init__.c create mode 100644 shared-module/traceback/__init__.c diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 9afa77328d..0ab2dede22 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -35,11 +35,11 @@ msgid "" "https://github.com/adafruit/circuitpython/issues\n" msgstr "" -#: py/obj.c +#: py/obj.c shared-bindings/traceback/__init__.c msgid " File \"%q\"" msgstr "" -#: py/obj.c +#: py/obj.c shared-bindings/traceback/__init__.c msgid " File \"%q\", line %d" msgstr "" @@ -322,7 +322,7 @@ msgstr "" msgid "*x must be assignment target" msgstr "" -#: py/obj.c +#: py/obj.c shared-bindings/traceback/__init__.c msgid ", in %q\n" msgstr "" @@ -1185,11 +1185,6 @@ msgstr "" msgid "Input/output error" msgstr "" -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#, c-format -msgid "Missing jmp_pin. Instruction %d jumps on pin" -msgstr "" - #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #, c-format msgid "Instruction %d shifts in more bits than pin count" @@ -1506,6 +1501,11 @@ msgstr "" msgid "Missing first_set_pin. Instruction %d sets pin(s)" msgstr "" +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#, c-format +msgid "Missing jmp_pin. Instruction %d jumps on pin" +msgstr "" + #: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -2227,7 +2227,7 @@ msgstr "" msgid "Touch alarms not available" msgstr "" -#: py/obj.c +#: py/obj.c shared-bindings/traceback/__init__.c msgid "Traceback (most recent call last):\n" msgstr "" @@ -2525,7 +2525,7 @@ msgid "argument name reused" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/gamepad/GamePad.c +#: shared-bindings/digitalio/DigitalInOut.c msgid "argument num/types mismatch" msgstr "" @@ -3089,6 +3089,10 @@ msgstr "" msgid "file must be a file opened in byte mode" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "file write is not available" +msgstr "" + #: shared-bindings/storage/__init__.c msgid "filesystem must provide mount method" msgstr "" @@ -3376,6 +3380,10 @@ msgstr "" msgid "invalid element_size %d, must be, 1, 2, or 4" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "invalid exception" +msgstr "" + #: extmod/modframebuf.c msgid "invalid format" msgstr "" @@ -3457,6 +3465,10 @@ msgstr "" msgid "lhs and rhs should be compatible" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "limit should be an int" +msgstr "" + #: py/emitnative.c msgid "local '%q' has type '%q' but source is '%q'" msgstr "" @@ -3609,10 +3621,6 @@ msgstr "" msgid "no active exception to reraise" msgstr "" -#: shared-bindings/socket/__init__.c shared-module/network/__init__.c -msgid "no available NIC" -msgstr "" - #: py/compile.c msgid "no binding for nonlocal found" msgstr "" @@ -4085,6 +4093,10 @@ msgstr "" msgid "source palette too large" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "stack is not ok" +msgstr "" + #: py/objstr.c msgid "start/end indices" msgstr "" diff --git a/ports/atmel-samd/boards/adafruit_proxlight_trinkey_m0/mpconfigboard.mk b/ports/atmel-samd/boards/adafruit_proxlight_trinkey_m0/mpconfigboard.mk index a617947f9c..cf9afb3d21 100644 --- a/ports/atmel-samd/boards/adafruit_proxlight_trinkey_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/adafruit_proxlight_trinkey_m0/mpconfigboard.mk @@ -19,6 +19,7 @@ CIRCUITPY_PWMIO = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 CIRCUITPY_USB_MIDI = 0 +CIRCUITPY_TRACEBACK = 0 CIRCUITPY_PIXELBUF = 1 CIRCUITPY_BUSDEVICE = 1 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 5bf46039a7..2acd0cb8db 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -293,6 +293,9 @@ endif ifeq ($(CIRCUITPY_TOUCHIO),1) SRC_PATTERNS += touchio/% endif +ifeq ($(CIRCUITPY_TRACEBACK),1) +SRC_PATTERNS += traceback/% +endif ifeq ($(CIRCUITPY_UHEAP),1) SRC_PATTERNS += uheap/% endif @@ -544,6 +547,7 @@ SRC_SHARED_MODULE_ALL = \ terminalio/Terminal.c \ terminalio/__init__.c \ time/__init__.c \ + traceback/__init__.c \ uheap/__init__.c \ ustack/__init__.c \ vectorio/Circle.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 8e31f0411b..ea57f17218 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -763,6 +763,13 @@ extern const struct _mp_obj_module_t touchio_module; #define TOUCHIO_MODULE #endif +#if CIRCUITPY_TRACEBACK +extern const struct _mp_obj_module_t traceback_module; +#define TRACEBACK_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_traceback), (mp_obj_t)&traceback_module }, +#else +#define TRACEBACK_MODULE +#endif + #if CIRCUITPY_UHEAP extern const struct _mp_obj_module_t uheap_module; #define UHEAP_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module }, @@ -917,6 +924,7 @@ extern const struct _mp_obj_module_t msgpack_module; SUPERVISOR_MODULE \ SYNTHIO_MODULE \ TOUCHIO_MODULE \ + TRACEBACK_MODULE \ UHEAP_MODULE \ USB_CDC_MODULE \ USB_HID_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 3d72be7491..20056d7377 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -324,6 +324,9 @@ CFLAGS += -DCIRCUITPY_TOUCHIO_USE_NATIVE=$(CIRCUITPY_TOUCHIO_USE_NATIVE) CIRCUITPY_TOUCHIO ?= 1 CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO) +CIRCUITPY_TRACEBACK ?= 1 +CFLAGS += -DCIRCUITPY_TRACEBACK=$(CIRCUITPY_TRACEBACK) + # For debugging. CIRCUITPY_UHEAP ?= 0 CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP) diff --git a/py/modsys.c b/py/modsys.c index cbd1712920..484e29f411 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -120,25 +120,6 @@ STATIC mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit); -STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) { - #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES - void *stream_obj = &mp_sys_stdout_obj; - if (n_args > 1) { - mp_get_stream_raise(args[1], MP_STREAM_OP_WRITE); - stream_obj = MP_OBJ_TO_PTR(args[1]); - } - - mp_print_t print = {stream_obj, mp_stream_write_adaptor}; - mp_obj_print_exception(&print, args[0]); - #else - (void)n_args; - mp_obj_print_exception(&mp_plat_print, args[0]); - #endif - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_print_exception_obj, 1, 2, mp_sys_print_exception); - #if MICROPY_PY_SYS_EXC_INFO STATIC mp_obj_t mp_sys_exc_info(void) { mp_obj_t cur_exc = MP_OBJ_FROM_PTR(MP_STATE_VM(cur_exception)); diff --git a/shared-bindings/traceback/__init__.c b/shared-bindings/traceback/__init__.c new file mode 100644 index 0000000000..3d3914bfd1 --- /dev/null +++ b/shared-bindings/traceback/__init__.c @@ -0,0 +1,171 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/stream.h" +#include "py/runtime.h" + +#include "supervisor/shared/stack.h" + +//| """Traceback Module +//| +//| This module provides a standard interface to print stack traces of programs. +//| This is useful when you want to print stack traces under program control. +//| +//| """ +//| ... +//| + +//| def print_exception(etype: Type[BaseException], value: BaseException, tb: TracebackType, +//| limit: Optional[int] = None, file: Optional[io.FileIO] = None, chain: Optional[bool] = True) -> None: +//| +//| """Prints exception information and stack trace entries. +//| +//| .. note: Setting `chain` will have no effect as chained exceptions are not yet implemented. +//| +//| :param Type[BaseException] etype: This is ignored and inferred from the type of ``value``. +//| :param BaseException value: The exception. Must be an instance of `BaseException`. +//| :param TracebackType tb: The traceback object. If `None`, the traceback will not be printed. +//| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive. +//| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. +//| :param io.FileIO file: If file is omitted or `None`, the output goes to `sys.stderr`; otherwise it should be an open +//| file or file-like object to receive the output. +//| :param bool chain: If `True` then chained exceptions will be printed. +//| +//| """ +//| ... +//| +STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_etype, ARG_value, ARG_tb, ARG_limit, ARG_file, ARG_chain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_etype, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_value, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_tb, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_limit, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_file, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_chain, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t exc = args[ARG_value].u_obj; + if (!mp_obj_is_exception_instance(exc)) { + mp_raise_TypeError(translate("invalid exception")); + } + + mp_print_t print = mp_plat_print; + if (args[ARG_file].u_obj != mp_const_none) { + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_get_stream_raise(args[ARG_file].u_obj, MP_STREAM_OP_WRITE); + print.data = MP_OBJ_TO_PTR(args[ARG_file].u_obj); + print.print_strn = mp_stream_write_adaptor; + #else + mp_raise_NotImplementedError(translate("file write is not available")); + #endif + } + + mp_int_t limit = 0; + bool print_tb = true; + if (args[ARG_limit].u_obj != mp_const_none) { + if (!mp_obj_get_int_maybe(args[ARG_limit].u_obj, &limit)) { + mp_raise_TypeError(translate("limit should be an int")); + } + print_tb = !(limit == 0); + } + + if (args[ARG_tb].u_obj != mp_const_none && print_tb) { + if (!stack_ok()) { + mp_raise_RuntimeError(translate("stack is not ok")); + } + size_t n, *values; + mp_obj_exception_get_traceback(exc, &n, &values); + if (n > 0) { + assert(n % 3 == 0); + // Decompress the format strings + const compressed_string_t *traceback = MP_ERROR_TEXT("Traceback (most recent call last):\n"); + char decompressed[decompress_length(traceback)]; + decompress(traceback, decompressed); + #if MICROPY_ENABLE_SOURCE_LINE + const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\", line %d"); + #else + const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\""); + #endif + char decompressed_frame[decompress_length(frame)]; + decompress(frame, decompressed_frame); + const compressed_string_t *block_fmt = MP_ERROR_TEXT(", in %q\n"); + char decompressed_block[decompress_length(block_fmt)]; + decompress(block_fmt, decompressed_block); + + // Set traceback formatting + // Default: Print full traceback + int i = n - 3, j; + if (limit > 0) { + // Print upto limit traceback + // from caller's frame + limit = n - (limit * 3); + } else if (limit < 0) { + // Print upto limit traceback + // from last + i = 0, limit = 3 + (limit * 3); + } + + // Print the traceback + mp_print_str(&print, decompressed); + for (; i >= limit; i -= 3) { + j = (i < 0) ? -i : i; + #if MICROPY_ENABLE_SOURCE_LINE + mp_printf(&print, decompressed_frame, values[j], (int)values[j + 1]); + #else + mp_printf(&print, decompressed_frame, values[j]); + #endif + // The block name can be NULL if it's unknown + qstr block = values[j + 2]; + if (block == MP_QSTRnull) { + mp_print_str(&print, "\n"); + } else { + mp_printf(&print, decompressed_block, block); + } + } + } + } + mp_obj_print_helper(&print, exc, PRINT_EXC); + mp_print_str(&print, "\n"); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_print_exception_obj, 3, traceback_print_exception); + +STATIC const mp_rom_map_elem_t traceback_module_globals_table[] = { + // module name + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_traceback) }, + // module functions + { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&traceback_print_exception_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(traceback_module_globals, traceback_module_globals_table); + +const mp_obj_module_t traceback_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&traceback_module_globals, +}; diff --git a/shared-module/traceback/__init__.c b/shared-module/traceback/__init__.c new file mode 100644 index 0000000000..4bd8276f34 --- /dev/null +++ b/shared-module/traceback/__init__.c @@ -0,0 +1 @@ +// empty file From 193a8d2e67a93b5ce62523d021713fe3a8c08988 Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Mon, 2 Aug 2021 12:00:48 +0530 Subject: [PATCH 2/4] add traceback object --- lib/utils/pyexec.c | 2 +- py/mpstate.h | 5 +++ py/obj.h | 2 + py/objexcept.c | 69 +++++++++++++++++------------ py/objexcept.h | 5 +-- py/objgenerator.c | 2 +- py/objtraceback.c | 42 ++++++++++++++++++ py/objtraceback.h | 39 ++++++++++++++++ py/py.cmake | 1 + py/py.mk | 1 + py/runtime.c | 9 ++-- py/scheduler.c | 2 +- shared-bindings/watchdog/__init__.c | 4 +- 13 files changed, 139 insertions(+), 44 deletions(-) create mode 100644 py/objtraceback.c create mode 100644 py/objtraceback.h diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 2651189915..b3c1ac34bb 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -242,7 +242,7 @@ STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) { mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host if (c == CHAR_CTRL_C) { #if MICROPY_KBD_EXCEPTION - MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + MP_STATE_VM(mp_kbd_exception).traceback->data = NULL; nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); #else mp_raise_type(&mp_type_KeyboardInterrupt); diff --git a/py/mpstate.h b/py/mpstate.h index 423463109d..9f7d72a686 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -120,6 +120,9 @@ typedef struct _mp_state_vm_t { qstr_pool_t *last_pool; + // non-heap memory for creating a traceback if we can't allocate RAM + mp_obj_traceback_t mp_emergency_traceback_obj; + // non-heap memory for creating an exception if we can't allocate RAM mp_obj_exception_t mp_emergency_exception_obj; @@ -137,6 +140,8 @@ typedef struct _mp_state_vm_t { #if MICROPY_KBD_EXCEPTION // exception object of type KeyboardInterrupt mp_obj_exception_t mp_kbd_exception; + // traceback object to store traceback + mp_obj_traceback_t mp_kbd_traceback; #endif // exception object of type ReloadException diff --git a/py/obj.h b/py/obj.h index b259128a2a..b91932b11e 100644 --- a/py/obj.h +++ b/py/obj.h @@ -692,6 +692,7 @@ extern const mp_obj_type_t mp_type_bytearray; extern const mp_obj_type_t mp_type_memoryview; extern const mp_obj_type_t mp_type_float; extern const mp_obj_type_t mp_type_complex; +extern const mp_obj_type_t mp_type_traceback; extern const mp_obj_type_t mp_type_tuple; extern const mp_obj_type_t mp_type_list; extern const mp_obj_type_t mp_type_map; // map (the python builtin, not the dict implementation detail) @@ -791,6 +792,7 @@ extern const struct _mp_obj_bool_t mp_const_true_obj; extern const struct _mp_obj_str_t mp_const_empty_bytes_obj; extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj; extern const struct _mp_obj_dict_t mp_const_empty_dict_obj; +extern const struct _mp_obj_traceback_t mp_const_empty_traceback_obj; extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj; extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj; extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj; diff --git a/py/objexcept.c b/py/objexcept.c index d5a858a442..6488413115 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -156,7 +156,15 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, con // Populate the exception object o_exc->base.type = type; - o_exc->traceback_data = NULL; + + // Try to allocate memory for the traceback, with fallback to emergency traceback object + o_exc->traceback = m_new_obj_maybe(mp_obj_traceback_t); + if (o_exc->traceback == NULL) { + o_exc->traceback = &MP_STATE_VM(mp_emergency_traceback_obj); + } + + // Populate the traceback object + *o_exc->traceback = mp_const_empty_traceback_obj; mp_obj_tuple_t *o_tuple; if (n_args == 0) { @@ -208,22 +216,25 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] != MP_OBJ_NULL) { // store/delete attribute - if (attr == MP_QSTR___traceback__ && dest[1] == mp_const_none) { - // We allow 'exc.__traceback__ = None' assignment as low-level - // optimization of pre-allocating exception instance and raising - // it repeatedly - this avoids memory allocation during raise. - // However, uPy will keep adding traceback entries to such - // exception instance, so before throwing it, traceback should - // be cleared like above. - self->traceback_len = 0; + if (attr == MP_QSTR___traceback__) { + if (dest[1] == mp_const_none) { + self->traceback->data = NULL; + } else { + if (!mp_obj_is_type(dest[1], &mp_type_traceback)) { + mp_raise_TypeError(MP_ERROR_TEXT("invalid traceback")); + } + self->traceback = MP_OBJ_TO_PTR(dest[1]); + } dest[0] = MP_OBJ_NULL; // indicate success } return; } if (attr == MP_QSTR_args) { dest[0] = MP_OBJ_FROM_PTR(self->args); - } else if (self->base.type == &mp_type_StopIteration && attr == MP_QSTR_value) { + } else if (attr == MP_QSTR_value && self->base.type == &mp_type_StopIteration) { dest[0] = mp_obj_exception_get_value(self_in); + } else if (attr == MP_QSTR___traceback__) { + dest[0] = (self->traceback->data) ? MP_OBJ_FROM_PTR(self->traceback) : mp_const_none; #if MICROPY_CPYTHON_COMPAT } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(self->base.type), MP_OBJ_FROM_PTR(&mp_type_OSError))) { if (attr == MP_QSTR_errno) { @@ -552,7 +563,7 @@ void mp_obj_exception_clear_traceback(mp_obj_t self_in) { GET_NATIVE_EXCEPTION(self, self_in); // just set the traceback to the null object // we don't want to call any memory management functions here - self->traceback_data = NULL; + self->traceback->data = NULL; } void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block) { @@ -561,16 +572,16 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs // append this traceback info to traceback data // if memory allocation fails (eg because gc is locked), just return - if (self->traceback_data == NULL) { - self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN); - if (self->traceback_data == NULL) { + if (self->traceback->data == NULL) { + self->traceback->data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN); + if (self->traceback->data == NULL) { #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) { // There is room in the emergency buffer for traceback data size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TRACEBACK_OFFSET); - self->traceback_data = tb; - self->traceback_alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t); + self->traceback->data = tb; + self->traceback->alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t); } else { // Can't allocate and no room in emergency buffer return; @@ -581,28 +592,28 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs #endif } else { // Allocated the traceback data on the heap - self->traceback_alloc = TRACEBACK_ENTRY_LEN; + self->traceback->alloc = TRACEBACK_ENTRY_LEN; } - self->traceback_len = 0; - } else if (self->traceback_len + TRACEBACK_ENTRY_LEN > self->traceback_alloc) { + self->traceback->len = 0; + } else if (self->traceback->len + TRACEBACK_ENTRY_LEN > self->traceback->alloc) { #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF - if (self->traceback_data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) { + if (self->traceback->data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) { // Can't resize the emergency buffer return; } #endif // be conservative with growing traceback data - size_t *tb_data = m_renew_maybe(size_t, self->traceback_data, self->traceback_alloc, - self->traceback_alloc + TRACEBACK_ENTRY_LEN, true); + size_t *tb_data = m_renew_maybe(size_t, self->traceback->data, self->traceback->alloc, + self->traceback->alloc + TRACEBACK_ENTRY_LEN, true); if (tb_data == NULL) { return; } - self->traceback_data = tb_data; - self->traceback_alloc += TRACEBACK_ENTRY_LEN; + self->traceback->data = tb_data; + self->traceback->alloc += TRACEBACK_ENTRY_LEN; } - size_t *tb_data = &self->traceback_data[self->traceback_len]; - self->traceback_len += TRACEBACK_ENTRY_LEN; + size_t *tb_data = &self->traceback->data[self->traceback->len]; + self->traceback->len += TRACEBACK_ENTRY_LEN; tb_data[0] = file; tb_data[1] = line; tb_data[2] = block; @@ -611,12 +622,12 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values) { GET_NATIVE_EXCEPTION(self, self_in); - if (self->traceback_data == NULL) { + if (self->traceback->data == NULL) { *n = 0; *values = NULL; } else { - *n = self->traceback_len; - *values = self->traceback_data; + *n = self->traceback->len; + *values = self->traceback->data; } } diff --git a/py/objexcept.h b/py/objexcept.h index d7b39add87..261885e54d 100644 --- a/py/objexcept.h +++ b/py/objexcept.h @@ -28,13 +28,12 @@ #include "py/obj.h" #include "py/objtuple.h" +#include "py/objtraceback.h" typedef struct _mp_obj_exception_t { mp_obj_base_t base; - size_t traceback_alloc : (8 * sizeof(size_t) / 2); - size_t traceback_len : (8 * sizeof(size_t) / 2); - size_t *traceback_data; mp_obj_tuple_t *args; + mp_obj_traceback_t *traceback; } mp_obj_exception_t; void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); diff --git a/py/objgenerator.c b/py/objgenerator.c index 2139e4478a..7bafd9ce98 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -38,7 +38,7 @@ #include "supervisor/shared/translate.h" // Instance of GeneratorExit exception - needed by generator.close() -const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t *)&mp_const_empty_tuple_obj}; +const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, (mp_obj_tuple_t *)&mp_const_empty_tuple_obj, (mp_obj_traceback_t *)&mp_const_empty_traceback_obj}; /******************************************************************************/ /* generator wrapper */ diff --git a/py/objtraceback.c b/py/objtraceback.c new file mode 100644 index 0000000000..f1f74142d6 --- /dev/null +++ b/py/objtraceback.c @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/runtime.h" +#include "py/objtraceback.h" + +const mp_obj_traceback_t mp_const_empty_traceback_obj = {{&mp_type_traceback}, 0, 0, NULL}; + +STATIC void mp_obj_traceback_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_traceback_t *o = MP_OBJ_TO_PTR(o_in); + mp_printf(print, "", o); +} + +const mp_obj_type_t mp_type_traceback = { + { &mp_type_type }, + .name = MP_QSTR_traceback, + .print = mp_obj_traceback_print, +}; diff --git a/py/objtraceback.h b/py/objtraceback.h new file mode 100644 index 0000000000..992fe89b01 --- /dev/null +++ b/py/objtraceback.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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_PY_OBJTRACEBACK_H +#define MICROPY_INCLUDED_PY_OBJTRACEBACK_H + +#include "py/obj.h" + +typedef struct _mp_obj_traceback_t { + mp_obj_base_t base; + size_t alloc : (8 * sizeof(size_t) / 2); + size_t len : (8 * sizeof(size_t) / 2); + size_t *data; +} mp_obj_traceback_t; + +#endif // MICROPY_INCLUDED_PY_OBJTRACEBACK_H diff --git a/py/py.cmake b/py/py.cmake index 2b5d437b57..6f0514e0e3 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -95,6 +95,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/objstr.c ${MICROPY_PY_DIR}/objstringio.c ${MICROPY_PY_DIR}/objstrunicode.c + ${MICROPY_PY_DIR}/objtraceback.c ${MICROPY_PY_DIR}/objtuple.c ${MICROPY_PY_DIR}/objtype.c ${MICROPY_PY_DIR}/objzip.c diff --git a/py/py.mk b/py/py.mk index 94ac82f6f0..7c3eaf4f46 100644 --- a/py/py.mk +++ b/py/py.mk @@ -153,6 +153,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objstr.o \ objstrunicode.o \ objstringio.o \ + objtraceback.o \ objtuple.o \ objtype.o \ objzip.o \ diff --git a/py/runtime.c b/py/runtime.c index ceb5e83b14..4d4fc3592b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -79,17 +79,14 @@ void mp_init(void) { #if MICROPY_KBD_EXCEPTION // initialise the exception object for raising KeyboardInterrupt MP_STATE_VM(mp_kbd_exception).base.type = &mp_type_KeyboardInterrupt; - MP_STATE_VM(mp_kbd_exception).traceback_alloc = 0; - MP_STATE_VM(mp_kbd_exception).traceback_len = 0; - MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; MP_STATE_VM(mp_kbd_exception).args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + MP_STATE_VM(mp_kbd_exception).traceback = &MP_STATE_VM(mp_kbd_traceback); + *MP_STATE_VM(mp_kbd_exception).traceback = mp_const_empty_traceback_obj; #endif MP_STATE_VM(mp_reload_exception).base.type = &mp_type_ReloadException; - MP_STATE_VM(mp_reload_exception).traceback_alloc = 0; - MP_STATE_VM(mp_reload_exception).traceback_len = 0; - MP_STATE_VM(mp_reload_exception).traceback_data = NULL; MP_STATE_VM(mp_reload_exception).args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + MP_STATE_VM(mp_reload_exception).traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj; // call port specific initialization if any #ifdef MICROPY_PORT_INIT_FUNC diff --git a/py/scheduler.c b/py/scheduler.c index f11317dc1d..733722e46a 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -40,7 +40,7 @@ void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { #if MICROPY_KBD_EXCEPTION // This function may be called asynchronously at any time so only do the bare minimum. void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) { - MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + MP_STATE_VM(mp_kbd_exception).traceback->data = NULL; mp_sched_exception(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); } #endif diff --git a/shared-bindings/watchdog/__init__.c b/shared-bindings/watchdog/__init__.c index 0911aca282..dbe06d1323 100644 --- a/shared-bindings/watchdog/__init__.c +++ b/shared-bindings/watchdog/__init__.c @@ -61,10 +61,8 @@ const mp_obj_type_t mp_type_WatchDogTimeout = { mp_obj_exception_t mp_watchdog_timeout_exception = { .base.type = &mp_type_WatchDogTimeout, - .traceback_alloc = 0, - .traceback_len = 0, - .traceback_data = NULL, .args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj, + .traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj, }; STATIC const mp_rom_map_elem_t watchdog_module_globals_table[] = { From fb0f2583db3674255da1da4fa7352e385ab4a244 Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Wed, 4 Aug 2021 12:22:21 +0530 Subject: [PATCH 3/4] update traceback module --- locale/circuitpython.pot | 8 ++++---- shared-bindings/traceback/__init__.c | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 0ab2dede22..d9d4bca80c 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -3421,6 +3421,10 @@ msgstr "" msgid "invalid syntax for number" msgstr "" +#: py/objexcept.c shared-bindings/traceback/__init__.c +msgid "invalid traceback" +msgstr "" + #: py/objtype.c msgid "issubclass() arg 1 must be a class" msgstr "" @@ -4093,10 +4097,6 @@ msgstr "" msgid "source palette too large" msgstr "" -#: shared-bindings/traceback/__init__.c -msgid "stack is not ok" -msgstr "" - #: py/objstr.c msgid "start/end indices" msgstr "" diff --git a/shared-bindings/traceback/__init__.c b/shared-bindings/traceback/__init__.c index 3d3914bfd1..c3c7521044 100644 --- a/shared-bindings/traceback/__init__.c +++ b/shared-bindings/traceback/__init__.c @@ -27,8 +27,6 @@ #include "py/stream.h" #include "py/runtime.h" -#include "supervisor/shared/stack.h" - //| """Traceback Module //| //| This module provides a standard interface to print stack traces of programs. @@ -97,11 +95,12 @@ STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_arg } if (args[ARG_tb].u_obj != mp_const_none && print_tb) { - if (!stack_ok()) { - mp_raise_RuntimeError(translate("stack is not ok")); + if (!mp_obj_is_type(args[ARG_tb].u_obj, &mp_type_traceback)) { + mp_raise_TypeError(translate("invalid traceback")); } - size_t n, *values; - mp_obj_exception_get_traceback(exc, &n, &values); + mp_obj_traceback_t *tb = MP_OBJ_TO_PTR(args[ARG_tb].u_obj); + size_t n = (tb->data) ? tb->len : 0; + size_t *values = (tb->data) ? tb->data : NULL; if (n > 0) { assert(n % 3 == 0); // Decompress the format strings From 870f26bfc44f020bdc6770b336bf6e5c88ffdb0f Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Thu, 5 Aug 2021 19:19:19 +0530 Subject: [PATCH 4/4] traceback module refinements --- shared-bindings/traceback/__init__.c | 68 ++++---------------- shared-module/traceback/__init__.c | 94 +++++++++++++++++++++++++++- shared-module/traceback/__init__.h | 36 +++++++++++ 3 files changed, 140 insertions(+), 58 deletions(-) create mode 100644 shared-module/traceback/__init__.h diff --git a/shared-bindings/traceback/__init__.c b/shared-bindings/traceback/__init__.c index c3c7521044..d5290559ca 100644 --- a/shared-bindings/traceback/__init__.c +++ b/shared-bindings/traceback/__init__.c @@ -27,6 +27,8 @@ #include "py/stream.h" #include "py/runtime.h" +#include "shared-module/traceback/__init__.h" + //| """Traceback Module //| //| This module provides a standard interface to print stack traces of programs. @@ -50,7 +52,7 @@ //| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. //| :param io.FileIO file: If file is omitted or `None`, the output goes to `sys.stderr`; otherwise it should be an open //| file or file-like object to receive the output. -//| :param bool chain: If `True` then chained exceptions will be printed. +//| :param bool chain: If `True` then chained exceptions will be printed (note: not yet implemented). //| //| """ //| ... @@ -69,10 +71,10 @@ STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_arg mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t exc = args[ARG_value].u_obj; - if (!mp_obj_is_exception_instance(exc)) { + if (!mp_obj_is_exception_instance(args[ARG_value].u_obj)) { mp_raise_TypeError(translate("invalid exception")); } + mp_obj_exception_t exc = *(mp_obj_exception_t *)MP_OBJ_TO_PTR(args[ARG_value].u_obj); mp_print_t print = mp_plat_print; if (args[ARG_file].u_obj != mp_const_none) { @@ -91,67 +93,19 @@ STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_arg if (!mp_obj_get_int_maybe(args[ARG_limit].u_obj, &limit)) { mp_raise_TypeError(translate("limit should be an int")); } - print_tb = !(limit == 0); + print_tb = (limit != 0); } if (args[ARG_tb].u_obj != mp_const_none && print_tb) { if (!mp_obj_is_type(args[ARG_tb].u_obj, &mp_type_traceback)) { mp_raise_TypeError(translate("invalid traceback")); } - mp_obj_traceback_t *tb = MP_OBJ_TO_PTR(args[ARG_tb].u_obj); - size_t n = (tb->data) ? tb->len : 0; - size_t *values = (tb->data) ? tb->data : NULL; - if (n > 0) { - assert(n % 3 == 0); - // Decompress the format strings - const compressed_string_t *traceback = MP_ERROR_TEXT("Traceback (most recent call last):\n"); - char decompressed[decompress_length(traceback)]; - decompress(traceback, decompressed); - #if MICROPY_ENABLE_SOURCE_LINE - const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\", line %d"); - #else - const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\""); - #endif - char decompressed_frame[decompress_length(frame)]; - decompress(frame, decompressed_frame); - const compressed_string_t *block_fmt = MP_ERROR_TEXT(", in %q\n"); - char decompressed_block[decompress_length(block_fmt)]; - decompress(block_fmt, decompressed_block); - - // Set traceback formatting - // Default: Print full traceback - int i = n - 3, j; - if (limit > 0) { - // Print upto limit traceback - // from caller's frame - limit = n - (limit * 3); - } else if (limit < 0) { - // Print upto limit traceback - // from last - i = 0, limit = 3 + (limit * 3); - } - - // Print the traceback - mp_print_str(&print, decompressed); - for (; i >= limit; i -= 3) { - j = (i < 0) ? -i : i; - #if MICROPY_ENABLE_SOURCE_LINE - mp_printf(&print, decompressed_frame, values[j], (int)values[j + 1]); - #else - mp_printf(&print, decompressed_frame, values[j]); - #endif - // The block name can be NULL if it's unknown - qstr block = values[j + 2]; - if (block == MP_QSTRnull) { - mp_print_str(&print, "\n"); - } else { - mp_printf(&print, decompressed_block, block); - } - } - } + exc.traceback = MP_OBJ_TO_PTR(args[ARG_tb].u_obj); + } else { + exc.traceback = NULL; } - mp_obj_print_helper(&print, exc, PRINT_EXC); - mp_print_str(&print, "\n"); + + shared_module_traceback_print_exception(&exc, &print, limit); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_print_exception_obj, 3, traceback_print_exception); diff --git a/shared-module/traceback/__init__.c b/shared-module/traceback/__init__.c index 4bd8276f34..388b1c18de 100644 --- a/shared-module/traceback/__init__.c +++ b/shared-module/traceback/__init__.c @@ -1 +1,93 @@ -// empty file +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "shared-module/traceback/__init__.h" + +void shared_module_traceback_print_exception(mp_obj_exception_t *exc, mp_print_t *print, mp_int_t limit) { + // Print traceback + if (exc->traceback != NULL) { + size_t n = exc->traceback->len; + size_t *values = exc->traceback->data; + if (n > 0) { + assert(n % 3 == 0); + // Decompress the format strings + const compressed_string_t *traceback = MP_ERROR_TEXT("Traceback (most recent call last):\n"); + char decompressed[decompress_length(traceback)]; + decompress(traceback, decompressed); + #if MICROPY_ENABLE_SOURCE_LINE + const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\", line %d"); + #else + const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\""); + #endif + char decompressed_frame[decompress_length(frame)]; + decompress(frame, decompressed_frame); + const compressed_string_t *block_fmt = MP_ERROR_TEXT(", in %q\n"); + char decompressed_block[decompress_length(block_fmt)]; + decompress(block_fmt, decompressed_block); + + // Set traceback formatting + // Default: Print full traceback + limit = limit * 3; + mp_int_t i = n - 3, j; + if (limit > 0) { + // Print upto limit traceback + // entries from caller's frame + if ((unsigned)limit > n) { + limit = n; + } + limit = n - limit; + } else if (limit < 0) { + // Print upto limit traceback + // entries from last + if ((unsigned)-limit > n) { + limit = -n; + } + i = 0, limit = limit + 3; + } + + // Print the traceback + mp_print_str(print, decompressed); + for (; i >= limit; i -= 3) { + j = (i < 0) ? -i : i; + #if MICROPY_ENABLE_SOURCE_LINE + mp_printf(print, decompressed_frame, values[j], (int)values[j + 1]); + #else + mp_printf(print, decompressed_frame, values[j]); + #endif + // The block name can be NULL if it's unknown + qstr block = values[j + 2]; + if (block == MP_QSTRnull) { + mp_print_str(print, "\n"); + } else { + mp_printf(print, decompressed_block, block); + } + } + } + } + // Print exception + mp_obj_print_helper(print, exc, PRINT_EXC); + mp_print_str(print, "\n"); +} diff --git a/shared-module/traceback/__init__.h b/shared-module/traceback/__init__.h new file mode 100644 index 0000000000..73edd07504 --- /dev/null +++ b/shared-module/traceback/__init__.h @@ -0,0 +1,36 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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_SHARED_MODULE_TRACEBACK___INIT___H +#define MICROPY_INCLUDED_SHARED_MODULE_TRACEBACK___INIT___H + +#include "py/objexcept.h" +#include "py/objtraceback.h" + +extern void shared_module_traceback_print_exception(mp_obj_exception_t *exc, + mp_print_t *print, mp_int_t limit); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_TRACEBACK___INIT___H