diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e2a9f972..6d8e7d975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,12 +49,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Function.ex and Protocol.ex improving Elixir 1.18 support - Added WiFi support for ESP32P4 via esp-wifi-external for build with ESP-IDF v5.4 and later - Added Process.link/1 and unlink/1 to Elixir Process.ex +- Added `erlang:system_info/1` keys `atomvm_free_heap_size` and `atomvm_minimum_free_size` on STM32 platform ### Changed - Removed `externalterm_to_term_copy` added in [0.6.5] and introduced flags to `externalterm_to_term` to perform copy. - Release images for ESP32 chips are built with ESP-IDF v5.4 - ESP32: SPI peripheral defaults to `"spi2"` instead of deprecated `hspi` +- Deprecated ESP32 `erlang:system_info/1` memory keys `esp32_free_heap_size` and +`esp32_minimum_free_size` in favor of `atomvm_` prefixed keys with warnings to update applications +before the old keys are removed. ### Fixed diff --git a/UPDATING.md b/UPDATING.md index 23803b7ab..ae8a5bb39 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -13,6 +13,9 @@ port socket driver, are also represented by a port and some matching code may ne `is_pid/1` to `is_port/1`. - Ports and pids can be registered. Function `globalcontext_get_registered_process` result now is a term that can be a `port()` or a `pid()`. +- ESP32 `erlang:system_info/1` memory keys `esp32_free_heap_size` and `esp32_minimum_free_size` have +been deprecated in favor of `atomvm_` prefixed keys. Applications should be updated to use +`atomvm_free_heap_size` and `atomvm_minimum_free_size` instead. ## v0.6.4 -> v0.6.5 diff --git a/doc/src/programmers-guide.md b/doc/src/programmers-guide.md index 424700cab..90e84f4f9 100644 --- a/doc/src/programmers-guide.md +++ b/doc/src/programmers-guide.md @@ -624,6 +624,11 @@ For example, io:format("Atom Count: ~p~n", [erlang:system_info(atom_count)]). ``` +In addition AtomVM supports the following keys on ESP32 and STM32 platforms: + +* `atomvm_free_heap_size` Returns the available free space in the heap. +* `atomvm_minimum_free_size` Returns the smallest ever free space available in the heap since boot, this will tell you how close you have come to running out of free memory. + ```{note} Additional platform-specific information is supported, depending on the platform type. See below. ``` @@ -1141,9 +1146,7 @@ As noted above, the [`erlang:system_info/1`](./apidocs/erlang/estdlib/erlang.md# You can request ESP32-specific information using using the following input atoms: -* `esp32_free_heap_size` Returns the available free space in the ESP32 heap. * `esp32_largest_free_block` Returns the size of the largest free continuous block in the ESP32 heap. -* `esp32_minimum_free_size` Returns the smallest ever free space available in the ESP32 heap since boot, this will tell you how close you have come to running out of free memory. * `esp32_chip_info` Returns map of the form `#{features := Features, cores := Cores, revision := Revision, model := Model}`, where `Features` is a list of features enabled in the chip, from among the following atoms: `[emb_flash, bgn, ble, bt]`; `Cores` is the number of CPU cores on the chip; `Revision` is the chip version; and `Model` is one of the following atoms: `esp32`, `esp32_s2`, `esp32_s3`, `esp32_c3`, etc. * `esp_idf_version` Return the IDF SDK version, as a string. @@ -1591,7 +1594,7 @@ The read options take the form of a proplist, if the key `raw` is true (`{raw, t If the key `voltage` is true (or simply appears in the list as an atom), then a calibrated voltage value will be returned in millivolts in the second element of the returned tuple. Otherwise, this element will be the atom `undefined`. -You may specify the number of samples (1 - 100000) to be taken and averaged over using the tuple `{samples, Samples :: 1..100000}`, the default is `64`. +You may specify the number of samples (1 - 100000) to be taken and averaged over using the tuple `{samples, Samples :: 1..100000}`, the default is `64`. ```{warning} Using a large number of samples can significantly increase the amount of time before a response, up to several seconds. diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl index b588d1e2a..90a6b179b 100644 --- a/libs/estdlib/src/erlang.erl +++ b/libs/estdlib/src/erlang.erl @@ -289,11 +289,15 @@ process_info(_Pid, _Key) -> %%
  • schedulers the number of schedulers, equal to the number of online processors (integer)
  • %%
  • schedulers_online the current number of schedulers (integer)
  • %% +%% The following keys are supported on ESP32 and STM32 platforms: +%% +%% %% The following keys are supported on the ESP32 platform: %% %% diff --git a/src/platforms/esp32/components/avm_sys/sys.c b/src/platforms/esp32/components/avm_sys/sys.c index 8318ae759..edcc6e5ac 100644 --- a/src/platforms/esp32/components/avm_sys/sys.c +++ b/src/platforms/esp32/components/avm_sys/sys.c @@ -72,8 +72,12 @@ static void *select_thread_loop(void *); static void select_thread_signal(struct ESP32PlatformData *platform); // clang-format off +// TODO: remove deprecated `atomvm_free_heap_size_atom` for the 0.8.x release cycle +static const char *const atomvm_free_heap_size_atom = "\x15" "atomvm_free_heap_size"; static const char *const esp_free_heap_size_atom = "\x14" "esp32_free_heap_size"; static const char *const esp_largest_free_block_atom = "\x18" "esp32_largest_free_block"; +static const char *const atomvm_get_minimum_free_size_atom = "\x18" "atomvm_minimum_free_size"; +// TODO: remove deprecated `esp_get_minimum_free_size_atom` for the 0.8.x release cycle static const char *const esp_get_minimum_free_size_atom = "\x17" "esp32_minimum_free_size"; static const char *const esp_chip_info_atom = "\xF" "esp32_chip_info"; static const char *const esp_idf_version_atom = "\xF" "esp_idf_version"; @@ -499,13 +503,27 @@ static term get_features(Context *ctx, uint32_t features) term sys_get_info(Context *ctx, term key) { GlobalContext *glb = ctx->global; + // TODO: remove this deprecated key for the 0.8.x release cycle if (key == globalcontext_make_atom(glb, esp_free_heap_size_atom)) { + ESP_LOGW(TAG, "The system_info/1 key 'esp32_free_heap_size' has been deprecated in favor of the \ + muti-platform key 'atomvm_free_heap_size'. This key will be removed in a future release, please \ + update your application to use 'atomvm_free_heap_size'."); + return term_from_int32(esp_get_free_heap_size()); + } + if (key == globalcontext_make_atom(glb, atomvm_free_heap_size_atom)) { return term_from_int32(esp_get_free_heap_size()); } if (key == globalcontext_make_atom(glb, esp_largest_free_block_atom)) { return term_from_int32(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); } + // TODO: remove this deprecated key for the 0.8.x release cycle if (key == globalcontext_make_atom(glb, esp_get_minimum_free_size_atom)) { + ESP_LOGW(TAG, "The system_info/1 key 'esp32_minimum_free_size' has been deprecated in favor of the \ + muti-platform key 'atomvm_minimum_free_size'. This key will be removed in a future release, please \ + update your application to use 'atomvm_minimum_free_size'."); + return term_from_int32(esp_get_minimum_free_heap_size()); + } + if (key == globalcontext_make_atom(glb, atomvm_get_minimum_free_size_atom)) { return term_from_int32(esp_get_minimum_free_heap_size()); } if (key == globalcontext_make_atom(glb, esp_chip_info_atom)) { diff --git a/src/platforms/stm32/cmake/libopencm3.cmake b/src/platforms/stm32/cmake/libopencm3.cmake index 6e0ad658c..47d4a2cdf 100644 --- a/src/platforms/stm32/cmake/libopencm3.cmake +++ b/src/platforms/stm32/cmake/libopencm3.cmake @@ -145,7 +145,7 @@ message(STATUS "Generated Linker File : ${CMAKE_CURRENT_BINARY_DIR}/${LINKER_S # ARCH_FLAGS has to be passed as a string here JOIN("${ARCH_FLAGS}" " " ARCH_FLAGS) # Set linker flags -set(LINKER_FLAGS "${LINKER_FLAGS} -specs=nosys.specs -nostartfiles -Wl,--undefined,_printf_float -Wl,--undefined,_scanf_float -T${CMAKE_CURRENT_BINARY_DIR}/${LINKER_SCRIPT} ${ARCH_FLAGS}") +set(LINKER_FLAGS "${LINKER_FLAGS} -specs=nosys.specs -nostartfiles -Wl,--undefined,_printf_float -Wl,--undefined,_scanf_float -Wl,--print-memory-usage -T${CMAKE_CURRENT_BINARY_DIR}/${LINKER_SCRIPT} ${ARCH_FLAGS}") message(STATUS "Linker Flags : ${LINKER_FLAGS}") # Compiler flags diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index c65a39cab..f18d0345d 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -51,6 +51,7 @@ extern uint8_t _ebss, _stack; static uint8_t *_cur_brk = NULL; static uint8_t *_heap_end = NULL; +static uint8_t *_heap_high_mark = NULL; /* * If not overridden, this puts the heap into the left @@ -63,13 +64,14 @@ static void __local_ram(uint8_t **start, uint8_t **end) { *start = &_ebss; *end = (uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE)); + _heap_high_mark = 0; } void *_sbrk_r(struct _reent *reent, ptrdiff_t diff) { uint8_t *_old_brk; - if (_heap_end == NULL) { + if (UNLIKELY(_heap_end == NULL)) { local_heap_setup(&_cur_brk, &_heap_end); } @@ -79,9 +81,23 @@ void *_sbrk_r(struct _reent *reent, ptrdiff_t diff) return (void *) -1; } _cur_brk += diff; + if (_cur_brk > _heap_high_mark) { + _heap_high_mark = _cur_brk; + } + return _old_brk; } +static int sys_get_free_heap() +{ + return (int) ((uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE))) - (_cur_brk == NULL ? (int) &_ebss : (int) _cur_brk); +} + +static int sys_least_free_heap() +{ + return (int) ((uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE))) - (int) _heap_high_mark; +} + // Monotonically increasing number of milliseconds from reset static volatile uint64_t system_millis; @@ -263,8 +279,13 @@ Context *sys_create_port(GlobalContext *glb, const char *driver_name, term opts) term sys_get_info(Context *ctx, term key) { - UNUSED(ctx); - UNUSED(key); + GlobalContext *glb = ctx->global; + if (key == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x15", "atomvm_free_heap_size"))) { + return term_from_int32(sys_get_free_heap()); + } + if (key == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x18", "atomvm_minimum_free_size"))) { + return term_from_int32(sys_least_free_heap()); + } return UNDEFINED_ATOM; }