Giter Site home page Giter Site logo

atomvm / atomvm Goto Github PK

View Code? Open in Web Editor NEW
1.4K 1.4K 91.0 7.56 MB

Tiny Erlang VM

License: Apache License 2.0

C 53.71% CMake 4.63% Erlang 38.04% Elixir 2.38% Shell 0.39% Python 0.05% CodeQL 0.06% JavaScript 0.57% HTML 0.11% TypeScript 0.07%
c elixir embedded erlang esp32 hacktoberfest stm32

atomvm's Introduction

AtomVM

Brings Erlang, Elixir and other functional languages to really small systems.

AtomVM implements from scratch a minimal Erlang VM that supports a subset of ErlangVM features and that is able to run unmodified BEAM binaries on really small systems like MCUs.

Supported Platforms

AtomVM aims to be easily portable to new platforms with a minimum effort, so additional platforms might be supported in a near future.

Getting Started

There is much more information, including a more complete "Getting Started Guide", extensive documentation, examples, detailed build instructions, and contact information available on the AtomVM project website.

Don't forget to check out the examples repository to help get you started on your next IoT project.

Please, use v0.6.x releases, main branch is for development purposes and it might be unstable.

Dependencies

Required for building:

Documentation and Coverage:

  • gcov and lcov are optionally required to generate coverage report (make coverage).
  • For documentation build requirements consult the Documentation README.

Step-by-Step Build Instructions (generic unix platform)

$ mkdir build
$ cd build
$ cmake ..
$ make
$ ./src/AtomVM ./examples/erlang/hello_world.avm

Run tests within build directory with:

$ ./tests/test-erlang
$ ./tests/test-enif
$ ./tests/test-mailbox
$ ./tests/test-structs
$ ./src/AtomVM ./tests/libs/estdlib/test_estdlib.avm
$ ./src/AtomVM ./tests/libs/eavmlib/test_eavmlib.avm
$ ./src/AtomVM ./tests/libs/alisp/test_alisp.avm

Complete Build Instructions are available in the documentation for Generic UNIX (Linux, MacOS, FreeBSD, DragonFly), ESP32, STM32, Raspberry Pi Pico (rp2040), and WASM (NodeJS/Web).

Project Status

Build and Test

AtomVM is still in its early stages, but it can run simple applications similar to those available in examples and tests.

AtomVM might crash with a similar message:

Undecoded opcode: 15
Aborted (core dumped)

This basically means that an instruction has not been implemented yet, or that an outdated version has been used. Please, make sure to always run AtomVM using latest version.

Known Limitations

This project is a work in progress, so there are several known limitations, that will prevent to run unmodified software, some of them are:

  • There is a minimal standard library, so several standard functions are missing.
  • Several instructions are not yet implemented.

All of these limitations are going to be fixed in a reasonable amount of time.

About This Project

This project has been created by Davide Bettio, and now is developed from a growing number of contributors.

How to Contribute

Any kind of contribution is welcome, you can either contribute to this repository by improving the virtual machine, the core libraries or the documentation or by contributing to any of the organization repositories.

License

This project is under the terms of the Apache 2.0 license.

atomvm's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

atomvm's Issues

Crash on esp32 with module with missing export

A modification of the blink example module that contains console output crashes on an esp32 (WROOM) if the write/2 function is not exported, which is strange, because that function does seem to be required outside of this module, and the crash is a memory error.

Here is the modified module:

-module (blink2).

%% crashes without export of write/2
-export([start/0, do_open_port/2, set_direction/3, set_level/3]).
%-export([start/0, do_open_port/2, set_direction/3, set_level/3, write/2]).

start() ->
    GPIO = do_open_port("gpio", []),
    set_direction(GPIO, 2, output),
    Console = do_open_port("console", []),
    loop({GPIO, Console}, 0).

loop({GPIO, Console}=Both, 0) ->
    write(Console, "-\n"),
    set_level(GPIO, 2, 0),
    sleep(1000),
    loop(Both, 1);
loop({GPIO, Console}=Both, 1) ->
    write(Console, "+\n"),
    set_level(GPIO, 2, 1),
    sleep(1000),
    loop(Both, 0).

do_open_port(PortName, Param) ->
    open_port({spawn, PortName}, Param).

set_direction(GPIO, GPIONum, Direction) ->
    GPIO ! {self(), set_direction, GPIONum, Direction},
    receive
        Ret ->
            Ret
    end.

set_level(GPIO, GPIONum, Level) ->
    GPIO ! {self(), set_level, GPIONum, Level},
    receive
        Ret ->
            Ret
    end.

sleep(T) ->
    receive
        after T -> ok
    end.

write(Console, String) ->
    Console ! {self(), String},
    receive
        ReturnStatus ->
            ReturnStatus
    end.

Here is the crash:

ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:5864
load:0x40078000,len:9176
load:0x40080000,len:6008
0x40080000: _iram_start at /work/src/github/espressif/esp-idf-v3.1.1/components/freertos/xtensa_vectors.S:1685

entry 0x4008032c
0x4008032c: _KernelExceptionVector at ??:?

I (29) boot: ESP-IDF v3.1.1 2nd stage bootloader
I (29) boot: compile time 13:31:16
I (37) boot: Enabling RNG early entropy source...
I (38) boot: SPI Speed      : 40MHz
I (38) boot: SPI Mode       : DIO
I (42) boot: SPI Flash Size : 4MB
I (46) boot: Partition Table:
I (49) boot: ## Label            Usage          Type ST Offset   Length
I (57) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (64) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (72) boot:  2 factory          factory app      00 00 00010000 00100000
I (79) boot:  3 main.avm         RF data          01 01 00110000 00100000
I (87) boot: End of partition table
I (91) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x14ce0 ( 85216) map
I (129) esp_image: segment 1: paddr=0x00024d08 vaddr=0x3ffb0000 size=0x03934 ( 14644) load
I (135) esp_image: segment 2: paddr=0x00028644 vaddr=0x3ffb3934 size=0x00000 (     0) load
I (136) esp_image: segment 3: paddr=0x0002864c vaddr=0x40080000 size=0x00400 (  1024) load
0x40080000: _iram_start at /work/src/github/espressif/esp-idf-v3.1.1/components/freertos/xtensa_vectors.S:1685

I (146) esp_image: segment 4: paddr=0x00028a54 vaddr=0x40080400 size=0x075bc ( 30140) load
I (167) esp_image: segment 5: paddr=0x00030018 vaddr=0x400d0018 size=0x76720 (485152) map
0x400d0018: _stext at ??:?

I (337) esp_image: segment 6: paddr=0x000a6740 vaddr=0x400879bc size=0x09270 ( 37488) load
0x400879bc: rcUpdateTxDoneAmpdu2 at ??:?

I (353) esp_image: segment 7: paddr=0x000af9b8 vaddr=0x400c0000 size=0x00000 (     0) load
I (353) esp_image: segment 8: paddr=0x000af9c0 vaddr=0x50000000 size=0x00000 (     0) load
I (369) boot: Loaded app from partition at offset 0x10000
I (370) boot: Disabling RNG early entropy source...
I (371) cpu_start: Pro cpu up.
I (375) cpu_start: Starting app cpu, entry point is 0x40080fd8
0x40080fd8: call_start_cpu1 at /work/src/github/espressif/esp-idf-v3.1.1/components/esp32/cpu_start.c:231

I (367) cpu_start: App cpu up.
I (385) heap_init: Initializing. RAM available for dynamic allocation:
I (392) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (398) heap_init: At 3FFB9938 len 000266C8 (153 KiB): DRAM
I (404) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
I (411) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (417) heap_init: At 40090C2C len 0000F3D4 (60 KiB): IRAM
I (423) cpu_start: Pro cpu start user code
I (106) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC      : 0x4000c26c  PS      : 0x00060830  A0      : 0x8012ac56  A1      : 0x3ffbb620  
A2      : 0xbf512928  A3      : 0x3f40e57c  A4      : 0x00000004  A5      : 0x3ffbb6a8  
A6      : 0x00000007  A7      : 0x00000001  A8      : 0x00000001  A9      : 0xbf512928  
A10     : 0x0000ff00  A11     : 0x3ffae95c  A12     : 0x00000000  A13     : 0x3ffbb2f0  
A14     : 0x00000002  A15     : 0x00000001  SAR     : 0x00000004  EXCCAUSE: 0x0000001c  
EXCVADDR: 0xbf512928  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xffffffff  

Backtrace: 0x4000c26c:0x3ffbb620 0x4012ac53:0x3ffbb640 0x4012723f:0x3ffbb670 0x400d27e2:0x3ffbb6d0 0x400d0c36:0x3ffbb700
0x4012ac53: scan_iff at /work/src/github/fadushin/AtomVM/src/libAtomVM/iff.c:61

0x4012723f: module_new_from_iff_binary at /work/src/github/fadushin/AtomVM/src/libAtomVM/module.c:154

0x400d27e2: app_main at /work/src/github/fadushin/AtomVM/src/platforms/esp32/main/main.c:63

0x400d0c36: main_task at /work/src/github/espressif/esp-idf-v3.1.1/components/esp32/cpu_start.c:476


Rebooting...

Minimum hardware requirements (Arduino support?)

Considering that this VM should be small, on what hardware platform is it still possible to run on?
This project looks a little similar to MicroPython from it's possible use case.

  • How much of a performance improvement does AtomVM have over BEAM?
  • Is it possible to run it on microcontrollers, like the Arduino Uno?
  • What features will be omitted from the VM to keep it small?

esp-idf version 3.2 required, otherwise make fails with implicit declaration error

Ubuntu 18.04
esp-idf version 3.1.1, 3.1.4 (both confirmed to work with my ESP32-WROOM-32D, the example hello_world program flashes and outputs as expected to make monitor, both also working with mruby/c)

Trying to follow blogpost
https://medium.com/@Bettio/atomvm-how-to-run-elixir-code-on-a-3-microcontroller-b414773498a6
with a little help from
http://blog.dushin.net/2018/11/running-atomvm-on-macos-and-an-esp32/
(as the Medium blogpost does not mention building AtomVM itself)

The make flash in AtomVM/src/platforms/esp32 fails with:

/home/tomek/esp/elixir-esp32/AtomVM/src/platforms/esp32/main/spidriver.c: In function 'spidriver_transfer_at':
/home/tomek/esp/elixir-esp32/AtomVM/src/platforms/esp32/main/spidriver.c:123:15: error: implicit declaration of function 'spi_device_polling_transmit' [-Werror=implicit-function-declaration]
     int ret = spi_device_polling_transmit(spi_data->handle, &spi_data->transaction);
               ^
/home/tomek/esp/elixir-esp32/AtomVM/src/platforms/esp32/main/spidriver.c:123:9: warning: unused variable 'ret' [-Wunused-variable]
     int ret = spi_device_polling_transmit(spi_data->handle, &spi_data->transaction);
         ^
cc1: some warnings being treated as errors
/home/tomek/esp/esp-idf/make/component_wrapper.mk:285: recipe for target 'spidriver.o' failed
make[1]: *** [spidriver.o] Error 1
/home/tomek/esp/esp-idf/make/project.mk:468: recipe for target 'component-main-build' failed
make: *** [component-main-build] Error 2
tomek@tomek-ux32ln:~/esp/elixir-esp32/AtomVM/src/platforms/esp32$ 

I put the entire build log from make flash on pastebin:
https://pastebin.com/kwesaMjs

I'm not finding spi_device_polling_transmit mention in esp-idf 3.1(.1) docs, it's mentioned in docs for 3.2 onwards, so maybe there is a specific 3.2.x esp-idf version that AtomVM builds with?

What's funny is I got past this one with some weird state of esp-idf master git-checkout'd to v3.1.4, but then it failed later on trying to build a component that was a leftover folder from master (3.3?), not present in 3.1 release.

Throw errors in NIFs

Several NIFs must throw an error when they are called with an invalid parameter or when they cannot accomplish the task, right now they are just returning error atom or NIL term.
Following NIFs need to be changed to behave properly:

  • erlang:list_to_existing_atom/1
  • erlang:binary_to_existing_atom/2

memory leak (possibly related to string/list handling)

there seems to be a memory leak (possibly related to string/list handling).

my test case:

-module (ht).
-export([start/0]).

start() ->
console:puts("start\n"),
loop(0),
console:puts("done\n"),
ok
.

loop(581) ->
console:puts("loop done!\n");
loop(N) ->
S = "N=" ++ integer_to_list(N) ++ "\n",
S = t(S),
loop(N+1)
.

t(S) -> S
.

compile, pack and run under linux as

ulimit -Sv 5127
~/esp-erl/AtomVM/build/src/AtomVM ht.avm

will result in (at least on my machine):
start
loop done!
Aborted ~/esp-erl/AtomVM/build/src/AtomVM ht.avm

replacing 581 by 582 will produce the same result while replacing it by 583 will result in
start
Aborted ~/esp-erl/AtomVM/build/src/AtomVM ${F}.avm

replacing 581 by 580

will result in:
start
loop done!
done
Return value: ok

the behaviour is even worse (lower threshold) when the pgm runs on my esp32.

i would try to find and fix the root cause(s?) of this problem but i'm afraid, i'm not sufficiently familiar with the design principles especially resource flow of atomvm.

compilation for esp32

Hello, I am trying to compile for the esp32. Tried both version of IDF stable and current. Both don't compile the recent version. after running make, I always get:

GENCONFIG * * Restart config... * * * Security features * Require signed app images (SECURE_SIGNED_APPS_NO_SECURE_BOOT) [N/y/?] (NEW) aborted!

Console input/output is redirected. Run 'make oldconfig' to update configuration.

make: *** No rule to make target AtomVM/src/platforms/esp32/build/include/config/auto.conf', needed by AtomVM/src/platforms/esp32/build/bootloader/bootloader.bin'. Stop.

Unable to match structured literals

I am unable to match against literal tuples and lists (except []) when using literal expression in case statements. atoms and numbers seem fine, and trying to match variables that are bound to tuples and lists are fine. It's only when the comparison is with an actual literal that there seems to be an issue.

Example code:

-module(test_list_match).

-export([start/0]).

start() ->
    test_type_match(atom, foo),
    test_type_match(number, 1342),
    test_type_match(tuple, {foo, bar}),
    test_type_match(nil, []),
    test_type_match(list, [foo, bar]),
    case f(foo) of
        foo ->
            erlang:display({ok, literal, foo}),
            ok;
        _ ->
            erlang:display({fail, literal, foo}),
            fail
    end,
    case f(1342) of
        1342 ->
            erlang:display({ok, literal, 1342}),
            ok;
        _ ->
            erlang:display({fail, literal, 1342}),
            fail
    end,
    case f({foo, bar}) of
        {foo, bar} ->
            erlang:display({ok, literal, {foo, bar}}),
            ok;
        _ ->
            erlang:display({fail, literal, {foo, bar}}),
            fail
    end,
    case f([]) of
        [] ->
            erlang:display({ok, literal, []}),
            ok;
        _ ->
            erlang:display({fail, literal, []}),
            fail
    end,
    case f([foo, bar]) of
        [foo, bar] ->
            erlang:display({ok, literal, [foo, bar]}),
            ok;
        _ ->
            erlang:display({fail, literal, [foo, bar]}),
            fail
    end,
    ok.


test_type_match(Type, Datum) ->
    case f(Datum) of
        Datum ->
            erlang:display({ok, Type, Datum}),
            ok;
        _ ->
            erlang:display({fail, Type, Datum}),
            fail
    end.

f(X) -> X.

Example output:

./src/AtomVM test_list_match.beam 
{ok,atom,foo}
{ok,number,1342}
{ok,tuple,{foo,bar}}
{ok,nil,[]}
{ok,list,[foo,bar]}
{ok,literal,foo}
{ok,literal,1342}
{fail,literal,{foo,bar}}
{ok,literal,[]}
{fail,literal,[foo,bar]}
Return value: 28b

whereas under OTP/20:

erl
Erlang/OTP 20 [erts-9.3.3.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]

Eshell V9.3.3.5  (abort with ^G)
1> test_list_match:start().
{ok,atom,foo}
{ok,number,1342}
{ok,tuple,{foo,bar}}
{ok,nil,[]}
{ok,list,[foo,bar]}
{ok,literal,foo}
{ok,literal,1342}
{ok,literal,{foo,bar}}
{ok,literal,[]}
{ok,literal,[foo,bar]}
ok

esp32 - receive with timeout > 9 does never return

the pgm

-module (r).
-export([start/0]).

start() ->
MSecs = 10,
console:puts("good night ..\n"),
receive
after MSecs -> ok
end,
console:puts("good morning\n")
.

will output
Starting: r.beam...

good night ..
E (10305) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
...
which means, the task hangs in an infinite loop without any event,mbx,.. related activity. this seems to bre true for any MSecs > 9.

the same pgm works fine for MSecs < 10. i.e. it will output
Starting: r.beam...

good night ..
good morning

the same pgm works fine in AtomVM running under linux for any MSecs > 0

Add --startup-module/--entry-point switch to PackBEAM

PackBEAM uses the first given .beam module as startup-module. This behavior is error prone and should be fixed. A new switch --startup-module (or maybe --entry-point) is proposed, so the startup module is explicitly set.

Interesting crash on spawn when passing structure with nested list

I can get AtomVM to crash in some edge cases by passing a list in a nested structure to spawn.

Here is a sample program that crashes AtomVM:

-module(test_spawn_list).

-export([start/0, loop/1]).

start() ->
    Pid = spawn(?MODULE, loop, [{foo, {bar, [haddocks, eyes]}}]),
    Pid ! stop,
    0.

loop(List) ->
    receive
        stop -> ok;
        _ -> loop(List)
    end.

The output is:

Seed is 1547674607
-- EXECUTING TEST: test_spawn_list.beam
- Found unknown boxed type: f
Abort trap: 6

The stack trace is:

#3	0x000000010001cfe7 in memory_scan_and_copy at /work/src/github/fadushin/AtomVM/src/libAtomVM/memory.c:336
#4	0x000000010001c9e7 in memory_gc at /work/src/github/fadushin/AtomVM/src/libAtomVM/memory.c:115
#5	0x000000010001c529 in mailbox_peek at /work/src/github/fadushin/AtomVM/src/libAtomVM/mailbox.c:98
#6	0x0000000100008a77 in context_execute_loop at /work/src/github/fadushin/AtomVM/src/libAtomVM/opcodesswitch.h:1219
#7	0x0000000100001355 in test_modules_execution at /work/src/github/fadushin/AtomVM/tests/test.c:256
#8	0x0000000100001514 in main at /work/src/github/fadushin/AtomVM/tests/test.c:291

We hit the abort statement in this part of the code in memory_scan_and_copy :

        } else if ((t & 0x3) == 0x0) {
            TRACE("Found boxed header (%lx)\n", t);

            switch (t & TERM_BOXED_TAG_MASK) {
                case TERM_BOXED_TUPLE: {
                    int arity = term_get_size_from_boxed_header(t);
                    TRACE("- Boxed is tuple (%lx), arity: %i\n", t, arity);

                    for (int i = 1; i <= arity; i++) {
                        TRACE("-- Elem: %lx\n", ptr[i]);
                        ptr[i] = memory_shallow_copy_term(ptr[i], &new_heap, move);
                    }
                    break;
                }

                case TERM_BOXED_REF:
                    TRACE("- Found ref.\n");
                    break;

                case TERM_BOXED_HEAP_BINARY:
                    TRACE("- Found binary.\n");
                    break;

                default:
                    fprintf(stderr, "- Found unknown boxed type: %lx\n", (t >> 2) & 0xF);
                    abort(); <-- here
            }

            ptr += term_get_size_from_boxed_header(t) + 1;

        } else if (term_is_nonempty_list(t)) {

esp32 - Hardware Interrupts

It would be nice if one could use hardware interrupts (timer, I/O-Pins...) to invoke Erlang functions. Is it intended to do it or is it already implemented?

Port to STM32

STM32 are powerful 32 bits MCUs, some of them have enough resources to run AtomVM.
It would be nice to have support for them.

Minimum requirement RAM 256k?

Hi,

I am guessing with this minimum requirement, it would not be possible to port AtomVM to run on an ESP8266 ?

Regards,
Zinahe A.

Consider Gimli as general hash function

Hi just found this cool project, good work. One of the dependency is GNU Perfect Hash Function, I'm not sure the use for it, whether its the general hash function used internally for phash/1 or phash2/1, or for other purpose.
I have discovered a gimli permutation function used in libhydrogen
A good selling point for gimli is that it perform well across all hardware spec, in their reference implementation cortex-M3 and cortex-M4 is included(only depend on libopencm3)
Make use of it could shrink the dependency even further

bifs_hash.h not found

Hello! I've been trying to use AtomVM, but at compile time I get this error:

src/libAtomVM/bif.c:30:23: fatal error: bifs_hash.h: No such file or directory
compilation terminated.

I'm unsure where bifs_hash comes from.

cross-compile for linux-mips

Hi! I have a question - can I cross-compile it for using in linux under mips arch ?
I've tried to build it using debian distro in qemu emulator avm files making fails ...
I've also tried to use cross-compiler toolchains but I received SEGFAULT when tried to run it using qemu-mips ....

SEGV in console port

Consider the following module, console.erl:

-module(console).

-export([start/0, puts/1, puts/2]).

puts(String) ->
    puts(get_pid(), String).

puts(Console, String) ->
    call(Console, String).

%% Internal operations

call(Console, Msg) ->
    Console ! {self(), Msg},
    receive
        ok -> ok
    end.

get_pid() ->
    case whereis(console) of
        undefined ->
            start();
        Pid when is_pid(Pid) -> Pid;
        WTF ->
            erlang:display(WTF),
            WTF
    end.

start() ->
    Pid = erlang:open_port({spawn, "console"}, []),
    erlang:register(console, Pid),
    Pid.

The following program will crash AtomVM:

-module(pingpong).

-export([start/0, ping/2, pong/1]).

start() ->
    Console = console:start(),
    Pong = spawn(pingpong, pong, [Console]),
    spawn(pingpong, ping, [Console, Pong]),
    receive
        ok -> ok
    end.


ping(Console, Pong) ->
    Pong ! {self(), ping},
    receive
        {Pong, pong} -> 
            console:puts("+"),
            %console:puts(Console, "+"),
            ping(Console, Pong)
    end.


pong(Console) ->
    receive
        {Ping, ping} -> 
            console:puts("-"),
            %console:puts(Console, "-"),
            Ping ! {self(), pong},
            pong(Console)
    end.

Example:

18:50:34 spock:/work/src/github/fadushin/AtomVM-incremental/examples/erlang> ../../build/tools/packbeam/PackBEAM -l  pingpong.avm
pingpong.beam *
console.beam 
18:50:42 spock:/work/src/github/fadushin/AtomVM-incremental/examples/erlang> ../../build/src/AtomVM pingpong.avm 
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+console
term is not a pid: 8b
Segmentation fault: 11

The crash is in:

            //TODO: implement send/0
            case OP_SEND: {
                #ifdef IMPL_CODE_LOADER
                    TRACE("send/0\n");
                #endif

                #ifdef IMPL_EXECUTE_LOOP
                    int local_process_id = term_to_local_process_id(ctx->x[0]);
                    TRACE("send/0 target_pid=%i\n", local_process_id);
                    Context *target = globalcontext_get_process(ctx->global, local_process_id);

                    mailbox_send(target, ctx->x[1]);
                #endif

                NEXT_INSTRUCTION(1);
                break;
            }

after term_to_local_process_id returns 0 because register x[0] is not valid pid.

The program does not crash if a reference to the Console pid is maintained by the application, and not acquired via the process registry (see commented out lines in pingpong module).

Implement socket driver

Implement a driver suitable for implementation of TCP and UDP protocols on all supported platforms.

This task may also include a partial implementation of gen_udp and get_tcp modules that use the driver to support these protocols.

Improve memory handling in port drivers

Memory handling in port drivers is quite complicated, and accidental GCs might trigger bugs due to invalid terms and issues such as the one described in #21 are likely to happen.
A different easier to use approach should be designed.

sending message to a registered process via it's name does not work.

running the pgm

-module(pr).
-export([start/0, ping/1, pong/0]).
ping(0) ->
pong ! finished,
erlang:display("ping finished");

ping(N) ->
erlang:display("ping started"),
pong ! {ping, self()},
erlang:display("ping sent"),
receive
pong ->
erlang:display("Ping received pong")
end,
ping(N - 1).

pong() ->
erlang:display("pong started"),
receive
finished ->
erlang:display("Pong finished");
{ping, Ping_PID} ->
erlang:display("Pong received ping"),
Ping_PID ! pong,
pong()
end.

start() ->
register(pong, spawn(pr, pong, [])),
spawn(pr, ping, [3]).

results in:
"pong started"
"ping started"
term is not a pid: a4b
"ping sent"
Hang detected

after replacing

pong ! Msg,

by

P = whereis(pong), P ! Msg,

the expected output
"pong started"
"ping started"
"ping sent"
"Pong received ping"
"pong started"
"Ping received pong"
"ping started"
"ping sent"
"Pong received ping"
"pong started"
"Ping received pong"
"ping started"
"ping sent"
"Pong received ping"
"pong started"
"Ping received pong"
"ping finished"
"Pong finished"
Return value: <0.3.0>

is displayed.

Link fails on CentOS6

The linker is unable to resolve clock-gettime with the default linker flags on CentOS 6.

12:12:02 marx:/work/src/github/fadushin/AtomVM/build> echo ${CC}
clang
12:15:37 marx:/work/src/github/fadushin/AtomVM/build> clang --version
clang version 3.4.2 (tags/RELEASE_34/dot2-final)
Target: x86_64-redhat-linux-gnu
Thread model: posix
12:15:47 marx:/work/src/github/fadushin/AtomVM/build> make
[ 1%] Built target generated-nifs-hash
[ 1%] Built target generated
[ 11%] Built target libAtomVM
Linking C executable AtomVM
CMakeFiles/AtomVM.dir/platforms/Linux/sys.c.o: In function sys_waitevents': /work/src/github/fadushin/AtomVM/src/platforms/Linux/sys.c:(.text+0x1b): undefined reference to clock_gettime'
/work/src/github/fadushin/AtomVM/src/platforms/Linux/sys.c:(.text+0x320): undefined reference to clock_gettime' CMakeFiles/AtomVM.dir/platforms/Linux/sys.c.o: In function sys_set_timestamp_from_relative_to_abs':
/work/src/github/fadushin/AtomVM/src/platforms/Linux/sys.c:(.text+0x43b): undefined reference to clock_gettime' CMakeFiles/AtomVM.dir/platforms/Linux/sys.c.o: In function sys_time':
/work/src/github/fadushin/AtomVM/src/platforms/Linux/sys.c:(.text+0x4e8): undefined reference to `clock_gettime'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [src/AtomVM] Error 1
make[1]: *** [src/CMakeFiles/AtomVM.dir/all] Error 2
make: *** [all] Error 2

Adding rt to the end of the linker flags seems to fix this.

memory leak?

the program

-module (ri).
-export([start/0 ]).

repeat(_,0) -> ok;
repeat(F,N) -> F(N), repeat(F, N - 1).

start() -> repeat(
fun(N) ->
ok=test({N})
end,
10000000),
ok
.

test(_S) -> ok.

when run under linux as
ulimit -Sv 5127
~/esp-erl/AtomVM/build/src/AtomVM ri.avm

results in
7324 Aborted ~/esp-erl/AtomVM/build/src/AtomVM ri.avm

examinig the stack trace using gdb yields

gdb ~/esp-erl/AtomVM/build/src/AtomVM core
..
(gdb) bt
#0 0x00007f8ce8b8d8bb in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007f8ce8b78535 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x0000556b1aae487a in context_execute_loop (ctx=0x556b1adfff70, mod=0x556b1ae00b70, function_name=0x556b1ab0d070 "start", arity=0)
at /home/splge/esp-erl/AtomVM/src/libAtomVM/opcodesswitch.h:1171
#3 0x0000556b1aadf659 in main (argc=2, argv=0x7fffda875728) at /home/splge/esp-erl/AtomVM/src/main.c:86

replacing ok=test({N}) by ok=test([N]) produces exactly the same result.

replacing 10000000 by 1000 or replacing test[{N}) by test(N) results in Return value: ok

esp32 - wrong include paths

The recent esp32-snapshots (Toolchain version: esp32-2019r1) require 2 include-File changes in order to compile AtomVM for esp32:

AtomVM/src/platforms/esp32/main:

sys.c

#include <posix/sys/socket.h>
 -> #include "absolute path to $(IDF_PATH)/components/lwip/lwip/src/include/compat/posix/sys/socket.h"

network_driver.c:

#include <lwip/apps/sntp.h>
--> #include <apps/sntp/sntp.h>

There is a component.mk in the components/libatomvm where a INCLUDE_FIXUP_DIR could be set. Obviously this does not affect the generation in main. The component.mk file in main however is empty. Maybe one could specify the Include-Corrections there

esp32 - get_tcp

Is it already possible to create a tcp (gen_tcp) server in AtomVM?
What would be necessary to implement it?

receive related memory leak

the pgm

-module(nx).

-export([start/0]).

repeat(_,0) -> ok;
repeat(F,N) -> F(), repeat(F, N - 1).

start() ->
Nrep = 10000000,
repeat(
fun() -> receive after 1 -> ok end end,
Nrep)
.

results in

Failed to allocate memory: {Atom_HOME}/src/libAtomVM/scheduler.c:68.
./gen.sh: line 17: 13623 Aborted ${ATOM_BIN}/AtomVM nx.avm

when run (under linux) as

ulimit -Sv 10850
${ATOM_BIN}/AtomVM nx.avm

changing Nrep to 1000 leads to normal pgm termination (Return value: ok).

Add support to OTP21

OTP21 is not yet supported, implement all the required features to run applications built using OTP21.

Following issues must be closed to support OTP21:

Implement get_hd and get_tl

Code built with OTP21 has get_tl (opcode 163) and get_hd (opcode 162) in it.
Implement those instructions to run BEAM files built with OTP21.

Memory reservation

Systems such as ESP32 are heavily memory constrained, it is required to have a memory reservation system to ensure a minimum memory amount to important tasks. Also some systems have both fast and slow memory areas, so a good memory allocation system should manage them.

SEGV on list creation

The following code will crash with a SEGV:

-module(test_make_list).

-export([start/0]).

start() ->
    length(make_list(a,b,c)) + 2.


make_list(A, B, C) ->
    [{foo, "bar"}, {gnu, "gnat"}, {a, A}, {b, B}, {c, C}].

Here is the intermediate byte code (.S):


{exports, [{module_info,0},{module_info,1},{start,0}]}.

{attributes, []}.

{labels, 9}.


{function, start, 0, 2}.
  {label,1}.
    {line,[{location,"../tests/erlang_tests/test_make_list.erl",5}]}.
    {func_info,{atom,test_make_list},{atom,start},0}.
  {label,2}.
    {allocate,0,0}.
    {move,{atom,b},{x,1}}.
    {move,{atom,c},{x,2}}.
    {move,{atom,a},{x,0}}.
    {line,[{location,"../tests/erlang_tests/test_make_list.erl",6}]}.
    {call,3,{f,4}}.
    {line,[{location,"../tests/erlang_tests/test_make_list.erl",6}]}.
    {gc_bif,length,{f,0},1,[{x,0}],{x,0}}.
    {line,[{location,"../tests/erlang_tests/test_make_list.erl",6}]}.
    {gc_bif,'+',{f,0},1,[{x,0},{integer,2}],{x,0}}.
    {deallocate,0}.
    return.


{function, make_list, 3, 4}.
  {label,3}.
    {line,[{location,"../tests/erlang_tests/test_make_list.erl",9}]}.
    {func_info,{atom,test_make_list},{atom,make_list},3}.
  {label,4}.
    {test_heap,19,3}.
    {put_tuple,2,{x,3}}.
    {put,{atom,c}}.
    {put,{x,2}}.
    {put_list,{x,3},nil,{x,2}}.
    {put_tuple,2,{x,3}}.
    {put,{atom,b}}.
    {put,{x,1}}.
    {put_list,{x,3},{x,2},{x,1}}.
    {put_tuple,2,{x,2}}.
    {put,{atom,a}}.
    {put,{x,0}}.
    {put_list,{x,2},{x,1},{x,0}}.
    {put_list,{literal,{gnu,"gnat"}},{x,0},{x,1}}.
    {put_list,{literal,{foo,"bar"}},{x,1},{x,0}}.
    return.


{function, module_info, 0, 6}.
  {label,5}.
    {line,[]}.
    {func_info,{atom,test_make_list},{atom,module_info},0}.
  {label,6}.
    {move,{atom,test_make_list},{x,0}}.
    {line,[]}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 8}.
  {label,7}.
    {line,[]}.
    {func_info,{atom,test_make_list},{atom,module_info},1}.
  {label,8}.
    {move,{x,0},{x,1}}.
    {move,{atom,test_make_list},{x,0}}.
    {line,[]}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

Here is the execution with instruction-level tracing:

-- Loading code
label/1 label=1
Mark label 1 here at 0
line/1: 1
func_info/3 module_name_a=1, function_name_a=2, arity=0
label/1 label=2
Mark label 2 here at 8
allocate/2 stack_need=0, live=0
move/2
move/2
move/2
line/1: 2
call/2, arity=3, label=4
line/1: 2
gc_bif1/5
line/1: 2
gc_bif2/6
deallocate/1 n_words=0
return/0
label/1 label=3
Mark label 3 here at 47
line/1: 3
func_info/3 module_name_a=1, function_name_a=9, arity=3
label/1 label=4
Mark label 4 here at 55
test_heap/2 heap_need=19, live_registers=3
put_tuple/2 size=2, dest=x3
put/2
put/2
op_put_list/3
put_tuple/2 size=2, dest=x3
put/2
put/2
op_put_list/3
put_tuple/2 size=2, dest=x2
put/2
put/2
op_put_list/3
op_put_list/3
op_put_list/3
return/0
label/1 label=5
Mark label 5 here at 105
line/1: 0
func_info/3 module_name_a=1, function_name_a=10, arity=0
label/1 label=6
Mark label 6 here at 113
move/2
line/1: 0
call_ext_only/2, arity=1, index=2
label/1 label=7
Mark label 7 here at 123
line/1: 0
func_info/3 module_name_a=1, function_name_a=10, arity=1
label/1 label=8
Mark label 8 here at 131
move/2
move/2
line/1: 0
call_ext_only/2, arity=2, index=3
int_call_end!
-- Code loading finished --
-- Executing code
label/1 label=2
allocate/2 stack_need=0, live=0
move/2 8b, x1
move/2 cb, x2
move/2 10b, x0
line/1: 2
call/2, arity=3, label=4
label/1 label=4
test_heap/2 heap_need=19, live_registers=3
put_tuple/2 size=2, dest=x3
put/2 elem=0, value=0xcb
put/2 elem=1, value=0xcb
op_put_list/3
put_tuple/2 size=2, dest=x3
put/2 elem=0, value=0x8b
put/2 elem=1, value=0x8b
op_put_list/3
put_tuple/2 size=2, dest=x2
put/2 elem=0, value=0x10b
put/2 elem=1, value=0x10b
op_put_list/3
op_put_list/3

Here is the stack trace:

(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x109e78f98)
  * frame #0: 0x000000010001c028 AtomVM`term_list_init_prepend(list_elem=0x0000000109e78f98, head=4461153994, tail=4461153873) at term.h:804
    frame #1: 0x0000000100013d9d AtomVM`context_execute_loop(ctx=0x0000000109abfed0, mod=0x0000000109a70f90, function_name="start", arity=0) at opcodesswitch.h:2210
    frame #2: 0x000000010000137e AtomVM`main(argc=2, argv=0x00007ffeefbff060) at main.c:86
    frame #3: 0x00007fff7e6e5ed9 libdyld.dylib`start + 1

AtomVM crashes

the code below crashes AtomVM at least when running on an esp32 leaving the message

Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: hello_world.beam...

Unexpected 0 term.
abort() was called at PC 0x4012223f on core 0
0x4012223f: memory_copy_term_tree at /home/splge/esp-erl/AtomVM/src/libAtomVM/memory.c:180

%% crashing code begin --

-module (hello_world).
-export([start/0, do_open_port/2, write/2]).

say_hello(Console, 10) ->
result_to_int(write(Console, "done\n"))
;
say_hello(Console, N) ->
result_to_int(write(Console, "Hello World\n")),
say_hello(Console, N - 1)
.

start() ->
Console = do_open_port("console", []),
say_hello(Console, 1)
.

do_open_port(PortName, Param) ->
open_port({spawn, PortName}, Param).

write(Console, String) ->
Console ! {self(), String},
receive
ReturnStatus ->
ReturnStatus
end.

result_to_int(ok) ->
10;
result_to_int(_) ->
20.

%% crashing code end --

remark: when the initial call is say_hello(Console, 0) or when i d not call result_to_int(write(Console, "Hello World\n")) in say_hello(Console, N) i do not observe the crash.

Web assembly

screenshot_20181207_012539

Here is what I did:
Apply this patch with patch -p1 < patch

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b339d48..83d21fc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ project (AtomVM)
 
 add_subdirectory(src)
 add_subdirectory(tests)
-add_subdirectory(tools/packbeam)
+# add_subdirectory(tools/packbeam)
 
 if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
     set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b8fc83f..f152599 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -32,13 +32,15 @@ set(
 if(CMAKE_COMPILER_IS_GNUCC)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -ggdb")
 endif()
+# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s FORCE_FILESYSTEM=1 -s ENVIRONMENT=node --preload-file hello_world.beam")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -v --embed-file hello_world.beam")
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR} libAtomVM/)
 
