Преглед изворни кода

Enhance wasm loading with LoadArgs and support module names (#3265)

- Add new API wasm_runtime_load_ex() in wasm_export.h
  and wasm_module_new_ex in wasm_c_api.h
- Put aot_create_perf_map() into a separated file aot_perf_map.c
- In perf.map, function names include user specified module name
- Enhance the script to help flamegraph generations
liang.he пре 1 година
родитељ
комит
4ef724bbff

+ 11 - 105
core/iwasm/aot/aot_loader.c

@@ -16,6 +16,10 @@
 #include "debug/jit_debug.h"
 #endif
 
+#if WASM_ENABLE_LINUX_PERF != 0
+#include "aot_perf_map.h"
+#endif
+
 #define YMM_PLT_PREFIX "__ymm@"
 #define XMM_PLT_PREFIX "__xmm@"
 #define REAL_PLT_PREFIX "__real@"
@@ -3601,104 +3605,6 @@ fail:
     return ret;
 }
 
-#if WASM_ENABLE_LINUX_PERF != 0
-struct func_info {
-    uint32 idx;
-    void *ptr;
-};
-
-static uint32
-get_func_size(const AOTModule *module, struct func_info *sorted_func_ptrs,
-              uint32 idx)
-{
-    uint32 func_sz;
-
-    if (idx == module->func_count - 1)
-        func_sz = (uintptr_t)module->code + module->code_size
-                  - (uintptr_t)(sorted_func_ptrs[idx].ptr);
-    else
-        func_sz = (uintptr_t)(sorted_func_ptrs[idx + 1].ptr)
-                  - (uintptr_t)(sorted_func_ptrs[idx].ptr);
-
-    return func_sz;
-}
-
-static int
-compare_func_ptrs(const void *f1, const void *f2)
-{
-    return (intptr_t)((struct func_info *)f1)->ptr
-           - (intptr_t)((struct func_info *)f2)->ptr;
-}
-
-static struct func_info *
-sort_func_ptrs(const AOTModule *module, char *error_buf, uint32 error_buf_size)
-{
-    uint64 content_len;
-    struct func_info *sorted_func_ptrs;
-    unsigned i;
-
-    content_len = (uint64)sizeof(struct func_info) * module->func_count;
-    sorted_func_ptrs = loader_malloc(content_len, error_buf, error_buf_size);
-    if (!sorted_func_ptrs)
-        return NULL;
-
-    for (i = 0; i < module->func_count; i++) {
-        sorted_func_ptrs[i].idx = i;
-        sorted_func_ptrs[i].ptr = module->func_ptrs[i];
-    }
-
-    qsort(sorted_func_ptrs, module->func_count, sizeof(struct func_info),
-          compare_func_ptrs);
-
-    return sorted_func_ptrs;
-}
-
-static bool
-create_perf_map(const AOTModule *module, char *error_buf, uint32 error_buf_size)
-{
-    struct func_info *sorted_func_ptrs = NULL;
-    char perf_map_info[128] = { 0 };
-    FILE *perf_map = NULL;
-    uint32 i;
-    pid_t pid = getpid();
-    bool ret = false;
-
-    sorted_func_ptrs = sort_func_ptrs(module, error_buf, error_buf_size);
-    if (!sorted_func_ptrs)
-        goto quit;
-
-    snprintf(perf_map_info, 128, "/tmp/perf-%d.map", pid);
-    perf_map = fopen(perf_map_info, "w");
-    if (!perf_map) {
-        LOG_WARNING("warning: can't create /tmp/perf-%d.map, because %s", pid,
-                    strerror(errno));
-        goto quit;
-    }
-
-    for (i = 0; i < module->func_count; i++) {
-        memset(perf_map_info, 0, 128);
-        snprintf(perf_map_info, 128, "%lx  %x  aot_func#%u\n",
-                 (uintptr_t)sorted_func_ptrs[i].ptr,
-                 get_func_size(module, sorted_func_ptrs, i),
-                 sorted_func_ptrs[i].idx);
-
-        fwrite(perf_map_info, 1, strlen(perf_map_info), perf_map);
-    }
-
-    LOG_VERBOSE("generate /tmp/perf-%d.map", pid);
-    ret = true;
-
-quit:
-    if (sorted_func_ptrs)
-        free(sorted_func_ptrs);
-
-    if (perf_map)
-        fclose(perf_map);
-
-    return ret;
-}
-#endif /* WASM_ENABLE_LINUX_PERF != 0*/
-
 static bool
 load_from_sections(AOTModule *module, AOTSection *sections,
                    bool is_load_from_file_buf, char *error_buf,
@@ -3889,7 +3795,7 @@ load_from_sections(AOTModule *module, AOTSection *sections,
 }
 
 static AOTModule *
-create_module(char *error_buf, uint32 error_buf_size)
+create_module(char *name, char *error_buf, uint32 error_buf_size)
 {
     AOTModule *module =
         loader_malloc(sizeof(AOTModule), error_buf, error_buf_size);
@@ -3901,7 +3807,7 @@ create_module(char *error_buf, uint32 error_buf_size)
 
     module->module_type = Wasm_Module_AoT;
 
-    module->name = "";
+    module->name = name;
 
 #if WASM_ENABLE_MULTI_MODULE != 0
     module->import_module_list = &module->import_module_list_head;
@@ -3937,7 +3843,7 @@ AOTModule *
 aot_load_from_sections(AOTSection *section_list, char *error_buf,
                        uint32 error_buf_size)
 {
-    AOTModule *module = create_module(error_buf, error_buf_size);
+    AOTModule *module = create_module("", error_buf, error_buf_size);
 
     if (!module)
         return NULL;
@@ -4183,7 +4089,7 @@ load(const uint8 *buf, uint32 size, AOTModule *module, char *error_buf,
 
 #if WASM_ENABLE_LINUX_PERF != 0
     if (wasm_runtime_get_linux_perf())
-        if (!create_perf_map(module, error_buf, error_buf_size))
+        if (!aot_create_perf_map(module, error_buf, error_buf_size))
             goto fail;
 #endif
 
@@ -4193,10 +4099,10 @@ fail:
 }
 
 AOTModule *
-aot_load_from_aot_file(const uint8 *buf, uint32 size, char *error_buf,
-                       uint32 error_buf_size)
+aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args,
+                       char *error_buf, uint32 error_buf_size)
 {
-    AOTModule *module = create_module(error_buf, error_buf_size);
+    AOTModule *module = create_module(args->name, error_buf, error_buf_size);
 
     if (!module)
         return NULL;

+ 120 - 0
core/iwasm/aot/aot_perf_map.c

@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 Intel Corporation. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "aot_perf_map.h"
+#include "bh_log.h"
+#include "bh_platform.h"
+
+#if WASM_ENABLE_LINUX_PERF != 0
+struct func_info {
+    uint32 idx;
+    void *ptr;
+};
+
+static uint32
+get_func_size(const AOTModule *module, struct func_info *sorted_func_ptrs,
+              uint32 idx)
+{
+    uint32 func_sz;
+
+    if (idx == module->func_count - 1)
+        func_sz = (uintptr_t)module->code + module->code_size
+                  - (uintptr_t)(sorted_func_ptrs[idx].ptr);
+    else
+        func_sz = (uintptr_t)(sorted_func_ptrs[idx + 1].ptr)
+                  - (uintptr_t)(sorted_func_ptrs[idx].ptr);
+
+    return func_sz;
+}
+
+static int
+compare_func_ptrs(const void *f1, const void *f2)
+{
+    return (intptr_t)((struct func_info *)f1)->ptr
+           - (intptr_t)((struct func_info *)f2)->ptr;
+}
+
+static struct func_info *
+sort_func_ptrs(const AOTModule *module, char *error_buf, uint32 error_buf_size)
+{
+    uint64 content_len;
+    struct func_info *sorted_func_ptrs;
+    unsigned i;
+
+    content_len = (uint64)sizeof(struct func_info) * module->func_count;
+    sorted_func_ptrs = wasm_runtime_malloc(content_len);
+    if (!sorted_func_ptrs) {
+        snprintf(error_buf, error_buf_size,
+                 "allocate memory failed when creating perf map");
+        return NULL;
+    }
+
+    for (i = 0; i < module->func_count; i++) {
+        sorted_func_ptrs[i].idx = i;
+        sorted_func_ptrs[i].ptr = module->func_ptrs[i];
+    }
+
+    qsort(sorted_func_ptrs, module->func_count, sizeof(struct func_info),
+          compare_func_ptrs);
+
+    return sorted_func_ptrs;
+}
+
+bool
+aot_create_perf_map(const AOTModule *module, char *error_buf,
+                    uint32 error_buf_size)
+{
+    struct func_info *sorted_func_ptrs = NULL;
+    char perf_map_path[64] = { 0 };
+    char perf_map_info[128] = { 0 };
+    FILE *perf_map = NULL;
+    uint32 i;
+    pid_t pid = getpid();
+    bool ret = false;
+
+    sorted_func_ptrs = sort_func_ptrs(module, error_buf, error_buf_size);
+    if (!sorted_func_ptrs)
+        goto quit;
+
+    snprintf(perf_map_path, sizeof(perf_map_path) - 1, "/tmp/perf-%d.map", pid);
+    perf_map = fopen(perf_map_path, "a");
+    if (!perf_map) {
+        LOG_WARNING("warning: can't create /tmp/perf-%d.map, because %s", pid,
+                    strerror(errno));
+        goto quit;
+    }
+
+    const char *module_name = aot_get_module_name((AOTModule *)module);
+    for (i = 0; i < module->func_count; i++) {
+        memset(perf_map_info, 0, 128);
+        if (strlen(module_name) > 0)
+            snprintf(perf_map_info, 128, "%lx  %x  [%s]#aot_func#%u\n",
+                     (uintptr_t)sorted_func_ptrs[i].ptr,
+                     get_func_size(module, sorted_func_ptrs, i), module_name,
+                     sorted_func_ptrs[i].idx);
+        else
+            snprintf(perf_map_info, 128, "%lx  %x  aot_func#%u\n",
+                     (uintptr_t)sorted_func_ptrs[i].ptr,
+                     get_func_size(module, sorted_func_ptrs, i),
+                     sorted_func_ptrs[i].idx);
+
+        /* fwrite() is thread safe */
+        fwrite(perf_map_info, 1, strlen(perf_map_info), perf_map);
+    }
+
+    LOG_VERBOSE("write map information from %s into /tmp/perf-%d.map",
+                module_name, pid);
+    ret = true;
+
+quit:
+    if (sorted_func_ptrs)
+        wasm_runtime_free(sorted_func_ptrs);
+
+    if (perf_map)
+        fclose(perf_map);
+
+    return ret;
+}
+#endif /* WASM_ENABLE_LINUX_PERF != 0 */

+ 15 - 0
core/iwasm/aot/aot_perf_map.h

@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2019 Intel Corporation. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef _AOT_PERF_MAP_H_
+#define _AOT_PERF_MAP_H_
+
+#include "aot_runtime.h"
+
+bool
+aot_create_perf_map(const AOTModule *module, char *error_buf,
+                    uint32 error_buf_size);
+
+#endif /* _AOT_PERF_MAP_H_ */

+ 2 - 2
core/iwasm/aot/aot_runtime.h

@@ -444,8 +444,8 @@ typedef struct LLVMProfileData_64 {
  * @return return AOT module loaded, NULL if failed
  */
 AOTModule *
-aot_load_from_aot_file(const uint8 *buf, uint32 size, char *error_buf,
-                       uint32 error_buf_size);
+aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args,
+                       char *error_buf, uint32 error_buf_size);
 
 /**
  * Load a AOT module from a specified AOT section list.

+ 12 - 3
core/iwasm/common/wasm_c_api.c

@@ -2234,7 +2234,8 @@ quit:
 #endif /* WASM_ENABLE_WASM_CACHE != 0 */
 
 wasm_module_t *
-wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary)
+wasm_module_new_ex(wasm_store_t *store, const wasm_byte_vec_t *binary,
+                   const LoadArgs *args)
 {
     char error_buf[128] = { 0 };
     wasm_module_ex_t *module_ex = NULL;
@@ -2290,8 +2291,8 @@ wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary)
     if (!module_ex->binary->data)
         goto free_binary;
 
-    module_ex->module_comm_rt = wasm_runtime_load(
-        (uint8 *)module_ex->binary->data, (uint32)module_ex->binary->size,
+    module_ex->module_comm_rt = wasm_runtime_load_ex(
+        (uint8 *)module_ex->binary->data, (uint32)module_ex->binary->size, args,
         error_buf, (uint32)sizeof(error_buf));
     if (!(module_ex->module_comm_rt)) {
         LOG_ERROR("%s", error_buf);
@@ -2337,6 +2338,14 @@ quit:
     return NULL;
 }
 
+wasm_module_t *
+wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary)
+{
+    LoadArgs args = { 0 };
+    args.name = "";
+    return wasm_module_new_ex(store, binary, &args);
+}
+
 bool
 wasm_module_validate(wasm_store_t *store, const wasm_byte_vec_t *binary)
 {

+ 26 - 8
core/iwasm/common/wasm_runtime_common.c

@@ -65,7 +65,7 @@
 #if WASM_ENABLE_MULTI_MODULE != 0
 /**
  * A safety insurance to prevent
- * circular depencies which leads stack overflow
+ * circular dependencies which leads stack overflow
  * try to break early
  */
 typedef struct LoadingModule {
@@ -1333,11 +1333,15 @@ register_module_with_null_name(WASMModuleCommon *module_common, char *error_buf,
 }
 
 WASMModuleCommon *
-wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf,
-                  uint32 error_buf_size)
+wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args,
+                     char *error_buf, uint32 error_buf_size)
 {
     WASMModuleCommon *module_common = NULL;
 
+    if (!args) {
+        return NULL;
+    }
+
     if (get_package_type(buf, size) == Wasm_Module_Bytecode) {
 #if WASM_ENABLE_INTERP != 0
         module_common =
@@ -1345,13 +1349,13 @@ wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf,
 #if WASM_ENABLE_MULTI_MODULE != 0
                                           true,
 #endif
-                                          error_buf, error_buf_size);
+                                          args, error_buf, error_buf_size);
 #endif
     }
     else if (get_package_type(buf, size) == Wasm_Module_AoT) {
 #if WASM_ENABLE_AOT != 0
         module_common = (WASMModuleCommon *)aot_load_from_aot_file(
-            buf, size, error_buf, error_buf_size);
+            buf, size, args, error_buf, error_buf_size);
 #endif
     }
     else {
@@ -1367,10 +1371,21 @@ wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf,
         LOG_DEBUG("WASM module load failed");
         return NULL;
     }
+
+    /*TODO: use file name as name and register with name? */
     return register_module_with_null_name(module_common, error_buf,
                                           error_buf_size);
 }
 
+WASMModuleCommon *
+wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf,
+                  uint32 error_buf_size)
+{
+    LoadArgs args = { 0 };
+    args.name = "";
+    return wasm_runtime_load_ex(buf, size, &args, error_buf, error_buf_size);
+}
+
 WASMModuleCommon *
 wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot,
                                 char *error_buf, uint32 error_buf_size)
