Que signifie exécuter du code C sur un navigateur? WebAssembly était l'une des technologies qui m'intéressait car je travaillais avec C dans mon travail précédent. Dans cet article, en plus de HelloWorld, j'ai résumé les résultats d'une petite expérience.
** Installez Emscripten (compilateur) **
#La version installée cette fois
$ 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)
↓
** Créez hello.c **
hello.c
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello World\n");
return 0;
}
↓
compiler
#Avant la compilation
$ ls
hello.c
#compiler
$ emcc hello.c -s WASM=1 -o hello.html
# wasm, js,html a été généré
$ 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 lignes(!)
-rw-r--r-- 1 arene staff 21727 5 21 21:50 hello.wasm
Le fichier js généré après la compilation contient 2600 lignes. Vous pouvez voir que c'est une technologie difficile. Bien sûr, le code qui ne pouvait pas être compilé en C ne pouvait pas non plus être compilé dans WASM.
↓ ** Activer l'assemblage Web expérimental sur chrome: // flags / **
↓ ** Configurez un serveur Web sur votre PC local **
#Emscripten fournit même un simple serveur Web
$ emrun --no_browser --port 8080 .
Web server root directory: /Users/arene/temporary/wasm
Now listening at http://0.0.0.0:8080/
↓ ** Ouvrez hello.html dans votre navigateur **
Le code généré automatiquement lors de la compilation de HelloWorld ressemblait à ceci. (Comme il n'a pas été correctement analysé et qu'il y en a beaucoup, seules les parties où il est facile de saisir l'atmosphère sont extraites)
hello.html
<!--Omettre les logos, les écrans de type terminal et autres éléments inutiles-->
<script async type="text/javascript" src="hello.js"></script>
hello.js
//Le code réel est de 2600 lignes
//Afin d'avoir un aperçu, je l'ai extrait comme ça
//L'ordre de définition des fonctions a également été modifié pour pouvoir être lu depuis le début.(Réellement`run();`Est à la fin du fichier)
var wasmBinaryFile = 'hello.wasm';
if (!isDataURI(wasmBinaryFile)) {
wasmBinaryFile = locateFile(wasmBinaryFile);
}
run(); //Ce gars au bas du fichier est le point de départ de tout
function run(args) {
//Des omissions très diverses
callMain(args)
}
function callMain(args) {
//Des omissions très diverses
var entryFunction = Module['_main']; // Module['xxx']Il y a beaucoup de choses, y compris la fonction principale.
var ret = entryFunction(argc, argv);
exit(ret, true);
}
//Le module C fonctionne comme ceci["asm"]Lié à
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)
};
** Préparez du code qui fuit 100 Ko de mémoire pour chaque 1 s **
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); //Allouez dynamiquement de la mémoire. Puisqu'il n'est pas libéré, une fuite de mémoire se produit pour chaque boucle.
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;
}
↓ ** Après la compilation, exécutez-le dans un navigateur et mesurez les performances avec devtool **
JS Heap n'a pas augmenté et aucune fuite de mémoire n'a été détectée sur la face avant. ...Qu'est-ce que ça veut dire? (Parce qu'il y a un GC dans liste de propositions, il semble que le GC n'a pas fonctionné)
Je pensais que la raison pour laquelle je ne pouvais pas confirmer la fuite de mémoire dans la section précédente était que la largeur de la fuite était petite et difficile à comprendre, j'ai donc augmenté la largeur de la fuite. Je l'ai réécrit en un code qui fuit 1 Mo toutes les 100 ms, pour un total de 100 Mo, et lorsque je l'ai vérifié de la même manière, j'ai rencontré l'erreur suivante.
On ne sait pas quelle couche est limitée, navigateur, WASM, OS, mais il semble que la mémoire disponible soit limitée. (Bien sûr bien sûr)
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)
Enfin, j'ai essayé d'exécuter le code qui a causé l'erreur d'exécution. Le résultat, bien sûr, était une erreur d'exécution sur le navigateur. Cependant, le code après la ligne qui a causé l'erreur d'exécution a également été exécuté. (Si vous l'exécutez en tant que C normalement, il tombera au point d'erreur, donc c'est légèrement différent. C'est un comportement assez effrayant.)
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;
}
#Lors de la compilation et de l'exécution en langage C normalement, cela échoue au niveau de la partie erreur
$ gcc segfault.c
$ ./a.out
Before Segmentation Fault
[1] 22777 segmentation fault ./a.out
Lorsqu'il est exécuté à partir du navigateur, le traitement se poursuit même après la partie erreur
Erreur de connexion à la console
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
Finalement, tout sera terminé sur le navigateur, et j'ai un rêve.
Recommended Posts