Add simple stack measure for jerry-test (#3717)

This change introduces simple stack measuring by coloring the stack
before doing the actual JS work.

The dynamic linking should be avoided as it will use the stack during
the symbol resolving.

At this moment the `JERRY_STACK_MEASURE` compile time define will enable
the measurement for the jerry-test binary.

A usable build configuration:

```
./tools/build.py --jerry-cmdline-test=on --compile-flag=-DJERRY_STACK_MEASURE=1 --compile-flag=-static
```

JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.usz@partner.samsung.com
This commit is contained in:
Péter Gál 2020-07-02 11:54:52 +02:00 committed by GitHub
parent cd1e067671
commit eb77f96d20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 22 deletions

View File

@ -17,9 +17,11 @@ project (jerry-main C)
# Optional build settings
set(ENABLE_LINK_MAP OFF CACHE BOOL "Enable generating a link map file?")
set(JERRY_TEST_STACK_MEASURE OFF CACHE BOOL "Enable stack measurement for the jerry-test binary?")
# Status messages
message(STATUS "ENABLE_LINK_MAP " ${ENABLE_LINK_MAP})
message(STATUS "JERRY_TEST_STACK_MEASURE " ${JERRY_TEST_STACK_MEASURE})
# Generate map file
if(ENABLE_LINK_MAP)
@ -69,6 +71,7 @@ endif()
if(JERRY_CMDLINE_TEST)
jerry_create_executable("jerry-test" "main-unix-test.c" "benchmarking.c")
target_link_libraries("jerry-test" jerry-port-default-minimal)
target_compile_definitions("jerry-test" PRIVATE -DJERRY_TEST_STACK_MEASURE=1)
endif()
if(JERRY_CMDLINE_SNAPSHOT)

View File

@ -68,22 +68,13 @@ print_help (char *name)
name);
} /* print_help */
int
main (int argc,
char **argv)
{
union
{
double d;
unsigned u;
} now = { .d = jerry_port_get_current_time () };
srand (now.u);
if (argc <= 1 || (argc == 2 && (!strcmp ("-h", argv[1]) || !strcmp ("--help", argv[1]))))
{
print_help (argv[0]);
return JERRY_STANDALONE_EXIT_CODE_OK;
}
/* 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_create_undefined ();
@ -120,16 +111,134 @@ main (int argc,
ret_value = jerry_create_undefined ();
}
int ret_code = JERRY_STANDALONE_EXIT_CODE_OK;
if (jerry_value_is_error (ret_value))
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Unhandled exception: Script Error!\n");
ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL;
}
int ret_code = !jerry_value_is_error (ret_value) ? JERRY_STANDALONE_EXIT_CODE_OK : JERRY_STANDALONE_EXIT_CODE_FAIL;
jerry_release_value (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 */