I tried to move Hello World (+ α) written in C language from JavaScript [WebAssembly]

What does it mean to execute C code on a browser? WebAssembly was one of the technologies I was interested in because I was dealing with C in my previous job. In this article, in addition to Hello World, I've summarized the results of a little experiment.


## What is WebAssembly? *WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.* (Quoted from [Official](https://webassembly.org/))
## Postscript (what I did) * Try moving Hello World * What happens if I run a code that leaks memory? * What happens if you use up memory? * What happens if I run code that causes a runtime error?
## Try moving Hello World Reference: https://laboradian.com/tried-webassembly/ (Same procedure as [MDN](https://developer.mozilla.org/ja/docs/WebAssembly/C_to_wasm), but easier to understand with supplementary explanation)

** Install Emscripten (compiler) **

#The version installed this time
$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.39.16
clang version 11.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 3774bcf9f84520a8c35bf765d9a528040d68a14b)
Target: x86_64-apple-darwin19.5.0
Thread model: posix
shared:INFO: (Emscripten: Running sanity checks)

** Create hello.c **

hello.c


#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("Hello World\n");
  return 0;
}

compile

#Before compilation
$ ls
hello.c

#compile
$ emcc hello.c -s WASM=1 -o hello.html

# wasm, js,html was generated
$ ls -l
total 496
-rw-r--r--  1 arene  staff      95  5 19 21:55 hello.c
-rw-r--r--  1 arene  staff  102675  5 21 21:50 hello.html
-rw-r--r--  1 arene  staff  115917  5 21 21:50 hello.js    #2600 lines(!)
-rw-r--r--  1 arene  staff   21727  5 21 21:50 hello.wasm

The js file generated after compilation has a whopping 2600 lines. You can see that it is a difficult technology. Of course, code that couldn't be compiled as C couldn't be compiled into WASM either.

↓   ** Enable Experimental WebAssembly on chrome: // flags / **

↓   ** Set up a web server on your local PC **

#Emscripten even has a simple web server
$ emrun --no_browser --port 8080 .
Web server root directory: /Users/arene/temporary/wasm
Now listening at http://0.0.0.0:8080/

↓   ** Open hello.html in your browser **

1.png

The code that was automatically generated when Hello World was compiled looked like this. (Since it has not been analyzed properly & there are a lot of them, I have extracted only the parts where it is easy to grasp the atmosphere)

hello.html


<!--Logos, terminal-like screens, and other unnecessary items are omitted.-->
<script async type="text/javascript" src="hello.js"></script>

hello.js


//The actual code is 2600 lines
//In the sense of getting an overview, I extracted it like that
//The definition order of the functions has also been changed so that it can be read from the beginning.(Actually`run();`Is at the end of the file)

var wasmBinaryFile = 'hello.wasm';
if (!isDataURI(wasmBinaryFile)) {
  wasmBinaryFile = locateFile(wasmBinaryFile);
}

run(); //This guy at the bottom of the file is the starting point for everything

function run(args) {
  //Very various omissions
  callMain(args)
}
function callMain(args) {
  //Very various omissions
  var entryFunction = Module['_main']; // Module['xxx']There are a lot of things, and various things including the main function are stuck in
  var ret = entryFunction(argc, argv);
  exit(ret, true);
}

//Module C function like this["asm"]Linked to
var asm = createWasm();
Module["asm"] = asm;
var _main = Module["_main"] = function() {
  assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
  assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
  return Module["asm"]["main"].apply(null, arguments)
}; 
var _malloc = Module["_malloc"] = function() {
  assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
  assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
  return Module["asm"]["malloc"].apply(null, arguments)
}; 
var _free = Module["_free"] = function() {
  assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. waMBit for main() to be called)');
  assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
  return Module["asm"]["free"].apply(null, arguments)
};

## What happens if I run a code that leaks memory?

** Prepare a code that leaks 100KB memory every 1s **

memoryLeak.c


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void memoryLeak(void) {
  int shouldLoop = 1;
  int loopCount = 0;
  while(shouldLoop) {
    loopCount++;
    if (loopCount > 10) {
      shouldLoop = 0;
    }
    char* p = malloc(1024 * 100); //Dynamically allocate memory. Since it is not released, a memory leak occurs for each loop.
    printf("memory leak: leak 100KB!\n");
    usleep(1000 * 1000); // 1000msec
  }
  return;
}

int main(int argc, char *argv[]) {
  printf("memory leak: start\n");
  memoryLeak();
  printf("memory leak: end\n");
  return 0;
}

↓   ** After compiling, run it in a browser and measure performance with devtool **

1.png

