Skip to content

Commit 193a8d2

Browse files
committed
add traceback object
1 parent f371c0a commit 193a8d2

File tree

13 files changed

+139
-44
lines changed

13 files changed

+139
-44
lines changed

lib/utils/pyexec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) {
242242
mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
243243
if (c == CHAR_CTRL_C) {
244244
#if MICROPY_KBD_EXCEPTION
245-
MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
245+
MP_STATE_VM(mp_kbd_exception).traceback->data = NULL;
246246
nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
247247
#else
248248
mp_raise_type(&mp_type_KeyboardInterrupt);

py/mpstate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ typedef struct _mp_state_vm_t {
120120

121121
qstr_pool_t *last_pool;
122122

123+
// non-heap memory for creating a traceback if we can't allocate RAM
124+
mp_obj_traceback_t mp_emergency_traceback_obj;
125+
123126
// non-heap memory for creating an exception if we can't allocate RAM
124127
mp_obj_exception_t mp_emergency_exception_obj;
125128

@@ -137,6 +140,8 @@ typedef struct _mp_state_vm_t {
137140
#if MICROPY_KBD_EXCEPTION
138141
// exception object of type KeyboardInterrupt
139142
mp_obj_exception_t mp_kbd_exception;
143+
// traceback object to store traceback
144+
mp_obj_traceback_t mp_kbd_traceback;
140145
#endif
141146

142147
// exception object of type ReloadException

py/obj.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ extern const mp_obj_type_t mp_type_bytearray;
692692
extern const mp_obj_type_t mp_type_memoryview;
693693
extern const mp_obj_type_t mp_type_float;
694694
extern const mp_obj_type_t mp_type_complex;
695+
extern const mp_obj_type_t mp_type_traceback;
695696
extern const mp_obj_type_t mp_type_tuple;
696697
extern const mp_obj_type_t mp_type_list;
697698
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;
791792
extern const struct _mp_obj_str_t mp_const_empty_bytes_obj;
792793
extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj;
793794
extern const struct _mp_obj_dict_t mp_const_empty_dict_obj;
795+
extern const struct _mp_obj_traceback_t mp_const_empty_traceback_obj;
794796
extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj;
795797
extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj;
796798
extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj;

py/objexcept.c

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,15 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, con
156156

157157
// Populate the exception object
158158
o_exc->base.type = type;
159-
o_exc->traceback_data = NULL;
159+
160+
// Try to allocate memory for the traceback, with fallback to emergency traceback object
161+
o_exc->traceback = m_new_obj_maybe(mp_obj_traceback_t);
162+
if (o_exc->traceback == NULL) {
163+
o_exc->traceback = &MP_STATE_VM(mp_emergency_traceback_obj);
164+
}
165+
166+
// Populate the traceback object
167+
*o_exc->traceback = mp_const_empty_traceback_obj;
160168

161169
mp_obj_tuple_t *o_tuple;
162170
if (n_args == 0) {
@@ -208,22 +216,25 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
208216
mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in);
209217
if (dest[0] != MP_OBJ_NULL) {
210218
// store/delete attribute
211-
if (attr == MP_QSTR___traceback__ && dest[1] == mp_const_none) {
212-
// We allow 'exc.__traceback__ = None' assignment as low-level
213-
// optimization of pre-allocating exception instance and raising
214-
// it repeatedly - this avoids memory allocation during raise.
215-
// However, uPy will keep adding traceback entries to such
216-
// exception instance, so before throwing it, traceback should
217-
// be cleared like above.
218-
self->traceback_len = 0;
219+
if (attr == MP_QSTR___traceback__) {
220+
if (dest[1] == mp_const_none) {
221+
self->traceback->data = NULL;
222+
} else {
223+
if (!mp_obj_is_type(dest[1], &mp_type_traceback)) {
224+
mp_raise_TypeError(MP_ERROR_TEXT("invalid traceback"));
225+
}
226+
self->traceback = MP_OBJ_TO_PTR(dest[1]);
227+
}
219228
dest[0] = MP_OBJ_NULL; // indicate success
220229
}
221230
return;
222231
}
223232
if (attr == MP_QSTR_args) {
224233
dest[0] = MP_OBJ_FROM_PTR(self->args);
225-
} else if (self->base.type == &mp_type_StopIteration && attr == MP_QSTR_value) {
234+
} else if (attr == MP_QSTR_value && self->base.type == &mp_type_StopIteration) {
226235
dest[0] = mp_obj_exception_get_value(self_in);
236+
} else if (attr == MP_QSTR___traceback__) {
237+
dest[0] = (self->traceback->data) ? MP_OBJ_FROM_PTR(self->traceback) : mp_const_none;
227238
#if MICROPY_CPYTHON_COMPAT
228239
} else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(self->base.type), MP_OBJ_FROM_PTR(&mp_type_OSError))) {
229240
if (attr == MP_QSTR_errno) {
@@ -552,7 +563,7 @@ void mp_obj_exception_clear_traceback(mp_obj_t self_in) {
552563
GET_NATIVE_EXCEPTION(self, self_in);
553564
// just set the traceback to the null object
554565
// we don't want to call any memory management functions here
555-
self->traceback_data = NULL;
566+
self->traceback->data = NULL;
556567
}
557568

558569
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
561572
// append this traceback info to traceback data
562573
// if memory allocation fails (eg because gc is locked), just return
563574

564-
if (self->traceback_data == NULL) {
565-
self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
566-
if (self->traceback_data == NULL) {
575+
if (self->traceback->data == NULL) {
576+
self->traceback->data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
577+
if (self->traceback->data == NULL) {
567578
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
568579
if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) {
569580
// There is room in the emergency buffer for traceback data
570581
size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf)
571582
+ EMG_BUF_TRACEBACK_OFFSET);
572-
self->traceback_data = tb;
573-
self->traceback_alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t);
583+
self->traceback->data = tb;
584+
self->traceback->alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t);
574585
} else {
575586
// Can't allocate and no room in emergency buffer
576587
return;
@@ -581,28 +592,28 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs
581592
#endif
582593
} else {
583594
// Allocated the traceback data on the heap
584-
self->traceback_alloc = TRACEBACK_ENTRY_LEN;
595+
self->traceback->alloc = TRACEBACK_ENTRY_LEN;
585596
}
586-
self->traceback_len = 0;
587-
} else if (self->traceback_len + TRACEBACK_ENTRY_LEN > self->traceback_alloc) {
597+
self->traceback->len = 0;
598+
} else if (self->traceback->len + TRACEBACK_ENTRY_LEN > self->traceback->alloc) {
588599
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
589-
if (self->traceback_data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) {
600+
if (self->traceback->data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) {
590601
// Can't resize the emergency buffer
591602
return;
592603
}
593604
#endif
594605
// be conservative with growing traceback data
595-
size_t *tb_data = m_renew_maybe(size_t, self->traceback_data, self->traceback_alloc,
596-
self->traceback_alloc + TRACEBACK_ENTRY_LEN, true);
606+
size_t *tb_data = m_renew_maybe(size_t, self->traceback->data, self->traceback->alloc,
607+
self->traceback->alloc + TRACEBACK_ENTRY_LEN, true);
597608
if (tb_data == NULL) {
598609
return;
599610
}
600-
self->traceback_data = tb_data;
601-
self->traceback_alloc += TRACEBACK_ENTRY_LEN;
611+
self->traceback->data = tb_data;
612+
self->traceback->alloc += TRACEBACK_ENTRY_LEN;
602613
}
603614

604-
size_t *tb_data = &self->traceback_data[self->traceback_len];
605-
self->traceback_len += TRACEBACK_ENTRY_LEN;
615+
size_t *tb_data = &self->traceback->data[self->traceback->len];
616+
self->traceback->len += TRACEBACK_ENTRY_LEN;
606617
tb_data[0] = file;
607618
tb_data[1] = line;
608619
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
611622
void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values) {
612623
GET_NATIVE_EXCEPTION(self, self_in);
613624

614-
if (self->traceback_data == NULL) {
625+
if (self->traceback->data == NULL) {
615626
*n = 0;
616627
*values = NULL;
617628
} else {
618-
*n = self->traceback_len;
619-
*values = self->traceback_data;
629+
*n = self->traceback->len;
630+
*values = self->traceback->data;
620631
}
621632
}
622633

py/objexcept.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,12 @@
2828

2929
#include "py/obj.h"
3030
#include "py/objtuple.h"
31+
#include "py/objtraceback.h"
3132

3233
typedef struct _mp_obj_exception_t {
3334
mp_obj_base_t base;
34-
size_t traceback_alloc : (8 * sizeof(size_t) / 2);
35-
size_t traceback_len : (8 * sizeof(size_t) / 2);
36-
size_t *traceback_data;
3735
mp_obj_tuple_t *args;
36+
mp_obj_traceback_t *traceback;
3837
} mp_obj_exception_t;
3938

4039
void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind);

py/objgenerator.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
#include "supervisor/shared/translate.h"
3939

4040
// Instance of GeneratorExit exception - needed by generator.close()
41-
const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t *)&mp_const_empty_tuple_obj};
41+
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};
4242

