Skip to content

Commit 5b7fd20

Browse files
committed
Add support for storing args during an exception raised by an irq.
The user code should call micropython.alloc_emergency_exception_buf(size) where size is the size of the buffer used to print the argument passed to the exception. With the test code from adafruit#732, and a call to micropython.alloc_emergenncy_exception_buf(100) the following error is now printed: ```python >>> import heartbeat_irq Uncaught exception in Timer(4) interrupt handler Traceback (most recent call last): File "0://heartbeat_irq.py", line 14, in heartbeat_cb NameError: name 'led' is not defined ```
1 parent 05c255f commit 5b7fd20

File tree

9 files changed

+164
-4
lines changed

9 files changed

+164
-4
lines changed

py/modmicropython.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_current_obj, mp_micropython_
5151
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_peak_obj, mp_micropython_mem_peak);
5252
#endif
5353

54+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
55+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf);
56+
#endif
57+
5458
STATIC const mp_map_elem_t mp_module_micropython_globals_table[] = {
5559
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_micropython) },
5660
#if MICROPY_MEM_STATS
5761
{ MP_OBJ_NEW_QSTR(MP_QSTR_mem_total), (mp_obj_t)&mp_micropython_mem_total_obj },
5862
{ MP_OBJ_NEW_QSTR(MP_QSTR_mem_current), (mp_obj_t)&mp_micropython_mem_current_obj },
5963
{ MP_OBJ_NEW_QSTR(MP_QSTR_mem_peak), (mp_obj_t)&mp_micropython_mem_peak_obj },
6064
#endif
65+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
66+
{ MP_OBJ_NEW_QSTR(MP_QSTR_alloc_emergency_exception_buf), (mp_obj_t)&mp_alloc_emergency_exception_buf_obj },
67+
#endif
6168
};
6269

6370
STATIC const mp_obj_dict_t mp_module_micropython_globals = {

py/mpconfig.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@
163163
#define MICROPY_STACK_CHECK (1)
164164
#endif
165165

166+
// Whether to have an emergency exception buffer
167+
#ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
168+
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0)
169+
#endif
170+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
171+
# ifndef MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE
172+
# define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0) // 0 - implies dynamic allocation
173+
# endif
174+
#endif
175+
166176
// Whether to include REPL helper function
167177
#ifndef MICROPY_HELPER_REPL
168178
#define MICROPY_HELPER_REPL (0)
@@ -378,6 +388,14 @@ typedef double mp_float_t;
378388
/*****************************************************************************/
379389
/* Miscellaneous settings */
380390

391+
// On embedded platforms, these will typically enable/disable irqs.
392+
#ifndef MICROPY_BEGIN_ATOMIC_SECTION
393+
#define MICROPY_BEGIN_ATOMIC_SECTION()
394+
#endif
395+
#ifndef MICROPY_END_ATOMIC_SECTION
396+
#define MICROPY_END_ATOMIC_SECTION()
397+
#endif
398+
381399
// Allow to override static modifier for global objects, e.g. to use with
382400
// object code analysis tools which don't support static symbols.
383401
#ifndef STATIC

py/obj.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, mp_uint_t line,
453453
void mp_obj_exception_get_traceback(mp_obj_t self_in, mp_uint_t *n, mp_uint_t **values);
454454
mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in);
455455
mp_obj_t mp_obj_exception_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args);
456+
mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in);
457+
void mp_init_emergency_exception_buf(void);
456458

457459
// str
458460
mp_obj_t mp_obj_str_builder_start(const mp_obj_type_t *type, uint len, byte **data);

py/objexcept.c

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@
2727
#include <string.h>
2828
#include <stdarg.h>
2929
#include <assert.h>
30+
#include <stdio.h>
3031

