clone.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*
  2. * Copyright (C) 2019 Intel Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include <pthread.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #include "wasm_c_api.h"
  11. #define WORKER_NUMBER 10
  12. /******************************* VM *******************************/
  13. /* Use wasm_vm_t and vm_xxx to simulate a minimal Wasm VM in Envoy */
  14. typedef struct _vm {
  15. wasm_engine_t *engine;
  16. wasm_store_t *store;
  17. wasm_module_t *module;
  18. wasm_shared_module_t *shared_module;
  19. wasm_instance_t *instance;
  20. wasm_func_t **function_list;
  21. wasm_memory_t *memory;
  22. wasm_table_t *table;
  23. wasm_extern_vec_t *exports;
  24. } wasm_vm_t;
  25. typedef enum _clone_level {
  26. not_cloneable = 0,
  27. compiled_bytecode,
  28. instantiated_module
  29. } clone_level;
  30. typedef struct _thread_arg_t {
  31. char name[32];
  32. bool *ready_go_flag;
  33. pthread_mutex_t *ready_go_lock;
  34. pthread_cond_t *ready_go_cond;
  35. const wasm_vm_t *base_vm;
  36. } thread_arg_t;
  37. wasm_vm_t *
  38. vm_new()
  39. {
  40. wasm_vm_t *vm = NULL;
  41. vm = malloc(sizeof(struct _vm));
  42. if (!vm)
  43. goto fail;
  44. memset(vm, 0, sizeof(wasm_vm_t));
  45. vm->engine = wasm_engine_new();
  46. if (!vm->engine)
  47. goto fail;
  48. vm->store = wasm_store_new(vm->engine);
  49. if (!vm->store)
  50. goto fail;
  51. return vm;
  52. fail:
  53. if (vm) {
  54. if (vm->engine)
  55. wasm_engine_delete(vm->engine);
  56. free(vm);
  57. }
  58. return NULL;
  59. }
  60. wasm_vm_t *
  61. vm_release(wasm_vm_t *vm)
  62. {
  63. if (!vm)
  64. return NULL;
  65. if (vm->function_list) {
  66. free(vm->function_list);
  67. vm->function_list = NULL;
  68. }
  69. vm->memory = NULL;
  70. if (vm->exports) {
  71. wasm_extern_vec_delete(vm->exports);
  72. free(vm->exports);
  73. vm->exports = NULL;
  74. }
  75. wasm_instance_delete(vm->instance);
  76. vm->instance = NULL;
  77. wasm_shared_module_delete(vm->shared_module);
  78. vm->shared_module = NULL;
  79. wasm_module_delete(vm->module);
  80. vm->module = NULL;
  81. wasm_store_delete(vm->store);
  82. vm->store = NULL;
  83. wasm_engine_delete(vm->engine);
  84. vm->engine = NULL;
  85. free(vm);
  86. return NULL;
  87. }
  88. bool
  89. vm_load(wasm_vm_t *vm, const wasm_byte_vec_t *binary)
  90. {
  91. vm->module = wasm_module_new(vm->store, binary);
  92. vm->shared_module = wasm_module_share(vm->module);
  93. return vm->module != NULL;
  94. }
  95. bool
  96. vm_link(wasm_vm_t *vm, wasm_extern_vec_t *imports)
  97. {
  98. vm->instance = wasm_instance_new(vm->store, vm->module, imports, NULL);
  99. if (!vm->instance)
  100. goto fail;
  101. vm->exports = malloc(sizeof(wasm_extern_vec_t));
  102. if (!vm->exports)
  103. goto fail;
  104. memset(vm->exports, 0, sizeof(wasm_extern_vec_t));
  105. wasm_instance_exports(vm->instance, vm->exports);
  106. /* an exported memory, and two exported functions */
  107. assert(vm->exports->size == 3);
  108. /* bind memory */
  109. assert(wasm_extern_kind(vm->exports->data[0]) == WASM_EXTERN_MEMORY);
  110. vm->memory = wasm_extern_as_memory(vm->exports->data[0]);
  111. vm->function_list = malloc(2 * sizeof(wasm_func_t *));
  112. if (!vm->function_list)
  113. goto fail;
  114. memset(vm->function_list, 0, 2 * sizeof(wasm_func_t *));
  115. /* bind wasm_set_byte(...) */
  116. assert(wasm_extern_kind(vm->exports->data[1]) == WASM_EXTERN_FUNC);
  117. vm->function_list[0] = wasm_extern_as_func(vm->exports->data[1]);
  118. /* bind wasm_get_byte(...) */
  119. assert(wasm_extern_kind(vm->exports->data[2]) == WASM_EXTERN_FUNC);
  120. vm->function_list[1] = wasm_extern_as_func(vm->exports->data[2]);
  121. return true;
  122. fail:
  123. return false;
  124. }
  125. wasm_vm_t *
  126. vm_clone_from_module(const wasm_vm_t *base)
  127. {
  128. printf("Initializing...\n");
  129. wasm_vm_t *secondary = NULL;
  130. secondary = vm_new();
  131. if (secondary) {
  132. printf("Reuse module and bypass vm_load()...");
  133. secondary->module =
  134. wasm_module_obtain(base->store, base->shared_module);
  135. if (!secondary->module)
  136. secondary = vm_release(secondary);
  137. }
  138. return secondary;
  139. }
  140. wasm_vm_t *
  141. vm_clone_from_instance(const wasm_vm_t *base)
  142. {
  143. /**
  144. * if do a clone of the level instantiated_module, need to malloc and
  145. * initialize
  146. * - global. WASMGlobalInstance and global data
  147. * - memory. WASMMemoryInstance, memory_data and heap
  148. * - table. WASMTableInstance, table_data
  149. * - exports. all global, memory and table
  150. *
  151. * it is almost everything in wasm_instantiate() except function.
  152. */
  153. (void)base;
  154. printf("Unsupported\n");
  155. return NULL;
  156. }
  157. wasm_vm_t *
  158. vm_clone(const wasm_vm_t *base, clone_level level)
  159. {
  160. if (level == not_cloneable)
  161. return NULL;
  162. if (level == compiled_bytecode)
  163. return vm_clone_from_module(base);
  164. else
  165. return vm_clone_from_instance(base);
  166. }
  167. bool
  168. vm_memory_set_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t byte)
  169. {
  170. byte_t *data = wasm_memory_data(vm->memory);
  171. assert(data);
  172. *(data + offset) = byte;
  173. return true;
  174. }
  175. bool
  176. vm_memory_get_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t *byte)
  177. {
  178. byte_t *data = wasm_memory_data(vm->memory);
  179. assert(data);
  180. *byte = *(data + offset);
  181. return true;
  182. }
  183. bool
  184. vm_function_set_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t byte)
  185. {
  186. wasm_val_t a_v[2] = { WASM_I32_VAL(offset), WASM_I32_VAL(byte) };
  187. wasm_val_vec_t args = WASM_ARRAY_VEC(a_v);
  188. wasm_val_vec_t results = WASM_EMPTY_VEC;
  189. wasm_trap_t *trap = wasm_func_call(vm->function_list[0], &args, &results);
  190. if (trap) {
  191. printf("call wasm_set_byte failed");
  192. wasm_trap_delete(trap);
  193. return false;
  194. }
  195. return true;
  196. }
  197. bool
  198. vm_function_get_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t *byte)
  199. {
  200. wasm_val_t a_v[1] = { WASM_I32_VAL(offset) };
  201. wasm_val_vec_t args = WASM_ARRAY_VEC(a_v);
  202. wasm_val_t r_v[1] = { WASM_INIT_VAL };
  203. wasm_val_vec_t results = WASM_ARRAY_VEC(r_v);
  204. wasm_trap_t *trap = wasm_func_call(vm->function_list[1], &args, &results);
  205. if (trap) {
  206. printf("call wasm_get_byte failed");
  207. wasm_trap_delete(trap);
  208. return false;
  209. }
  210. assert(results.data->kind == WASM_I32);
  211. *byte = results.data->of.i32;
  212. return true;
  213. }
  214. static bool
  215. load_wasm_file_content(const char *file_name, wasm_byte_vec_t *out)
  216. {
  217. bool ret = false;
  218. #if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0
  219. FILE *file = fopen(file_name, "rb");
  220. #else
  221. FILE *file = fopen(file_name, "rb");
  222. #endif
  223. if (!file) {
  224. printf("> Error loading .wasm!\n");
  225. goto quit;
  226. }
  227. int offset = fseek(file, 0L, SEEK_END);
  228. if (offset == -1) {
  229. printf("> Error loading .wasm!\n");
  230. goto close_file;
  231. }
  232. long file_size = ftell(file);
  233. if (file_size == -1) {
  234. printf("> Error loading .wasm!\n");
  235. goto close_file;
  236. }
  237. offset = fseek(file, 0L, SEEK_SET);
  238. if (offset == -1) {
  239. printf("> Error loading .wasm!\n");
  240. goto close_file;
  241. }
  242. wasm_byte_vec_new_uninitialized(out, file_size);
  243. if (fread(out->data, file_size, 1, file) != 1) {
  244. printf("> Error loading content!\n");
  245. goto close_file;
  246. }
  247. ret = true;
  248. close_file:
  249. fclose(file);
  250. quit:
  251. return ret;
  252. }
  253. static pthread_key_t name_key;
  254. wasm_trap_t *
  255. report_cb(const wasm_val_vec_t *args, wasm_val_vec_t *results)
  256. {
  257. (void)results;
  258. assert(args->data[0].kind == WASM_I32);
  259. uint32_t chk_pnt_no = args->data[0].of.i32;
  260. char *name = pthread_getspecific(name_key);
  261. printf("[%s] Pass CHK POINT #%u\n", name, chk_pnt_no);
  262. return NULL;
  263. }
  264. bool
  265. run_code_start(wasm_vm_t **out)
  266. {
  267. bool ret = false;
  268. printf("Initializing...\n");
  269. wasm_vm_t *vm = vm_new();
  270. if (!vm)
  271. goto fail;
  272. printf("Loading binary...\n");
  273. wasm_byte_vec_t binary = { 0 };
  274. #if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0
  275. const char *file_name = "clone.aot";
  276. #else
  277. const char *file_name = "clone.wasm";
  278. #endif
  279. if (!load_wasm_file_content(file_name, &binary))
  280. goto release_vm;
  281. printf("Compiling module...\n");
  282. ret = vm_load(vm, &binary);
  283. wasm_byte_vec_delete(&binary);
  284. if (!ret)
  285. goto release_vm;
  286. printf("Creating callback...\n");
  287. wasm_functype_t *callback_type =
  288. wasm_functype_new_1_0(wasm_valtype_new_i32());
  289. if (!callback_type)
  290. goto release_vm;
  291. wasm_func_t *callback = wasm_func_new(vm->store, callback_type, report_cb);
  292. wasm_functype_delete(callback_type);
  293. if (!callback)
  294. goto release_vm;
  295. printf("Instantiating module...\n");
  296. wasm_extern_t *externs[] = { wasm_func_as_extern(callback) };
  297. wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
  298. ret = vm_link(vm, &imports);
  299. wasm_func_delete(callback);
  300. if (!ret)
  301. goto release_vm;
  302. *out = vm;
  303. return true;
  304. release_vm:
  305. vm_release(vm);
  306. fail:
  307. return false;
  308. }
  309. bool
  310. run_warm_start_w_compiled_bytecode(const wasm_vm_t *first, wasm_vm_t **out)
  311. {
  312. bool ret;
  313. wasm_vm_t *secondary = vm_clone(first, compiled_bytecode);
  314. if (!secondary)
  315. goto fail;
  316. printf("Creating callback...\n");
  317. wasm_functype_t *callback_type =
  318. wasm_functype_new_1_0(wasm_valtype_new_i32());
  319. if (!callback_type)
  320. goto release_vm;
  321. wasm_func_t *callback =
  322. wasm_func_new(secondary->store, callback_type, report_cb);
  323. wasm_functype_delete(callback_type);
  324. if (!callback)
  325. goto release_vm;
  326. printf("Instantiating module...\n");
  327. wasm_extern_t *externs[] = { wasm_func_as_extern(callback) };
  328. wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
  329. ret = vm_link(secondary, &imports);
  330. wasm_func_delete(callback);
  331. if (!ret)
  332. goto release_vm;
  333. *out = secondary;
  334. return true;
  335. release_vm:
  336. vm_release(secondary);
  337. fail:
  338. return false;
  339. }
  340. bool
  341. run_warm_start_w_instantiated_module(const wasm_vm_t *first, wasm_vm_t **out)
  342. {
  343. wasm_vm_t *secondary = vm_clone(first, instantiated_module);
  344. if (!secondary)
  345. return false;
  346. *out = secondary;
  347. return true;
  348. }
  349. void
  350. run_test(const wasm_vm_t *vm)
  351. {
  352. uint8_t byte = 0xFF;
  353. /* read initialization */
  354. vm_function_get_byte(vm, 10, &byte);
  355. assert(byte == 0x0);
  356. vm_memory_get_byte(vm, 10, &byte);
  357. assert(byte == 0x0);
  358. /* read after writing */
  359. vm_function_set_byte(vm, 16, 0xab);
  360. vm_function_get_byte(vm, 16, &byte);
  361. assert(byte == 0xab);
  362. vm_memory_set_byte(vm, 16, 0xcd);
  363. vm_memory_get_byte(vm, 16, &byte);
  364. assert(byte == 0xcd);
  365. /* reading and writing across */
  366. vm_function_set_byte(vm, 16, 0xef);
  367. vm_memory_get_byte(vm, 16, &byte);
  368. assert(byte == 0xef);
  369. vm_memory_set_byte(vm, 16, 0x67);
  370. vm_function_get_byte(vm, 16, &byte);
  371. assert(byte == 0x67);
  372. printf("All Passed ...\n");
  373. }
  374. static void *
  375. thrd_func(void *arg)
  376. {
  377. thread_arg_t *thrd_arg = (thread_arg_t *)arg;
  378. sleep(rand() % 5);
  379. printf("Running warm start at %s...\n", thrd_arg->name);
  380. pthread_setspecific(name_key, thrd_arg->name);
  381. wasm_vm_t *vm;
  382. if (!run_warm_start_w_compiled_bytecode(thrd_arg->base_vm, &vm))
  383. return NULL;
  384. pthread_mutex_trylock(thrd_arg->ready_go_lock);
  385. while (!(*thrd_arg->ready_go_flag)) {
  386. pthread_cond_wait(thrd_arg->ready_go_cond, thrd_arg->ready_go_lock);
  387. }
  388. pthread_mutex_unlock(thrd_arg->ready_go_lock);
  389. printf("Running test at %s...\n", thrd_arg->name);
  390. run_test(vm);
  391. vm_release(vm);
  392. pthread_exit(NULL);
  393. return NULL;
  394. }
  395. int
  396. main()
  397. {
  398. int ret = EXIT_FAILURE;
  399. bool ready_go_flag = false;
  400. pthread_mutex_t ready_go_lock = PTHREAD_MUTEX_INITIALIZER;
  401. pthread_cond_t ready_go_cond = PTHREAD_COND_INITIALIZER;
  402. pthread_key_create(&name_key, NULL);
  403. pthread_setspecific(name_key, "Execution Thread");
  404. printf("Running cold start at the execution thread...\n");
  405. wasm_vm_t *base_vm;
  406. if (!run_code_start(&base_vm))
  407. goto quit;
  408. run_test(base_vm);
  409. printf("Running warm start at other threads...\n");
  410. pthread_t tids[WORKER_NUMBER] = { 0 };
  411. thread_arg_t thrd_args[WORKER_NUMBER] = { 0 };
  412. for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) {
  413. thread_arg_t *thrd_arg = thrd_args + i;
  414. snprintf(thrd_arg->name, 32, "Worker#%lu", i);
  415. thrd_arg->ready_go_cond = &ready_go_cond;
  416. thrd_arg->ready_go_lock = &ready_go_lock;
  417. thrd_arg->ready_go_flag = &ready_go_flag;
  418. thrd_arg->base_vm = base_vm;
  419. int ret = pthread_create(&tids[i], NULL, thrd_func, thrd_arg);
  420. if (ret != 0)
  421. break;
  422. }
  423. sleep(1);
  424. pthread_mutex_trylock(&ready_go_lock);
  425. ready_go_flag = true;
  426. pthread_mutex_unlock(&ready_go_lock);
  427. pthread_cond_broadcast(&ready_go_cond);
  428. sleep(3);
  429. for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) {
  430. if (tids[i] != 0)
  431. pthread_join(tids[i], NULL);
  432. }
  433. vm_release(base_vm);
  434. ret = EXIT_SUCCESS;
  435. quit:
  436. return ret;
  437. }