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
5255STATIC 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
338432void 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 );
0 commit comments