@@ -6501,6 +6516,7 @@ wasm_runtime_load_depended_module(const WASMModuleCommon *parent_module,
     bool ret = false;
     uint8 *buffer = NULL;
     uint32 buffer_size = 0;
+    LoadArgs args = { 0 };
 
     /* check the registered module list of the parent */
     sub_module = wasm_runtime_search_sub_module(parent_module, sub_module_name);
@@ -6547,16 +6563,18 @@ wasm_runtime_load_depended_module(const WASMModuleCommon *parent_module,
         LOG_DEBUG("moudle %s type error", sub_module_name);
         goto destroy_file_buffer;
     }
+
+    args.name = (char *)sub_module_name;
     if (get_package_type(buffer, buffer_size) == Wasm_Module_Bytecode) {
 #if WASM_ENABLE_INTERP != 0
-        sub_module = (WASMModuleCommon *)wasm_load(buffer, buffer_size, false,
-                                                   error_buf, error_buf_size);
+        sub_module = (WASMModuleCommon *)wasm_load(
+            buffer, buffer_size, false, &args, error_buf, error_buf_size);
 #endif
     }
     else if (get_package_type(buffer, buffer_size) == Wasm_Module_AoT) {
 #if WASM_ENABLE_AOT != 0
         sub_module = (WASMModuleCommon *)aot_load_from_aot_file(
-            buffer, buffer_size, error_buf, error_buf_size);
+            buffer, buffer_size, &args, error_buf, error_buf_size);
 #endif
     }
     if (!sub_module) {

+ 1 - 1
core/iwasm/compilation/aot_llvm.c

@@ -85,7 +85,7 @@ aot_add_llvm_func1(const AOTCompContext *comp_ctx, LLVMModuleRef module,
                    uint32 func_index, uint32 param_count, LLVMTypeRef func_type,
                    const char *prefix)
 {
-    char func_name[48];
+    char func_name[48] = { 0 };
     LLVMValueRef func;
     LLVMValueRef local_value;
     uint32 i, j;

+ 11 - 0
core/iwasm/include/wasm_c_api.h

@@ -517,10 +517,21 @@ struct WASMModuleCommon;
 typedef struct WASMModuleCommon *wasm_module_t;
 #endif
 
+#ifndef LOAD_ARGS_OPTION_DEFINED
+#define LOAD_ARGS_OPTION_DEFINED
+typedef struct LoadArgs {
+    char *name;
+    /* TODO: more fields? */
+} LoadArgs;
+#endif /* LOAD_ARGS_OPTION_DEFINED */
 
 WASM_API_EXTERN own wasm_module_t* wasm_module_new(
   wasm_store_t*, const wasm_byte_vec_t* binary);
 
+// please refer to wasm_runtime_load_ex(...) in core/iwasm/include/wasm_export.h
+WASM_API_EXTERN own wasm_module_t* wasm_module_new_ex(
+  wasm_store_t*, const wasm_byte_vec_t* binary, const LoadArgs *args);
+
 WASM_API_EXTERN void wasm_module_delete(own wasm_module_t*);
 
 WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary);

+ 15 - 0
core/iwasm/include/wasm_export.h

@@ -183,6 +183,14 @@ typedef struct RuntimeInitArgs {
     bool enable_linux_perf;
 } RuntimeInitArgs;
 
+#ifndef LOAD_ARGS_OPTION_DEFINED
+#define LOAD_ARGS_OPTION_DEFINED
+typedef struct LoadArgs {
+    char *name;
+    /* TODO: more fields? */
+} LoadArgs;
+#endif /* LOAD_ARGS_OPTION_DEFINED */
+
 #ifndef INSTANTIATION_ARGS_OPTION_DEFINED
 #define INSTANTIATION_ARGS_OPTION_DEFINED
 /* WASM module instantiation arguments */
@@ -419,6 +427,13 @@ WASM_RUNTIME_API_EXTERN wasm_module_t
 wasm_runtime_load(uint8_t *buf, uint32_t size,
                   char *error_buf, uint32_t error_buf_size);
 
+/**
+ * Load a WASM module with specified load argument.
+ */
+WASM_RUNTIME_API_EXTERN wasm_module_t
+wasm_runtime_load_ex(uint8_t *buf, uint32_t size, const LoadArgs *args,
+                     char *error_buf, uint32_t error_buf_size);
+
 /**
  * Load a WASM module from a specified WASM or AOT section list.
  *

+ 5 - 5
core/iwasm/interpreter/wasm_loader.c

@@ -6043,7 +6043,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
 }
 
 static WASMModule *
-create_module(char *error_buf, uint32 error_buf_size)
+create_module(char *name, char *error_buf, uint32 error_buf_size)
 {
     WASMModule *module =
         loader_malloc(sizeof(WASMModule), error_buf, error_buf_size);
@@ -6058,7 +6058,7 @@ create_module(char *error_buf, uint32 error_buf_size)
     /* Set start_function to -1, means no start function */
     module->start_function = (uint32)-1;
 
-    module->name = "";
+    module->name = name;
 
 #if WASM_ENABLE_FAST_INTERP == 0
     module->br_table_cache_list = &module->br_table_cache_list_head;
@@ -6138,7 +6138,7 @@ WASMModule *
 wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf,
                                uint32 error_buf_size)
 {
-    WASMModule *module = create_module(error_buf, error_buf_size);
+    WASMModule *module = create_module("", error_buf, error_buf_size);
     if (!module)
         return NULL;
 
@@ -6479,9 +6479,9 @@ wasm_loader_load(uint8 *buf, uint32 size,
 #if WASM_ENABLE_MULTI_MODULE != 0
                  bool main_module,
 #endif
-                 char *error_buf, uint32 error_buf_size)
+                 const LoadArgs *args, char *error_buf, uint32 error_buf_size)
 {
-    WASMModule *module = create_module(error_buf, error_buf_size);
+    WASMModule *module = create_module(args->name, error_buf, error_buf_size);
     if (!module) {
         return NULL;
     }

+ 1 - 1
core/iwasm/interpreter/wasm_loader.h

@@ -28,7 +28,7 @@ wasm_loader_load(uint8 *buf, uint32 size,
 #if WASM_ENABLE_MULTI_MODULE != 0
                  bool main_module,
 #endif
-                 char *error_buf, uint32 error_buf_size);
+                 const LoadArgs *args, char *error_buf, uint32 error_buf_size);
 
 /**
  * Load a WASM module from a specified WASM section list.

+ 5 - 5
core/iwasm/interpreter/wasm_mini_loader.c

@@ -2994,7 +2994,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
 }
 
 static WASMModule *
-create_module(char *error_buf, uint32 error_buf_size)
+create_module(char *name, char *error_buf, uint32 error_buf_size)
 {
     WASMModule *module =
         loader_malloc(sizeof(WASMModule), error_buf, error_buf_size);
@@ -3009,7 +3009,7 @@ create_module(char *error_buf, uint32 error_buf_size)
     /* Set start_function to -1, means no start function */
     module->start_function = (uint32)-1;
 
-    module->name = "";
+    module->name = name;
 
 #if WASM_ENABLE_FAST_INTERP == 0
     module->br_table_cache_list = &module->br_table_cache_list_head;
@@ -3035,7 +3035,7 @@ WASMModule *
 wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf,
                                uint32 error_buf_size)
 {
-    WASMModule *module = create_module(error_buf, error_buf_size);
+    WASMModule *module = create_module("", error_buf, error_buf_size);
     if (!module)
         return NULL;
 
@@ -3206,10 +3206,10 @@ load(const uint8 *buf, uint32 size, WASMModule *module, char *error_buf,
 }
 
 WASMModule *
-wasm_loader_load(uint8 *buf, uint32 size, char *error_buf,
+wasm_loader_load(uint8 *buf, uint32 size, const LoadArgs *args, char *error_buf,
                  uint32 error_buf_size)
 {
-    WASMModule *module = create_module(error_buf, error_buf_size);
+    WASMModule *module = create_module(args->name, error_buf, error_buf_size);
     if (!module) {
         return NULL;
     }

+ 2 - 2
core/iwasm/interpreter/wasm_runtime.c

@@ -60,13 +60,13 @@ wasm_load(uint8 *buf, uint32 size,
 #if WASM_ENABLE_MULTI_MODULE != 0
           bool main_module,
 #endif
-          char *error_buf, uint32 error_buf_size)
+          const LoadArgs *name, char *error_buf, uint32 error_buf_size)
 {
     return wasm_loader_load(buf, size,
 #if WASM_ENABLE_MULTI_MODULE != 0
                             main_module,
 #endif
-                            error_buf, error_buf_size);
+                            name, error_buf, error_buf_size);
 }
 
 WASMModule *

+ 1 - 1
core/iwasm/interpreter/wasm_runtime.h

@@ -508,7 +508,7 @@ wasm_load(uint8 *buf, uint32 size,
 #if WASM_ENABLE_MULTI_MODULE != 0
           bool main_module,
 #endif
-          char *error_buf, uint32 error_buf_size);
+          const LoadArgs *args, char *error_buf, uint32 error_buf_size);
 
 WASMModule *
 wasm_load_from_sections(WASMSection *section_list, char *error_buf,

+ 63 - 0
samples/linux-perf/CMakeLists.txt

@@ -0,0 +1,63 @@
+# Copyright (C) 2019 Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required (VERSION 3.14)
+
+project(linux_perf_sample)
+
+if(NOT CMAKE_HOST_LINUX)
+  message(FATAL_ERROR "This sample only works on linux")
+endif()
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE Release)
+endif()
+
+set(CMAKE_CXX_STANDARD 17)
+
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
+find_package(WASISDK REQUIRED)
+
+################  runtime settings  ################
+string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM)
+include(CheckPIESupported)
+
+# AOT and JIT byd default
+set(WAMR_BUILD_AOT 1)
+set(WAMR_BUILD_INTERP 0)
+set(WAMR_BUILD_JIT 1)
+# wasm32-wasi
+set(WAMR_BUILD_LIBC_BUILTIN 0)
+set(WAMR_BUILD_LIBC_WASI 1)
+# mvp
+set(WAMR_BUILD_BULK_MEMORY 1)
+set(WAMR_BUILD_REF_TYPES 1)
+set(WAMR_BUILD_SIMD 1)
+set(WAMR_BUILD_TAIL_CALL 1)
+# trap information
+set(WAMR_BUILD_DUMP_CALL_STACK 1)
+# linux perf
+set(WAMR_BUILD_LINUX_PERF 1)
+#
+#set(WAMR_BUILD_THREAD_MGR 0)
+
+# vmlib
+set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
+include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
+add_library(vmlib SHARED ${WAMR_RUNTIME_LIB_SOURCE})
+target_include_directories(vmlib INTERFACE ${WAMR_ROOT_DIR}/core/iwasm/include)
+target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl)
+
+################ host  ################
+add_executable(${PROJECT_NAME} host/demo.c)
+target_link_libraries(${PROJECT_NAME} vmlib)
+
+################ aot + wasm  ################
+include(ExternalProject)
+ExternalProject_Add(wasm
+  SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm"
+  CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm -B build
+                      -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN}
+  BUILD_COMMAND     ${CMAKE_COMMAND} --build build
+  INSTALL_COMMAND   ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR}
+)

+ 90 - 0
samples/linux-perf/README.md

@@ -0,0 +1,90 @@
+# linux perf sample introduction
+
+This is a sample to show how to use the Linux perf tool to profile the execution of a WebAssembly application. And how to use the [Flamegraph](https://www.brendangregg.com/flamegraphs.html) tool to visualize the profiling result.
+
+## Build and run the sample
+
+There are two Wasm modules and their instance will be created and run in the sample. [The first module](./wasm/fib.c) is a simple Wasm module that calculates the Fibonacci number. [The second module](./wasm/ackermann.c) is a simple Wasm module that execute the Ackermann function. The target is enable to profile the execution of both two modules separately.
+
+```bash
+$ cmake -S . -B build
+$ cmake --build build
+```
+
+### Profile the execution
+
+```bash
+$ cd build
+$ perf record -k mono -g --output=perf.data -- ./linux_perf_sample
+```
+
+Enable to use `perf report --stdio` to do a quick analysis of the profiling result.
+
+### Visualize the profiling result
+
+Need to download Flamegraph tool from [Flamegraph](https://github.com/brendangregg/FlameGraph/releases/tag/v1.0) firstly.
+
+```bash
+$ perf script > out.perf
+$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
+$ ./FlameGraph/flamegraph.pl out.folded > perf.svg
+```
+
+In this result, you'll see two modules's profiling result and all wasm functions are named as "aot_func#N" which is a little hard to distinguish.
+
+![perf.png](./pics/perf.png)
+
+### Separate profiling result
+
+[process_folded_data.py](../../test-tools/flame-graph-helper/process_folded_data.py) is a script can a) translate "aot_func#N" into its original function name in name sections, b) separate the profiling result of different modules.
+
+In this sample, we want to separate `fib` and `ackermann` profiling data from _out.folded_. In [demo](host/demo.c), we decide to name the module of `fib1.wasm` as `fib2` and the module of `ackermann1.wasm` as `ackermann2`.
+
+```bash
+$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names fib2=./fib1.wasm,ackermann2=./ackermann1.wasm out.folded
+-> write into out.fib2.translated
+-> write into out.ackermann2.translated
+-> write into out.translated
+```
+
+More scenarios:
+
+if only using one wasm during profiling, the script can be used like this:
+
+```bash
+$ python process_folded_data.py --wabt_home /opt/wabt --wasm <wasm_file> --folded <folded_file>
+```
+
+if only using one wasm during profiling and specify the module name via APIs, the script can be used like this:
+
+```bash
+$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names <module name>=<wasm_file> --folded <folded_file>
+```
+
+if only using one wasm during profiling and specify the module name, which is same with the basename of wasm file, via APIs, the script can be used like this:
+
+```bash
+$ python process_folded_data.py --wabt_home /opt/wabt --wasm <wasm_file> --folded <folded_file>
+```
+
+if using multiple wasm during profiling and specify module names, which are same with basename of wasm files, via APIs, the script can be used like this:
+
+```bash
+$ python process_folded_data.py --wabt_home /opt/wabt --wasm <wasm_file> --wasm <wasm_file> --wasm <wasm_file> --folded <folded_file>
+```
+
+if using multiple wasm during profiling and specify module names via APIs, the script can be used like this:
+
+```bash
+$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names <module_name>=<wasm_file>,<module_name>=<wasm_file>,<module_name>=<wasm_file> --folded <folded_file>
+```
+
+Now we have two flame-graphs for two wasm modules:
+
+![fib.svg](./pics/perf.fib.svg)
+
+![ackermann.svg](./pics/perf.ackermann.svg)
+
+## Reference
+
+- [perf_tune](../../doc/perf_tune.md)

+ 14 - 0
samples/linux-perf/cmake/FindWAMRC.cmake

@@ -0,0 +1,14 @@
+# Copyright (C) 2019 Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+include(FindPackageHandleStandardArgs)
+
+find_file(WAMRC_BIN
+  NAMES wamrc
+  DOC "search wamrc"
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../wamr-compiler/build
+  REQUIRED
+)
+
+find_package_handle_standard_args(WAMRC REQUIRED_VARS WAMRC_BIN)
+mark_as_advanced(WAMRC_BIN)

+ 23 - 0
samples/linux-perf/cmake/FindWASISDK.cmake

@@ -0,0 +1,23 @@
+# Copyright (C) 2019 Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+include(FindPackageHandleStandardArgs)
+
+file(GLOB WASISDK_SEARCH_PATH "/opt/wasi-sdk-*")
+find_path(WASISDK_HOME
+  NAMES share/wasi-sysroot
+  PATHS ${WASISDK_SEARCH_PATH}
+  NO_DEFAULT_PATH
+  REQUIRED
+)
+
+string(REGEX MATCH [0-9]+\.[0-9]+\.*[0-9]* WASISDK_VERSION ${WASISDK_HOME})
+
+find_package_handle_standard_args(WASISDK REQUIRED_VARS WASISDK_HOME VERSION_VAR WASISDK_VERSION)
+
+if(WASISDK_FOUND)
+  set(WASISDK_CC_COMMAND  ${WASISDK_HOME}/bin/clang)
+  set(WASISDK_CXX_COMMAND ${WASISDK_HOME}/bin/clang++)
+  set(WASISDK_TOOLCHAIN   ${WASISDK_HOME}/share/cmake/wasi-sdk.cmake)
+  set(WASISDK_SYSROOT     ${WASISDK_HOME}/share/wasi-sysroot)
+endif()

+ 198 - 0
samples/linux-perf/host/demo.c

