[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT/LIBWAMR PATCH 2/3] Add Makefile.uk



This is a port of wamr to Unikraft as an external library. It requires
libc, a network stack and pthread support, so in your application
Makefile the library dependency list should read:

  LIBS := ...:$(UK_LIBS)/pthread-embedded:$(UK_LIBS)/lwip:
              $(UK_LIBS)/newlib:$(UK_LIBS)/wamr:...

Please refer to README.md for information on running it.

Signed-off-by: Felipe Huici <felipe.huici@xxxxxxxxx>
---
 Makefile.uk                                        | 105 +++++++++
 include/bh_platform.h                              | 129 +++++++++++
 main.c                                             | 236 +++++++++++++++++++++
 ...m-log-fix-pthread-embedded-pthread-t-type.patch |  15 ++
 ...hread-fix-pthread-embedded-pthread-t-type.patch |  16 ++
 ...3-bh-thread-use-wasm-log-insteadof-bh-log.patch |  11 +
 6 files changed, 512 insertions(+)
 create mode 100644 Makefile.uk
 create mode 100644 include/bh_platform.h
 create mode 100644 main.c
 create mode 100644 
patches/0001-wasm-log-fix-pthread-embedded-pthread-t-type.patch
 create mode 100644 
patches/0002-bh-thread-fix-pthread-embedded-pthread-t-type.patch
 create mode 100644 patches/0003-bh-thread-use-wasm-log-insteadof-bh-log.patch

diff --git a/Makefile.uk b/Makefile.uk
new file mode 100644
index 0000000..a9f9a53
--- /dev/null
+++ b/Makefile.uk
@@ -0,0 +1,105 @@
+#  wamr Makefile.uk
+#
+#  Authors: Felipe Huici <felipe.huici@xxxxxxxxx>
+#
+#
+#  Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions
+#  are met:
+#
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the names of its
+#     contributors may be used to endorse or promote products derived from
+#     this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+#  THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+#
+
+################################################################################
+# Library registration
+################################################################################
+$(eval $(call addlib_s,libwamr,$(CONFIG_LIBWAMR)))
+
+################################################################################
+# Sources
+################################################################################
+LIBWAMR_VERSION=master
+LIBWAMR_URL=https://github.com/intel/wasm-micro-runtime/archive/$(LIBWAMR_VERSION).zip
+LIBWAMR_DIR=wasm-micro-runtime-master
+LIBWAMR_PATCHDIR=$(LIBWAMR_BASE)/patches
+$(eval $(call fetch,libwamr,$(LIBWAMR_URL),$(LIBWAMR_DIR).zip))
+$(eval $(call patch,libwamr,$(LIBWAMR_PATCHDIR),$(LIBWAMR_DIR)))
+
+################################################################################
+# Helpers
+################################################################################
+LIBWAMR_SRC=$(LIBWAMR_ORIGIN)/$(LIBWAMR_DIR)
+
+################################################################################
+# Library includes
+################################################################################
+CINCLUDES-$(CONFIG_LIBWAMR) += -I$(LIBWAMR_BASE)/include                       
  \
+                              -I$(LIBWAMR_SRC)/core/iwasm/runtime/vmcore-wasm  
 \
+                              
-I$(LIBWAMR_SRC)/core/iwasm/runtime/platform/include \
+                              -I$(LIBWAMR_SRC)/core/iwasm/runtime/include      
 \
+                              
-I$(LIBWAMR_SRC)/core/shared-lib/platform/include \
+                              -I$(LIBWAMR_SRC)/core/shared-lib/include         
 \
