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.
** 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 **
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)
};
** 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 **
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)
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)
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
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
Eventually, everything seems to be completed on the browser, and I have a dream.
Recommended Posts