3132
#include "mpconfig.h"
3233
#include "nlr.h"
3334
#include "misc.h"
3435
#include "qstr.h"
3536
#include "obj.h"
37+
#include "objlist.h"
38+
#include "objstr.h"
3639
#include "objtuple.h"
3740
#include "objtype.h"
3841
#include "runtime.h"
@@ -51,6 +54,53 @@ const mp_obj_exception_t mp_const_MemoryError_obj = {{&mp_type_MemoryError}, MP_
5154
// Local non-heap memory for allocating an exception when we run out of RAM
5255
STATIC mp_obj_exception_t mp_emergency_exception_obj;
5356

57+
// Optionally allocated buffer for storing the first argument of an exception
58+
// allocated when the heap is locked.
59+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
60+
# if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0
61+
STATIC byte mp_emergency_exception_buf[MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE];
62+
#define mp_emergency_exception_buf_size MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE
63+
64+
void mp_init_emergency_exception_buf(void) {
65+
// Nothing to do since the buffer was declared statically. We put this
66+
// definition here so that the calling code can call this function
67+
// regardless of how its configured (makes the calling code a bit cleaner).
68+
}
69+
70+
#else
71+
STATIC mp_int_t mp_emergency_exception_buf_size = 0;
72+
STATIC byte *mp_emergency_exception_buf = NULL;
73+
74+
void mp_init_emergency_exception_buf(void) {
75+
mp_emergency_exception_buf_size = 0;
76+
mp_emergency_exception_buf = NULL;
77+
}
78+
79+
mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) {
80+
mp_int_t size = mp_obj_get_int(size_in);
81+
void *buf = NULL;
82+
if (size > 0) {
83+
buf = m_malloc(size);
84+
}
85+
86+
int old_size = mp_emergency_exception_buf_size;
87+
void *old_buf = mp_emergency_exception_buf;
88+
89+
// Update the 2 variables atomically so that an interrupt can't occur
90+
// between the assignments.
91+
MICROPY_BEGIN_ATOMIC_SECTION();
92+
mp_emergency_exception_buf_size = size;
93+
mp_emergency_exception_buf = buf;
94+
MICROPY_END_ATOMIC_SECTION();
95+
96+
if (old_buf != NULL) {
97+
m_free(old_buf, old_size);
98+
}
99+
return mp_const_none;
100+
}
101+
#endif
102+
#endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
103+
54104
// Instance of GeneratorExit exception - needed by generator.close()
55105
// This would belong to objgenerator.c, but to keep mp_obj_exception_t
56106
// definition module-private so far, have it here.
@@ -268,6 +318,50 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char
268318
o->base.type = exc_type;
269319
o->traceback = MP_OBJ_NULL;
270320
o->args = mp_const_empty_tuple;
321+
322+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
323+
// If the user has provided a buffer, then we try to create a tuple
324+
// of length 1, which has a string object and the string data.
325+
326+
if (mp_emergency_exception_buf_size > (sizeof(mp_obj_tuple_t) + sizeof(mp_obj_str_t) + sizeof(mp_obj_t))) {
327+
mp_obj_tuple_t *tuple = (mp_obj_tuple_t *)mp_emergency_exception_buf;
328+
mp_obj_str_t *str = (mp_obj_str_t *)&tuple->items[1];
329+
330+
tuple->base.type = &mp_type_tuple;
331+
tuple->len = 1;
332+
tuple->items[0] = str;
333+
334+
byte *str_data = (byte *)&str[1];
335+
uint max_len = mp_emergency_exception_buf + mp_emergency_exception_buf_size
336+
- str_data;
337+
338+
va_list ap;
339+
va_start(ap, fmt);
340+
str->len = vsnprintf((char *)str_data, max_len, fmt, ap);
341+
va_end(ap);
342+
343+
str->base.type = &mp_type_str;
344+
str->hash = qstr_compute_hash(str_data, str->len);
345+
str->data = str_data;
346+
347+
o->args = tuple;
348+
349+
uint offset = &str_data[str->len] - mp_emergency_exception_buf;
350+
offset += sizeof(void *) - 1;
351+
offset &= ~(sizeof(void *) - 1);
352+
353+
if ((mp_emergency_exception_buf_size - offset) > (sizeof(mp_obj_list_t) + sizeof(mp_obj_t) * 3)) {
354+
// We have room to store some traceback.
355+
mp_obj_list_t *list = (mp_obj_list_t *)((byte *)mp_emergency_exception_buf + offset);
356+
list->base.type = &mp_type_list;
357+
list->items = (mp_obj_t)&list[1];
358+
list->alloc = (mp_emergency_exception_buf + mp_emergency_exception_buf_size - (byte *)list->items) / sizeof(list->items[0]);
359+
list->len = 0;
360+
361+
o->traceback = list;
362+
}
363+
}
364+
#endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
271365
} else {
272366
o->base.type = exc_type;
273367
o->traceback = MP_OBJ_NULL;
@@ -336,15 +430,24 @@ void mp_obj_exception_clear_traceback(mp_obj_t self_in) {
336430
}
337431

338432
void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, mp_uint_t line, qstr block) {
433+
GET_NATIVE_EXCEPTION(self, self_in);
434+
339435
#if MICROPY_ENABLE_GC
340436
if (gc_is_locked()) {
341-
// We can't allocate memory, so don't bother to try
342-
return;
437+
if (self->traceback == MP_OBJ_NULL) {
438+
// We can't allocate any memory, and no memory has been
439+
// pre-allocated, so there is nothing else we can do.
440+
return;
441+
}
442+
mp_obj_list_t *list = self->traceback;
443+
if (list->alloc <= (list->len + 3)) {
444+
// There is some preallocated memory, but not enough to store an
445+
// entire record.
446+
return;
447+
}
343448
}
344449
#endif
345450

346-
GET_NATIVE_EXCEPTION(self, self_in);
347-
348451
// for traceback, we are just using the list object for convenience, it's not really a list of Python objects
349452
if (self->traceback == MP_OBJ_NULL) {
350453
self->traceback = mp_obj_new_list(0, NULL);

py/qstrdefs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ Q(mem_total)
323323
Q(mem_current)
324324
Q(mem_peak)
325325

326+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
327+
Q(alloc_emergency_exception_buf)
328+
#endif
329+
326330
Q(<module>)
327331
Q(<lambda>)
328332
Q(<listcomp>)

stmhal/main.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c
119119
}
120120
#endif
121121

122+
void enable_irq(void) {
123+
__enable_irq();
124+
}
125+
126+
void disable_irq(void) {
127+
__disable_irq();
128+
}
129+
122130
STATIC mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
123131
STATIC mp_obj_t pyb_config_main = MP_OBJ_NULL;
124132
STATIC mp_obj_t pyb_config_usb_mode = MP_OBJ_NULL;
@@ -302,6 +310,9 @@ int main(void) {
302310

303311
// GC init
304312
gc_init(&_heap_start, &_heap_end);
313+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
314+
mp_init_emergency_exception_buf();
315+
#endif
305316

306317
// Change #if 0 to #if 1 if you want REPL on UART_6 (or another uart)
307318
// as well as on USB VCP

stmhal/mpconfigport.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@
5252
#define MICROPY_PY_IO (1)
5353
#define MICROPY_PY_IO_FILEIO (1)
5454

55+
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
56+
#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0)
57+
58+
void enable_irq(void);
59+
void disable_irq(void);
60+
61+
#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
62+
#define MICROPY_END_ATOMIC_SECTION() enable_irq()
63+
5564
// extra built in names to add to the global namespace
5665
extern const struct _mp_obj_fun_native_t mp_builtin_help_obj;
5766
extern const struct _mp_obj_fun_native_t mp_builtin_input_obj;

unix/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ int main(int argc, char **argv) {
273273
char *heap = malloc(heap_size);
274274
gc_init(heap, heap + heap_size);
275275
#endif
276+
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
277+
mp_init_emergency_exception_buf();
278+
#endif
276279

277280
qstr_init();
278281
mp_init();

unix/mpconfigport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
#define MICROPY_GCREGS_SETJMP (0)
6565
#endif
6666

67+
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
68+
#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (128)
69+
6770
extern const struct _mp_obj_module_t mp_module_os;
6871
extern const struct _mp_obj_module_t mp_module_time;
6972
extern const struct _mp_obj_module_t mp_module_socket;

0 commit comments

Comments
 (0)