The JS Heap did not increase and no memory leak was detected on the front side. ...What does it mean? (Because there is a GC in list of proposals, it seems that the GC did not work)


## What happens if you use up memory?

I wondered if the memory leak could not be confirmed in the previous section because the leak width was small and difficult to understand, so I expanded the leak width. I rewrote it to a code that leaks 1MB every 100ms, for a total of 100MB, and when I verified it in the same way, I encountered the following error.

It is unknown which layer is limited, browser, WASM, OS, but it seems that the available memory is limited. (Of course, of course)

console.log


 Cannot enlarge memory arrays to size 17534976 bytes (OOM). Either (1) compile with  -s INITIAL_MEMORY=X  with X higher than the current value 16777216, (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 
 
 memoryLeak.html:1246 Cannot enlarge memory arrays to size 17534976 bytes (OOM). Either (1) compile with  -s INITIAL_MEMORY=X  with X higher than the current value 16777216, (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 
 
 memoryLeak.html:1246 exception thrown: RuntimeError: abort(Cannot enlarge memory arrays to size 17534976 bytes (OOM). Either (1) compile with  -s INITIAL_MEMORY=X  with X higher than the current value 16777216, (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ) at Error
     at jsStackTrace (http://0.0.0.0:8080/memoryLeak.js:1978:17)
     at stackTrace (http://0.0.0.0:8080/memoryLeak.js:1995:16)
     at abort (http://0.0.0.0:8080/memoryLeak.js:1735:44)
     at abortOnCannotGrowMemory (http://0.0.0.0:8080/memoryLeak.js:2018:7)
     at _emscripten_resize_heap (http://0.0.0.0:8080/memoryLeak.js:2021:7)
     at wasm-function[13]:0x237a
     at wasm-function[11]:0xd36
     at wasm-function[8]:0x337
     at wasm-function[9]:0x407
     at Module._main (http://0.0.0.0:8080/memoryLeak.js:2212:32),RuntimeError: abort(Cannot enlarge memory arrays to size 17534976 bytes (OOM). Either (1) compile with  -s INITIAL_MEMORY=X  with X higher than the current value 16777216, (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ) at Error
     at jsStackTrace (http://0.0.0.0:8080/memoryLeak.js:1978:17)
     at stackTrace (http://0.0.0.0:8080/memoryLeak.js:1995:16)
     at abort (http://0.0.0.0:8080/memoryLeak.js:1735:44)
     at abortOnCannotGrowMemory (http://0.0.0.0:8080/memoryLeak.js:2018:7)
     at _emscripten_resize_heap (http://0.0.0.0:8080/memoryLeak.js:2021:7)
     at wasm-function[13]:0x237a
     at wasm-function[11]:0xd36
     at wasm-function[8]:0x337
     at wasm-function[9]:0x407
     at Module._main (http://0.0.0.0:8080/memoryLeak.js:2212:32)
     at abort (http://0.0.0.0:8080/memoryLeak.js:1741:9)
     at abortOnCannotGrowMemory (http://0.0.0.0:8080/memoryLeak.js:2018:7)
     at _emscripten_resize_heap (http://0.0.0.0:8080/memoryLeak.js:2021:7)
     at wasm-function[13]:0x237a
     at wasm-function[11]:0xd36
     at wasm-function[8]:0x337
     at wasm-function[9]:0x407
     at Module._main (http://0.0.0.0:8080/memoryLeak.js:2212:32)
     at callMain (http://0.0.0.0:8080/memoryLeak.js:2500:15)
     at doRun (http://0.0.0.0:8080/memoryLeak.js:2562:23)
 
 memoryLeak.js:1741 Uncaught RuntimeError: abort(Cannot enlarge memory arrays to size 17534976 bytes (OOM). Either (1) compile with  -s INITIAL_MEMORY=X  with X higher than the current value 16777216, (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ) at Error
     at jsStackTrace (http://0.0.0.0:8080/memoryLeak.js:1978:17)
     at stackTrace (http://0.0.0.0:8080/memoryLeak.js:1995:16)
     at abort (http://0.0.0.0:8080/memoryLeak.js:1735:44)
     at abortOnCannotGrowMemory (http://0.0.0.0:8080/memoryLeak.js:2018:7)
     at _emscripten_resize_heap (http://0.0.0.0:8080/memoryLeak.js:2021:7)
     at wasm-function[13]:0x237a
     at wasm-function[11]:0xd36
     at wasm-function[8]:0x337
     at wasm-function[9]:0x407
     at Module._main (http://0.0.0.0:8080/memoryLeak.js:2212:32)
     at abort (http://0.0.0.0:8080/memoryLeak.js:1741:9)
     at abortOnCannotGrowMemory (http://0.0.0.0:8080/memoryLeak.js:2018:7)
     at _emscripten_resize_heap (http://0.0.0.0:8080/memoryLeak.js:2021:7)
     at wasm-function[13]:0x237a
     at wasm-function[11]:0xd36
     at wasm-function[8]:0x337
     at wasm-function[9]:0x407
     at Module._main (http://0.0.0.0:8080/memoryLeak.js:2212:32)
     at callMain (http://0.0.0.0:8080/memoryLeak.js:2500:15)
     at doRun (http://0.0.0.0:8080/memoryLeak.js:2562:23)

## What happens if I run code that crashes with a runtime error?

Finally, I tried running the code that caused the runtime error. The result, of course, was a runtime error on the browser as well. However, the code after the line that caused the runtime error was also executed. (If you execute it as C normally, it will fall at the error part, so it is slightly different. It is quite scary behavior.)

segfault.c


#include <stdio.h>

int main(void) {
  int i;
  char str[4];
  char* p;
  
  printf("Before Segmentation Fault\n");
  for(i = 0; i < 16; i++) {
    str[i] = 'a';
    *p = str[i];
  }
  printf("After Segmentation Fault\n");
  return 0;
}
#When compiling and executing as C language normally, it fails at the error part
$ gcc segfault.c
$ ./a.out
Before Segmentation Fault
[1]    22777 segmentation fault  ./a.out

When executed from the browser, processing continues even after the error part 1.png

Console log on error

console.log


 segfault.html:1246 Runtime error: The application has corrupted its heap memory area (address zero)!
 segfault.js:1741 Uncaught RuntimeError: abort(Runtime error: The application has corrupted its heap memory area (address zero)!) at Error
     at jsStackTrace (http://0.0.0.0:8080/segfault.js:1978:17)
     at stackTrace (http://0.0.0.0:8080/segfault.js:1995:16)
     at abort (http://0.0.0.0:8080/segfault.js:1735:44)
     at checkStackCookie (http://0.0.0.0:8080/segfault.js:1446:46)
     at postRun (http://0.0.0.0:8080/segfault.js:1538:3)
     at doRun (http://0.0.0.0:8080/segfault.js:2545:5)
     at http://0.0.0.0:8080/segfault.js:2554:7
     at abort (http://0.0.0.0:8080/segfault.js:1741:9)
     at checkStackCookie (http://0.0.0.0:8080/segfault.js:1446:46)
     at postRun (http://0.0.0.0:8080/segfault.js:1538:3)
     at doRun (http://0.0.0.0:8080/segfault.js:2545:5)
     at http://0.0.0.0:8080/segfault.js:2554:7



## Finally Standard input, file I / O, threads, semaphores, shared memory, signals, calling js functions from C, defining multiple functions, file splitting, recursive calls, speed comparisons, etc. There are other areas of concern, but for the time being, I will end the experiment here.

Eventually, everything seems to be completed on the browser, and I have a dream.

Recommended Posts

I tried to move Hello World (+ α) written in C language from JavaScript [WebAssembly]
I tried to generate a C language program source from cURL
I tried to make an application in 3 months from inexperienced
I tried to chew C # (indexer)
I tried to chew C # (polymorphism: polymorphism)
"Teacher, I want to implement a login function in Spring" ① Hello World
Convert C language to JavaScript with Emscripten
I tried to get started with WebAssembly
I tried to build Ruby 3.0.0 from source
I tried to touch JavaScript Part.2 Object-oriented
[Rails / JavaScript / Ajax] I tried to create a like function in two ways.
In Xamarin.Forms Gawa native app, make request-response from C # to JavaScript Task <T>
I tried to implement polymorphic related in Nogizaka.
[Rails] I tried to raise the Rails version from 5.0 to 5.2
I tried to organize the session in Rails
I also tried WebAssembly with Nim and C
I tried to chew C # (basic of encapsulation)
I tried to implement deep learning in Java
I tried to output multiplication table in Java
I tried to build Micra mackerel in 1 hour!
I tried to develop an application in 2 languages
I tried to create Alexa skill in Java
When I tried to put scss, which was written separately from Rails, into Rails, I got SassC :: SyntaxError in Users :: Sessions # new.
I tried to explain what you can do in a popular language for web development from a beginner's point of view.
I tried to organize the cases used in programming
Tokoro I rewrote in the migration from Wicket 7 to 8
I tried to implement Firebase push notification in Java
# 2 [Note] I tried to calculate multiplication tables in Java.
I tried to create a Clova skill in Java
I tried to make a login function in Java
I tried to implement the Euclidean algorithm in Java
~ I tried to learn functional programming in Java now ~
I tried to chew C # (reading and writing files)
I tried to find out what changed in Java 9