Skip to content

stacktrace: Don't allocate module paths when no frame has a line - #2436

Merged
bettio merged 1 commit into
atomvm:release-0.7from
iobee-io:stacktrace-malloc-zero
Oct 27, 2026
Merged

bettio merged 1 commit into
atomvm:release-0.7from
iobee-io:stacktrace-malloc-zero

Conversation

@gabrielmancini

Copy link
Copy Markdown

stacktrace_build() allocates num_mods module path pairs, one per frame location with line information, and returns the raw stacktrace when malloc fails. When no frame has line information, num_mods is 0, and on ESP-IDF malloc(0) returns NULL (heap_caps_base.c), so the stacktrace comes back raw ({NumFrames, 0, 0, 0, [{ModIndex, Offset}, ...], Class}) instead of a list, and stderr shows:

Unable to allocate space for module paths.  Returning raw stacktrace.

That covers every application packed without line information on ESP32: we saw it on all our boards (ESP32 and ESP32-S3 with 8 MB of PSRAM), never related to memory. glibc returns a non-NULL pointer for malloc(0), which is why generic_unix never shows it.

The fix allocates only when num_mods > 0. With 0 the loop never touches module_paths, and free(NULL) is fine.

Test. test_stacktrace (ESP32) raises in a module compiled with no_line_info and checks that the stacktrace is a list with a frame of that module. The ESP32 test project never declared AVM_CREATE_STACKTRACES, so stacktraces were undefined there; it now enables it as src/platforms/esp32/CMakeLists.txt does, which is what a device runs with.

  • QEMU esp32, without the fix: the message above, and {not_a_list,{2,0,0,0,[{0,25},{0,20,erlang,error,1}],error}}; with it, test_stacktrace passes (test_socket fails here as it does on plain release-0.7, no network inside QEMU).
  • generic_unix test-erlang: all pass.
  • On hardware: one ESP32 with the fix printed the message 0 times in two days; boards without it, 109 and 13 times in the same period.

These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@petermm petermm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice find!

just a small nit:

Review: AtomVM PR #2436

PR: #2436
Title: stacktrace: Don't allocate module paths when no frame has a line
Reviewed head: aa78fe16abd5204e3c86a554196ee4c86962ca83
Base: 18b35870a0be75c42892db9a0762e8c7a7d61323

Summary

The allocation fix looks correct: when there are no source locations, no module-path entries are needed. Keeping module_paths as NULL avoids treating a permitted malloc(0) result as an allocation failure; cleanup via free(NULL) is valid. Positive-sized allocation failure retains its previous behavior.

One actionable test-configuration issue should be fixed. No additional correctness issues were found in the runtime change or the test packaging.

Findings

[P2] Register the stacktrace test only when stacktraces are enabled

Location: src/platforms/esp32/test/main/test_main.c, lines 382–386

The new test is registered unconditionally, but the test project's new AVM_CREATE_STACKTRACES CMake option still permits an explicit OFF. With -DAVM_CREATE_STACKTRACES=OFF, stacktrace_build() intentionally returns UNDEFINED_ATOM, and the Erlang test returns {not_a_list, undefined}. The C assertion then fails despite the VM behaving correctly for that configuration.

The default ON does not prevent this: CMake's option() preserves an explicit cache value. The host test suite already accounts for this configuration through SKIP_STACKTRACES in tests/test.c.

Suggested fix: Guard registration with the same feature definition that selects the implementation. libAtomVM exports AVM_CREATE_STACKTRACES as a public compile definition when enabled. The BEAM can remain compiled and packaged unconditionally.

diff --git a/src/platforms/esp32/test/main/test_main.c b/src/platforms/esp32/test/main/test_main.c
--- a/src/platforms/esp32/test/main/test_main.c
+++ b/src/platforms/esp32/test/main/test_main.c
@@ -379,12 +379,14 @@ TEST_CASE("test_list_to_atom", "[test_run]")
     TEST_ASSERT(ret_value == OK_ATOM);
 }
 
+#ifdef AVM_CREATE_STACKTRACES
 TEST_CASE("test_stacktrace", "[test_run]")
 {
     term ret_value = avm_test_case("test_stacktrace.beam");
     TEST_ASSERT(ret_value == OK_ATOM);
 }
+#endif
 
 TEST_CASE("test_list_to_binary", "[test_run]")
 {

Validation after applying: Build the ESP32 test project with AVM_CREATE_STACKTRACES=ON and OFF. With ON, the new test should be registered and pass on ESP-IDF; with OFF, it should not be registered. For the regression itself, retain the no-line-info scenario on a platform where malloc(0) returns NULL, checking that it fails before the runtime fix and passes afterward.

@gabrielmancini

Copy link
Copy Markdown
Author

Thanks! Guarded the registration with #ifdef AVM_CREATE_STACKTRACES, as suggested, and kept the BEAM built and packaged unconditionally. Amended into the same commit.

@gabrielmancini

Copy link
Copy Markdown
Author

@bettio this one has @petermm's approval now (after the #ifdef AVM_CREATE_STACKTRACES guard), whenever you have a moment.

The red jobs are all in CI setup, not in the build or the tests: setup-beam fails with "Requested loose Gleam version (1.11.1) not found in version list" (and the same for rebar3 3.24.0 / 3.25.1), plus FreeBSD 13.5 "Install deps" and the jobs cancelled after those. I can't re-run them from here; could someone re-run the failed jobs?

@bettio bettio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good. Just few nitpicks.

Comment thread src/libAtomVM/stacktrace.c Outdated
Comment thread src/platforms/esp32/test/main/test_erl_sources/test_stacktrace.erl Outdated
Comment thread src/platforms/esp32/test/main/test_erl_sources/test_stacktrace.erl Outdated
Comment thread CHANGELOG.md Outdated
@bettio

bettio commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

By the way, if you are making changes, let's do git rebase release-0.7

stacktrace_build() allocated num_mods module path pairs, one per frame
with line information, and returned the raw stacktrace when malloc
failed. With no such frame num_mods is 0, and malloc(0) returns NULL on
ESP-IDF, so every stacktrace of modules without line information came
back raw, with "Unable to allocate space for module paths" on stderr.
Allocate only when there is something to store.

The ESP32 test project now creates stacktraces like the firmware build
does, so test_no_line_info_stacktrace sees what a device sees.

Signed-off-by: Gabriel Mancini <gabriel.mancini@gmail.com>
@gabrielmancini

Copy link
Copy Markdown
Author

Thanks @bettio! Rebased on release-0.7 and amended into the same commit: removed the comment in stacktrace.c, renamed the test to test_no_line_info_stacktrace, it now calls ?MODULE:id(boom), and the CHANGELOG entry is at the end of ### Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants