Skip to content

Adding Embedded Linux platform #1480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: public
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions build/devices/linemb/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"modules": {
"*": [
"$(MODULES)/base/timer/*"
, "$(MODULES)/base/timer/lin/*"
, "$(MODULES)/base/time/*"
, "$(MODULES)/base/time/lin/*"
]
},
"preload": [
"timer",
"time"
]
}
220 changes: 220 additions & 0 deletions build/devices/linemb/xsProj-glib/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#include "xsAll.h"
#include "mc.xs.h"
#include "xs.h"
#include "xsAll.h"
#include "xsHost.h"
#include <fcntl.h>
#include <unistd.h>
#include <execinfo.h> // Add header file for retrieving call stack
#include <signal.h> // Add header file for signal handling
#include <pthread.h> // Add pthread header file
#include <string.h> // Add string.h for strsignal

#define ITERATION_TIMEOUT_US 5000000 // Set 5 second timeout threshold
#define FATAL_ERROR_TOKEN "\n\n!!!FATAL ERROR!!!\n\n"
extern txPreparation *xsPreparation;

// Use thread-local storage to save txMachine pointer
static __thread txMachine *gxMachine = NULL;

#if mxInstrument
gboolean on_instrumentation_timeout(gpointer data)
{
// Execute your code here
// Return TRUE if you want to continue calling, or FALSE if you want to stop after executing once
txMachine *the = (void *)data;
fxSampleInstrumentation(the, 0, NULL);
// Reset
the->garbageCollectionCount = 0;
the->stackPeak = the->stack;
the->peakParserSize = 0;
the->floatingPointOps = 0;
the->promisesSettledCount = 0;

return TRUE; // Continue calling
}
#endif

// Modify dump_js_stack function, add thread check
void dump_js_stack(txMachine* the) {
fprintf(stderr, "JavaScript stack trace:\n");
txSlot *aFrame = the->frame;
while (aFrame) {
char name[128] = "";
fxBufferFrameName(the, name, sizeof(name), aFrame, "");

txSlot* environment = mxFrameToEnvironment(aFrame);
if (environment->ID != XS_NO_ID)
printf("%s: %s:%d\n", name, fxGetKeyName(the, environment->ID), environment->value.environment.line);
else
printf("%s\n", name);

aFrame = aFrame->next;
}
}

static void fatal_error_exit() {
void *buffer[50];
int nptrs = backtrace(buffer, 50);
char **strings = backtrace_symbols(buffer, nptrs);
printf("==== Native stack trace: =====\n");
for (int i = 0; i < nptrs; i++)
{
printf("%s\n", strings[i]);
}
free(strings);
printf("==============================\n");
printf(FATAL_ERROR_TOKEN);

signal(SIGQUIT, SIG_DFL);
exit(1);
}

static void fatal_error_handler(int signum) {

printf("!!!! Signal %s (%d) caught. Stack trace:\n", strsignal(signum), signum);
// Reset signal handler to allow default behavior to terminate program
signal(signum, SIG_DFL);
fatal_error_exit();
}

// Modified: Unified signal handling function
static void signal_handler(int signum) {
fatal_error_handler(signum);
}

static void timeout_handler(int signum) {
if (gxMachine) {
printf("!!!! Execution timeout !!!!\n");
dump_js_stack(gxMachine);
fatal_error_exit();
}
}

int main(int argc, char *argv[]) {
// Use setvbuf to disable buffering
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);

// Register all signals to be captured
signal(SIGABRT, signal_handler);
signal(SIGFPE, signal_handler);
signal(SIGILL, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGBUS, signal_handler);
signal(SIGPIPE, signal_handler);
signal(SIGALRM, timeout_handler); // Used for timeout

int error = 0;
txPreparation *preparation = xsPreparation();

txMachine *the = fxPrepareMachine(NULL, preparation, "linemb", NULL, NULL);

setvbuf(stdout, NULL, _IONBF, 0);

gxMachine = the; // Save to thread-local storage
#if mxInstrument
fxDescribeInstrumentation(the, 0, NULL, NULL);
#endif
xsBeginHost(the);
{
xsVars(2);
{
// XS: set global string array argv, and put input string into it
xsTry
{
xsResult = xsNewArray(0);
xsSet(xsGlobal, xsID("argv"), xsResult);
for (int i = 0; i < argc; i++)
{
xsVar(0) = xsString(argv[i]);
xsCall1(xsResult, xsID("push"), xsVar(0));
}
}
xsCatch
{
xsStringValue message = xsToString(xsException);
fprintf(stderr, "### %s\n", message);
error = 1;
}
}
{
xsResult = xsAwaitImport("main", XS_IMPORT_NAMESPACE);
}
}
xsEndHost(the);

// Start event loop
GMainContext *main_context = g_main_context_default();
g_main_loop_new(main_context, FALSE);

#if mxInstrument
g_timeout_add_seconds(1, on_instrumentation_timeout, (void *)the);
#endif

// g_main_loop_run(main_loop);
while (TRUE)
{
// Set timer: trigger SIGALRM on timeout
struct itimerval timer;
timer.it_value.tv_sec = ITERATION_TIMEOUT_US / 1000000;
timer.it_value.tv_usec = ITERATION_TIMEOUT_US % 1000000;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &timer, NULL);

// Process one event blocking
g_main_context_iteration(NULL, TRUE);

// Disable timer
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &timer, NULL);
}

xsDeleteMachine(the);

return error;
}