+
+################################################################################
+# Library flags
+################################################################################
+LIBWAMR_SUPPRESS_FLAGS += -Wno-implicit-function-declaration \
+                         -Wno-sign-compare                  \
+                         -Wno-pointer-to-int-cast           \
+                         -Wno-unused-parameter              \
+                         -Wno-int-conversion                \
+                         -Wno-unused-label                  \
+                         -Wno-unused-but-set-variable
+
+LIBWAMR_CFLAGS-y += -DNVALGRIND $(LIBWAMR_SUPPRESS_FLAGS)
+LIBWAMR_CXXFLAGS-y += -DNVALGRIND $(LIBWAMR_SUPPRESS_FLAGS)
+
+################################################################################
+# Glue code
+################################################################################
+LIBWAMR_SRCS-y += $(LIBWAMR_BASE)/main.c
+
+################################################################################
+# Sources
+################################################################################
+LIBWAMR_SRCS-y += 
$(LIBWAMR_SRC)/core/iwasm/runtime/vmcore-wasm/invokeNative_general.c
+LIBWAMR_SRCS-y += 
$(LIBWAMR_SRC)/core/iwasm/runtime/vmcore-wasm/wasm_application.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/runtime/vmcore-wasm/wasm_interp.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/runtime/vmcore-wasm/wasm_loader.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/runtime/vmcore-wasm/wasm_runtime.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/shared-lib/mem-alloc/bh_memory.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/runtime/utils/wasm_log.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/shared-lib/platform/linux/bh_thread.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/runtime/utils/wasm_hashmap.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/lib/native/libc/libc_wrapper.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/runtime/utils/wasm_dlfcn.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/shared-lib/mem-alloc/mem_alloc.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/shared-lib/platform/linux/bh_platform.c
+LIBWAMR_SRCS-y += 
$(LIBWAMR_SRC)/core/iwasm/runtime/platform/zephyr/wasm_native.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/lib/native/base/base_lib_export.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/iwasm/products/linux/ext_lib_export.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/shared-lib/mem-alloc/ems/ems_kfc.c
+LIBWAMR_SRCS-y += $(LIBWAMR_SRC)/core/shared-lib/mem-alloc/ems/ems_alloc.c
diff --git a/include/bh_platform.h b/include/bh_platform.h
new file mode 100644
index 0000000..00c25b4
--- /dev/null
+++ b/include/bh_platform.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BH_PLATFORM_H
+#define _BH_PLATFORM_H
+
+#include "bh_config.h"
+#include "bh_types.h"
+#include "bh_memory.h"
+#include <inttypes.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#ifndef __cplusplus
+int snprintf(char *buffer, size_t count, const char *format, ...);
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint64_t uint64;
+typedef int64_t int64;
+
+extern void DEBUGME(void);
+
+#define DIE do{bh_debug("Die here\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); 
DEBUGME(void); while(1);}while(0)
+
+#define BH_PLATFORM "Linux"
+
+/* NEED qsort */
+
+#include <stdarg.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <limits.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#define _STACK_SIZE_ADJUSTMENT (32 * 1024)
+
+/* Stack size of applet manager thread.  */
+#define BH_APPLET_MANAGER_THREAD_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT)
+
+/* Stack size of HMC thread.  */
+#define BH_HMC_THREAD_STACK_SIZE            (4 * 1024 + _STACK_SIZE_ADJUSTMENT)
+
+/* Stack size of watchdog thread.  */
+#define BH_WATCHDOG_THREAD_SIZE             (4 * 1024 + _STACK_SIZE_ADJUSTMENT)
+
+/* Stack size of applet threads's native part.  */
+#define BH_APPLET_PRESERVED_STACK_SIZE      (8 * 1024 + _STACK_SIZE_ADJUSTMENT)
+
+/* Stack size of remote invoke listen thread.  */
+#define BH_REMOTE_INVOKE_THREAD_STACK_SIZE  (4 * 1024 + _STACK_SIZE_ADJUSTMENT)
+
+/* Stack size of remote post listen thread.  */
+#define BH_REMOTE_POST_THREAD_STACK_SIZE    (4 * 1024 + _STACK_SIZE_ADJUSTMENT)
+
+/* Maximal recursion depth of interpreter.  */
+#define BH_MAX_INTERP_RECURSION_DEPTH       8
+
+/* Default thread priority */
+#define BH_THREAD_DEFAULT_PRIORITY 0
+
+#define BH_ROUTINE_MODIFIER
+#define BHT_TIMEDOUT ETIMEDOUT
+
+#define INVALID_THREAD_ID 0xFFffFFff
+#define INVALID_SEM_ID SEM_FAILED
+
+typedef pthread_t korp_tid;
+typedef pthread_mutex_t korp_mutex;
+typedef sem_t korp_sem;
+typedef pthread_cond_t korp_cond;
+typedef pthread_t korp_thread;
+typedef void* (*thread_start_routine_t)(void*);
+
+#define wa_malloc bh_malloc
+#define wa_free bh_free
+#define wa_strdup bh_strdup
+
+double fmod(double x, double y);
+float fmodf(float x, float y);
+
+/* Definitions for applet debugging */
+#define APPLET_DEBUG_LISTEN_PORT 8000
+#define BH_SOCKET_INVALID_SOCK -1
+#define BH_WAIT_FOREVER 0xFFFFFFFF
+typedef int bh_socket_t;
+
+#ifndef NULL
+#  define NULL ((void*) 0)
+#endif
+
+#define bh_assert assert
+
+extern int b_memcpy_s(void * s1, unsigned int s1max, const void * s2,
+        unsigned int n);
+extern int b_strcat_s(char * s1, size_t s1max, const char * s2);
+extern int b_strcpy_s(char * s1, size_t s1max, const char * s2);
+extern int fopen_s(FILE ** pFile, const char *filename, const char *mode);
+
+extern char *bh_strdup(const char *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..91f2148
--- /dev/null
+++ b/main.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "bh_platform.h"
+#include "wasm_assert.h"
+#include "wasm_log.h"
+#include "wasm_platform_log.h"
+#include "wasm_thread.h"
+#include "wasm_export.h"
+#include "wasm_memory.h"
+#include "bh_memory.h"
+
+#include <uk/plat/memory.h>
+
+static int app_argc;
+static char **app_argv;
+
+static int print_help()
+{
+    wasm_printf("Usage: iwasm [-options] wasm_file [args...]\n");
+    wasm_printf("options:\n");
+    wasm_printf("  -f|--function name     Specify function name to run in 
module\n"
+                "                         rather than main\n");
+#if WASM_ENABLE_LOG != 0
+    wasm_printf("  -v=X                   Set log verbose level (0 to 2, 
default is 1),\n"
+                "                         larger level with more log\n");
+#endif
+    wasm_printf("  --repl                 Start a very simple REPL 
(read-eval-print-loop) mode\n"
+                "                         that runs commands in the form of 
`FUNC ARG...`\n");
+    return 1;
+}
+
+static void*
+app_instance_main(wasm_module_inst_t module_inst)
+{
+    const char *exception;
+
+    wasm_application_execute_main(module_inst, app_argc, app_argv);
+    if ((exception = wasm_runtime_get_exception(module_inst)))
+        wasm_printf("%s\n", exception);
+    return NULL;
+}
+
+static void*
+app_instance_func(wasm_module_inst_t module_inst, const char *func_name)
+{
+    const char *exception;
+
+    wasm_application_execute_func(module_inst, func_name, app_argc - 1,
+                                  app_argv + 1);
+    if ((exception = wasm_runtime_get_exception(module_inst)))
+        wasm_printf("%s\n", exception);
+    return NULL;
+}
+
+/**
+ * Split a space separated strings into an array of strings
+ * Returns NULL on failure
+ * Memory must be freed by caller
+ * Based on: http://stackoverflow.com/a/11198630/471795
+ */
+static char **
+split_string(char *str, int *count)
+{
+    char **res = NULL;
+    char *p;
+    int idx = 0;
+
+    /* split string and append tokens to 'res' */
+    do {
+        p = strtok(str, " ");
+        str = NULL;
+        res = (char**) realloc(res, sizeof(char*) * (idx + 1));
+        if (res == NULL) {
+            return NULL;
+        }
+        res[idx++] = p;
+    } while (p);
+
+    if (count) {
+        *count = idx - 1;
+    }
+    return res;
+}
+
+static void*
+app_instance_repl(wasm_module_inst_t module_inst)
+{
+    char *cmd = NULL;
+    size_t len = 0;
+    ssize_t n;
+
+    while ((wasm_printf("webassembly> "), n = getline(&cmd, &len, stdin)) != 
-1) {
+        wasm_assert(n > 0);
+        if (cmd[n - 1] == '\n') {
+            if (n == 1)
+                continue;
+            else
+                cmd[n - 1] = '\0';
+        }
+        app_argv = split_string(cmd, &app_argc);
+        if (app_argv == NULL) {
+            LOG_ERROR("Wasm prepare param failed: split string failed.\n");
+            break;
+        }
+        if (app_argc != 0) {
+            wasm_application_execute_func(module_inst, app_argv[0],
+                                          app_argc - 1, app_argv + 1);
+        }
+        free(app_argv);
+    }
+    free(cmd);
+    return NULL;
+}
+
+static char global_heap_buf[512 * 1024] = { 0 };
+
+int wasm_main(int argc, char *argv[])
+{
+    char *wasm_file = NULL;
+    const char *func_name = NULL;
+    uint8 *wasm_file_buf = NULL;
+    int wasm_file_size;
+    wasm_module_t wasm_module = NULL;
+    wasm_module_inst_t wasm_module_inst = NULL;
+    char error_buf[128];
+#if WASM_ENABLE_LOG != 0
+    int log_verbose_level = 1;
+#endif
+    bool is_repl_mode = false;
+
+    /* Process options.  */
+    for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
+        if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) {
+            argc--, argv++;
+            func_name = argv[0];
+        }
+#if WASM_ENABLE_LOG != 0
+        else if (!strncmp(argv[0], "-v=", 3)) {
+            log_verbose_level = atoi(argv[0] + 3);
+            if (log_verbose_level < 0 || log_verbose_level > 2)
+                return print_help();
+        }
+#endif
+ 
+        else if (!strcmp(argv[0], "--repl")) 
+            is_repl_mode = true;
+    }
+    
+    wasm_file = argv[0];
+    app_argc = argc;
+    app_argv = argv;
+
+    if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf))
+        != 0) {
+        wasm_printf("Init global heap failed.\n");
+        return -1;
+    }
+
+    /* initialize runtime environment */
+    if (!wasm_runtime_init())
+        goto fail1;
+
+    wasm_log_set_verbose_level(log_verbose_level);
+
+    /* load from initrd */
+    struct ukplat_memregion_desc img;
+    if (ukplat_memregion_find_initrd0(&img) >= 0) {
+      wasm_file_buf = (uint8*)img.base;
+      wasm_file_size = img.len;
+    
+      /* load WASM module */
+      if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size,
+                                            error_buf, sizeof(error_buf)))) {
+          wasm_printf("%s\n", error_buf);
+          goto fail3;
+      }
+    
+      /* instantiate the module */
+      if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module,
+                                                        16 * 1024, /* stack 
size */
+                                                        8 * 1024,  /* heap 
size */
+                                                        error_buf,
+                                                        sizeof(error_buf)))) {
+          wasm_printf("%s\n", error_buf);
+          goto fail4;
+      }
+    }
+
+    if (is_repl_mode) {
+        printf("Entering REPL mode...\n");
+        app_instance_repl(wasm_module_inst);
+    }
+    else if (func_name)
+        app_instance_func(wasm_module_inst, func_name);
+    else
+        app_instance_main(wasm_module_inst);
+
+    /* destroy the module instance */
+    wasm_runtime_deinstantiate(wasm_module_inst);
+
+fail4:
+    /* unload the module */
+    wasm_runtime_unload(wasm_module);
+
+fail3:
+    /* free the file buffer */
+    wasm_free(wasm_file_buf);
+
+fail2:
+    /* destroy runtime environment */
+    wasm_runtime_destroy();
+
+fail1:
+    bh_memory_destroy();
+    return 0;
+}
+
diff --git a/patches/0001-wasm-log-fix-pthread-embedded-pthread-t-type.patch 
b/patches/0001-wasm-log-fix-pthread-embedded-pthread-t-type.patch
new file mode 100644
index 0000000..8630539
--- /dev/null
+++ b/patches/0001-wasm-log-fix-pthread-embedded-pthread-t-type.patch
@@ -0,0 +1,15 @@
+--- /core/iwasm/runtime/utils/wasm_log.c.orig  2019-07-30 10:01:46.715217329 
+0200
++++ /core/iwasm/runtime/utils/wasm_log.c       2019-07-30 10:07:26.891746359 
+0200
+@@ -57,8 +57,11 @@
+   /* Try to own the log stream and start the log output.  */
+   ws_mutex_lock (&log_stream_lock);
+   self = ws_self_thread ();
++#ifdef CONFIG_LIBPTHREAD_EMBEDDED
++  wasm_printf ("[%X]: ", (int)self.p);  
++#else
+   wasm_printf ("[%X]: ", (int)self);
+-
++#endif
+   return true;
+ }
+ 
diff --git a/patches/0002-bh-thread-fix-pthread-embedded-pthread-t-type.patch 
b/patches/0002-bh-thread-fix-pthread-embedded-pthread-t-type.patch
new file mode 100644
index 0000000..7101ce2
--- /dev/null
+++ b/patches/0002-bh-thread-fix-pthread-embedded-pthread-t-type.patch
@@ -0,0 +1,16 @@
+--- /core/shared-lib/platform/linux/bh_thread.c.orig   2019-07-30 
10:08:21.243191688 +0200
++++ /core/shared-lib/platform/linux/bh_thread.c        2019-07-30 
10:08:55.110846278 +0200
+@@ -93,8 +93,12 @@
+     bh_assert(tid);
+     bh_assert(start);
+ 
++#ifdef CONFIG_LIBPTHREAD_EMBEDDED
++    tid->p = INVALID_THREAD_ID;
++#else
+     *tid = INVALID_THREAD_ID;
+-
++#endif
++    
+     pthread_attr_init(&tattr);
+     pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
+     if (pthread_attr_setstacksize(&tattr, stack_size) != 0) {
diff --git a/patches/0003-bh-thread-use-wasm-log-insteadof-bh-log.patch 
b/patches/0003-bh-thread-use-wasm-log-insteadof-bh-log.patch
new file mode 100644
index 0000000..6a5e923
--- /dev/null
+++ b/patches/0003-bh-thread-use-wasm-log-insteadof-bh-log.patch
@@ -0,0 +1,11 @@
+--- /core/shared-lib/platform/linux/bh_thread.c.orig   2019-07-30 
11:10:57.884821508 +0200
++++ /core/shared-lib/platform/linux/bh_thread.c        2019-07-30 
11:11:54.492244787 +0200
+@@ -16,7 +16,7 @@
+ 
+ #include "bh_thread.h"
+ #include "bh_assert.h"
+-#include "bh_log.h"
++#include "wasm_log.h"
+ #include "bh_memory.h"
+ #include <stdio.h>
+ #include <stdlib.h>
-- 
2.11.0


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.