-add_executable(AtomVM main.c)
-target_link_libraries(AtomVM libAtomVM libAtomVM${PLATFORM_LIB_SUFFIX})
-set_property(TARGET AtomVM PROPERTY C_STANDARD 99)
+add_executable(AtomVM.html main.c)
+target_link_libraries(AtomVM.html libAtomVM libAtomVM${PLATFORM_LIB_SUFFIX})
+set_property(TARGET AtomVM.html PROPERTY C_STANDARD 99)
 
 if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
-    set_target_properties(AtomVM PROPERTIES COMPILE_FLAGS "-O0 -fprofile-arcs -ftest-coverage")
+    set_target_properties(AtomVM.html PROPERTIES COMPILE_FLAGS "-O0 -fprofile-arcs -ftest-coverage")
 endif()
diff --git a/src/libAtomVM/CMakeLists.txt b/src/libAtomVM/CMakeLists.txt
index 3fc09f7..252efe1 100644
--- a/src/libAtomVM/CMakeLists.txt
+++ b/src/libAtomVM/CMakeLists.txt
@@ -84,3 +84,4 @@ set_property(TARGET libAtomVM PROPERTY C_STANDARD 99)
 if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
     set_target_properties(libAtomVM PROPERTIES COMPILE_FLAGS "-O0 -fprofile-arcs -ftest-coverage")
 endif()
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${HOME}/dev/zlib")

Run this to get the make files
emconfigure cmake -D CMAKE_C_COMPILER=emcc -D CMAKE_CXX_COMPILER=emcc -D ZLIB_INCLUDE_DIR=$HOME/dev/zlib -D ZLIB_LIBRARY=$HOME/dev/zlib/libz.a .

Then run:
emmake make

Then add arguments: ['/hello_world.beam'], to Atom.html after var Module = {

This is such a cool project ;)

esp32 - blinky.avm not running

I am not getting any payload program running.

Followed:
http://blog.dushin.net/2018/11/running-atomvm-on-macos-and-an-esp32/

What is working:

  • AtomVM build works
  • Flashing of bootloader works
  • Compiling and packbin of blink.erl yields blink.avm
  • Flashing of a compiled blink.avm with the esptool.py seems to work but...
  • program is never started

In Serial monitor I see:
Starting avm_calendar.beam... ... No start/0 function found. AtomVM finished with return value = [] going to sleep forever

I am sure I have a AtomVM on the board but for some reason the target program is never executed.
Hints?

`### rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:6456
load:0x40078000,len:12200
load:0x40080400,len:6640
entry 0x40080774
I (29) boot: ESP-IDF v4.1-dev-141-ga7e8d87d3 2nd stage bootloader
I (29) boot: compile time 23:42:07
I (29) boot: Enabling RNG early entropy source...
I (35) boot: SPI Speed : 40MHz
I (39) boot: SPI Mode : DIO
I (43) boot: SPI Flash Size : 4MB
I (47) boot: Partition Table:
I (51) boot: ## Label Usage Type ST Offset Length
I (58) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (66) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (73) boot: 2 factory factory app 00 00 00010000 00100000
I (81) boot: 3 main.avm RF data 01 01 00110000 00100000
I (88) boot: End of partition table
I (92) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x19854 (104532) map
I (139) esp_image: segment 1: paddr=0x0002987c vaddr=0x3ffb0000 size=0x038a0 ( 14496) load
I (145) esp_image: segment 2: paddr=0x0002d124 vaddr=0x40080000 size=0x00400 ( 1024) load
0x40080000: _WindowOverflow4 at /home/florian/esp/esp-idf/components/freertos/xtensa_vectors.S:1778
I (146) esp_image: segment 3: paddr=0x0002d52c vaddr=0x40080400 size=0x02ae4 ( 10980) load
I (159) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x88658 (558680) map
0x400d0018: _stext at ??:?
I (363) esp_image: segment 5: paddr=0x000b8678 vaddr=0x40082ee4 size=0x10350 ( 66384) load
0x40082ee4: spi_flash_restore_cache at /home/florian/esp/esp-idf/components/spi_flash/cache_utils.c:285
I (403) boot: Loaded app from partition at offset 0x10000
I (403) boot: Disabling RNG early entropy source...
I (404) cpu_start: Pro cpu up.
I (407) cpu_start: Application information:
I (412) cpu_start: Project name: atomvvm-esp32
I (417) cpu_start: App version: 5e31347-dirty
I (423) cpu_start: Compile time: Sep 5 2019 23:42:12
I (429) cpu_start: ELF file SHA256: b2f317d9bd3d1a10...
I (435) cpu_start: ESP-IDF: v4.1-dev-141-ga7e8d87d3
I (441) cpu_start: Starting app cpu, entry point is 0x40081340
0x40081340: call_start_cpu1 at /home/florian/esp/esp-idf/components/esp32/cpu_start.c:280
I (0) cpu_start: App cpu up.
I (452) heap_init: Initializing. RAM available for dynamic allocation:
I (459) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (465) heap_init: At 3FFB9850 len 000267B0 (153 KiB): DRAM
I (471) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (477) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (484) heap_init: At 40093234 len 0000CDCC (51 KiB): IRAM
I (490) cpu_start: Pro cpu start user code
I (508) spi_flash: detected chip: generic
I (509) spi_flash: flash io: dio
I (509) cpu_start: Chip Revision: 1
W (510) cpu_start: Chip revision is higher than the one configured in menuconfig. Suggest to upgrade it.
I (521) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Found AVM partition: size: 1048576, address: 0x110000
Booting file mapped at: 0x3f420000, size: 1048576
Starting: avm_calendar.beam...

No start/0 function found.
AtomVM finished with return value = []
going to sleep forever..

`

Reimplement garbage collector

Current garbage collector implementation looks a bit buggy and it is not easy to maintain, a copy GC and a generational GC will be evaluated and implemented.

Support on Android/Termux

Termux is a terminal app on android which recreates a linux environment similar to Debian.

Howerver, there is an issue with building it on android as explained on this post:

AtomVM requires a normal zlib (not /system/lib/libz.so). It mentioned as optional though, but build fails without it.

Add support to funs

Funs are not yet supported and they are a really important core feature in Elixir language.
Add support to them.

esp32 - tcp_server_blink example

Hello,

Hooray, I observed that in the meantime tcp_server-Example is available.
Is it already operational? When connecting with browser Im getting a reboot:

`I (2071) NETWORK: SYSTEM_EVENT_STA_CONNECTED received.
I (4761) event: sta ip: 192.168.2.104, mask: 255.255.255.0, gw: 192.168.2.1
I (4761) NETWORK: SYSTEM_EVENT_STA_GOT_IP: 192.168.2.104
connected
{{192,168,2,104},{255,255,255,0},{192,168,2,1}}
Listening on port 44404.
Waiting to accept connection...
Accepted connection.
Waiting to receive data...
Waiting to accept connection...
<<71,69,84,32,47,32,72,84,84,80,47,49,46,49,13,10,72,111,115,116,58,32,49,57,50,46,49,54,56,46,50,46,49,48,52,58,52,52,52,48,52,13,10,85,115,101,114,45,65,103,101,110,116,58,32,77,111,122,105,108,108,97,47,53,46,48,32,40,88,49,49,59,32,85,98,117,110,116,117,59,32,76,105,110,117,120,32,120,56,54,95,54,52,59,32,114,118,58,54,56,46,48,41,32,71,101,99,107,111,47,50,48,49,48,48,49,48,49,32,70,105,114,101,102,111,120,47,54,56,46,48,13,10,65,99,99,101,112,116,58,32,116,101,120,116,47,104,116,109,108,44,97,112,112,108,105,99,97,116,105,111,110,47,120,104,116,109,108,43,120,109,108,44,97,112,112,108,105,99,97,116,105,111,110,47,120,109,108,59,113,61,48,46,57,44,42,47,42,59,113,61,48,46,56,13,10,65,99,99,101,112,116,45,76,97,110,103,117,97,103,101,58,32,100,101,44,101,110,45,85,83,59,113,61,48,46,55,44,101,110,59,113,61,48,46,51,13,10,65,99,99,101,112,116,45,69,110,99,111,100,105,110,103,58,32,103,122,105,112,44,32,100,101,102,108,97,116,101,13,10,68,78,84,58,32,49,13,10,67,111,110,110,101,99,116,105,111,110,58,32,107,101,101,112,45,97,108,105,118,101,13,10,85,112,103,114,97,100,101,45,73,110,115,101,99,117,114,101,45,82,101,113,117,101,115,116,115,58,32,49,13,10,80,114,97,103,109,97,58,32,110,111,45,99,97,99,104,101,13,10,67,97,99,104,101,45,67,111,110,116,114,111,108,58,32,110,111,45,99,97,99,104,101,13,10,13,10>>
Sending packet back to client...
Waiting to receive data...
warning: event descriptor not found
abort() was called at PC 0x4014ae2d on core 1
0x4014ae2d: socket_callback at /home/florian/AtomVM/src/platforms/esp32/main/socket_driver.c:90

Backtrace: 0x40091780:0x3ffbe500 0x400919ad:0x3ffbe520 0x4014ae2d:0x3ffbe540 0x40149ba1:0x3ffbe570 0x4013ab21:0x3ffbe590 0x40140ae3:0x3ffbe5b0 0x401455e5:0x3ffbe5d0 0x40135c77:0x3ffbe5f0 0x4008e841:0x3ffbe620
0x40091780: invoke_abort at /home/florian/esp/esp-idf/components/esp32/panic.c:707

0x400919ad: abort at /home/florian/esp/esp-idf/components/esp32/panic.c:707

0x4014ae2d: socket_callback at /home/florian/AtomVM/src/platforms/esp32/main/socket_driver.c:90

0x40149ba1: recv_tcp at /home/florian/esp/esp-idf/components/lwip/lwip/src/api/api_msg.c:1447 (discriminator 6)

0x4013ab21: tcp_input at /home/florian/esp/esp-idf/components/lwip/lwip/src/core/tcp_in.c:435 (discriminator 1)

0x40140ae3: ip4_input at /home/florian/esp/esp-idf/components/lwip/lwip/src/core/ipv4/ip4.c:746

0x401455e5: ethernet_input at /home/florian/esp/esp-idf/components/lwip/lwip/src/netif/ethernet.c:184

0x40135c77: tcpip_thread at /home/florian/esp/esp-idf/components/lwip/lwip/src/api/tcpip.c:483

0x4008e841: vPortTaskWrapper at /home/florian/esp/esp-idf/components/freertos/port.c:403

Rebooting...
ets Jun 8 2016 00:22:57
`

Multi-core?

Since most ESP32s have 2 cores, does AtomVM support parallel execution?

Port to Windows

Windows is not yet supported, it would be useful to have AtomVM on Windows.

Implement packBEAM in erlang

packBEAM should be also implemented in Erlang, so it can be integrated into existing Erlang and Elixir tooling in a easier way.

Sending a nested tuple crashes AtomVM (abort)

Note: I have simplified the test case somewhat.

The following code will crash AtomVM (GitHub hash 30839c0):

-module(test_send).

-export([start/0, waitforit/0]).