void fxAbort(xsMachine *the, int status)
{
xsStringValue msg = (char*)fxAbortString(status);
#if MODDEF_XS_ABORTHOOK
if ((XS_JAVASCRIPT_STACK_OVERFLOW_EXIT != status) && (XS_NATIVE_STACK_OVERFLOW_EXIT != status) & (XS_DEBUGGER_EXIT != status)) {
xsBooleanValue ignore = false;

fxBeginHost(the);
{
mxPush(mxException);
txSlot *exception = the->stack;
mxException = xsUndefined;
mxTry(the) {
txID abortID = fxFindName(the, "abort");
mxOverflow(-8);
mxPush(mxGlobal);
if (fxHasID(the, abortID)) {
mxPush(mxGlobal);
fxCallID(the, abortID);
mxPushStringC((char *)msg);
mxPushSlot(exception);
fxRunCount(the, 2);
ignore = (XS_BOOLEAN_KIND == the->stack->kind) && !the->stack->value.boolean;
mxPop();
}
}
mxCatch(the) {
}
}
fxEndHost(the);
if (ignore)
return;
}
#endif
xsLog("XS abort: %s\n", msg);

fatal_error_exit();
}
129 changes: 129 additions & 0 deletions documentation/devices/linemb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Using the Moddable SDK with embedded Linux (Raspberry Pi Zero)
This is a demo document to demostrate how to bring up an embedded Linux device with the Moddable SDK.

# Prepare
check the general get started guide [Moddable SDK - Getting Started](../Moddable SDK - Getting Started.md)
Notice this is only tested with Ubuntu 22.04 for the host machine.

Key steps:
```bash
sudo
export MODDABLE=[your moddalbe root folder]
export PATH=$PATH:$MODDABLE/build/bin/lin/release
cd $MODDABLE/build/makefiles/lin
make
```

# Install toolchain
There are several CPU types supported. Use the following commands to install the corresponding toolchain:

## ARM(32-bit) - armhf
```bash
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
```

## ARM(64-bit) - arm64
```bash
sudo apt-get update
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
```

## amd64(x86_64, the host) - x86_64
```bash
sudo apt-get install build-essential
```

# Sample project
Below are the build commands for different CPU types:

## x86_64 example
```bash
cd $MODDABLE/examples/helloworld
mcconfig -d -m -p linemb/x86_64
```

The generated executable can be found at:
`$MODDABLE/build/bin/linemb/x86_64/debug/helloworld/helloworld`

## Running on ARM devices
### ARM(32-bit)
```bash
cd $MODDABLE/examples/helloworld
mcconfig -d -m -p linemb/armhf
```

The generated executable can be found at:
`$MODDABLE/build/bin/linemb/armhf/debug/helloworld/helloworld`

### ARM(64-bit)
```bash
cd $MODDABLE/examples/helloworld
mcconfig -d -m -p linemb/arm64
```

The generated executable can be found at:
`$MODDABLE/build/bin/linemb/arm64/debug/helloworld/helloworld`

### Copy to device
Find a way to copy this file to the target board (e.g., using scp):

```bash
scp $MODDABLE/build/bin/linemb/armhf/debug/helloworld/helloworld [email protected]:/root/
```

You should see output like this:
```
# ./helloworld
instruments key: Chunk used,Chunk available,Slot used,Slot available,Stack used,Stack available,Garbage collections,Keys used,Modules loaded,Parser used,Floating Point,Promises settled
Hello, world - sample
instruments: 248,32768,2432,65504,1344,12288,0,2,1,0,0,0
instruments: 248,32768,2432,65504,416,12288,0,2,1,0,0,0
instruments: 248,32768,2432,65504,416,12288,0,2,1,0,0,0
```

# Fix "module unsupported" issue

If you encounter an error like "XXX module unsupported" when using the linemb platform, it's because the module's `manifest.json` file doesn't specify a C language implementation for the linemb platform.

The simplest solution is to copy the implementation from the "lin" platform. Follow these steps:

1. Open the module's `manifest.json` file (e.g., `modules/files/file/manifest.json` for file module)
2. Ensure the linemb platform configuration includes the module implementation, for example:

```json
"linemb": {
"modules": {
"*": "$(MODULES)/files/file/lin/*"
},
"config": {
"file": {
"root": "/tmp/"
}
}
}
```

Please note that many modules for the `lin` platform (especially hardware-related ones) have not been thoroughly tested. It's recommended to enable modules according to your specific needs and perform adequate testing to ensure proper functionality.

If you encounter issues with the `lin` platform implementation, a better approach is to create a linemb-specific implementation:

1. Create a new folder for the linemb platform implementation (e.g., `modules/files/file/linemb/`)
2. Implement the necessary C files specifically for the linemb platform
3. Update the manifest.json to use this implementation:

```json
"linemb": {
"modules": {
"*": "$(MODULES)/files/file/linemb/*"
},
"config": {
"file": {
"root": "/tmp/"
}
}
}
```

This approach allows you to create optimized implementations tailored to the linemb platform.

4 changes: 4 additions & 0 deletions examples/manifest_base.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
},
"wasm": {
"include": "$(BUILD_SIMULATOR)/manifest.json"
},
"linemb/*": {
"include": "$(BUILD)/devices/linemb/manifest.json",
"strip": []
}
}
}
1 change: 1 addition & 0 deletions examples/manifest_net.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
},
"mac": {},
"lin": {},
"linemb": {},
"win": {},
"...": {
"error": "manifest_net - unsupported platform"
Expand Down
Loading