@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "wasm_c_api.h"
+
+#define own
+
+/* return a copy of the file stem of a file path */
+static own char *
+stem(const char *file_path)
+{
+    char *base_name = basename(file_path);
+    char *s = strdup(base_name);
+    char *dot = strchr(s, '.');
+    assert(dot);
+    *dot = '\0';
+    return s;
+}
+
+static void
+guest_i32_to_wasm_i32_array(int *args, unsigned argc, wasm_val_t *data,
+                            unsigned datac)
+{
+    for (unsigned i = 0; i < argc && i < datac; i++) {
+        memset(&data[i], 0, sizeof(wasm_val_t));
+        data[i].kind = WASM_I32;
+        data[i].of.i32 = args[i];
+    }
+}
+
+int
+load_run_wasm_file(wasm_engine_t *engine, const char *file_path, int *args,
+                   unsigned argc)
+{
+    wasm_store_t *store = wasm_store_new(engine);
+    // Load binary.
+    printf("Loading binary...\n");
+    FILE *file = fopen(file_path, "rb");
+    assert(file);
+
+    int ret = fseek(file, 0L, SEEK_END);
+    assert(ret == 0);
+
+    long file_size = ftell(file);
+    assert(file_size != -1);
+
+    ret = fseek(file, 0L, SEEK_SET);
+    assert(ret == 0);
+
+    wasm_byte_vec_t binary = { 0 };
+    wasm_byte_vec_new_uninitialized(&binary, file_size);
+
+    size_t nread = fread(binary.data, file_size, 1, file);
+    fclose(file);
+
+    // Compile.
+    printf("Compiling module...\n");
+
+    // Use its file name as the module name
+    char *file_name = stem(file_path);
+    assert(file_name);
+
+    LoadArgs load_args = { 0 };
+    load_args.name = file_name;
+    own wasm_module_t *module = wasm_module_new_ex(store, &binary, &load_args);
+    wasm_byte_vec_delete(&binary);
+    assert(module);
+
+    // Use export type to find the function index to call later
+    wasm_exporttype_vec_t export_types = { 0 };
+    wasm_module_exports(module, &export_types);
+    int func_to_call = -1;
+    for (unsigned i = 0; i < export_types.num_elems; i++) {
+        const wasm_name_t *name = wasm_exporttype_name(export_types.data[i]);
+        if (strncmp(name->data, "run", 3) == 0) {
+            func_to_call = i;
+            break;
+        }
+    }
+    assert(func_to_call != -1);
+
+    // Instantiate.
+    printf("Instantiating module...\n");
+    wasm_extern_vec_t imports = WASM_EMPTY_VEC;
+    own wasm_instance_t *instance = wasm_instance_new_with_args(
+        store, module, &imports, NULL, 16 * 1024 * 1024, 1 * 1024 * 1024);
+    assert(instance);
+
+    // Extract export.
+    printf("Extracting export...\n");
+    own wasm_extern_vec_t exports;
+    wasm_instance_exports(instance, &exports);
+    assert(exports.size);
+
+    assert(wasm_extern_kind(exports.data[func_to_call]) == WASM_EXTERN_FUNC);
+    const wasm_func_t *run_func =
+        wasm_extern_as_func(exports.data[func_to_call]);
+    assert(run_func);
+
+    wasm_module_delete(module);
+    wasm_instance_delete(instance);
+
+    // Call.
+    printf("Calling export...\n");
+    wasm_val_t as[4] = { 0 };
+    guest_i32_to_wasm_i32_array(args, argc, as, 4);
+
+    wasm_val_vec_t params = WASM_ARRAY_VEC(as);
+    wasm_val_t rs[1] = { WASM_I32_VAL(0) };
+    wasm_val_vec_t results = WASM_ARRAY_VEC(rs);
+    wasm_trap_t *trap = wasm_func_call(run_func, &params, &results);
+    assert(!trap);
+
+    wasm_extern_vec_delete(&exports);
+    free(file_name);
+    wasm_store_delete(store);
+
+    {
+        nread = nread;
+        ret = ret;
+        trap = trap;
+    }
+    return 0;
+}
+
+void *
+load_run_fib_wasm(void *arg)
+{
+    wasm_engine_t *engine = (wasm_engine_t *)arg;
+    int args[] = { 40 };
+    load_run_wasm_file(engine, "./fib1.wasm", args, 1);
+    return NULL;
+}
+
+void *
+load_run_fib_aot(void *arg)
+{
+    wasm_engine_t *engine = (wasm_engine_t *)arg;
+    int args[] = { 40 };
+    load_run_wasm_file(engine, "./fib2.aot", args, 1);
+    return NULL;
+}
+
+void *
+load_run_ackermann_wasm(void *arg)
+{
+    wasm_engine_t *engine = (wasm_engine_t *)arg;
+    int args[] = { 3, 12 };
+    load_run_wasm_file(engine, "./ackermann1.wasm", args, 2);
+    return NULL;
+}
+
+void *
+load_run_ackermann_aot(void *arg)
+{
+    wasm_engine_t *engine = (wasm_engine_t *)arg;
+    int args[] = { 3, 12 };
+    load_run_wasm_file(engine, "./ackermann2.aot", args, 2);
+    return NULL;
+}
+
+int
+main(int argc, const char *argv[])
+{
+    // Initialize.
+    printf("Initializing...\n");
+    wasm_config_t *config = wasm_config_new();
+    wasm_config_set_linux_perf_opt(config, true);
+    wasm_engine_t *engine = wasm_engine_new_with_config(config);
+
+    pthread_t tid[4] = { 0 };
+    /* FIXME: uncomment when it is able to run two modules with llvm-jit */
+    // pthread_create(&tid[0], NULL, load_run_fib_wasm, (void *)engine);
+    // pthread_create(&tid[2], NULL, load_run_ackermann_wasm, (void *)engine);
+
+    pthread_create(&tid[1], NULL, load_run_fib_aot, (void *)engine);
+    pthread_create(&tid[3], NULL, load_run_ackermann_aot, (void *)engine);
+
+    for (unsigned i = 0; i < sizeof(tid) / sizeof(tid[0]); i++)
+        pthread_join(tid[i], NULL);
+
+    // Shut down.
+    printf("Shutting down...\n");
+    wasm_engine_delete(engine);
+
+    // All done.
+    printf("Done.\n");
+    return 0;
+}

+ 1349 - 0
samples/linux-perf/pics/perf.ackermann.svg