start() ->
    Pid = spawn(test_send, waitforit, []),
    Self = self(),
    % sendit(Pid, foo),
    % sendit(Pid, {}),
    % sendit(Pid, {foo}),
    % sendit(Pid, {foo, bar}),
    % sendit(Pid, {foo, {gnu, gnat}}),
    % sendit(Pid, {Self, foo}),
    % sendit(Pid, {Self, []}),
    %% ^^ pass
    %% vv fail
    sendit(Pid, {Self, {}}),
    % sendit(Pid, {Self, {a}}),
    % sendit(Pid, {Self, [a]}),
    ok.


sendit(Pid, Msg) ->
    Pid ! Msg,
    receive after 100 -> ok end.

waitforit() ->
    receive
        Msg -> erlang:display(Msg)
    end,
    waitforit().

With the stack trace:

#2	0x00007fff583451c9 in abort ()
#3	0x000000010001a2be in term_put_tuple_element at /work/src/github/fadushin/AtomVM/src/libAtomVM/term.h:619
#4	0x0000000100014873 in context_execute_loop at /work/src/github/fadushin/AtomVM/src/libAtomVM/opcodesswitch.h:2054
#5	0x0000000100001a8e in main at /work/src/github/fadushin/AtomVM/src/main.c:84
#6	0x00007fff5829ced9 in start ()
#7	0x00007fff5829ced9 in start ()

Code location at crash:

static inline void term_put_tuple_element(term t, uint32_t elem_index, term put_value)
{
    if (UNLIKELY(!term_is_boxed(t))) {
        abort();
    }

    term *boxed_value = term_to_term_ptr(t);
    if ( ((boxed_value[0] & 0x3F) == 0) && (elem_index < (boxed_value[0] >> 6)) )  {
        boxed_value[elem_index + 1] = put_value;
    } else {
        abort(); <-- crash here
    }
}

Local variables in context_execute_loop:

ctx	Context *	0x1007047a0	0x00000001007047a0
mod	Module *	0x100616c90	0x0000000100616c90
function_name	const char *	"start"	0x000000010003449e
arity	int	0
code	uint8_t *	"\x01\x10\x99\x10\x02\x12\""	0x00000001004d7090
i	unsigned int	33
function_len	int	5
tmp_atom_name	uint8_t *	"\x03"	0x0000000100708160
label	int	2
remaining_reductions	int	1023
next_off	int	8
size	int	2
dreg	int	2
dreg_type	uint8_t	'\x03'
t	term	4302326418
j	int	1
put_value	term	4345299626

Here are the compiled BEAM instructions for this code:

{module, test_send}.  %% version = 0

{exports, [{module_info,0},{module_info,1},{start,0},{waitforit,0}]}.

{attributes, []}.

{labels, 15}.


{function, start, 0, 2}.
  {label,1}.
    {line,[{location,"test_send.erl",5}]}.
    {func_info,{atom,test_send},{atom,start},0}.
  {label,2}.
    {allocate,0,0}.
    {move,{atom,waitforit},{x,1}}.
    {move,nil,{x,2}}.
    {move,{atom,test_send},{x,0}}.
    {line,[{location,"test_send.erl",6}]}.
    {call_ext,3,{extfunc,erlang,spawn,3}}.
    {test_heap,3,1}.
    {bif,self,{f,0},[],{x,1}}.
    {put_tuple,2,{x,2}}.
    {put,{x,1}}.
    {put,{literal,{}}}.
    {move,{x,2},{x,1}}.
    {line,[{location,"test_send.erl",17}]}.
    {call,2,{f,4}}.
    {move,{atom,ok},{x,0}}.
    {deallocate,0}.
    return.


{function, sendit, 2, 4}.
  {label,3}.
    {line,[{location,"test_send.erl",23}]}.
    {func_info,{atom,test_send},{atom,sendit},2}.
  {label,4}.
    {allocate,0,2}.
    {line,[{location,"test_send.erl",24}]}.
    send.
    {line,[{location,"test_send.erl",25}]}.
  {label,5}.
    {wait_timeout,{f,5},{integer,100}}.
    timeout.
    {move,{atom,ok},{x,0}}.
    {deallocate,0}.
    return.


{function, waitforit, 0, 7}.
  {label,6}.
    {line,[{location,"test_send.erl",27}]}.
    {func_info,{atom,test_send},{atom,waitforit},0}.
  {label,7}.
    {allocate,0,0}.
    {line,[{location,"test_send.erl",28}]}.
  {label,8}.
    {loop_rec,{f,9},{x,0}}.
    remove_message.
    {line,[{location,"test_send.erl",29}]}.
    {call_ext,1,{extfunc,erlang,display,1}}.
    {jump,{f,10}}.
  {label,9}.
    {wait,{f,8}}.
  {label,10}.
    {call_last,0,{f,7},0}.


{function, module_info, 0, 12}.
  {label,11}.
    {line,[]}.
    {func_info,{atom,test_send},{atom,module_info},0}.
  {label,12}.
    {move,{atom,test_send},{x,0}}.
    {line,[]}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 14}.
  {label,13}.
    {line,[]}.
    {func_info,{atom,test_send},{atom,module_info},1}.
  {label,14}.
    {move,{x,0},{x,1}}.
    {move,{atom,test_send},{x,0}}.
    {line,[]}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

And here is the output when instruction-level tracing is enabled:

-- Loading code
label/1 label=1
Mark label 1 here at 0
line/1: 1
func_info/3 module_name_a=1, function_name_a=2, arity=0
label/1 label=2
Mark label 2 here at 8
allocate/2 stack_need=0, live=0
move/2
move/2
move/2
line/1: 2
call_ext/2, arity=3, index=0
test_heap/2 heap_need=3, live_registers=1
bif0/2 bif=1, dreg=1
put_tuple/2 size=2, dest=x2
put/2
put/2
move/2
line/1: 3
call/2, arity=2, label=4
move/2
deallocate/1 n_words=0
return/0
label/1 label=3
Mark label 3 here at 55
line/1: 4
func_info/3 module_name_a=1, function_name_a=8, arity=2
label/1 label=4
Mark label 4 here at 63
allocate/2 stack_need=0, live=2
line/1: 5
send/0
line/1: 6
label/1 label=5
Mark label 5 here at 73
wait_timeout/2, label: 5
timeout/0
move/2
deallocate/1 n_words=0
return/0
label/1 label=6
Mark label 6 here at 86
line/1: 7
func_info/3 module_name_a=1, function_name_a=3, arity=0
label/1 label=7
Mark label 7 here at 94
allocate/2 stack_need=0, live=0
line/1: 8
label/1 label=8
Mark label 8 here at 101
loop_rec/2, dreg=0
remove_message/0
line/1: 9
call_ext/2, arity=1, index=2
jump/1 label=10
label/1 label=9
Mark label 9 here at 114
wait/1
label/1 label=10
Mark label 10 here at 118
call_last/3, arity=0, label=7, dellocate=0
label/1 label=11
Mark label 11 here at 124
line/1: 0
func_info/3 module_name_a=1, function_name_a=10, arity=0
label/1 label=12
Mark label 12 here at 132
move/2
line/1: 0
call_ext_only/2, arity=1, index=3
label/1 label=13
Mark label 13 here at 142
line/1: 0
func_info/3 module_name_a=1, function_name_a=10, arity=1
label/1 label=14
Mark label 14 here at 150
move/2
move/2
line/1: 0
call_ext_only/2, arity=2, index=4
int_call_end!
-- Code loading finished --
-- Executing code
label/1 label=2
allocate/2 stack_need=0, live=0
move/2 8b, x1
move/2 3b, x2
move/2 b, x0
line/1: 2
call_ext/2, arity=3, index=0
-- Loading code
label/1 label=1
Mark label 1 here at 0
line/1: 1
func_info/3 module_name_a=1, function_name_a=2, arity=0
label/1 label=2
Mark label 2 here at 8
allocate/2 stack_need=0, live=0
move/2
move/2
move/2
line/1: 2
call_ext/2, arity=3, index=0
test_heap/2 heap_need=3, live_registers=1
bif0/2 bif=1, dreg=1
put_tuple/2 size=2, dest=x2
put/2
put/2
move/2
line/1: 3
call/2, arity=2, label=4
move/2
deallocate/1 n_words=0
return/0
label/1 label=3
Mark label 3 here at 55
line/1: 4
func_info/3 module_name_a=1, function_name_a=8, arity=2
label/1 label=4
Mark label 4 here at 63
allocate/2 stack_need=0, live=2
line/1: 5
send/0
line/1: 6
label/1 label=5
Mark label 5 here at 73
wait_timeout/2, label: 5
timeout/0
move/2
deallocate/1 n_words=0
return/0
label/1 label=6
Mark label 6 here at 86
line/1: 7
func_info/3 module_name_a=1, function_name_a=3, arity=0
label/1 label=7
Mark label 7 here at 94
allocate/2 stack_need=0, live=0
line/1: 8
label/1 label=8
Mark label 8 here at 101
loop_rec/2, dreg=0
remove_message/0
line/1: 9
call_ext/2, arity=1, index=2
jump/1 label=10
label/1 label=9
Mark label 9 here at 114
wait/1
label/1 label=10
Mark label 10 here at 118
call_last/3, arity=0, label=7, dellocate=0
label/1 label=11
Mark label 11 here at 124
line/1: 0
func_info/3 module_name_a=1, function_name_a=10, arity=0
label/1 label=12
Mark label 12 here at 132
move/2
line/1: 0
call_ext_only/2, arity=1, index=3
label/1 label=13
Mark label 13 here at 142
line/1: 0
func_info/3 module_name_a=1, function_name_a=10, arity=1
label/1 label=14
Mark label 14 here at 150
move/2
move/2
line/1: 0
call_ext_only/2, arity=2, index=4
int_call_end!
-- Code loading finished --
test_heap/2 heap_need=3, live_registers=1
bif0/2 bif=1, dreg=1
put_tuple/2 size=2, dest=x2
put/2 elem=0, value=0x13
put/2 elem=1, value=0x1030002aa