4343
/******************************************************************************/
4444
/* generator wrapper */

py/objtraceback.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 microDev
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 "py/runtime.h"
28+
#include "py/objtraceback.h"
29+
30+
const mp_obj_traceback_t mp_const_empty_traceback_obj = {{&mp_type_traceback}, 0, 0, NULL};
31+
32+
STATIC void mp_obj_traceback_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
33+
(void)kind;
34+
mp_obj_traceback_t *o = MP_OBJ_TO_PTR(o_in);
35+
mp_printf(print, "<traceback object at 0x%p>", o);
36+
}
37+
38+
const mp_obj_type_t mp_type_traceback = {
39+
{ &mp_type_type },
40+
.name = MP_QSTR_traceback,
41+
.print = mp_obj_traceback_print,
42+
};

py/objtraceback.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 microDev
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_PY_OBJTRACEBACK_H
28+
#define MICROPY_INCLUDED_PY_OBJTRACEBACK_H
29+
30+
#include "py/obj.h"
31+
32+
typedef struct _mp_obj_traceback_t {
33+
mp_obj_base_t base;
34+
size_t alloc : (8 * sizeof(size_t) / 2);
35+
size_t len : (8 * sizeof(size_t) / 2);
36+
size_t *data;
37+
} mp_obj_traceback_t;
38+
39+
#endif // MICROPY_INCLUDED_PY_OBJTRACEBACK_H

