/* Copyright JS Foundation and other contributors, http://js.foundation * * 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. */ #include #include #include "jerryscript-port.h" #include "jerryscript.h" /** * Maximum size of source code / snapshots buffer */ #define JERRY_BUFFER_SIZE (1048576) /** * Standalone Jerry exit codes */ #define JERRY_STANDALONE_EXIT_CODE_OK (0) #define JERRY_STANDALONE_EXIT_CODE_FAIL (1) static uint8_t buffer[JERRY_BUFFER_SIZE]; static const uint8_t * read_file (const char *file_name, size_t *out_size_p) { FILE *file = fopen (file_name, "rb"); if (file == NULL) { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name); return NULL; } size_t bytes_read = fread (buffer, 1u, sizeof (buffer), file); if (!bytes_read) { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name); fclose (file); return NULL; } fclose (file); *out_size_p = bytes_read; return (const uint8_t *) buffer; } /* read_file */ static void print_help (char *name) { printf ("Usage: %s [OPTION]... [FILE]...\n" "\n" "Options:\n" " -h, --help\n" "\n", name); } /* print_help */ /* Global "argc" and "argv" used to avoid argument passing via stack. */ static int argc; static char **argv; static JERRY_ATTR_NOINLINE int run (void) { jerry_init (JERRY_INIT_EMPTY); jerry_value_t ret_value = jerry_undefined (); for (int i = 1; i < argc; i++) { const char *file_name = argv[i]; size_t source_size; const jerry_char_t *source_p = read_file (file_name, &source_size); if (source_p == NULL) { ret_value = jerry_throw_sz (JERRY_ERROR_COMMON, ""); break; } else { ret_value = jerry_parse (source_p, source_size, NULL); if (!jerry_value_is_exception (ret_value)) { jerry_value_t func_val = ret_value; ret_value = jerry_run (func_val); jerry_value_free (func_val); } } if (jerry_value_is_exception (ret_value)) { break; } jerry_value_free (ret_value); ret_value = jerry_undefined (); } int ret_code = !jerry_value_is_exception (ret_value) ? JERRY_STANDALONE_EXIT_CODE_OK : JERRY_STANDALONE_EXIT_CODE_FAIL; jerry_value_free (ret_value); jerry_cleanup (); return ret_code; } /* run */ #if defined(JERRY_TEST_STACK_MEASURE) && (JERRY_TEST_STACK_MEASURE) /** * How this stack measuring works: * * 1) Get the current stack pointer before doing the test execution. * This will be the "Stack bottom". * 2) Fill the stack towards lower addresses with a placeholder 32 bit value. * A "STACK_MEASURE_RANGE" big area will be filled with the value starting * from "Stack bottom". * The "Stack bottom" + "STACK_MEASURE_RANGE" will be the "Stack top". * 3) Run the tests. * 4) Check the stack backwards from "Stack top" to see where the 32 bit placeholder * value is not present. The point where the 32 bit value is not found is * considered to be the "Stack max". The "Stack bottom" - "Stack max" substraction * will give the stack usage in bytes. * * * Based on this the expected stack layout is: * The stack is expected to "grow" towards lower address. * * |-------| * | | <- low address - "Stack top" * |-------| * | | * |-------| * .... * |-------| * | | <- "Stack max" * |-------| * .... * |-------| * | | <- high address - "Stack bottom" * |-------| * */ #if !(defined(__linux__) && __linux__) #error "Unsupported stack measurement platform!" #endif /* !(defined ( __linux__) && __linux__) */ #if defined(__x86_64__) #define STACK_SAVE(TARGET) \ { \ __asm volatile("mov %%rsp, %0" : "=m"(TARGET)); \ } #elif defined(__i386__) #define STACK_SAVE(TARGET) \ { \ __asm volatile("mov %%esp, %0" : "=m"(TARGET)); \ } #elif defined(__arm__) #define STACK_SAVE(TARGET) \ { \ __asm volatile("mov %0, sp" : "=r"(TARGET)); \ } #else /* !defined (__x86_64__) && !defined (__i386__) && !defined (__arm__) */ #error "Unsupported stack measurement target!" #endif /* !defined (__x86_64__) && !defined (__i386__) && !defined (__arm__) */ static void *g_stack_bottom = 0x0; #define STACK_MEASURE_RANGE ((2 * 1024 * 1024)) #define STACK_PATTERN (0xDEADBEEF) #define STACK_INIT(TARGET, SIZE) \ do \ { \ for (size_t idx = 0; idx < (SIZE / sizeof (uint32_t)); idx++) \ { \ ((uint32_t *) (TARGET))[idx] = STACK_PATTERN; \ } \ } while (0) #define STACK_USAGE(TARGET, SIZE) stack_usage (TARGET, SIZE) #define STACK_TOP_PTR(TARGET, SIZE) (uint32_t *) (((uint8_t *) TARGET) - SIZE) static void stack_usage (uint32_t *stack_top_p, size_t length_in_bytes) { uint32_t *stack_bottom_p = stack_top_p + (length_in_bytes / sizeof (uint32_t)); uint32_t *stack_p = stack_top_p; while (stack_p < stack_bottom_p) { if (*stack_p != STACK_PATTERN) { break; } stack_p++; } jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Used stack: %d\n", (int) ((uint8_t *) stack_bottom_p - (uint8_t *) stack_p)); } /* stack_usage */ #else /* (JERRY_TEST_STACK_MEASURE) && (JERRY_TEST_STACK_MEASURE) */ #define STACK_SAVE(TARGET) #define STACK_INIT(TARGET, SIZE) #define STACK_USAGE(TARGET, SIZE) #endif /* #if defined (JERRY_TEST_STACK_MEASURE) && (JERRY_TEST_STACK_MEASURE) */ int main (int main_argc, char **main_argv) { union { double d; unsigned u; } now = { .d = jerry_port_get_current_time () }; srand (now.u); argc = main_argc; argv = main_argv; if (argc <= 1 || (argc == 2 && (!strcmp ("-h", argv[1]) || !strcmp ("--help", argv[1])))) { print_help (argv[0]); return JERRY_STANDALONE_EXIT_CODE_OK; } STACK_SAVE (g_stack_bottom); STACK_INIT (STACK_TOP_PTR (g_stack_bottom, STACK_MEASURE_RANGE), STACK_MEASURE_RANGE); int result = run (); STACK_USAGE (STACK_TOP_PTR (g_stack_bottom, STACK_MEASURE_RANGE), STACK_MEASURE_RANGE); if (result == JERRY_STANDALONE_EXIT_CODE_FAIL) { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unhandled exception: Script Error!\n"); } return result; } /* main */