diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 5df1f3c93..bfef5a32d 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -38,6 +38,30 @@ Functions Read the raw value of the internal Hall sensor, returning an integer. +.. function:: idf_heap_info(capabilities) + + Returns information about the ESP-IDF heap memory regions. One of them contains + the MicroPython heap and the others are used by ESP-IDF, e.g., for network + buffers and other data. This data is useful to get a sense of how much memory + is available to ESP-IDF and the networking stack in particular. It may shed + some light on situations where ESP-IDF operations fail due to allocation failures. + The information returned is *not* useful to troubleshoot Python allocation failures, + use `micropython.mem_info()` instead. + + The capabilities parameter corresponds to ESP-IDF's ``MALLOC_CAP_XXX`` values but the + two most useful ones are predefined as `esp32.HEAP_DATA` for data heap regions and + `esp32.HEAP_EXEC` for executable regions as used by the native code emitter. + + The return value is a list of 4-tuples, where each 4-tuple corresponds to one heap + and contains: the total bytes, the free bytes, the largest free block, and + the minimum free seen over time. + + Example after booting:: + + >>> import esp32; esp32.idf_heap_info(esp32.HEAP_DATA) + [(240, 0, 0, 0), (7288, 0, 0, 0), (16648, 4, 4, 4), (79912, 35712, 35512, 35108), + (15072, 15036, 15036, 15036), (113840, 0, 0, 0)] + Flash partitions ---------------- @@ -88,6 +112,10 @@ Constants Used in `Partition.find` to specify the partition type. +.. data:: HEAP_DATA + HEAP_EXEC + + Used in `idf_heap_info`. .. _esp32.RMT: diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 0ce3b957b..9554dbd69 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -33,6 +33,9 @@ #include "soc/sens_reg.h" #include "driver/gpio.h" #include "driver/adc.h" +#include "esp_heap_caps.h" +#include "multi_heap.h" +#include "../heap_private.h" #include "py/nlr.h" #include "py/obj.h" @@ -145,6 +148,28 @@ STATIC mp_obj_t esp32_hall_sensor(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp32_hall_sensor_obj, esp32_hall_sensor); +STATIC mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) { + mp_int_t cap = mp_obj_get_int(cap_in); + multi_heap_info_t info; + heap_t *heap; + mp_obj_t heap_list = mp_obj_new_list(0, 0); + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, cap)) { + multi_heap_get_info(heap->heap, &info); + mp_obj_t data[] = { + MP_OBJ_NEW_SMALL_INT(heap->end - heap->start), // total heap size + MP_OBJ_NEW_SMALL_INT(info.total_free_bytes), // total free bytes + MP_OBJ_NEW_SMALL_INT(info.largest_free_block), // largest free contiguous + MP_OBJ_NEW_SMALL_INT(info.minimum_free_bytes), // minimum free seen + }; + mp_obj_t this_heap = mp_obj_new_tuple(4, data); + mp_obj_list_append(heap_list, this_heap); + } + } + return heap_list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info); + STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) }, @@ -153,6 +178,7 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) }, { MP_ROM_QSTR(MP_QSTR_raw_temperature), MP_ROM_PTR(&esp32_raw_temperature_obj) }, { MP_ROM_QSTR(MP_QSTR_hall_sensor), MP_ROM_PTR(&esp32_hall_sensor_obj) }, + { MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, { MP_ROM_QSTR(MP_QSTR_RMT), MP_ROM_PTR(&esp32_rmt_type) }, @@ -160,6 +186,9 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_WAKEUP_ALL_LOW), MP_ROM_FALSE }, { MP_ROM_QSTR(MP_QSTR_WAKEUP_ANY_HIGH), MP_ROM_TRUE }, + + { MP_ROM_QSTR(MP_QSTR_HEAP_DATA), MP_ROM_INT(MALLOC_CAP_8BIT) }, + { MP_ROM_QSTR(MP_QSTR_HEAP_EXEC), MP_ROM_INT(MALLOC_CAP_EXEC) }, }; STATIC MP_DEFINE_CONST_DICT(esp32_module_globals, esp32_module_globals_table); diff --git a/tests/esp32/esp32_idf_heap_info.py b/tests/esp32/esp32_idf_heap_info.py new file mode 100644 index 000000000..f141ae07b --- /dev/null +++ b/tests/esp32/esp32_idf_heap_info.py @@ -0,0 +1,33 @@ +# Test the esp32's esp32.idf_heap_info to return sane data +try: + import esp32 +except ImportError: + print("SKIP") + raise SystemExit + +# region tuple is: (size, free, largest free, min free) +# we check that each region's size is > 0 and that the free amounts are smaller than the size +def chk_heap(kind, regions): + chk = [True, True, True, True] + for r in regions: + chk = [ + chk[0] and r[0] > 0, + chk[1] and r[1] <= r[0], + chk[2] and r[2] <= r[0], + chk[3] and r[3] <= r[0], + ] + print(kind, chk) + + +# try getting heap regions +regions = esp32.idf_heap_info(esp32.HEAP_DATA) +print("HEAP_DATA >2:", len(regions) > 2) +chk_heap("HEAP_DATA", regions) + +# try getting code regions +regions = esp32.idf_heap_info(esp32.HEAP_EXEC) +print("HEAP_EXEC >2:", len(regions) > 2) +chk_heap("HEAP_EXEC", regions) + +# try invalid param +print(esp32.idf_heap_info(-1)) diff --git a/tests/esp32/esp32_idf_heap_info.py.exp b/tests/esp32/esp32_idf_heap_info.py.exp new file mode 100644 index 000000000..2b63bf325 --- /dev/null +++ b/tests/esp32/esp32_idf_heap_info.py.exp @@ -0,0 +1,5 @@ +HEAP_DATA >2: True +HEAP_DATA [True, True, True, True] +HEAP_EXEC >2: True +HEAP_EXEC [True, True, True, True] +[]