py/py.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ set(MICROPY_SOURCE_PY
9595
${MICROPY_PY_DIR}/objstr.c
9696
${MICROPY_PY_DIR}/objstringio.c
9797
${MICROPY_PY_DIR}/objstrunicode.c
98+
${MICROPY_PY_DIR}/objtraceback.c
9899
${MICROPY_PY_DIR}/objtuple.c
99100
${MICROPY_PY_DIR}/objtype.c
100101
${MICROPY_PY_DIR}/objzip.c

py/py.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\
153153
objstr.o \
154154
objstrunicode.o \
155155
objstringio.o \
156+
objtraceback.o \
156157
objtuple.o \
157158
objtype.o \
158159
objzip.o \

py/runtime.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,14 @@ void mp_init(void) {
7979
#if MICROPY_KBD_EXCEPTION
8080
// initialise the exception object for raising KeyboardInterrupt
8181
MP_STATE_VM(mp_kbd_exception).base.type = &mp_type_KeyboardInterrupt;
82-
MP_STATE_VM(mp_kbd_exception).traceback_alloc = 0;
83-
MP_STATE_VM(mp_kbd_exception).traceback_len = 0;
84-
MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
8582
MP_STATE_VM(mp_kbd_exception).args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
83+
MP_STATE_VM(mp_kbd_exception).traceback = &MP_STATE_VM(mp_kbd_traceback);
84+
*MP_STATE_VM(mp_kbd_exception).traceback = mp_const_empty_traceback_obj;
8685
#endif
8786

8887
MP_STATE_VM(mp_reload_exception).base.type = &mp_type_ReloadException;
89-
MP_STATE_VM(mp_reload_exception).traceback_alloc = 0;
90-
MP_STATE_VM(mp_reload_exception).traceback_len = 0;
91-
MP_STATE_VM(mp_reload_exception).traceback_data = NULL;
9288
MP_STATE_VM(mp_reload_exception).args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
89+
MP_STATE_VM(mp_reload_exception).traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj;
9390

9491
// call port specific initialization if any
9592
#ifdef MICROPY_PORT_INIT_FUNC

py/scheduler.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) {
4040
#if MICROPY_KBD_EXCEPTION
4141
// This function may be called asynchronously at any time so only do the bare minimum.
4242
void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) {
43-
MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
43+
MP_STATE_VM(mp_kbd_exception).traceback->data = NULL;
4444
mp_sched_exception(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
4545
}
4646
#endif

shared-bindings/watchdog/__init__.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,8 @@ const mp_obj_type_t mp_type_WatchDogTimeout = {
6161

6262
mp_obj_exception_t mp_watchdog_timeout_exception = {
6363
.base.type = &mp_type_WatchDogTimeout,
64-
.traceback_alloc = 0,
65-
.traceback_len = 0,
66-
.traceback_data = NULL,
6764
.args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj,
65+
.traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj,
6866
};
6967

7068
STATIC const mp_rom_map_elem_t watchdog_module_globals_table[] = {

0 commit comments

Comments
 (0)