@@ -0,0 +1,1349 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" width="1200" height="2134" onload="init(evt)" viewBox="0 0 1200 2134" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<!-- Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples. -->
+<!-- NOTES:  -->
+<defs>
+	<linearGradient id="background" y1="0" y2="1" x1="0" x2="0" >
+		<stop stop-color="#eeeeee" offset="5%" />
+		<stop stop-color="#eeeeb0" offset="95%" />
+	</linearGradient>
+</defs>
+<style type="text/css">
+	text { font-family:Verdana; font-size:12px; fill:rgb(0,0,0); }
+	#search, #ignorecase { opacity:0.1; cursor:pointer; }
+	#search:hover, #search.show, #ignorecase:hover, #ignorecase.show { opacity:1; }
+	#subtitle { text-anchor:middle; font-color:rgb(160,160,160); }
+	#title { text-anchor:middle; font-size:17px}
+	#unzoom { cursor:pointer; }
+	#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
+	.hide { display:none; }
+	.parent { opacity:0.5; }
+</style>
+<script type="text/ecmascript">
+<![CDATA[
+	"use strict";
+	var details, searchbtn, unzoombtn, matchedtxt, svg, searching, currentSearchTerm, ignorecase, ignorecaseBtn;
+	function init(evt) {
+		details = document.getElementById("details").firstChild;
+		searchbtn = document.getElementById("search");
+		ignorecaseBtn = document.getElementById("ignorecase");
+		unzoombtn = document.getElementById("unzoom");
+		matchedtxt = document.getElementById("matched");
+		svg = document.getElementsByTagName("svg")[0];
+		searching = 0;
+		currentSearchTerm = null;
+
+		// use GET parameters to restore a flamegraphs state.
+		var params = get_params();
+		if (params.x && params.y)
+			zoom(find_group(document.querySelector('[x="' + params.x + '"][y="' + params.y + '"]')));
+                if (params.s) search(params.s);
+	}
+
+	// event listeners
+	window.addEventListener("click", function(e) {
+		var target = find_group(e.target);
+		if (target) {
+			if (target.nodeName == "a") {
+				if (e.ctrlKey === false) return;
+				e.preventDefault();
+			}
+			if (target.classList.contains("parent")) unzoom(true);
+			zoom(target);
+			if (!document.querySelector('.parent')) {
+				// we have basically done a clearzoom so clear the url
+				var params = get_params();
+				if (params.x) delete params.x;
+				if (params.y) delete params.y;
+				history.replaceState(null, null, parse_params(params));
+				unzoombtn.classList.add("hide");
+				return;
+			}
+
+			// set parameters for zoom state
+			var el = target.querySelector("rect");
+			if (el && el.attributes && el.attributes.y && el.attributes._orig_x) {
+				var params = get_params()
+				params.x = el.attributes._orig_x.value;
+				params.y = el.attributes.y.value;
+				history.replaceState(null, null, parse_params(params));
+			}
+		}
+		else if (e.target.id == "unzoom") clearzoom();
+		else if (e.target.id == "search") search_prompt();
+		else if (e.target.id == "ignorecase") toggle_ignorecase();
+	}, false)
+
+	// mouse-over for info
+	// show
+	window.addEventListener("mouseover", function(e) {
+		var target = find_group(e.target);
+		if (target) details.nodeValue = "Function: " + g_to_text(target);
+	}, false)
+
+	// clear
+	window.addEventListener("mouseout", function(e) {
+		var target = find_group(e.target);
+		if (target) details.nodeValue = ' ';
+	}, false)
+
+	// ctrl-F for search
+	// ctrl-I to toggle case-sensitive search
+	window.addEventListener("keydown",function (e) {
+		if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
+			e.preventDefault();
+			search_prompt();
+		}
+		else if (e.ctrlKey && e.keyCode === 73) {
+			e.preventDefault();
+			toggle_ignorecase();
+		}
+	}, false)
+
+	// functions
+	function get_params() {
+		var params = {};
+		var paramsarr = window.location.search.substr(1).split('&');
+		for (var i = 0; i < paramsarr.length; ++i) {
+			var tmp = paramsarr[i].split("=");
+			if (!tmp[0] || !tmp[1]) continue;
+			params[tmp[0]]  = decodeURIComponent(tmp[1]);
+		}
+		return params;
+	}
+	function parse_params(params) {
+		var uri = "?";
+		for (var key in params) {
+			uri += key + '=' + encodeURIComponent(params[key]) + '&';
+		}
+		if (uri.slice(-1) == "&")
+			uri = uri.substring(0, uri.length - 1);
+		if (uri == '?')
+			uri = window.location.href.split('?')[0];
+		return uri;
+	}
+	function find_child(node, selector) {
+		var children = node.querySelectorAll(selector);
+		if (children.length) return children[0];
+	}
+	function find_group(node) {
+		var parent = node.parentElement;
+		if (!parent) return;
+		if (parent.id == "frames") return node;
+		return find_group(parent);
+	}
+	function orig_save(e, attr, val) {
+		if (e.attributes["_orig_" + attr] != undefined) return;
+		if (e.attributes[attr] == undefined) return;
+		if (val == undefined) val = e.attributes[attr].value;
+		e.setAttribute("_orig_" + attr, val);
+	}
+	function orig_load(e, attr) {
+		if (e.attributes["_orig_"+attr] == undefined) return;
+		e.attributes[attr].value = e.attributes["_orig_" + attr].value;
+		e.removeAttribute("_orig_"+attr);
+	}
+	function g_to_text(e) {
+		var text = find_child(e, "title").firstChild.nodeValue;
+		return (text)
+	}
+	function g_to_func(e) {
+		var func = g_to_text(e);
+		// if there's any manipulation we want to do to the function
+		// name before it's searched, do it here before returning.
+		return (func);
+	}
+	function update_text(e) {
+		var r = find_child(e, "rect");
+		var t = find_child(e, "text");
+		var w = parseFloat(r.attributes.width.value) -3;
+		var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,"");
+		t.attributes.x.value = parseFloat(r.attributes.x.value) + 3;
+
+		// Smaller than this size won't fit anything
+		if (w < 2 * 12 * 0.59) {
+			t.textContent = "";
+			return;
+		}
+
+		t.textContent = txt;
+		var sl = t.getSubStringLength(0, txt.length);
+		// check if only whitespace or if we can fit the entire string into width w
+		if (/^ *$/.test(txt) || sl < w)
+			return;
+
+		// this isn't perfect, but gives a good starting point
+		// and avoids calling getSubStringLength too often
+		var start = Math.floor((w/sl) * txt.length);
+		for (var x = start; x > 0; x = x-2) {
+			if (t.getSubStringLength(0, x + 2) <= w) {
+				t.textContent = txt.substring(0, x) + "..";
+				return;
+			}
+		}
+		t.textContent = "";
+	}
+
+	// zoom
+	function zoom_reset(e) {
+		if (e.attributes != undefined) {
+			orig_load(e, "x");
+			orig_load(e, "width");
+		}
+		if (e.childNodes == undefined) return;
+		for (var i = 0, c = e.childNodes; i < c.length; i++) {
+			zoom_reset(c[i]);
+		}
+	}
+	function zoom_child(e, x, ratio) {
+		if (e.attributes != undefined) {
+			if (e.attributes.x != undefined) {
+				orig_save(e, "x");
+				e.attributes.x.value = (parseFloat(e.attributes.x.value) - x - 10) * ratio + 10;
+				if (e.tagName == "text")
+					e.attributes.x.value = find_child(e.parentNode, "rect[x]").attributes.x.value + 3;
+			}
+			if (e.attributes.width != undefined) {
+				orig_save(e, "width");
+				e.attributes.width.value = parseFloat(e.attributes.width.value) * ratio;
+			}
+		}
+
+		if (e.childNodes == undefined) return;
+		for (var i = 0, c = e.childNodes; i < c.length; i++) {
+			zoom_child(c[i], x - 10, ratio);
+		}
+	}
+	function zoom_parent(e) {
+		if (e.attributes) {
+			if (e.attributes.x != undefined) {
+				orig_save(e, "x");
+				e.attributes.x.value = 10;
+			}
+			if (e.attributes.width != undefined) {
+				orig_save(e, "width");
+				e.attributes.width.value = parseInt(svg.width.baseVal.value) - (10 * 2);
+			}
+		}
+		if (e.childNodes == undefined) return;
+		for (var i = 0, c = e.childNodes; i < c.length; i++) {
+			zoom_parent(c[i]);
+		}
+	}
+	function zoom(node) {
+		var attr = find_child(node, "rect").attributes;
+		var width = parseFloat(attr.width.value);
+		var xmin = parseFloat(attr.x.value);
+		var xmax = parseFloat(xmin + width);
+		var ymin = parseFloat(attr.y.value);
+		var ratio = (svg.width.baseVal.value - 2 * 10) / width;
+
+		// XXX: Workaround for JavaScript float issues (fix me)
+		var fudge = 0.0001;
+
+		unzoombtn.classList.remove("hide");
+
+		var el = document.getElementById("frames").children;
+		for (var i = 0; i < el.length; i++) {
+			var e = el[i];
+			var a = find_child(e, "rect").attributes;
+			var ex = parseFloat(a.x.value);
+			var ew = parseFloat(a.width.value);
+			var upstack;
+			// Is it an ancestor
+			if (0 == 0) {
+				upstack = parseFloat(a.y.value) > ymin;
+			} else {
+				upstack = parseFloat(a.y.value) < ymin;
+			}
+			if (upstack) {
+				// Direct ancestor
+				if (ex <= xmin && (ex+ew+fudge) >= xmax) {
+					e.classList.add("parent");
+					zoom_parent(e);
+					update_text(e);
+				}
+				// not in current path
+				else
+					e.classList.add("hide");
+			}
+			// Children maybe
+			else {
+				// no common path
+				if (ex < xmin || ex + fudge >= xmax) {
+					e.classList.add("hide");
+				}
+				else {
+					zoom_child(e, xmin, ratio);
+					update_text(e);
+				}
+			}
+		}
+		search();
+	}
+	function unzoom(dont_update_text) {
+		unzoombtn.classList.add("hide");
+		var el = document.getElementById("frames").children;
+		for(var i = 0; i < el.length; i++) {
+			el[i].classList.remove("parent");
+			el[i].classList.remove("hide");
+			zoom_reset(el[i]);
+			if(!dont_update_text) update_text(el[i]);
+		}
+		search();
+	}
+	function clearzoom() {
+		unzoom();
+
+		// remove zoom state
+		var params = get_params();
+		if (params.x) delete params.x;
+		if (params.y) delete params.y;
+		history.replaceState(null, null, parse_params(params));
+	}
+
+	// search
+	function toggle_ignorecase() {
+		ignorecase = !ignorecase;
+		if (ignorecase) {
+			ignorecaseBtn.classList.add("show");
+		} else {
+			ignorecaseBtn.classList.remove("show");
+		}
+		reset_search();
+		search();
+	}
+	function reset_search() {
+		var el = document.querySelectorAll("#frames rect");
+		for (var i = 0; i < el.length; i++) {
+			orig_load(el[i], "fill")
+		}
+		var params = get_params();
+		delete params.s;
+		history.replaceState(null, null, parse_params(params));
+	}
+	function search_prompt() {
+		if (!searching) {
+			var term = prompt("Enter a search term (regexp " +
+			    "allowed, eg: ^ext4_)"
+			    + (ignorecase ? ", ignoring case" : "")
+			    + "\nPress Ctrl-i to toggle case sensitivity", "");
+			if (term != null) search(term);
+		} else {
+			reset_search();
+			searching = 0;
+			currentSearchTerm = null;
+			searchbtn.classList.remove("show");
+			searchbtn.firstChild.nodeValue = "Search"
+			matchedtxt.classList.add("hide");
+			matchedtxt.firstChild.nodeValue = ""
+		}
+	}
+	function search(term) {
+		if (term) currentSearchTerm = term;
+
+		var re = new RegExp(currentSearchTerm, ignorecase ? 'i' : '');
+		var el = document.getElementById("frames").children;
+		var matches = new Object();
+		var maxwidth = 0;
+		for (var i = 0; i < el.length; i++) {
+			var e = el[i];
+			var func = g_to_func(e);
+			var rect = find_child(e, "rect");
+			if (func == null || rect == null)
+				continue;
+
+			// Save max width. Only works as we have a root frame
+			var w = parseFloat(rect.attributes.width.value);
+			if (w > maxwidth)
+				maxwidth = w;
+
+			if (func.match(re)) {
+				// highlight
+				var x = parseFloat(rect.attributes.x.value);
+				orig_save(rect, "fill");
+				rect.attributes.fill.value = "rgb(230,0,230)";
+
+				// remember matches
+				if (matches[x] == undefined) {
+					matches[x] = w;
+				} else {
+					if (w > matches[x]) {
+						// overwrite with parent
+						matches[x] = w;
+					}
+				}
+				searching = 1;
+			}
+		}
+		if (!searching)
+			return;
+		var params = get_params();
+		params.s = currentSearchTerm;
+		history.replaceState(null, null, parse_params(params));
+
+		searchbtn.classList.add("show");
+		searchbtn.firstChild.nodeValue = "Reset Search";
+
+		// calculate percent matched, excluding vertical overlap
+		var count = 0;
+		var lastx = -1;
+		var lastw = 0;
+		var keys = Array();
+		for (k in matches) {
+			if (matches.hasOwnProperty(k))
+				keys.push(k);
+		}
+		// sort the matched frames by their x location
+		// ascending, then width descending
+		keys.sort(function(a, b){
+			return a - b;
+		});
+		// Step through frames saving only the biggest bottom-up frames
+		// thanks to the sort order. This relies on the tree property
+		// where children are always smaller than their parents.
+		var fudge = 0.0001;	// JavaScript floating point
+		for (var k in keys) {
+			var x = parseFloat(keys[k]);
+			var w = matches[keys[k]];
+			if (x >= lastx + lastw - fudge) {
+				count += w;
+				lastx = x;
+				lastw = w;
+			}
+		}
+		// display matched percent
+		matchedtxt.classList.remove("hide");
+		var pct = 100 * count / maxwidth;
+		if (pct != 100) pct = pct.toFixed(1)
+		matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%";
+	}
+]]>
+</script>
+<rect x="0.0" y="0" width="1200.0" height="2134.0" fill="url(#background)"  />
+<text id="title" x="600.00" y="24" >Flame Graph</text>
+<text id="details" x="10.00" y="2117" > </text>
+<text id="unzoom" x="10.00" y="24" class="hide">Reset Zoom</text>
+<text id="search" x="1090.00" y="24" >Search</text>
+<text id="ignorecase" x="1174.00" y="24" >ic</text>
+<text id="matched" x="1090.00" y="2117" > </text>
+<g id="frames">
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1861" width="0.2" height="15.0" fill="rgb(218,174,22)" rx="2" ry="2" />
+<text  x="1192.84" y="1871.5" ></text>
+</g>
+<g >
+<title>load_run_wasm_file (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="2021" width="0.2" height="15.0" fill="rgb(225,65,43)" rx="2" ry="2" />
+<text  x="1192.84" y="2031.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1061" width="0.2" height="15.0" fill="rgb(243,172,6)" rx="2" ry="2" />
+<text  x="1192.84" y="1071.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1141" width="0.2" height="15.0" fill="rgb(239,117,15)" rx="2" ry="2" />
+<text  x="1192.84" y="1151.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="501" width="0.2" height="15.0" fill="rgb(219,107,47)" rx="2" ry="2" />
+<text  x="1192.84" y="511.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1013" width="0.2" height="15.0" fill="rgb(241,34,43)" rx="2" ry="2" />
+<text  x="1192.84" y="1023.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="85" width="1179.8" height="15.0" fill="rgb(238,121,45)" rx="2" ry="2" />
+<text  x="13.00" y="95.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="837" width="0.2" height="15.0" fill="rgb(244,181,9)" rx="2" ry="2" />
+<text  x="1192.84" y="847.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1621" width="0.2" height="15.0" fill="rgb(243,60,29)" rx="2" ry="2" />
+<text  x="1192.84" y="1631.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1301" width="1179.8" height="15.0" fill="rgb(231,200,44)" rx="2" ry="2" />
+<text  x="13.00" y="1311.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>invoke_ii_i (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1941" width="0.2" height="15.0" fill="rgb(214,26,54)" rx="2" ry="2" />
+<text  x="1192.84" y="1951.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1301" width="0.2" height="15.0" fill="rgb(235,150,49)" rx="2" ry="2" />
+<text  x="1192.84" y="1311.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="453" width="1179.8" height="15.0" fill="rgb(232,146,6)" rx="2" ry="2" />
+<text  x="13.00" y="463.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1189" width="0.2" height="15.0" fill="rgb(209,56,9)" rx="2" ry="2" />
+<text  x="1192.84" y="1199.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="53" width="1179.8" height="15.0" fill="rgb(246,187,9)" rx="2" ry="2" />
+<text  x="13.00" y="63.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1333" width="0.2" height="15.0" fill="rgb(220,100,14)" rx="2" ry="2" />
+<text  x="1192.84" y="1343.5" ></text>
+</g>
+<g >
+<title>load_run_ackermann_aot (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="2037" width="0.2" height="15.0" fill="rgb(241,93,7)" rx="2" ry="2" />
+<text  x="1192.84" y="2047.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="597" width="1179.8" height="15.0" fill="rgb(212,218,48)" rx="2" ry="2" />
+<text  x="13.00" y="607.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1637" width="1179.8" height="15.0" fill="rgb(205,33,39)" rx="2" ry="2" />
+<text  x="13.00" y="1647.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1813" width="1179.8" height="15.0" fill="rgb(241,97,14)" rx="2" ry="2" />
+<text  x="13.00" y="1823.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="997" width="0.2" height="15.0" fill="rgb(223,78,8)" rx="2" ry="2" />
+<text  x="1192.84" y="1007.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1349" width="0.2" height="15.0" fill="rgb(251,214,28)" rx="2" ry="2" />
+<text  x="1192.84" y="1359.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1477" width="1179.8" height="15.0" fill="rgb(239,52,22)" rx="2" ry="2" />
+<text  x="13.00" y="1487.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1893" width="1179.8" height="15.0" fill="rgb(222,155,36)" rx="2" ry="2" />
+<text  x="13.00" y="1903.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1077" width="1179.8" height="15.0" fill="rgb(214,76,15)" rx="2" ry="2" />
+<text  x="13.00" y="1087.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="213" width="1179.8" height="15.0" fill="rgb(236,144,24)" rx="2" ry="2" />
+<text  x="13.00" y="223.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="261" width="1179.8" height="15.0" fill="rgb(216,144,33)" rx="2" ry="2" />
+<text  x="13.00" y="271.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1861" width="1179.8" height="15.0" fill="rgb(227,154,21)" rx="2" ry="2" />
+<text  x="13.00" y="1871.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="293" width="1179.8" height="15.0" fill="rgb(251,84,34)" rx="2" ry="2" />
+<text  x="13.00" y="303.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1589" width="1179.8" height="15.0" fill="rgb(249,46,42)" rx="2" ry="2" />
+<text  x="13.00" y="1599.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1973" width="1179.8" height="15.0" fill="rgb(218,84,35)" rx="2" ry="2" />
+<text  x="13.00" y="1983.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1733" width="0.2" height="15.0" fill="rgb(242,136,52)" rx="2" ry="2" />
+<text  x="1192.84" y="1743.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="469" width="0.2" height="15.0" fill="rgb(249,165,47)" rx="2" ry="2" />
+<text  x="1192.84" y="479.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="69" width="1179.8" height="15.0" fill="rgb(210,197,41)" rx="2" ry="2" />
+<text  x="13.00" y="79.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="725" width="0.2" height="15.0" fill="rgb(246,65,43)" rx="2" ry="2" />
+<text  x="1192.84" y="735.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="517" width="1179.8" height="15.0" fill="rgb(221,83,37)" rx="2" ry="2" />
+<text  x="13.00" y="527.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="949" width="0.2" height="15.0" fill="rgb(208,178,8)" rx="2" ry="2" />
+<text  x="1192.84" y="959.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="149" width="1179.8" height="15.0" fill="rgb(251,138,40)" rx="2" ry="2" />
+<text  x="13.00" y="159.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1493" width="1179.8" height="15.0" fill="rgb(228,62,47)" rx="2" ry="2" />
+<text  x="13.00" y="1503.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="613" width="1179.8" height="15.0" fill="rgb(241,115,50)" rx="2" ry="2" />
+<text  x="13.00" y="623.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="405" width="1179.8" height="15.0" fill="rgb(208,124,36)" rx="2" ry="2" />
+<text  x="13.00" y="415.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="517" width="0.2" height="15.0" fill="rgb(207,169,9)" rx="2" ry="2" />
+<text  x="1192.84" y="527.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1509" width="0.2" height="15.0" fill="rgb(212,205,54)" rx="2" ry="2" />
+<text  x="1192.84" y="1519.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1397" width="0.2" height="15.0" fill="rgb(209,153,11)" rx="2" ry="2" />
+<text  x="1192.84" y="1407.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="869" width="0.2" height="15.0" fill="rgb(240,204,19)" rx="2" ry="2" />
+<text  x="1192.84" y="879.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="773" width="0.2" height="15.0" fill="rgb(206,185,36)" rx="2" ry="2" />
+<text  x="1192.84" y="783.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="117" width="1179.8" height="15.0" fill="rgb(237,183,32)" rx="2" ry="2" />
+<text  x="13.00" y="127.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1765" width="0.2" height="15.0" fill="rgb(245,212,47)" rx="2" ry="2" />
+<text  x="1192.84" y="1775.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="101" width="1179.8" height="15.0" fill="rgb(243,180,50)" rx="2" ry="2" />
+<text  x="13.00" y="111.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="341" width="1179.8" height="15.0" fill="rgb(221,121,28)" rx="2" ry="2" />
+<text  x="13.00" y="351.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="901" width="0.2" height="15.0" fill="rgb(209,88,49)" rx="2" ry="2" />
+<text  x="1192.84" y="911.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1669" width="1179.8" height="15.0" fill="rgb(242,65,14)" rx="2" ry="2" />
+<text  x="13.00" y="1679.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="437" width="1179.8" height="15.0" fill="rgb(215,189,49)" rx="2" ry="2" />
+<text  x="13.00" y="447.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] run (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1925" width="0.2" height="15.0" fill="rgb(216,223,24)" rx="2" ry="2" />
+<text  x="1192.84" y="1935.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="181" width="1179.8" height="15.0" fill="rgb(230,198,10)" rx="2" ry="2" />
+<text  x="13.00" y="191.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1557" width="1179.8" height="15.0" fill="rgb(213,158,46)" rx="2" ry="2" />
+<text  x="13.00" y="1567.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1749" width="0.2" height="15.0" fill="rgb(230,172,22)" rx="2" ry="2" />
+<text  x="1192.84" y="1759.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="853" width="1179.8" height="15.0" fill="rgb(253,58,54)" rx="2" ry="2" />
+<text  x="13.00" y="863.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="373" width="1179.8" height="15.0" fill="rgb(212,43,7)" rx="2" ry="2" />
+<text  x="13.00" y="383.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1237" width="0.2" height="15.0" fill="rgb(216,145,25)" rx="2" ry="2" />
+<text  x="1192.84" y="1247.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="885" width="1179.8" height="15.0" fill="rgb(211,30,7)" rx="2" ry="2" />
+<text  x="13.00" y="895.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="741" width="1179.8" height="15.0" fill="rgb(218,138,48)" rx="2" ry="2" />
+<text  x="13.00" y="751.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="917" width="1179.8" height="15.0" fill="rgb(254,64,13)" rx="2" ry="2" />
+<text  x="13.00" y="927.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="629" width="0.2" height="15.0" fill="rgb(241,29,38)" rx="2" ry="2" />
+<text  x="1192.84" y="639.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1365" width="1179.8" height="15.0" fill="rgb(210,67,9)" rx="2" ry="2" />
+<text  x="13.00" y="1375.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1029" width="1179.8" height="15.0" fill="rgb(247,127,34)" rx="2" ry="2" />
+<text  x="13.00" y="1039.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1125" width="1179.8" height="15.0" fill="rgb(248,88,49)" rx="2" ry="2" />
+<text  x="13.00" y="1135.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="821" width="0.2" height="15.0" fill="rgb(238,93,54)" rx="2" ry="2" />
+<text  x="1192.84" y="831.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1141" width="1179.8" height="15.0" fill="rgb(254,88,20)" rx="2" ry="2" />
+<text  x="13.00" y="1151.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="805" width="0.2" height="15.0" fill="rgb(235,49,2)" rx="2" ry="2" />
+<text  x="1192.84" y="815.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1157" width="1179.8" height="15.0" fill="rgb(249,193,18)" rx="2" ry="2" />
+<text  x="13.00" y="1167.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="2021" width="1179.8" height="15.0" fill="rgb(216,212,43)" rx="2" ry="2" />
+<text  x="13.00" y="2031.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="837" width="1179.8" height="15.0" fill="rgb(223,141,25)" rx="2" ry="2" />
+<text  x="13.00" y="847.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1477" width="0.2" height="15.0" fill="rgb(248,107,12)" rx="2" ry="2" />
+<text  x="1192.84" y="1487.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1461" width="1179.8" height="15.0" fill="rgb(221,48,2)" rx="2" ry="2" />
+<text  x="13.00" y="1471.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1829" width="0.2" height="15.0" fill="rgb(249,67,32)" rx="2" ry="2" />
+<text  x="1192.84" y="1839.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1349" width="1179.8" height="15.0" fill="rgb(210,135,47)" rx="2" ry="2" />
+<text  x="13.00" y="1359.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="661" width="1179.8" height="15.0" fill="rgb(221,49,24)" rx="2" ry="2" />
+<text  x="13.00" y="671.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1653" width="0.2" height="15.0" fill="rgb(237,68,33)" rx="2" ry="2" />
+<text  x="1192.84" y="1663.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1093" width="1179.8" height="15.0" fill="rgb(224,183,14)" rx="2" ry="2" />
+<text  x="13.00" y="1103.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>wasm_runtime_call_wasm (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1989" width="0.2" height="15.0" fill="rgb(237,32,19)" rx="2" ry="2" />
+<text  x="1192.84" y="1999.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="613" width="0.2" height="15.0" fill="rgb(239,200,48)" rx="2" ry="2" />
+<text  x="1192.84" y="623.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1525" width="1179.8" height="15.0" fill="rgb(249,154,26)" rx="2" ry="2" />
+<text  x="13.00" y="1535.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="933" width="0.2" height="15.0" fill="rgb(221,55,12)" rx="2" ry="2" />
+<text  x="1192.84" y="943.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1813" width="0.2" height="15.0" fill="rgb(246,4,53)" rx="2" ry="2" />
+<text  x="1192.84" y="1823.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1589" width="0.2" height="15.0" fill="rgb(243,58,51)" rx="2" ry="2" />
+<text  x="1192.84" y="1599.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1285" width="0.2" height="15.0" fill="rgb(250,177,35)" rx="2" ry="2" />
+<text  x="1192.84" y="1295.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1461" width="0.2" height="15.0" fill="rgb(220,144,38)" rx="2" ry="2" />
+<text  x="1192.84" y="1471.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1605" width="0.2" height="15.0" fill="rgb(223,172,32)" rx="2" ry="2" />
+<text  x="1192.84" y="1615.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="549" width="1179.8" height="15.0" fill="rgb(207,97,17)" rx="2" ry="2" />
+<text  x="13.00" y="559.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1029" width="0.2" height="15.0" fill="rgb(236,194,28)" rx="2" ry="2" />
+<text  x="1192.84" y="1039.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1445" width="1179.8" height="15.0" fill="rgb(223,141,4)" rx="2" ry="2" />
+<text  x="13.00" y="1455.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1189" width="1179.8" height="15.0" fill="rgb(221,109,43)" rx="2" ry="2" />
+<text  x="13.00" y="1199.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="917" width="0.2" height="15.0" fill="rgb(246,77,30)" rx="2" ry="2" />
+<text  x="1192.84" y="927.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="421" width="1179.8" height="15.0" fill="rgb(209,36,5)" rx="2" ry="2" />
+<text  x="13.00" y="431.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1701" width="0.2" height="15.0" fill="rgb(220,195,37)" rx="2" ry="2" />
+<text  x="1192.84" y="1711.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="485" width="0.2" height="15.0" fill="rgb(240,38,48)" rx="2" ry="2" />
+<text  x="1192.84" y="495.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="677" width="0.2" height="15.0" fill="rgb(206,38,41)" rx="2" ry="2" />
+<text  x="1192.84" y="687.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="645" width="0.2" height="15.0" fill="rgb(205,182,30)" rx="2" ry="2" />
+<text  x="1192.84" y="655.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1989" width="1179.8" height="15.0" fill="rgb(221,221,3)" rx="2" ry="2" />
+<text  x="13.00" y="1999.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="165" width="1179.8" height="15.0" fill="rgb(243,45,16)" rx="2" ry="2" />
+<text  x="13.00" y="175.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="645" width="1179.8" height="15.0" fill="rgb(230,75,45)" rx="2" ry="2" />
+<text  x="13.00" y="655.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="389" width="1179.8" height="15.0" fill="rgb(206,184,25)" rx="2" ry="2" />
+<text  x="13.00" y="399.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1381" width="0.2" height="15.0" fill="rgb(234,222,31)" rx="2" ry="2" />
+<text  x="1192.84" y="1391.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1637" width="0.2" height="15.0" fill="rgb(232,49,28)" rx="2" ry="2" />
+<text  x="1192.84" y="1647.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1109" width="1179.8" height="15.0" fill="rgb(254,43,52)" rx="2" ry="2" />
+<text  x="13.00" y="1119.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1333" width="1179.8" height="15.0" fill="rgb(237,136,26)" rx="2" ry="2" />
+<text  x="13.00" y="1343.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1397" width="1179.8" height="15.0" fill="rgb(211,123,35)" rx="2" ry="2" />
+<text  x="13.00" y="1407.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1413" width="1179.8" height="15.0" fill="rgb(232,105,53)" rx="2" ry="2" />
+<text  x="13.00" y="1423.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1221" width="0.2" height="15.0" fill="rgb(244,118,46)" rx="2" ry="2" />
+<text  x="1192.84" y="1231.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1429" width="1179.8" height="15.0" fill="rgb(239,149,50)" rx="2" ry="2" />
+<text  x="13.00" y="1439.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1909" width="0.2" height="15.0" fill="rgb(238,51,52)" rx="2" ry="2" />
+<text  x="1192.84" y="1919.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="2053" width="1179.8" height="15.0" fill="rgb(241,179,35)" rx="2" ry="2" />
+<text  x="13.00" y="2063.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1205" width="0.2" height="15.0" fill="rgb(228,57,46)" rx="2" ry="2" />
+<text  x="1192.84" y="1215.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1685" width="1179.8" height="15.0" fill="rgb(220,77,21)" rx="2" ry="2" />
+<text  x="13.00" y="1695.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1557" width="0.2" height="15.0" fill="rgb(237,7,48)" rx="2" ry="2" />
+<text  x="1192.84" y="1567.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="789" width="0.2" height="15.0" fill="rgb(221,125,1)" rx="2" ry="2" />
+<text  x="1192.84" y="799.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1173" width="0.2" height="15.0" fill="rgb(215,103,28)" rx="2" ry="2" />
+<text  x="1192.84" y="1183.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1253" width="0.2" height="15.0" fill="rgb(233,212,39)" rx="2" ry="2" />
+<text  x="1192.84" y="1263.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1317" width="1179.8" height="15.0" fill="rgb(220,194,22)" rx="2" ry="2" />
+<text  x="13.00" y="1327.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="629" width="1179.8" height="15.0" fill="rgb(205,164,36)" rx="2" ry="2" />
+<text  x="13.00" y="639.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="709" width="0.2" height="15.0" fill="rgb(246,179,15)" rx="2" ry="2" />
+<text  x="1192.84" y="719.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1509" width="1179.8" height="15.0" fill="rgb(223,116,20)" rx="2" ry="2" />
+<text  x="13.00" y="1519.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>all (11,485,868,643 samples, 100%)</title><rect x="10.0" y="2085" width="1180.0" height="15.0" fill="rgb(234,224,30)" rx="2" ry="2" />
+<text  x="13.00" y="2095.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="2005" width="1179.8" height="15.0" fill="rgb(246,20,52)" rx="2" ry="2" />
+<text  x="13.00" y="2015.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>aot_call_function (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1973" width="0.2" height="15.0" fill="rgb(240,138,20)" rx="2" ry="2" />
+<text  x="1192.84" y="1983.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="533" width="0.2" height="15.0" fill="rgb(217,8,47)" rx="2" ry="2" />
+<text  x="1192.84" y="543.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1045" width="0.2" height="15.0" fill="rgb(247,86,42)" rx="2" ry="2" />
+<text  x="1192.84" y="1055.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="309" width="1179.8" height="15.0" fill="rgb(234,75,39)" rx="2" ry="2" />
+<text  x="13.00" y="319.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1109" width="0.2" height="15.0" fill="rgb(251,61,5)" rx="2" ry="2" />
+<text  x="1192.84" y="1119.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="741" width="0.2" height="15.0" fill="rgb(238,91,42)" rx="2" ry="2" />
+<text  x="1192.84" y="751.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1077" width="0.2" height="15.0" fill="rgb(217,41,30)" rx="2" ry="2" />
+<text  x="1192.84" y="1087.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1781" width="1179.8" height="15.0" fill="rgb(229,182,16)" rx="2" ry="2" />
+<text  x="13.00" y="1791.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1445" width="0.2" height="15.0" fill="rgb(238,210,23)" rx="2" ry="2" />
+<text  x="1192.84" y="1455.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="869" width="1179.8" height="15.0" fill="rgb(247,74,49)" rx="2" ry="2" />
+<text  x="13.00" y="879.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1669" width="0.2" height="15.0" fill="rgb(253,141,39)" rx="2" ry="2" />
+<text  x="1192.84" y="1679.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="981" width="1179.8" height="15.0" fill="rgb(213,0,50)" rx="2" ry="2" />
+<text  x="13.00" y="991.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>linux_perf_samp (11,485,868,643 samples, 100.00%)</title><rect x="10.0" y="2069" width="1180.0" height="15.0" fill="rgb(224,176,51)" rx="2" ry="2" />
+<text  x="13.00" y="2079.5" >linux_perf_samp</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1573" width="1179.8" height="15.0" fill="rgb(206,154,16)" rx="2" ry="2" />
+<text  x="13.00" y="1583.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="933" width="1179.8" height="15.0" fill="rgb(225,96,26)" rx="2" ry="2" />
+<text  x="13.00" y="943.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1621" width="1179.8" height="15.0" fill="rgb(233,65,14)" rx="2" ry="2" />
+<text  x="13.00" y="1631.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1221" width="1179.8" height="15.0" fill="rgb(213,22,53)" rx="2" ry="2" />
+<text  x="13.00" y="1231.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1877" width="0.2" height="15.0" fill="rgb(245,184,19)" rx="2" ry="2" />
+<text  x="1192.84" y="1887.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="965" width="0.2" height="15.0" fill="rgb(240,98,8)" rx="2" ry="2" />
+<text  x="1192.84" y="975.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1893" width="0.2" height="15.0" fill="rgb(225,104,38)" rx="2" ry="2" />
+<text  x="1192.84" y="1903.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="469" width="1179.8" height="15.0" fill="rgb(247,17,10)" rx="2" ry="2" />
+<text  x="13.00" y="479.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1045" width="1179.8" height="15.0" fill="rgb(248,102,9)" rx="2" ry="2" />
+<text  x="13.00" y="1055.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1797" width="1179.8" height="15.0" fill="rgb(224,36,45)" rx="2" ry="2" />
+<text  x="13.00" y="1807.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1269" width="0.2" height="15.0" fill="rgb(214,98,12)" rx="2" ry="2" />
+<text  x="1192.84" y="1279.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="709" width="1179.8" height="15.0" fill="rgb(216,9,53)" rx="2" ry="2" />
+<text  x="13.00" y="719.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1941" width="1179.8" height="15.0" fill="rgb(223,208,41)" rx="2" ry="2" />
+<text  x="13.00" y="1951.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1381" width="1179.8" height="15.0" fill="rgb(212,101,6)" rx="2" ry="2" />
+<text  x="13.00" y="1391.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="565" width="0.2" height="15.0" fill="rgb(235,63,50)" rx="2" ry="2" />
+<text  x="1192.84" y="575.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1285" width="1179.8" height="15.0" fill="rgb(209,1,41)" rx="2" ry="2" />
+<text  x="13.00" y="1295.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="805" width="1179.8" height="15.0" fill="rgb(218,147,12)" rx="2" ry="2" />
+<text  x="13.00" y="815.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="197" width="1179.8" height="15.0" fill="rgb(249,115,38)" rx="2" ry="2" />
+<text  x="13.00" y="207.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1877" width="1179.8" height="15.0" fill="rgb(238,104,17)" rx="2" ry="2" />
+<text  x="13.00" y="1887.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="357" width="1179.8" height="15.0" fill="rgb(235,165,22)" rx="2" ry="2" />
+<text  x="13.00" y="367.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1365" width="0.2" height="15.0" fill="rgb(250,176,49)" rx="2" ry="2" />
+<text  x="1192.84" y="1375.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="501" width="1179.8" height="15.0" fill="rgb(241,7,11)" rx="2" ry="2" />
+<text  x="13.00" y="511.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="533" width="1179.8" height="15.0" fill="rgb(245,204,7)" rx="2" ry="2" />
+<text  x="13.00" y="543.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1173" width="1179.8" height="15.0" fill="rgb(248,47,5)" rx="2" ry="2" />
+<text  x="13.00" y="1183.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="901" width="1179.8" height="15.0" fill="rgb(227,108,16)" rx="2" ry="2" />
+<text  x="13.00" y="911.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>wasm_func_call (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="2005" width="0.2" height="15.0" fill="rgb(211,210,10)" rx="2" ry="2" />
+<text  x="1192.84" y="2015.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="693" width="0.2" height="15.0" fill="rgb(218,72,15)" rx="2" ry="2" />
+<text  x="1192.84" y="703.5" ></text>
+</g>
+<g >
+<title>invoke_native_with_hw_bound_check (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1957" width="0.2" height="15.0" fill="rgb(221,175,9)" rx="2" ry="2" />
+<text  x="1192.84" y="1967.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="965" width="1179.8" height="15.0" fill="rgb(227,58,24)" rx="2" ry="2" />
+<text  x="13.00" y="975.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="245" width="1179.8" height="15.0" fill="rgb(238,113,44)" rx="2" ry="2" />
+<text  x="13.00" y="255.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1125" width="0.2" height="15.0" fill="rgb(246,168,9)" rx="2" ry="2" />
+<text  x="1192.84" y="1135.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1845" width="1179.8" height="15.0" fill="rgb(205,4,46)" rx="2" ry="2" />
+<text  x="13.00" y="1855.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1541" width="1179.8" height="15.0" fill="rgb(248,85,7)" rx="2" ry="2" />
+<text  x="13.00" y="1551.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1685" width="0.2" height="15.0" fill="rgb(238,102,36)" rx="2" ry="2" />
+<text  x="1192.84" y="1695.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1605" width="1179.8" height="15.0" fill="rgb(213,52,48)" rx="2" ry="2" />
+<text  x="13.00" y="1615.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1829" width="1179.8" height="15.0" fill="rgb(232,215,1)" rx="2" ry="2" />
+<text  x="13.00" y="1839.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1013" width="1179.8" height="15.0" fill="rgb(227,122,14)" rx="2" ry="2" />
+<text  x="13.00" y="1023.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1205" width="1179.8" height="15.0" fill="rgb(253,63,9)" rx="2" ry="2" />
+<text  x="13.00" y="1215.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="821" width="1179.8" height="15.0" fill="rgb(206,57,36)" rx="2" ry="2" />
+<text  x="13.00" y="831.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1845" width="0.2" height="15.0" fill="rgb(233,34,49)" rx="2" ry="2" />
+<text  x="1192.84" y="1855.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="725" width="1179.8" height="15.0" fill="rgb(205,150,16)" rx="2" ry="2" />
+<text  x="13.00" y="735.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1797" width="0.2" height="15.0" fill="rgb(245,1,33)" rx="2" ry="2" />
+<text  x="1192.84" y="1807.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1909" width="1179.8" height="15.0" fill="rgb(243,174,38)" rx="2" ry="2" />
+<text  x="13.00" y="1919.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="853" width="0.2" height="15.0" fill="rgb(229,193,30)" rx="2" ry="2" />
+<text  x="1192.84" y="863.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1525" width="0.2" height="15.0" fill="rgb(234,127,25)" rx="2" ry="2" />
+<text  x="1192.84" y="1535.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="325" width="1179.8" height="15.0" fill="rgb(229,105,38)" rx="2" ry="2" />
+<text  x="13.00" y="335.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1269" width="1179.8" height="15.0" fill="rgb(249,102,22)" rx="2" ry="2" />
+<text  x="13.00" y="1279.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="2037" width="1179.8" height="15.0" fill="rgb(218,221,33)" rx="2" ry="2" />
+<text  x="13.00" y="2047.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1925" width="1179.8" height="15.0" fill="rgb(206,117,1)" rx="2" ry="2" />
+<text  x="13.00" y="1935.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="661" width="0.2" height="15.0" fill="rgb(241,69,7)" rx="2" ry="2" />
+<text  x="1192.84" y="671.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1413" width="0.2" height="15.0" fill="rgb(217,186,52)" rx="2" ry="2" />
+<text  x="1192.84" y="1423.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="693" width="1179.8" height="15.0" fill="rgb(235,84,10)" rx="2" ry="2" />
+<text  x="13.00" y="703.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="37" width="1179.8" height="15.0" fill="rgb(233,70,49)" rx="2" ry="2" />
+<text  x="13.00" y="47.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="581" width="1179.8" height="15.0" fill="rgb(249,47,31)" rx="2" ry="2" />
+<text  x="13.00" y="591.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="789" width="1179.8" height="15.0" fill="rgb(219,218,14)" rx="2" ry="2" />
+<text  x="13.00" y="799.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1717" width="1179.8" height="15.0" fill="rgb(239,227,14)" rx="2" ry="2" />
+<text  x="13.00" y="1727.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1317" width="0.2" height="15.0" fill="rgb(236,8,9)" rx="2" ry="2" />
+<text  x="1192.84" y="1327.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="885" width="0.2" height="15.0" fill="rgb(239,111,30)" rx="2" ry="2" />
+<text  x="1192.84" y="895.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1749" width="1179.8" height="15.0" fill="rgb(213,176,7)" rx="2" ry="2" />
+<text  x="13.00" y="1759.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1429" width="0.2" height="15.0" fill="rgb(231,165,14)" rx="2" ry="2" />
+<text  x="1192.84" y="1439.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="677" width="1179.8" height="15.0" fill="rgb(209,52,46)" rx="2" ry="2" />
+<text  x="13.00" y="687.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="997" width="1179.8" height="15.0" fill="rgb(233,6,33)" rx="2" ry="2" />
+<text  x="13.00" y="1007.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1253" width="1179.8" height="15.0" fill="rgb(209,58,18)" rx="2" ry="2" />
+<text  x="13.00" y="1263.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="581" width="0.2" height="15.0" fill="rgb(234,43,46)" rx="2" ry="2" />
+<text  x="1192.84" y="591.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="597" width="0.2" height="15.0" fill="rgb(244,24,24)" rx="2" ry="2" />
+<text  x="1192.84" y="607.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1701" width="1179.8" height="15.0" fill="rgb(222,63,34)" rx="2" ry="2" />
+<text  x="13.00" y="1711.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1781" width="0.2" height="15.0" fill="rgb(217,90,47)" rx="2" ry="2" />
+<text  x="1192.84" y="1791.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1157" width="0.2" height="15.0" fill="rgb(253,43,33)" rx="2" ry="2" />
+<text  x="1192.84" y="1167.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="549" width="0.2" height="15.0" fill="rgb(232,145,34)" rx="2" ry="2" />
+<text  x="1192.84" y="559.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1717" width="0.2" height="15.0" fill="rgb(205,181,51)" rx="2" ry="2" />
+<text  x="1192.84" y="1727.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="981" width="0.2" height="15.0" fill="rgb(219,54,0)" rx="2" ry="2" />
+<text  x="1192.84" y="991.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="757" width="0.2" height="15.0" fill="rgb(229,125,2)" rx="2" ry="2" />
+<text  x="1192.84" y="767.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1093" width="0.2" height="15.0" fill="rgb(236,206,31)" rx="2" ry="2" />
+<text  x="1192.84" y="1103.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1541" width="0.2" height="15.0" fill="rgb(244,129,48)" rx="2" ry="2" />
+<text  x="1192.84" y="1551.5" ></text>
+</g>
+<g >
+<title>start_thread (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="2053" width="0.2" height="15.0" fill="rgb(205,117,13)" rx="2" ry="2" />
+<text  x="1192.84" y="2063.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="757" width="1179.8" height="15.0" fill="rgb(227,82,20)" rx="2" ry="2" />
+<text  x="13.00" y="767.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="949" width="1179.8" height="15.0" fill="rgb(252,89,1)" rx="2" ry="2" />
+<text  x="13.00" y="959.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="277" width="1179.8" height="15.0" fill="rgb(236,80,43)" rx="2" ry="2" />
+<text  x="13.00" y="287.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1061" width="1179.8" height="15.0" fill="rgb(216,86,52)" rx="2" ry="2" />
+<text  x="13.00" y="1071.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1237" width="1179.8" height="15.0" fill="rgb(230,197,6)" rx="2" ry="2" />
+<text  x="13.00" y="1247.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1765" width="1179.8" height="15.0" fill="rgb(206,29,27)" rx="2" ry="2" />
+<text  x="13.00" y="1775.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="773" width="1179.8" height="15.0" fill="rgb(232,155,33)" rx="2" ry="2" />
+<text  x="13.00" y="783.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1493" width="0.2" height="15.0" fill="rgb(223,50,2)" rx="2" ry="2" />
+<text  x="1192.84" y="1503.5" ></text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="485" width="1179.8" height="15.0" fill="rgb(210,229,15)" rx="2" ry="2" />
+<text  x="13.00" y="495.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1957" width="1179.8" height="15.0" fill="rgb(230,138,34)" rx="2" ry="2" />
+<text  x="13.00" y="1967.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1733" width="1179.8" height="15.0" fill="rgb(244,88,42)" rx="2" ry="2" />
+<text  x="13.00" y="1743.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="133" width="1179.8" height="15.0" fill="rgb(245,210,18)" rx="2" ry="2" />
+<text  x="13.00" y="143.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="229" width="1179.8" height="15.0" fill="rgb(205,130,10)" rx="2" ry="2" />
+<text  x="13.00" y="239.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="565" width="1179.8" height="15.0" fill="rgb(244,179,48)" rx="2" ry="2" />
+<text  x="13.00" y="575.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%)</title><rect x="10.0" y="1653" width="1179.8" height="15.0" fill="rgb(226,169,30)" rx="2" ry="2" />
+<text  x="13.00" y="1663.5" >[Wasm] [ackermann2] ackermann</text>
+</g>
+<g >
+<title>[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%)</title><rect x="1189.8" y="1573" width="0.2" height="15.0" fill="rgb(245,51,51)" rx="2" ry="2" />
+<text  x="1192.84" y="1583.5" ></text>
+</g>
+</g>
+</svg>

+ 605 - 0
samples/linux-perf/pics/perf.fib.svg

@@ -0,0 +1,605 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" width="1200" height="758" onload="init(evt)" viewBox="0 0 1200 758" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<!-- Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples. -->
+<!-- NOTES:  -->
+<defs>
+	<linearGradient id="background" y1="0" y2="1" x1="0" x2="0" >
+		<stop stop-color="#eeeeee" offset="5%" />
+		<stop stop-color="#eeeeb0" offset="95%" />
+	</linearGradient>
+</defs>
+<style type="text/css">
+	text { font-family:Verdana; font-size:12px; fill:rgb(0,0,0); }
+	#search, #ignorecase { opacity:0.1; cursor:pointer; }
+	#search:hover, #search.show, #ignorecase:hover, #ignorecase.show { opacity:1; }
+	#subtitle { text-anchor:middle; font-color:rgb(160,160,160); }
+	#title { text-anchor:middle; font-size:17px}
+	#unzoom { cursor:pointer; }
+	#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
+	.hide { display:none; }
+	.parent { opacity:0.5; }
+</style>
+<script type="text/ecmascript">
+<![CDATA[
+	"use strict";
+	var details, searchbtn, unzoombtn, matchedtxt, svg, searching, currentSearchTerm, ignorecase, ignorecaseBtn;
+	function init(evt) {
+		details = document.getElementById("details").firstChild;
+		searchbtn = document.getElementById("search");
+		ignorecaseBtn = document.getElementById("ignorecase");
+		unzoombtn = document.getElementById("unzoom");
+		matchedtxt = document.getElementById("matched");
+		svg = document.getElementsByTagName("svg")[0];
+		searching = 0;
+		currentSearchTerm = null;
+
+		// use GET parameters to restore a flamegraphs state.
+		var params = get_params();
+		if (params.x && params.y)
+			zoom(find_group(document.querySelector('[x="' + params.x + '"][y="' + params.y + '"]')));
+                if (params.s) search(params.s);
+	}
+
+	// event listeners
+	window.addEventListener("click", function(e) {
+		var target = find_group(e.target);
+		if (target) {
+			if (target.nodeName == "a") {
+				if (e.ctrlKey === false) return;
+				e.preventDefault();
+			}
+			if (target.classList.contains("parent")) unzoom(true);
+			zoom(target);
+			if (!document.querySelector('.parent')) {
+				// we have basically done a clearzoom so clear the url
+				var params = get_params();
+				if (params.x) delete params.x;
+				if (params.y) delete params.y;
+				history.replaceState(null, null, parse_params(params));
+				unzoombtn.classList.add("hide");
+				return;
+			}
+
+			// set parameters for zoom state
+			var el = target.querySelector("rect");
+			if (el && el.attributes && el.attributes.y && el.attributes._orig_x) {
+				var params = get_params()
+				params.x = el.attributes._orig_x.value;
+				params.y = el.attributes.y.value;
+				history.replaceState(null, null, parse_params(params));
+			}
+		}
+		else if (e.target.id == "unzoom") clearzoom();
+		else if (e.target.id == "search") search_prompt();
+		else if (e.target.id == "ignorecase") toggle_ignorecase();
+	}, false)
+
+	// mouse-over for info
+	// show
+	window.addEventListener("mouseover", function(e) {
+		var target = find_group(e.target);
+		if (target) details.nodeValue = "Function: " + g_to_text(target);
+	}, false)
+
+	// clear
+	window.addEventListener("mouseout", function(e) {
+		var target = find_group(e.target);
+		if (target) details.nodeValue = ' ';
+	}, false)
+
+	// ctrl-F for search
+	// ctrl-I to toggle case-sensitive search
+	window.addEventListener("keydown",function (e) {
+		if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
+			e.preventDefault();
+			search_prompt();
+		}
+		else if (e.ctrlKey && e.keyCode === 73) {
+			e.preventDefault();
+			toggle_ignorecase();
+		}
+	}, false)
+
+	// functions
+	function get_params() {
+		var params = {};
+		var paramsarr = window.location.search.substr(1).split('&');
+		for (var i = 0; i < paramsarr.length; ++i) {
+			var tmp = paramsarr[i].split("=");
+			if (!tmp[0] || !tmp[1]) continue;
+			params[tmp[0]]  = decodeURIComponent(tmp[1]);
+		}
+		return params;
+	}
+	function parse_params(params) {
+		var uri = "?";
+		for (var key in params) {
+			uri += key + '=' + encodeURIComponent(params[key]) + '&';
+		}
+		if (uri.slice(-1) == "&")
+			uri = uri.substring(0, uri.length - 1);
+		if (uri == '?')
+			uri = window.location.href.split('?')[0];
+		return uri;
+	}
+	function find_child(node, selector) {
+		var children = node.querySelectorAll(selector);
+		if (children.length) return children[0];
+	}
+	function find_group(node) {
+		var parent = node.parentElement;
+		if (!parent) return;
+		if (parent.id == "frames") return node;
+		return find_group(parent);
+	}
+	function orig_save(e, attr, val) {
+		if (e.attributes["_orig_" + attr] != undefined) return;
+		if (e.attributes[attr] == undefined) return;
+		if (val == undefined) val = e.attributes[attr].value;
+		e.setAttribute("_orig_" + attr, val);
+	}
+	function orig_load(e, attr) {
+		if (e.attributes["_orig_"+attr] == undefined) return;
+		e.attributes[attr].value = e.attributes["_orig_" + attr].value;
+		e.removeAttribute("_orig_"+attr);
+	}
+	function g_to_text(e) {
+		var text = find_child(e, "title").firstChild.nodeValue;
+		return (text)
+	}
+	function g_to_func(e) {
+		var func = g_to_text(e);
+		// if there's any manipulation we want to do to the function
+		// name before it's searched, do it here before returning.
+		return (func);
+	}
+	function update_text(e) {
+		var r = find_child(e, "rect");
+		var t = find_child(e, "text");
+		var w = parseFloat(r.attributes.width.value) -3;
+		var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,"");
+		t.attributes.x.value = parseFloat(r.attributes.x.value) + 3;
+
+		// Smaller than this size won't fit anything
+		if (w < 2 * 12 * 0.59) {
+			t.textContent = "";
+			return;
+		}
+
+		t.textContent = txt;
+		var sl = t.getSubStringLength(0, txt.length);
+		// check if only whitespace or if we can fit the entire string into width w
+		if (/^ *$/.test(txt) || sl < w)
+			return;
+
+		// this isn't perfect, but gives a good starting point
+		// and avoids calling getSubStringLength too often
+		var start = Math.floor((w/sl) * txt.length);
+		for (var x = start; x > 0; x = x-2) {
+			if (t.getSubStringLength(0, x + 2) <= w) {
+				t.textContent = txt.substring(0, x) + "..";
+				return;
+			}
+		}
+		t.textContent = "";
+	}
+
+	// zoom
+	function zoom_reset(e) {
+		if (e.attributes != undefined) {
+			orig_load(e, "x");
+			orig_load(e, "width");
+		}
+		if (e.childNodes == undefined) return;
+		for (var i = 0, c = e.childNodes; i < c.length; i++) {
+			zoom_reset(c[i]);
+		}
+	}
+	function zoom_child(e, x, ratio) {
+		if (e.attributes != undefined) {
+			if (e.attributes.x != undefined) {
+				orig_save(e, "x");
+				e.attributes.x.value = (parseFloat(e.attributes.x.value) - x - 10) * ratio + 10;
+				if (e.tagName == "text")
+					e.attributes.x.value = find_child(e.parentNode, "rect[x]").attributes.x.value + 3;
+			}
+			if (e.attributes.width != undefined) {
+				orig_save(e, "width");
+				e.attributes.width.value = parseFloat(e.attributes.width.value) * ratio;
+			}
+		}
+
+		if (e.childNodes == undefined) return;
+		for (var i = 0, c = e.childNodes; i < c.length; i++) {
+			zoom_child(c[i], x - 10, ratio);
+		}
+	}
+	function zoom_parent(e) {
+		if (e.attributes) {
+			if (e.attributes.x != undefined) {
+				orig_save(e, "x");
+				e.attributes.x.value = 10;
+			}
+			if (e.attributes.width != undefined) {
+				orig_save(e, "width");
+				e.attributes.width.value = parseInt(svg.width.baseVal.value) - (10 * 2);
+			}
+		}
+		if (e.childNodes == undefined) return;
+		for (var i = 0, c = e.childNodes; i < c.length; i++) {
+			zoom_parent(c[i]);
+		}
+	}
+	function zoom(node) {
+		var attr = find_child(node, "rect").attributes;
+		var width = parseFloat(attr.width.value);
+		var xmin = parseFloat(attr.x.value);
+		var xmax = parseFloat(xmin + width);
+		var ymin = parseFloat(attr.y.value);
+		var ratio = (svg.width.baseVal.value - 2 * 10) / width;
+
+		// XXX: Workaround for JavaScript float issues (fix me)
+		var fudge = 0.0001;
+
+		unzoombtn.classList.remove("hide");
+
+		var el = document.getElementById("frames").children;
+		for (var i = 0; i < el.length; i++) {
+			var e = el[i];
+			var a = find_child(e, "rect").attributes;
+			var ex = parseFloat(a.x.value);
+			var ew = parseFloat(a.width.value);
+			var upstack;
+			// Is it an ancestor
+			if (0 == 0) {
+				upstack = parseFloat(a.y.value) > ymin;
+			} else {
+				upstack = parseFloat(a.y.value) < ymin;
+			}
+			if (upstack) {
+				// Direct ancestor
+				if (ex <= xmin && (ex+ew+fudge) >= xmax) {
+					e.classList.add("parent");
+					zoom_parent(e);
+					update_text(e);
+				}
+				// not in current path
+				else
+					e.classList.add("hide");
+			}
+			// Children maybe
+			else {
+				// no common path
+				if (ex < xmin || ex + fudge >= xmax) {
+					e.classList.add("hide");
+				}
+				else {
+					zoom_child(e, xmin, ratio);
+					update_text(e);
+				}
+			}
+		}
+		search();
+	}
+	function unzoom(dont_update_text) {
+		unzoombtn.classList.add("hide");
+		var el = document.getElementById("frames").children;
+		for(var i = 0; i < el.length; i++) {
+			el[i].classList.remove("parent");
+			el[i].classList.remove("hide");
+			zoom_reset(el[i]);
+			if(!dont_update_text) update_text(el[i]);
+		}
+		search();
+	}
+	function clearzoom() {
+		unzoom();
+
+		// remove zoom state
+		var params = get_params();
+		if (params.x) delete params.x;
+		if (params.y) delete params.y;
+		history.replaceState(null, null, parse_params(params));
+	}
+
+	// search
+	function toggle_ignorecase() {
+		ignorecase = !ignorecase;
+		if (ignorecase) {
+			ignorecaseBtn.classList.add("show");
+		} else {
+			ignorecaseBtn.classList.remove("show");
+		}
+		reset_search();
+		search();
+	}
+	function reset_search() {
+		var el = document.querySelectorAll("#frames rect");
+		for (var i = 0; i < el.length; i++) {
+			orig_load(el[i], "fill")
+		}
+		var params = get_params();
+		delete params.s;
+		history.replaceState(null, null, parse_params(params));
+	}
+	function search_prompt() {
+		if (!searching) {
+			var term = prompt("Enter a search term (regexp " +
+			    "allowed, eg: ^ext4_)"
+			    + (ignorecase ? ", ignoring case" : "")
+			    + "\nPress Ctrl-i to toggle case sensitivity", "");
+			if (term != null) search(term);
+		} else {
+			reset_search();
+			searching = 0;
+			currentSearchTerm = null;
+			searchbtn.classList.remove("show");
+			searchbtn.firstChild.nodeValue = "Search"
+			matchedtxt.classList.add("hide");
+			matchedtxt.firstChild.nodeValue = ""
+		}
+	}
+	function search(term) {
+		if (term) currentSearchTerm = term;
+
+		var re = new RegExp(currentSearchTerm, ignorecase ? 'i' : '');
+		var el = document.getElementById("frames").children;
+		var matches = new Object();
+		var maxwidth = 0;
+		for (var i = 0; i < el.length; i++) {
+			var e = el[i];
+			var func = g_to_func(e);
+			var rect = find_child(e, "rect");
+			if (func == null || rect == null)
+				continue;
+
+			// Save max width. Only works as we have a root frame
+			var w = parseFloat(rect.attributes.width.value);
+			if (w > maxwidth)
+				maxwidth = w;
+
+			if (func.match(re)) {
+				// highlight
+				var x = parseFloat(rect.attributes.x.value);
+				orig_save(rect, "fill");
+				rect.attributes.fill.value = "rgb(230,0,230)";
+
+				// remember matches
+				if (matches[x] == undefined) {
+					matches[x] = w;
+				} else {
+					if (w > matches[x]) {
+						// overwrite with parent
+						matches[x] = w;
+					}
+				}
+				searching = 1;
+			}
+		}
+		if (!searching)
+			return;
+		var params = get_params();
+		params.s = currentSearchTerm;
+		history.replaceState(null, null, parse_params(params));
+
+		searchbtn.classList.add("show");
+		searchbtn.firstChild.nodeValue = "Reset Search";
+
+		// calculate percent matched, excluding vertical overlap
+		var count = 0;
+		var lastx = -1;
+		var lastw = 0;
+		var keys = Array();
+		for (k in matches) {
+			if (matches.hasOwnProperty(k))
+				keys.push(k);
+		}
+		// sort the matched frames by their x location
+		// ascending, then width descending
+		keys.sort(function(a, b){
+			return a - b;
+		});
+		// Step through frames saving only the biggest bottom-up frames
+		// thanks to the sort order. This relies on the tree property
+		// where children are always smaller than their parents.
+		var fudge = 0.0001;	// JavaScript floating point
+		for (var k in keys) {
+			var x = parseFloat(keys[k]);
+			var w = matches[keys[k]];
+			if (x >= lastx + lastw - fudge) {
+				count += w;
+				lastx = x;
+				lastw = w;
+			}
+		}
+		// display matched percent
+		matchedtxt.classList.remove("hide");
+		var pct = 100 * count / maxwidth;
+		if (pct != 100) pct = pct.toFixed(1)
+		matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%";
+	}
+]]>
+</script>
+<rect x="0.0" y="0" width="1200.0" height="758.0" fill="url(#background)"  />
+<text id="title" x="600.00" y="24" >Flame Graph</text>
+<text id="details" x="10.00" y="741" > </text>
+<text id="unzoom" x="10.00" y="24" class="hide">Reset Zoom</text>
+<text id="search" x="1090.00" y="24" >Search</text>
+<text id="ignorecase" x="1174.00" y="24" >ic</text>
+<text id="matched" x="1090.00" y="741" > </text>
+<g id="frames">
+<g >
+<title>[Wasm] [fib2] fibonacci (1,321,095,222 samples, 93.80%)</title><rect x="83.2" y="341" width="1106.8" height="15.0" fill="rgb(218,159,32)" rx="2" ry="2" />
+<text  x="86.21" y="351.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (382,407,564 samples, 27.15%)</title><rect x="869.6" y="213" width="320.4" height="15.0" fill="rgb(245,35,33)" rx="2" ry="2" />
+<text  x="872.59" y="223.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (15,340,273 samples, 1.09%)</title><rect x="1177.1" y="101" width="12.9" height="15.0" fill="rgb(213,210,13)" rx="2" ry="2" />
+<text  x="1180.11" y="111.5" ></text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,359,552,763 samples, 96.53%)</title><rect x="51.0" y="357" width="1139.0" height="15.0" fill="rgb(252,138,7)" rx="2" ry="2" />
+<text  x="53.99" y="367.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="517" width="1180.0" height="15.0" fill="rgb(218,120,5)" rx="2" ry="2" />
+<text  x="13.00" y="527.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (27,274,310 samples, 1.94%)</title><rect x="1167.1" y="117" width="22.9" height="15.0" fill="rgb(234,117,51)" rx="2" ry="2" />
+<text  x="1170.11" y="127.5" >[..</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (62,450,767 samples, 4.43%)</title><rect x="1137.6" y="149" width="52.4" height="15.0" fill="rgb(225,21,1)" rx="2" ry="2" />
+<text  x="1140.64" y="159.5" >[Wasm..</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="485" width="1180.0" height="15.0" fill="rgb(233,228,10)" rx="2" ry="2" />
+<text  x="13.00" y="495.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,388,674,508 samples, 98.59%)</title><rect x="26.6" y="389" width="1163.4" height="15.0" fill="rgb(253,174,18)" rx="2" ry="2" />
+<text  x="29.59" y="399.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,170,751,868 samples, 83.12%)</title><rect x="209.2" y="309" width="980.8" height="15.0" fill="rgb(227,120,21)" rx="2" ry="2" />
+<text  x="212.17" y="319.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="533" width="1180.0" height="15.0" fill="rgb(254,106,47)" rx="2" ry="2" />
+<text  x="13.00" y="543.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (120,820,158 samples, 8.58%)</title><rect x="1088.7" y="165" width="101.3" height="15.0" fill="rgb(205,47,33)" rx="2" ry="2" />
+<text  x="1091.74" y="175.5" >[Wasm] [fib2..</text>
+</g>
+<g >
+<title>invoke_i_i (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="565" width="1180.0" height="15.0" fill="rgb(238,144,29)" rx="2" ry="2" />
+<text  x="13.00" y="575.5" >invoke_i_i</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,375,872,224 samples, 97.68%)</title><rect x="37.3" y="373" width="1152.7" height="15.0" fill="rgb(219,165,53)" rx="2" ry="2" />
+<text  x="40.32" y="383.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>load_run_wasm_file (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="645" width="1180.0" height="15.0" fill="rgb(233,129,29)" rx="2" ry="2" />
+<text  x="13.00" y="655.5" >load_run_wasm_file</text>
+</g>
+<g >
+<title>load_run_fib_aot (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="661" width="1180.0" height="15.0" fill="rgb(248,119,41)" rx="2" ry="2" />
+<text  x="13.00" y="671.5" >load_run_fib_aot</text>
+</g>
+<g >
+<title>[Wasm] [fib2] run (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="549" width="1180.0" height="15.0" fill="rgb(235,16,45)" rx="2" ry="2" />
+<text  x="13.00" y="559.5" >[Wasm] [fib2] run</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (42,420,273 samples, 3.01%)</title><rect x="1154.4" y="133" width="35.6" height="15.0" fill="rgb(239,213,43)" rx="2" ry="2" />
+<text  x="1157.42" y="143.5" >[Wa..</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,266,323,684 samples, 89.91%)</title><rect x="129.1" y="325" width="1060.9" height="15.0" fill="rgb(248,225,3)" rx="2" ry="2" />
+<text  x="132.10" y="335.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>linux_perf_samp (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="693" width="1180.0" height="15.0" fill="rgb(241,6,8)" rx="2" ry="2" />
+<text  x="13.00" y="703.5" >linux_perf_samp</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (280,259,464 samples, 19.90%)</title><rect x="955.2" y="197" width="234.8" height="15.0" fill="rgb(243,108,14)" rx="2" ry="2" />
+<text  x="958.17" y="207.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>start_thread (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="677" width="1180.0" height="15.0" fill="rgb(248,153,5)" rx="2" ry="2" />
+<text  x="13.00" y="687.5" >start_thread</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (2,334,521 samples, 0.17%)</title><rect x="1188.0" y="69" width="1.9" height="15.0" fill="rgb(237,160,49)" rx="2" ry="2" />
+<text  x="1190.97" y="79.5" ></text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (666,394,609 samples, 47.31%)</title><rect x="631.7" y="245" width="558.3" height="15.0" fill="rgb(213,187,32)" rx="2" ry="2" />
+<text  x="634.67" y="255.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (943,121,736 samples, 66.96%)</title><rect x="399.9" y="277" width="790.1" height="15.0" fill="rgb(219,155,33)" rx="2" ry="2" />
+<text  x="402.87" y="287.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,169,581 samples, 0.08%)</title><rect x="1188.9" y="53" width="1.0" height="15.0" fill="rgb(237,124,2)" rx="2" ry="2" />
+<text  x="1191.95" y="63.5" ></text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,169,581 samples, 0.08%)</title><rect x="1188.9" y="37" width="1.0" height="15.0" fill="rgb(251,93,20)" rx="2" ry="2" />
+<text  x="1191.95" y="47.5" ></text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (194,755,877 samples, 13.83%)</title><rect x="1026.8" y="181" width="163.2" height="15.0" fill="rgb(215,30,29)" rx="2" ry="2" />
+<text  x="1029.80" y="191.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,406,148,966 samples, 99.83%)</title><rect x="12.0" y="437" width="1178.0" height="15.0" fill="rgb(254,68,36)" rx="2" ry="2" />
+<text  x="14.95" y="447.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>all (1,408,481,525 samples, 100%)</title><rect x="10.0" y="709" width="1180.0" height="15.0" fill="rgb(236,29,9)" rx="2" ry="2" />
+<text  x="13.00" y="719.5" ></text>
+</g>
+<g >
+<title>wasm_func_call (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="629" width="1180.0" height="15.0" fill="rgb(245,143,47)" rx="2" ry="2" />
+<text  x="13.00" y="639.5" >wasm_func_call</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (5,943,602 samples, 0.42%)</title><rect x="1185.0" y="85" width="5.0" height="15.0" fill="rgb(251,87,10)" rx="2" ry="2" />
+<text  x="1187.98" y="95.5" ></text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="469" width="1180.0" height="15.0" fill="rgb(249,99,1)" rx="2" ry="2" />
+<text  x="13.00" y="479.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>wasm_runtime_call_wasm (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="613" width="1180.0" height="15.0" fill="rgb(241,228,3)" rx="2" ry="2" />
+<text  x="13.00" y="623.5" >wasm_runtime_call_wasm</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,401,486,191 samples, 99.50%)</title><rect x="15.9" y="405" width="1174.1" height="15.0" fill="rgb(242,25,16)" rx="2" ry="2" />
+<text  x="18.86" y="415.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>aot_call_function (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="597" width="1180.0" height="15.0" fill="rgb(234,207,25)" rx="2" ry="2" />
+<text  x="13.00" y="607.5" >aot_call_function</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (531,941,563 samples, 37.77%)</title><rect x="744.3" y="229" width="445.7" height="15.0" fill="rgb(233,184,39)" rx="2" ry="2" />
+<text  x="747.31" y="239.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,406,148,966 samples, 99.83%)</title><rect x="12.0" y="453" width="1178.0" height="15.0" fill="rgb(246,125,49)" rx="2" ry="2" />
+<text  x="14.95" y="463.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,061,055,435 samples, 75.33%)</title><rect x="301.1" y="293" width="888.9" height="15.0" fill="rgb(248,39,32)" rx="2" ry="2" />
+<text  x="304.07" y="303.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="501" width="1180.0" height="15.0" fill="rgb(217,150,5)" rx="2" ry="2" />
+<text  x="13.00" y="511.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (1,403,816,880 samples, 99.67%)</title><rect x="13.9" y="421" width="1176.1" height="15.0" fill="rgb(208,33,41)" rx="2" ry="2" />
+<text  x="16.91" y="431.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>[Wasm] [fib2] fibonacci (800,646,766 samples, 56.84%)</title><rect x="519.2" y="261" width="670.8" height="15.0" fill="rgb(252,110,19)" rx="2" ry="2" />
+<text  x="522.19" y="271.5" >[Wasm] [fib2] fibonacci</text>
+</g>
+<g >
+<title>invoke_native_with_hw_bound_check (1,408,481,525 samples, 100.00%)</title><rect x="10.0" y="581" width="1180.0" height="15.0" fill="rgb(243,145,41)" rx="2" ry="2" />
+<text  x="13.00" y="591.5" >invoke_native_with_hw_bound_check</text>
+</g>
+</g>
+</svg>

BIN
samples/linux-perf/pics/perf.png


+ 42 - 0
samples/linux-perf/wasm/CMakeLists.txt

@@ -0,0 +1,42 @@
+# Copyright (C) 2019 Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required (VERSION 3.14)
+
+project(linux_perf_sample_wasm)
+
+include(CMakePrintHelpers)
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE Release)
+endif()
+
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake)
+find_package(WAMRC REQUIRED)
+
+################ wasm  ################
+add_executable(fib_wasm fib.c)
+set_target_properties(fib_wasm PROPERTIES SUFFIX .wasm)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fib_wasm.wasm DESTINATION . RENAME fib1.wasm)
+
+add_executable(ackermann_wasm ackermann.c)
+set_target_properties(ackermann_wasm PROPERTIES SUFFIX .wasm)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ackermann_wasm.wasm DESTINATION . RENAME ackermann1.wasm)
+
+
+################ aot ################
+add_custom_target(fib_aot
+  ALL
+  COMMAND ${WAMRC_BIN} --enable-linux-perf -o fib2.aot fib_wasm.wasm
+  DEPENDS fib_wasm
+  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fib2.aot DESTINATION .)
+
+add_custom_target(ackermann_aot
+  ALL
+  COMMAND ${WAMRC_BIN} --enable-linux-perf -o ackermann2.aot ackermann_wasm.wasm
+  DEPENDS ackermann_wasm
+  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ackermann2.aot DESTINATION .)

+ 38 - 0
samples/linux-perf/wasm/ackermann.c

@@ -0,0 +1,38 @@
+#include <stdio.h>
+
+// Ackermann function
+unsigned long
+ackermann(unsigned long m, unsigned long n)
+{
+    if (m == 0) {
+        return n + 1;
+    }
+    else if (n == 0) {
+        return ackermann(m - 1, 1);
+    }
+    else {
+        return ackermann(m - 1, ackermann(m, n - 1));
+    }
+}
+
+__attribute__((export_name("run"))) int
+run(int m, int n)
+{
+    int result = ackermann(m, n);
+    printf("ackermann(%d, %d)=%d\n", m, n, result);
+    return result;
+}
+
+int
+main()
+{
+    unsigned long m, n, result;
+
+    // Example usage:
+    m = 3;
+    n = 2;
+    result = ackermann(m, n);
+    printf("Ackermann(%lu, %lu) = %lu\n", m, n, result);
+
+    return 0;
+}

+ 32 - 0
samples/linux-perf/wasm/fib.c

@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+fibonacci(int n)
+{
+    if (n <= 0)
+        return 0;
+
+    if (n == 1)
+        return 1;
+
+    return fibonacci(n - 1) + fibonacci(n - 2);
+}
+
+__attribute__((export_name("run"))) int
+run(int n)
+{
+    int result = fibonacci(n);
+    printf("fibonacci(%d)=%d\n", n, result);
+    return result;
+}
+
+int
+main(int argc, char **argv)
+{
+    int n = atoi(argv[1]);
+
+    printf("fibonacci(%d)=%d\n", n, fibonacci(n));
+
+    return 0;
+}

+ 2 - 0
test-tools/flame-graph-helper/.gitignore

@@ -0,0 +1,2 @@
+*.*
+!*.py

+ 325 - 0
test-tools/flame-graph-helper/process_folded_data.py

@@ -0,0 +1,325 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+"""
+It is used to process *out.folded* file generated by [FlameGraph](https://github.com/brendangregg/FlameGraph).
+
+- translate jitted function names, which are in a form like `aot_func#N` or `[module name]#aot_func#N`, into corresponding names in a name section in .wasm
+- divide the translated functions into different modules if the module name is specified in the symbol
+
+Usage:
+
+After
+``` bash
+# collect profiling data in perf.data
+
+$ perf script -i perf.data > out.perf
+
+$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
+```
+
+Use this script to translate the function names in out.folded
+
+```
+$ python translate_wasm_function_name.py --wabt_home <wabt-installation> --folded out.folded <.wasm>
+# out.folded -> out.folded.translated
+```
+
+"""
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import subprocess
+from typing import Dict, List
+
+
+# parse arguments like "foo=bar,fiz=biz" into a dictatory {foo:bar,fiz=biz}
+class ParseKVArgs(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string=None):
+        setattr(namespace, self.dest, dict())
+        for value in values.split(","):
+            k, v = value.split("=")
+            getattr(namespace, self.dest)[k] = v
+
+
+def calculate_import_function_count(
+    wasm_objdump_bin: Path, module_names: Dict[str, Path]
+) -> Dict[str, int]:
+    """
+    for every wasm file in <module_names>, calculate the number of functions in the import section.
+
+    using "<wasm_objdump_bin> -j Import -x <wasm_file>"
+    """
+
+    assert wasm_objdump_bin.exists()
+
+    import_function_counts = {}
+    for module_name, wasm_path in module_names.items():
+        assert wasm_path.exists()
+        command = f"{wasm_objdump_bin} -j Import -x {wasm_path}"
+        p = subprocess.run(
+            shlex.split(command),
+            capture_output=True,
+            check=False,
+            text=True,
+            universal_newlines=True,
+        )
+
+        if p.stderr:
+            print("No content in import section")
+            import_function_counts[module_name] = 0
+            continue
+
+        import_function_count = 0
+        for line in p.stdout.split(os.linesep):
+            line = line.strip()
+
+            if not line:
+                continue
+
+            if not " func" in line:
+                continue
+
+            m = re.search(r"^-\s+func", line)
+            assert m
+
+            import_function_count += 1
+
+        # print(f"! there are {import_function_count} import function in {module_name}")
+        import_function_counts[module_name] = import_function_count
+
+    return import_function_counts
+
+
+def collect_name_section_content(
+    wasm_objdump_bin: Path, module_names: Dict[str, Path]
+) -> Dict[str, Dict[int, str]]:
+    """
+    for every wasm file in <module_names>, get the content of name section.
+
+    execute "wasm_objdump_bin -j name -x wasm_file"
+    """
+    assert wasm_objdump_bin.exists()
+
+    name_sections = {}
+    for module_name, wasm_path in module_names.items():
+        assert wasm_path.exists()
+        command = f"{wasm_objdump_bin} -j name -x {wasm_path}"
+        p = subprocess.run(
+            shlex.split(command),
+            capture_output=True,
+            check=False,
+            text=True,
+            universal_newlines=True,
+        )
+
+        if p.stderr:
+            print("No content in name section")
+            name_sections[module_name] = {}
+            continue
+
+        name_section = {}
+        for line in p.stdout.split(os.linesep):
+            line = line.strip()
+
+            if not line:
+                continue
+
+            if not " func" in line:
+                continue
+
+            # - func[N] <__imported_wasi_snapshot_preview1_fd_close>
+            m = re.match(r"- func\[(\d+)\] <(.+)>", line)
+            assert m
+
+            func_index, func_name = m.groups()
+            name_section.update({int(func_index): func_name})
+
+        name_sections[module_name] = name_section
+
+    return name_sections
+
+
+def is_stack_check_mode(folded: Path) -> bool:
+    """
+    check if there is a function name looks like "aot_func_internal#N", it means that WAMR adds a stack check function before the original function.
+    """
+    with open(folded, "rt", encoding="utf-8") as f:
+        for line in f:
+            line = line.strip()
+            if "aot_func_internal" in line:
+                return True
+    return False
+
+
+def replace_function_name(
+    import_function_counts: Dict[str, int],
+    name_sections: Dict[str, Dict[int, str]],
+    folded_in: Path,
+    module_names: Dict[str, Path],
+) -> None:
+    """
+    read content in <folded_in>.  every line contains symbols which are separated by ";".
+
+    Usually, all jitted functions are in the form of "aot_func#N". N is its function index. Use the index to find the corresponding function name in the name section.
+
+    if there is a function name looks like "aot_func_internal#N", it means that WAMR adds a stack check function before the original function.
+    In this case, "aot_func#N" should be translated with "_precheck" as a suffix and "aot_func_internal#N" should be treated as the original one
+    """
+
+    assert folded_in.exists(), f"{folded_in} doesn't exist"
+
+    stack_check_mode = is_stack_check_mode(folded_in)
+
+    # every wasm has a translated out.folded, like out.<module_name>.folded.translated
+    folded_out_files = {}
+    for module_name in module_names.keys():
+        wasm_folded_out_path = folded_in.with_suffix(f".{module_name}.translated")
+        print(f"-> write into {wasm_folded_out_path}")
+        folded_out_files[module_name] = wasm_folded_out_path.open(
+            "wt", encoding="utf-8"
+        )
+    # Plus a default translated out.folded
+    default_folded_out_path = folded_in.with_suffix(".translated")
+    print(f"-> write into {default_folded_out_path}")
+    default_folded_out = default_folded_out_path.open("wt", encoding="utf-8")
+
+    with folded_in.open("rt", encoding="utf-8") as f_in:
+        for line in f_in:
+            line = line.strip()
+
+            m = re.match(r"(.*) (\d+)", line)
+            assert m
+            syms, samples = m.groups()
+
+            new_line = []
+            last_function_module_name = ""
+            for sym in syms.split(";"):
+                if not "aot_func" in sym:
+                    new_line.append(sym)
+                    continue
+
+                # [module_name]#aot_func#N or aot_func#N
+                splitted = sym.split("#")
+                module_name = "" if splitted[0] == "aot_func" else splitted[0]
+                # remove [ and ]
+                module_name = module_name[1:-1]
+
+                if len(module_name) == 0 and len(module_names) > 1:
+                    raise RuntimeError(
+                        f"❌ {sym} doesn't have a module name, but there are multiple wasm files"
+                    )
+
+                if not module_name in module_names:
+                    raise RuntimeError(
+                        f"❌ can't find corresponds wasm file for {module_name}"
+                    )
+
+                last_function_module_name = module_name
+
+                func_idx = int(splitted[-1])
+                # adjust index
+                func_idx = func_idx + import_function_counts[module_name]
+
+                # print(f"🔍 {module_name} {splitted[1]} {func_idx}")
+
+                if func_idx in name_sections[module_name]:
+                    if len(module_name) > 0:
+                        wasm_func_name = f"[Wasm] [{module_name}] {name_sections[module_name][func_idx]}"
+                    else:
+                        wasm_func_name = (
+                            f"[Wasm] {name_sections[module_name][func_idx]}"
+                        )
+                else:
+                    if len(module_name) > 0:
+                        wasm_func_name = f"[Wasm] [{module_name}] func[{func_idx}]"
+                    else:
+                        wasm_func_name = f"[Wasm] func[{func_idx}]"
+
+                if stack_check_mode:
+                    # aot_func_internal -> xxx
+                    # aot_func --> xxx_precheck
+                    if "aot_func" == splitted[1]:
+                        wasm_func_name += "_precheck"
+
+                new_line.append(wasm_func_name)
+
+            line = ";".join(new_line)
+            line += f" {samples}"
+
+            # always write into the default output
+            default_folded_out.write(line + os.linesep)
+            # based on the module name of last function, write into the corresponding output
+            if len(last_function_module_name) > 0:
+                folded_out_files[last_function_module_name].write(line + os.linesep)
+
+    default_folded_out.close()
+    for f in folded_out_files.values():
+        f.close()
+
+
+def main(wabt_home: str, folded: str, module_names: Dict[str, Path]) -> None:
+    wabt_home = Path(wabt_home)
+    assert wabt_home.exists()
+
+    folded = Path(folded)
+    assert folded.exists()
+
+    wasm_objdump_bin = wabt_home.joinpath("bin", "wasm-objdump")
+    import_function_counts = calculate_import_function_count(
+        wasm_objdump_bin, module_names
+    )
+
+    name_sections = collect_name_section_content(wasm_objdump_bin, module_names)
+
+    replace_function_name(import_function_counts, name_sections, folded, module_names)
+
+
+if __name__ == "__main__":
+    argparse = argparse.ArgumentParser()
+    argparse.add_argument(
+        "--wabt_home", required=True, help="wabt home, like /opt/wabt-1.0.33"
+    )
+    argparse.add_argument(
+        "--wasm",
+        action="append",
+        default=[],
+        help="wasm files for profiling before. like --wasm apple.wasm --wasm banana.wasm",
+    )
+    argparse.add_argument(
+        "--wasm_names",
+        action=ParseKVArgs,
+        default={},
+        metavar="module_name=wasm_file, ...",
+        help="multiple wasm files and their module names, like a=apple.wasm,b=banana.wasm,c=cake.wasm",
+    )
+    argparse.add_argument(
+        "folded_file",
+        help="a out.folded generated by flamegraph/stackcollapse-perf.pl",
+    )
+
+    args = argparse.parse_args()
+
+    if not args.wasm and not args.wasm_names:
+        print("Please specify wasm files with either --wasm or --wasm_names")
+        exit(1)
+
+    # - only one wasm file. And there is no [module name] in out.folded
+    # - multiple wasm files. via `--wasm X --wasm Y --wasm Z`. And there is [module name] in out.folded. use the basename of wasm as the module name
+    # - multiple wasm files. via `--wasm_names X=x,Y=y,Z=z`. And there is [module name] in out.folded. use the specified module name
+    module_names = {}
+    if args.wasm_names:
+        for name, wasm_path in args.wasm_names.items():
+            module_names[name] = Path(wasm_path)
+    else:
+        # use the basename of wasm as the module name
+        for wasm in args.wasm:
+            wasm_path = Path(wasm)
+            module_names[wasm_path.stem] = wasm_path
+
+    main(args.wabt_home, args.folded_file, module_names)

+ 0 - 213
test-tools/trans-jitted-func-name/trans_wasm_func_name.py

@@ -1,213 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019 Intel Corporation.  All rights reserved.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#
-"""
-It is used to translate jitted functions' names(in out.folded) to coorespond name in name section in .wasm
-
-Usage:
-
-After
-```
-$ perf script -i perf.data > out.perf
-
-# fold call stacks
-$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
-```
-
-Add a step:
-```
-# translate jitted functions' names
-$ python translate_wasm_function_name.py --wabt_home <wabt-installation> --folded out.folded <.wasm>
-# out.folded -> out.folded.translated
-$ ls out.folded.translated
-```
-
-Then
-```
-# generate flamegraph
-$ ./FlameGraph/flamegraph.pl out.folded.translated > perf.wasm.svg
-```
-
-"""
-
-import argparse
-import os
-from pathlib import Path
-import re
-import shlex
-import subprocess
-
-
-def preflight_check(wabt_home: Path) -> Path:
-    """
-    if wasm-objdump exists in wabt_home
-    """
-    wasm_objdump_bin = wabt_home.joinpath("bin", "wasm-objdump")
-    if not wasm_objdump_bin.exists():
-        raise RuntimeError(f"wasm-objdump not found in {wabt_home}")
-
-    return wasm_objdump_bin
-
-
-def collect_import_section_content(wasm_objdump_bin: Path, wasm_file: Path) -> dict:
-    """
-    execute "wasm_objdump_bin -j Import -x <wasm_file>" and return a dict like {function: X, global: Y, memory: Z, table: N}
-    """
-    assert wasm_objdump_bin.exists()
-    assert wasm_file.exists()
-
-    command = f"{wasm_objdump_bin} -j Import -x {wasm_file}"
-    p = subprocess.run(
-        shlex.split(command),
-        capture_output=True,
-        check=False,
-        text=True,
-        universal_newlines=True,
-    )
-
-    if p.stderr:
-        print("No content in import section")
-        return {}
-
-    import_section = {}
-    for line in p.stdout.split(os.linesep):
-        line = line.strip()
-
-        if not line:
-            continue
-
-        if re.search(r"^-\s+func", line):
-            import_section.update(function=import_section.get("function", 0) + 1)
-        else:
-            pass
-
-    assert len(import_section) > 0, "failed to retrive content of import section"
-    return import_section
-
-
-def collect_name_section_content(wasm_objdump_bin: Path, wasm_file: Path) -> dict:
-    """
-    execute "wasm_objdump_bin -j name -x wasm_file" and store the output in a dict
-    {1: xxxx, 2: yyyy, 3: zzzz}
-    """
-    assert wasm_objdump_bin.exists()
-    assert wasm_file.exists()
-
-    command = f"{wasm_objdump_bin} -j name -x {wasm_file}"
-    p = subprocess.run(
-        shlex.split(command),
-        capture_output=True,
-        check=False,
-        text=True,
-        universal_newlines=True,
-    )
-
-    if p.stderr:
-        raise RuntimeError(f"not found name section in {wasm_file}")
-
-    name_section = {}
-    for line in p.stdout.split(os.linesep):
-        line = line.strip()
-
-        if not line:
-            continue
-
-        # - func[0] <__imported_wasi_snapshot_preview1_fd_close>
-        if line.startswith("- func"):
-            m = re.match(r"- func\[(\d+)\] <(.+)>", line)
-            assert m
-
-            func_index, func_name = m.groups()
-            name_section.update({int(func_index): func_name})
-
-    assert name_section
-    return name_section
-
-
-def replace_function_name(
-    import_section: dict, name_section: dict, folded_in: str, folded_out: str
-) -> None:
-    """
-    read content in <folded_in>. each line will be like:
-
-    quiche::BalsaFrame::ProcessHeaders;non-virtual thunk to Envoy::Http::Http1::BalsaParser::MessageDone;Envoy::Http::Http1::ConnectionImpl::onMessageComplete;Envoy::Http::Http1::ConnectionImpl::onMessageCompleteImpl;Envoy::Http::Http1::ServerConnectionImpl::onMessageCompleteBase;Envoy::Http::ConnectionManagerImpl::ActiveStream::decodeHeaders;Envoy::Http::FilterManager::decodeHeaders;virtual thunk to Envoy::Extensions::Common::Wasm::Context::decodeHeaders;proxy_wasm::ContextBase::onRequestHeaders;proxy_wasm::wamr::Wamr::getModuleFunctionImpl<proxy_wasm::Word, proxy_wasm::Word, proxy_wasm::Word, proxy_wasm::Word>;wasm_func_call;wasm_runtime_call_wasm;wasm_call_function;call_wasm_with_hw_bound_check;wasm_interp_call_wasm;llvm_jit_call_func_bytecode;wasm_runtime_invoke_native;push_args_end;aot_func_internal#3302;aot_func_internal#3308;asm_sysvec_apic_timer_interrupt;sysvec_apic_timer_interrupt;__sysvec_apic_timer_interrupt;hrtimer_interrupt;__hrtimer_run_queues;__remove_hrtimer;rb_next 1110899
-
-    symbol names are spearated by ";"
-
-    if there is a symbol named like "aot_func#XXX" or "aot_func_internal#XXX", it will be replaced with the function name in name section by index
-    """
-    folded_in = Path(folded_in)
-    assert folded_in.exists()
-    folded_out = Path(folded_out)
-
-    import_function_count = import_section.get("function", 0)
-    with folded_in.open("rt", encoding="utf-8") as f_in, folded_out.open(
-        "wt", encoding="utf-8"
-    ) as f_out:
-        precheck_mode = False
-        for line in f_in:
-            line = line.strip()
-            if "aot_func_internal" in line:
-                precheck_mode = True
-
-        f_in.seek(0)
-        for line in f_in:
-            new_line = []
-            line = line.strip()
-
-            m = re.match(r"(.*) (\d+)", line)
-            syms, samples = m.groups()
-            for sym in syms.split(";"):
-                m = re.match(r"aot_func(_internal)?#(\d+)", sym)
-                if not m:
-                    new_line.append(sym)
-                    continue
-
-                func_idx = int(m.groups()[-1]) + import_function_count
-                if func_idx in name_section:
-                    wasm_func_name = f"[Wasm] {name_section[func_idx]}"
-                else:
-                    wasm_func_name = (
-                        f"[Wasm] function[{func_idx + import_function_count}]"
-                    )
-
-                if precheck_mode:
-                    # aot_func_internal -> xxx
-                    # aot_func --> xxx_precheck
-                    wasm_func_name += "_precheck" if not m.groups()[0] else ""
-                else:
-                    # aot_func --> xxx
-                    pass
-
-                new_line.append(wasm_func_name)
-
-            line = ";".join(new_line)
-            line += f" {samples}"
-            f_out.write(line + os.linesep)
-
-    print(f"⚙️ {folded_in} -> {folded_out}")
-
-
-def main(wabt_home: str, wasm_file: str, folded: str) -> None:
-    wabt_home = Path(wabt_home)
-    wasm_file = Path(wasm_file)
-
-    wasm_objdump_bin = preflight_check(wabt_home)
-    import_section = collect_import_section_content(wasm_objdump_bin, wasm_file)
-    name_section = collect_name_section_content(wasm_objdump_bin, wasm_file)
-
-    replace_function_name(import_section, name_section, folded, folded + ".translated")
-
-
-if __name__ == "__main__":
-    argparse = argparse.ArgumentParser()
-    argparse.add_argument(
-        "--folded", help="stackcollapse-perf.pl generated, like out.folded"
-    )
-    argparse.add_argument("wasm_file", help="wasm file")
-    argparse.add_argument("--wabt_home", help="wabt home, like /opt/wabt-1.0.33")
-
-    args = argparse.parse_args()
-    main(args.wabt_home, args.wasm_file, args.folded)