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:
+%%
+%% - atomvm_free_heap_size the number of (noncontiguous) free bytes in the STM32 heap (integer)
+%% - atomvm_minimum_free_size the smallest number of free bytes in the STM32 heap since boot (integer)
+%%
+%%
%% The following keys are supported on the ESP32 platform:
%%
-%% - esp32_free_heap_size the number of (noncontiguous) free bytes in the ESP32 heap (integer)
%% - esp32_largest_free_block the number of the largest contiguous free bytes in the ESP32 heap (integer)
-%% - esp32_minimum_free_size the smallest number of free bytes in the ESP32 heap since boot (integer)
%% - esp32_chip_info Details about the model and capabilities of the ESP32 device (map)
%%
%%
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;
}