t has the value:

t	term	0x0000000103200462

in term_put_tuple_element at the time of the crash.

SEGV when passing atoms through open_port (atom hash table corruption?)

I have some code that is passing a property list to a port, as follows:

open(Port, Params) -> open_port({spawn, "socket"}, [{proto, udp} | Params]).

In my driver initialization, I have the following code:

driver_status socket_driver_init(Context *ctx, term opts)
{
    driver_status ret;
    GlobalContext *glb = ctx->global;
    struct SocketDriverData *socket_driver_context = calloc(1, sizeof(struct SocketDriverData));

    ctx->native_handler = consume_socket_driver_mailbox;
    ctx->platform_data = socket_driver_context;
    
    if (!term_is_list(opts)) {
        fprintf(stderr, "opts is not a list\n");
        abort();
    }
    
    term proto = interop_proplist_get_value(opts, term_from_atom_string(glb, tag_proto_a));
    
    if (term_is_nil(proto)) {
        fprintf(stderr, "no proto in opts\n");
        abort();
    }

However, I am getting a segv when calling term_from_atom_string(glb, tag_proto_a) in the above snippet, where tag_proto_a is defined as:

static const char *const tag_proto_a = "\x5" "proto";

I can see that the proto atom is inserted into the Global Context's atom table in parse_external_terms:

https://github.com/bettio/AtomVM/blob/master/src/libAtomVM/externalterm.c#L69

However, by the time my port driver is initialized, the atom hash table seems to have corrupt data. The crash is in de-referencing node->key, in atomshashtable_get_value :

unsigned long atomshashtable_get_value(const struct AtomsHashTable *hash_table, const AtomString string, unsigned long default_value)
{
    unsigned long hash = sdbm_hash(string, atom_string_len(string));
    long index = hash % hash_table->capacity;

    const struct HNode *node = hash_table->buckets[index];
    while (node) {
        if (atom_are_equals(string, node->key)) {
            return node->value;
        }

        node = node->next;
    }

    return default_value;
}

Here is what node looks like:

node	const HNode *	0x2c	0x000000000000002c
next	HNode *	NULL	
next	HNode *	NULL	
key	AtomString	0x0	
value	unsigned long	
key	AtomString	0x0	
value	unsigned long	

Here is the stack trace:

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x000000010000245c in atomshashtable_get_value at /work/src/github/fadushin/AtomVM/src/libAtomVM/atomshashtable.c:115
#1	0x000000010001a800 in globalcontext_insert_atom at /work/src/github/fadushin/AtomVM/src/libAtomVM/globalcontext.c:149
#2	0x000000010002fd3d in term_from_atom_string at /work/src/github/fadushin/AtomVM/src/libAtomVM/context.h:188
#3	0x000000010002f78e in socket_driver_init at /work/src/github/fadushin/AtomVM/src/platforms/generic_unix/socket_driver.c:77
#4	0x00000001000308d5 in platform_open_port at /work/src/github/fadushin/AtomVM/src/platforms/generic_unix/sys.c:209
#5	0x000000010002c3ac in nif_erlang_open_port_2 at /work/src/github/fadushin/AtomVM/src/libAtomVM/nifs.c:285
#6	0x0000000100014a11 in context_execute_loop at /work/src/github/fadushin/AtomVM/src/libAtomVM/opcodesswitch.h:2137
#7	0x0000000100001fbe in main at /work/src/github/fadushin/AtomVM/src/main.c:84
#8	0x00007fff7c3dd08d in start ()
#9	0x00007fff7c3dd08d in start ()

Is it possible that the memory referenced during the initial population of the atom hash table (from external terms) is no longer valid? Should the hash table keep a copy of these atom strings?

Thanks!

bad_match on return

The following code crashes in an abort with the new gc

-export([start/0]).

start() ->
    Term = {a,b,c},
    Term = echo(Term),
    0.

echo(Term) ->
    Term.

Here is the code where it crashes, in opcodeswitch:

            case OP_BADMATCH: {
                int next_off = 1;
                term arg1;
                DECODE_COMPACT_TERM(arg1, code, i, next_off, next_off)

                #ifdef IMPL_CODE_LOADER
                    TRACE("badmatch/1\n");
                    USED_BY_TRACE(arg1);
                #endif

                #ifdef IMPL_EXECUTE_LOOP
                    TRACE("badmatch/1, v=0x%lx\n", arg1);

                    int target_label = get_catch_label_and_change_module(ctx, &mod);

                    if (target_label) {
                        JUMP_TO_ADDRESS(mod->labels[target_label]);
                    } else {
                        abort(); // <-- crash here
                    }
                #endif

                #ifdef IMPL_CODE_LOADER
                    NEXT_INSTRUCTION(next_off);
                #endif

                break;
            }

Here are the instructions:

-- Loading code
label/1 label=1
Mark label 1 here at 0
line/1: 1
func_info/3 module_name_a=1, function_name_a=2, arity=0
label/1 label=2
Mark label 2 here at 8
allocate/2 stack_need=0, live=0
move/2
line/1: 2
call/2, arity=1, label=5
is_eq_exact/2
move/2
deallocate/1 n_words=0
return/0
label/1 label=3
Mark label 3 here at 33
line/1: 2
badmatch/1
label/1 label=4
Mark label 4 here at 39
line/1: 3
func_info/3 module_name_a=1, function_name_a=3, arity=1
label/1 label=5
Mark label 5 here at 47
return/0
label/1 label=6
Mark label 6 here at 50
line/1: 0
func_info/3 module_name_a=1, function_name_a=4, arity=0
label/1 label=7
Mark label 7 here at 58
move/2
line/1: 0
call_ext_only/2, arity=1, index=0
label/1 label=8
Mark label 8 here at 68
line/1: 0
func_info/3 module_name_a=1, function_name_a=4, arity=1
label/1 label=9
Mark label 9 here at 76
move/2
move/2
line/1: 0
call_ext_only/2, arity=2, index=1
int_call_end!
-- Code loading finished --
-- Executing code
label/1 label=2
allocate/2 stack_need=0, live=0
move/2 100501ed2, x0
line/1: 2
call/2, arity=1, label=5
label/1 label=5
return/0
is_eq_exact/2, label=3, arg1=100501ed2, arg2=1005017e2
label/1 label=3
line/1: 2
badmatch/1, v=0x1005017c2

Erlang build dependency error

There is a small bug in the dependency management of the Erlang library and example builds, whereby if a source module is changed, the source will be re-compiled into a beam file, but it will not get re-packaged into an AVM module. This can make it annoying to rebuild for testing, and is likely due to some dark CMake incantation magic I failed to make.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.