diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c68467932..c7161317de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ cmake_minimum_required (VERSION 2.8.12) project (Jerry CXX C ASM) -# Require g++ of version >= 4.8.0 +# Require g++ of version >= 4.7.0 if(NOT CMAKE_COMPILER_IS_GNUCXX) message(FATAL_ERROR "g++ compiler is required") else() @@ -23,8 +23,8 @@ project (Jerry CXX C ASM) OUTPUT_VARIABLE GNU_CXX_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) - if(${GNU_CXX_VERSION} VERSION_LESS 4.8.0) - message(FATAL_ERROR "g++ compiler version 4.8.0 or higher required") + if(${GNU_CXX_VERSION} VERSION_LESS 4.7.0) + message(FATAL_ERROR "g++ compiler version 4.7.0 or higher required") endif() endif() @@ -119,6 +119,7 @@ project (Jerry CXX C ASM) COMPACT_PROFILE COMPACT_PROFILE_MINIMAL FULL_PROFILE + MINIMAL_FOOTPRINT MEMORY_STATISTICS) # Profiles @@ -131,6 +132,9 @@ project (Jerry CXX C ASM) # Minimal compact profile set(MODIFIER_SUFFIX_COMPACT_PROFILE_MINIMAL -cp_minimal) + # Minimal footprint + set(MODIFIER_SUFFIX_MINIMAL_FOOTPRINT -mfp) + # Memory statistics set(MODIFIER_SUFFIX_MEMORY_STATISTICS -mem_stats) @@ -140,7 +144,10 @@ project (Jerry CXX C ASM) "FULL_PROFILE" "COMPACT_PROFILE" "COMPACT_PROFILE_MINIMAL" + "FULL_PROFILE MINIMAL_FOOTPRINT" + "FULL_PROFILE MINIMAL_FOOTPRINT MEMORY_STATISTICS" "FULL_PROFILE MEMORY_STATISTICS" + "COMPACT_PROFILE_MINIMAL MINIMAL_FOOTPRINT" "COMPACT_PROFILE_MINIMAL MEMORY_STATISTICS") # MCU @@ -162,7 +169,7 @@ project (Jerry CXX C ASM) # Compiler / Linker flags set(COMPILE_FLAGS_JERRY "-fno-builtin") - set(LINKER_FLAGS_COMMON "") + set(LINKER_FLAGS_COMMON "-Wl,-z,noexecstack") # LTO if("${ENABLE_LTO}" STREQUAL "ON") @@ -177,7 +184,7 @@ project (Jerry CXX C ASM) set(COMPILE_FLAGS_JERRY "${COMPILE_FLAGS_JERRY} -g -gdwarf-4") # Warnings - set(COMPILE_FLAGS_JERRY "${COMPILE_FLAGS_JERRY} -Wall -Wextra -Wpedantic -Wlogical-op") + set(COMPILE_FLAGS_JERRY "${COMPILE_FLAGS_JERRY} -Wall -Wextra -pedantic -Wlogical-op") set(COMPILE_FLAGS_JERRY "${COMPILE_FLAGS_JERRY} -Wformat-nonliteral -Winit-self -Wno-stack-protector") set(COMPILE_FLAGS_JERRY "${COMPILE_FLAGS_JERRY} -Wconversion -Wsign-conversion -Wformat-security") set(COMPILE_FLAGS_JERRY "${COMPILE_FLAGS_JERRY} -Wmissing-declarations -Wno-attributes") @@ -250,6 +257,19 @@ project (Jerry CXX C ASM) OUTPUT_STRIP_TRAILING_WHITESPACE) set_property(TARGET ${PREFIX_IMPORTED_LIB}libgcc PROPERTY IMPORTED_LOCATION ${IMPORTED_LIBGCC_LOCATION}) + # libgcc_eh + add_library(${PREFIX_IMPORTED_LIB}libgcc_eh STATIC IMPORTED) + execute_process(COMMAND ${CMAKE_C_COMPILER} ${FLAGS_COMMON_ARCH} -print-file-name=libgcc_eh.a + OUTPUT_VARIABLE IMPORTED_LIBGCC_EH_LOCATION + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(EXISTS "${IMPORTED_LIBGCC_EH_LOCATION}") + set_property(TARGET ${PREFIX_IMPORTED_LIB}libgcc_eh + PROPERTY IMPORTED_LOCATION ${IMPORTED_LIBGCC_EH_LOCATION}) + else() + # If libgcc_eh not found, reference libgcc instead + set_property(TARGET ${PREFIX_IMPORTED_LIB}libgcc_eh + PROPERTY IMPORTED_LOCATION ${IMPORTED_LIBGCC_LOCATION}) + endif() # Platform-specific configuration set(MODIFIERS_LISTS ${MODIFIERS_LISTS_${PLATFORM_EXT}}) @@ -307,7 +327,8 @@ project (Jerry CXX C ASM) target_compile_definitions(${TARGET_NAME} PRIVATE ${DEFINES_JERRY}) target_include_directories(${TARGET_NAME} PRIVATE ${INCLUDE_CORE_INTERFACE}) target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${INCLUDE_LIBC_INTERFACE}) - target_link_libraries(${TARGET_NAME} ${PLUGINS_TARGET_NAME} ${CORE_TARGET_NAME} ${LIBC_TARGET_NAME} ${PREFIX_IMPORTED_LIB}libgcc) + target_link_libraries(${TARGET_NAME} ${PLUGINS_TARGET_NAME} ${CORE_TARGET_NAME} ${LIBC_TARGET_NAME} + ${PREFIX_IMPORTED_LIB}libgcc ${PREFIX_IMPORTED_LIB}libgcc_eh) add_cppcheck_target(${TARGET_NAME}) @@ -387,7 +408,8 @@ project (Jerry CXX C ASM) set_property(TARGET ${TARGET_NAME} PROPERTY LINK_FLAGS "${COMPILE_FLAGS_JERRY} ${CXX_FLAGS_JERRY} ${FLAGS_COMMON_UNITTESTS} ${LINKER_FLAGS_COMMON}") target_include_directories(${TARGET_NAME} PRIVATE ${INCLUDE_CORE_INTERFACE}) - target_link_libraries(${TARGET_NAME} ${CORE_TARGET_NAME} ${PREFIX_IMPORTED_LIB}libc ${PREFIX_IMPORTED_LIB}libgcc) + target_link_libraries(${TARGET_NAME} ${CORE_TARGET_NAME} ${PREFIX_IMPORTED_LIB}libc + ${PREFIX_IMPORTED_LIB}libgcc ${PREFIX_IMPORTED_LIB}libgcc_eh) add_cppcheck_target(${TARGET_NAME}) diff --git a/Makefile b/Makefile index 0cc3f45cf0..eec8d7fd86 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ export TARGET_RELEASE_MODES = release export TARGET_PC_SYSTEMS = linux export TARGET_NUTTX_SYSTEMS = nuttx -export TARGET_PC_MODS = cp cp_minimal mem_stats +export TARGET_PC_MODS = cp cp_minimal mem_stats mfp cp_minimal-mfp mfp-mem_stats export TARGET_NUTTX_MODS = $(TARGET_PC_MODS) export TARGET_MCU_MODS = cp cp_minimal diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index 27a0ab2b12..9caead6d25 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -66,6 +66,11 @@ project (JerryCore CXX C ASM) CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN) + # Minimal footprint + set(DEFINES_MINIMAL_FOOTPRINT + CONFIG_ECMA_LCACHE_DISABLE + CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE) + # Memory statistics set(DEFINES_MEMORY_STATISTICS MEM_STATS) diff --git a/jerry-core/config.h b/jerry-core/config.h index 6d39bd58f4..1e8b033b10 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -55,6 +55,11 @@ # define CONFIG_MEM_HEAP_AREA_SIZE (256 * 1024) #endif /* !CONFIG_MEM_HEAP_AREA_SIZE */ +/** + * Desired limit of heap usage + */ +#define CONFIG_MEM_HEAP_DESIRED_LIMIT (CONFIG_MEM_HEAP_AREA_SIZE / 32) + /** * Log2 of maximum possible offset in the heap * @@ -127,6 +132,11 @@ */ #define CONFIG_ECMA_STACK_FRAME_INLINED_VALUES_NUMBER (16) +/** + * Disable ECMA lookup cache + */ +// #define CONFIG_ECMA_LCACHE_DISABLE + /** * Link Global Environment to an empty declarative lexical environment * instead of lexical environment bound to Global Object. @@ -170,4 +180,9 @@ */ #define CONFIG_EXTENSION_MAX_ARGUMENTS_IN_FUNCTION 16 +/** + * Run GC after execution of each opcode + */ +// #define CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE + #endif /* !CONFIG_H */ diff --git a/jerry-core/ecma/base/ecma-lcache.cpp b/jerry-core/ecma/base/ecma-lcache.cpp index 7ef34fbc80..7b63da691a 100644 --- a/jerry-core/ecma/base/ecma-lcache.cpp +++ b/jerry-core/ecma/base/ecma-lcache.cpp @@ -26,6 +26,7 @@ * @{ */ +#ifndef CONFIG_ECMA_LCACHE_DISABLE /** * Entry of LCache hash table */ @@ -65,6 +66,7 @@ JERRY_STATIC_ASSERT (sizeof (ecma_lcache_hash_entry_t) == sizeof (uint64_t)); * LCache's hash table */ static ecma_lcache_hash_entry_t ecma_lcache_hash_table[ ECMA_LCACHE_HASH_ROWS_COUNT ][ ECMA_LCACHE_HASH_ROW_LENGTH ]; +#endif /* !CONFIG_ECMA_LCACHE_DISABLE */ /** * Initialize LCache @@ -72,9 +74,12 @@ static ecma_lcache_hash_entry_t ecma_lcache_hash_table[ ECMA_LCACHE_HASH_ROWS_CO void ecma_lcache_init (void) { +#ifndef CONFIG_ECMA_LCACHE_DISABLE memset (ecma_lcache_hash_table, 0, sizeof (ecma_lcache_hash_table)); +#endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_init */ +#ifndef CONFIG_ECMA_LCACHE_DISABLE /** * Invalidate specified LCache entry */ @@ -98,6 +103,7 @@ ecma_lcache_invalidate_entry (ecma_lcache_hash_entry_t *entry_p) /**< entry to i false); } } /* ecma_lcache_invalidate_entry */ +#endif /* !CONFIG_ECMA_LCACHE_DISABLE */ /** * Invalidate all entries in LCache @@ -105,6 +111,7 @@ ecma_lcache_invalidate_entry (ecma_lcache_hash_entry_t *entry_p) /**< entry to i void ecma_lcache_invalidate_all (void) { +#ifndef CONFIG_ECMA_LCACHE_DISABLE for (uint32_t row_index = 0; row_index < ECMA_LCACHE_HASH_ROWS_COUNT; row_index++) { for (uint32_t entry_index = 0; entry_index < ECMA_LCACHE_HASH_ROW_LENGTH; entry_index++) @@ -115,8 +122,10 @@ ecma_lcache_invalidate_all (void) } } } +#endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_invalidate_all */ +#ifndef CONFIG_ECMA_LCACHE_DISABLE /** * Invalidate entries of LCache's row that correspond to given (object, property) pair */ @@ -137,6 +146,7 @@ ecma_lcache_invalidate_row_for_object_property_pair (uint32_t row_index, /**< in } } } /* ecma_lcache_invalidate_row_for_object_property_pair */ +#endif /* !CONFIG_ECMA_LCACHE_DISABLE */ /** * Insert an entry into LCache @@ -151,6 +161,7 @@ ecma_lcache_insert (ecma_object_t *object_p, /**< object */ JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); +#ifndef CONFIG_ECMA_LCACHE_DISABLE prop_name_p = ecma_copy_or_ref_ecma_string (prop_name_p); ecma_string_hash_t hash_key = ecma_string_hash (prop_name_p); @@ -210,6 +221,9 @@ ecma_lcache_insert (ecma_object_t *object_p, /**< object */ ECMA_SET_NON_NULL_POINTER (ecma_lcache_hash_table[ hash_key ][ entry_index ].object_cp, object_p); ECMA_SET_NON_NULL_POINTER (ecma_lcache_hash_table[ hash_key ][ entry_index ].prop_name_cp, prop_name_p); ECMA_SET_POINTER (ecma_lcache_hash_table[ hash_key ][ entry_index ].prop_cp, prop_p); +#else /* CONFIG_ECMA_LCACHE_DISABLE */ + (void) prop_p; +#endif /* CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_insert */ /** @@ -228,6 +242,7 @@ ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ * if return value is false, * then the output parameter is not set */ { +#ifndef CONFIG_ECMA_LCACHE_DISABLE ecma_string_hash_t hash_key = ecma_string_hash (prop_name_p); unsigned int object_cp; @@ -255,6 +270,11 @@ ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ } } } +#else /* CONFIG_ECMA_LCACHE_DISABLE */ + (void) object_p; + (void) prop_name_p; + (void) prop_p_p; +#endif /* CONFIG_ECMA_LCACHE_DISABLE */ return false; } /* ecma_lcache_lookup */ @@ -276,6 +296,7 @@ ecma_lcache_invalidate (ecma_object_t *object_p, /**< object */ JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_p != NULL || prop_name_arg_p != NULL); +#ifndef CONFIG_ECMA_LCACHE_DISABLE ecma_string_t *prop_name_p = NULL; if (prop_p != NULL) @@ -318,6 +339,7 @@ ecma_lcache_invalidate (ecma_object_t *object_p, /**< object */ * Given (object, property name) pair should be in the row corresponding to computed hash. */ ecma_lcache_invalidate_row_for_object_property_pair (hash_key, object_cp, prop_cp); +#endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_invalidate */ /** diff --git a/jerry-core/jerry-extension.inc.h b/jerry-core/jerry-extension.inc.h index b34265b808..2ea5b6cce9 100644 --- a/jerry-core/jerry-extension.inc.h +++ b/jerry-core/jerry-extension.inc.h @@ -39,10 +39,10 @@ enum static const jerry_extension_field_t jerry_extension_fields [JERRY_EXTENSION_FIELDS_NUMBER + 1] = { #define EXTENSION_FIELD(_field_name, _type, _value) \ - { # _field_name, JERRY_API_DATA_TYPE_ ## _type, _value }, + { # _field_name, JERRY_API_DATA_TYPE_ ## _type, { _value } }, # include EXTENSION_DESCRIPTION_HEADER #undef EXTENSION_FIELD -#define EMPTY_FIELD_ENTRY { NULL, JERRY_API_DATA_TYPE_UNDEFINED, NULL } +#define EMPTY_FIELD_ENTRY { NULL, JERRY_API_DATA_TYPE_UNDEFINED, { NULL } } EMPTY_FIELD_ENTRY #undef EMPTY_FIELD_ENTRY }; @@ -87,7 +87,7 @@ static const jerry_extension_field_t jerry_extension_fields [JERRY_EXTENSION_FIE /* Functions' arguments description */ #define EXTENSION_ARG(_arg_index, _type) [_arg_index] = { \ (JERRY_API_DATA_TYPE_ ## _type), \ - false /* just for initialization, should be overwritten upon call */ \ + { false } /* just for initialization, should be overwritten upon call */ \ } #define EXTENSION_FUNCTION(_function_name, _function_to_call, _ret_value_type, _args_number, ...) \ static jerry_api_value_t jerry_extension_function_ ## _function_name ## _args [_args_number] = { \ @@ -103,13 +103,13 @@ static jerry_extension_function_t jerry_extension_functions [JERRY_EXTENSION_FUN #define EXTENSION_FUNCTION(_function_name, _function_to_call, _ret_value_type, _args_number, ...) \ { \ # _function_name, jerry_extension_ ## _function_name ## _wrapper, \ - { JERRY_API_DATA_TYPE_ ## _ret_value_type, false }, \ + { JERRY_API_DATA_TYPE_ ## _ret_value_type, { false } }, \ jerry_extension_function_ ## _function_name ## _args, \ _args_number \ }, # include EXTENSION_DESCRIPTION_HEADER #undef EXTENSION_FUNCTION -#define EMPTY_FUNCTION_ENTRY { NULL, NULL, { JERRY_API_DATA_TYPE_VOID, false }, NULL, 0 } +#define EMPTY_FUNCTION_ENTRY { NULL, NULL, { JERRY_API_DATA_TYPE_VOID, { false } }, NULL, 0 } EMPTY_FUNCTION_ENTRY #undef EMPTY_FUNCTION_ENTRY }; diff --git a/jerry-core/jerry.cpp b/jerry-core/jerry.cpp index 4220bf1c7c..ccfee573a2 100644 --- a/jerry-core/jerry.cpp +++ b/jerry-core/jerry.cpp @@ -27,6 +27,8 @@ #include "ecma-objects-general.h" #include "jerry.h" #include "jrt.h" +#include "mem-heap.h" +#include "mem-poolman.h" #include "parser.h" #include "serializer.h" #include "vm.h" @@ -901,14 +903,24 @@ jerry_api_eval (const char *source_p, /**< source code */ void jerry_init (jerry_flag_t flags) /**< combination of Jerry flags */ { - jerry_flags = flags; - -#ifndef MEM_STATS - if (flags & JERRY_FLAG_MEM_STATS) + if (flags & (JERRY_FLAG_MEM_STATS)) { +#ifndef MEM_STATS + flags &= ~(JERRY_FLAG_MEM_STATS + | JERRY_FLAG_MEM_STATS_PER_OPCODE + | JERRY_FLAG_MEM_STATS_SEPARATE); + printf ("Ignoring memory statistics option because of '!MEM_STATS' build configuration.\n"); - } #endif /* !MEM_STATS */ + } + else if (flags & (JERRY_FLAG_MEM_STATS_PER_OPCODE | JERRY_FLAG_MEM_STATS_SEPARATE)) + { + flags &= ~(JERRY_FLAG_MEM_STATS_PER_OPCODE | JERRY_FLAG_MEM_STATS_SEPARATE); + + printf ("Ignoring detailed memory statistics options because memory statistics dump mode is not enabled.\n"); + } + + jerry_flags = flags; mem_init (); serializer_init (); @@ -966,8 +978,17 @@ jerry_parse (const char* source_p, /**< script source */ serializer_print_opcodes (); parser_free (); - bool is_show_mem_stats = ((jerry_flags & JERRY_FLAG_MEM_STATS) != 0); - init_int (opcodes, is_show_mem_stats); +#ifdef MEM_STATS + if (jerry_flags & JERRY_FLAG_MEM_STATS_SEPARATE) + { + mem_stats_print (); + mem_stats_reset_peak (); + } +#endif /* MEM_STATS */ + + bool is_show_mem_stats_per_opcode = ((jerry_flags & JERRY_FLAG_MEM_STATS_PER_OPCODE) != 0); + + init_int (opcodes, is_show_mem_stats_per_opcode); return true; } /* jerry_parse */ diff --git a/jerry-core/jerry.h b/jerry-core/jerry.h index b373d4f925..45c60e5e52 100644 --- a/jerry-core/jerry.h +++ b/jerry-core/jerry.h @@ -31,12 +31,14 @@ */ typedef uint32_t jerry_flag_t; -#define JERRY_FLAG_EMPTY (0) /**< empty flag set */ -#define JERRY_FLAG_SHOW_OPCODES (1 << 0) /**< dump opcodes to stdout after parse */ -#define JERRY_FLAG_MEM_STATS (1 << 1) /**< dump per-opcode memory statistics during execution - * (in the mode full GC is performed after each opcode handler) */ -#define JERRY_FLAG_PARSE_ONLY (1 << 2) /**< parse only, prevents script execution (only for testing) - * FIXME: Remove. */ +#define JERRY_FLAG_EMPTY (0u) /**< empty flag set */ +#define JERRY_FLAG_SHOW_OPCODES (1u << 0) /**< dump opcodes to stdout after parse */ +#define JERRY_FLAG_MEM_STATS (1u << 1) /**< dump memory statistics */ +#define JERRY_FLAG_MEM_STATS_PER_OPCODE (1u << 2) /**< dump per-opcode memory statistics during execution + * (in the mode full GC is performed after each opcode handler) */ +#define JERRY_FLAG_MEM_STATS_SEPARATE (1u << 3) /**< dump memory statistics and reset peak values after parse */ +#define JERRY_FLAG_PARSE_ONLY (1u << 4) /**< parse only, prevents script execution (only for testing) + * FIXME: Remove. */ /** * Error codes diff --git a/jerry-core/mem/mem-allocator.cpp b/jerry-core/mem/mem-allocator.cpp index b1f650dc19..0adb83a1b0 100644 --- a/jerry-core/mem/mem-allocator.cpp +++ b/jerry-core/mem/mem-allocator.cpp @@ -185,3 +185,41 @@ mem_is_heap_pointer (void *pointer) /**< pointer */ return (uint8_pointer >= mem_heap_area && uint8_pointer <= (mem_heap_area + MEM_HEAP_AREA_SIZE)); } /* mem_is_heap_pointer */ #endif /* !JERRY_NDEBUG */ + +#ifdef MEM_STATS +/** + * Reset peak values in memory usage statistics + */ +void +mem_stats_reset_peak (void) +{ + mem_heap_stats_reset_peak (); + mem_pools_stats_reset_peak (); +} /* mem_stats_reset_peak */ + +/** + * Print memory usage statistics + */ +void +mem_stats_print (void) +{ + mem_heap_print (false, false, true); + + mem_pools_stats_t stats; + mem_pools_get_stats (&stats); + + printf ("Pools stats:\n"); + printf (" Chunk size: %zu\n" + " Pools: %zu\n" + " Allocated chunks: %zu\n" + " Free chunks: %zu\n" + " Peak pools: %zu\n" + " Peak allocated chunks: %zu\n\n", + MEM_POOL_CHUNK_SIZE, + stats.pools_count, + stats.allocated_chunks, + stats.free_chunks, + stats.peak_pools_count, + stats.peak_allocated_chunks); +} /* mem_stats_print */ +#endif /* MEM_STATS */ diff --git a/jerry-core/mem/mem-allocator.h b/jerry-core/mem/mem-allocator.h index cb872ca0ae..a7e45d4a78 100644 --- a/jerry-core/mem/mem-allocator.h +++ b/jerry-core/mem/mem-allocator.h @@ -93,6 +93,11 @@ extern void mem_unregister_a_try_give_memory_back_callback (mem_try_give_memory_ extern bool mem_is_heap_pointer (void *pointer); #endif /* !JERRY_NDEBUG */ +#ifdef MEM_STATS +extern void mem_stats_reset_peak (void); +extern void mem_stats_print (void); +#endif /* MEM_STATS */ + #endif /* !JERRY_MEM_ALLOCATOR_H */ /** diff --git a/jerry-core/mem/mem-heap.cpp b/jerry-core/mem/mem-heap.cpp index edd32d87eb..6fb4c5e93e 100644 --- a/jerry-core/mem/mem-heap.cpp +++ b/jerry-core/mem/mem-heap.cpp @@ -139,6 +139,9 @@ typedef struct size_t heap_size; /**< heap space size */ mem_block_header_t* first_block_p; /**< first block of the heap */ mem_block_header_t* last_block_p; /**< last block of the heap */ + size_t allocated_bytes; /**< total size of allocated heap space */ + size_t limit; /**< current limit of heap usage, that is upon being reached, + * causes call of "try give memory back" callbacks */ } mem_heap_state_t; /** @@ -382,6 +385,7 @@ mem_heap_init (uint8_t *heap_start, /**< first address of heap space */ mem_heap.heap_start = heap_start; mem_heap.heap_size = heap_size; + mem_heap.limit = CONFIG_MEM_HEAP_DESIRED_LIMIT; VALGRIND_NOACCESS_SPACE(heap_start, heap_size); @@ -510,6 +514,18 @@ void* mem_heap_alloc_block_internal (size_t size_in_bytes, /**< size return NULL; } + mem_heap.allocated_bytes += size_in_bytes; + + JERRY_ASSERT (mem_heap.allocated_bytes <= mem_heap.heap_size); + + if (mem_heap.allocated_bytes >= mem_heap.limit) + { + mem_heap.limit = JERRY_MIN (mem_heap.heap_size, + JERRY_MAX (mem_heap.limit + CONFIG_MEM_HEAP_DESIRED_LIMIT, + mem_heap.allocated_bytes)); + JERRY_ASSERT (mem_heap.limit >= mem_heap.allocated_bytes); + } + /* appropriate block found, allocating space */ size_t new_block_size_in_chunks = mem_get_block_chunks_count_from_data_size (size_in_bytes); size_t found_block_size_in_chunks = mem_get_block_chunks_count (block_p); @@ -625,6 +641,11 @@ mem_heap_alloc_block (size_t size_in_bytes, /**< size of region to a } else { + if (mem_heap.allocated_bytes + size_in_bytes >= mem_heap.limit) + { + mem_run_try_to_give_memory_back_callbacks (MEM_TRY_GIVE_MEMORY_BACK_SEVERITY_LOW); + } + void *data_space_p = mem_heap_alloc_block_internal (size_in_bytes, alloc_term); if (likely (data_space_p != NULL)) @@ -653,14 +674,10 @@ mem_heap_alloc_block (size_t size_in_bytes, /**< size of region to a } /* mem_heap_alloc_block */ /** - * Try to resize memory region. - * - * @return true - if resize is successful, - * false - if there is not enough memory in front of the block. + * Free the memory block. */ -bool -mem_heap_try_resize_block (void *ptr, /**< pointer to beginning of data space of the block to resize */ - size_t size_in_bytes) /**< new block size */ +void +mem_heap_free_block (void *ptr) /**< pointer to beginning of data space of the block */ { uint8_t *uint8_ptr = (uint8_t*) ptr; @@ -674,140 +691,25 @@ mem_heap_try_resize_block (void *ptr, /**< pointer to beginning of data space of VALGRIND_DEFINED_STRUCT(block_p); - JERRY_ASSERT(block_p->magic_num == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK); - - /* For heap statistics unit we show what is going on as though - * the block is freed and then new block (the same or resized) - * is allocated */ - MEM_HEAP_STAT_FREE_BLOCK (block_p); + mem_block_header_t *prev_block_p = mem_get_next_block_by_direction (block_p, MEM_DIRECTION_PREV); + mem_block_header_t *next_block_p = mem_get_next_block_by_direction (block_p, MEM_DIRECTION_NEXT); - size_t current_block_may_expand_up_to = mem_get_block_data_space_size (block_p); + JERRY_ASSERT (mem_heap.limit >= mem_heap.allocated_bytes); - bool is_resized = false; + size_t bytes = block_p->allocated_bytes; + JERRY_ASSERT (mem_heap.allocated_bytes >= bytes); + mem_heap.allocated_bytes -= bytes; - if (current_block_may_expand_up_to >= size_in_bytes) + if (mem_heap.allocated_bytes * 3 <= mem_heap.limit) { - is_resized = true; + mem_heap.limit /= 2; } - else + else if (mem_heap.allocated_bytes + CONFIG_MEM_HEAP_DESIRED_LIMIT <= mem_heap.limit) { - size_t need_additional_bytes = size_in_bytes - current_block_may_expand_up_to; - - mem_block_header_t *next_block_p = mem_get_next_block_by_direction (block_p, MEM_DIRECTION_NEXT); - - if (next_block_p != NULL) - { - VALGRIND_DEFINED_STRUCT (next_block_p); - - if (next_block_p->magic_num == MEM_MAGIC_NUM_OF_FREE_BLOCK) - { - size_t next_block_data_space_size = mem_get_block_data_space_size (next_block_p); - - if (next_block_data_space_size >= need_additional_bytes) - { - /* next block is free and contains enough space */ - - is_resized = true; - - size_t new_block_chunks_count = mem_get_block_chunks_count_from_data_size (size_in_bytes); - size_t current_block_chunks_count = mem_get_block_chunks_count (block_p); - size_t next_block_chunks_count = mem_get_block_chunks_count (next_block_p); - - JERRY_ASSERT (new_block_chunks_count <= current_block_chunks_count + next_block_chunks_count); - - size_t diff_in_chunks = (size_t) ((current_block_chunks_count + - next_block_chunks_count) - new_block_chunks_count); - - mem_block_header_t *block_after_next_p = mem_get_next_block_by_direction (next_block_p, - MEM_DIRECTION_NEXT); - mem_block_header_t *new_next_of_current_block_p; - mem_block_header_t *new_prev_of_block_after_next_p; - - if (diff_in_chunks > 0) - { - mem_block_header_t *new_free_block_p = (mem_block_header_t*) ((uint8_t*) block_p + - new_block_chunks_count * MEM_HEAP_CHUNK_SIZE); - - mem_init_block_header ((uint8_t*) new_free_block_p, - 0, - MEM_BLOCK_FREE, - block_p, - block_after_next_p); - - new_prev_of_block_after_next_p = new_free_block_p; - new_next_of_current_block_p = new_free_block_p; - } - else - { - new_prev_of_block_after_next_p = block_p; - new_next_of_current_block_p = block_after_next_p; - } - - mem_set_block_next (block_p, new_next_of_current_block_p); - if (block_after_next_p != NULL) - { - VALGRIND_DEFINED_STRUCT (block_after_next_p); - - mem_set_block_prev (block_after_next_p, new_prev_of_block_after_next_p); - - VALGRIND_NOACCESS_STRUCT (block_after_next_p); - } - else - { - mem_heap.last_block_p = new_prev_of_block_after_next_p; - } - } - } - else - { - JERRY_ASSERT (next_block_p->magic_num == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK); - } - - VALGRIND_NOACCESS_STRUCT (next_block_p); - } + mem_heap.limit -= CONFIG_MEM_HEAP_DESIRED_LIMIT; } - if (is_resized) - { - JERRY_ASSERT ((mem_heap_offset_t) size_in_bytes == size_in_bytes); - - if (size_in_bytes >= block_p->allocated_bytes) - { - VALGRIND_UNDEFINED_SPACE (uint8_ptr + block_p->allocated_bytes, size_in_bytes - block_p->allocated_bytes); - } - - mem_set_block_allocated_bytes (block_p, size_in_bytes); - } - - MEM_HEAP_STAT_ALLOC_BLOCK (block_p); - - VALGRIND_NOACCESS_STRUCT(block_p); - - mem_check_heap (); - - return is_resized; -} /* mem_heap_try_resize_block */ - -/** - * Free the memory block. - */ -void -mem_heap_free_block (void *ptr) /**< pointer to beginning of data space of the block */ -{ - uint8_t *uint8_ptr = (uint8_t*) ptr; - - /* checking that uint8_ptr points to the heap */ - JERRY_ASSERT(uint8_ptr >= mem_heap.heap_start - && uint8_ptr <= mem_heap.heap_start + mem_heap.heap_size); - - mem_check_heap (); - - mem_block_header_t *block_p = (mem_block_header_t*) uint8_ptr - 1; - - VALGRIND_DEFINED_STRUCT(block_p); - - mem_block_header_t *prev_block_p = mem_get_next_block_by_direction (block_p, MEM_DIRECTION_PREV); - mem_block_header_t *next_block_p = mem_get_next_block_by_direction (block_p, MEM_DIRECTION_NEXT); + JERRY_ASSERT (mem_heap.limit >= mem_heap.allocated_bytes); MEM_HEAP_STAT_FREE_BLOCK (block_p); @@ -1063,6 +965,7 @@ mem_check_heap (void) bool is_last_block_was_met = false; size_t chunk_sizes_sum = 0; + size_t allocated_sum = 0; for (mem_block_header_t *block_p = mem_heap.first_block_p, *next_block_p; block_p != NULL; @@ -1074,6 +977,11 @@ mem_check_heap (void) || block_p->magic_num == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK); chunk_sizes_sum += mem_get_block_chunks_count (block_p); + if (block_p->magic_num == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK) + { + allocated_sum += block_p->allocated_bytes; + } + next_block_p = mem_get_next_block_by_direction (block_p, MEM_DIRECTION_NEXT); if (block_p == mem_heap.last_block_p) @@ -1091,6 +999,7 @@ mem_check_heap (void) } JERRY_ASSERT(chunk_sizes_sum * MEM_HEAP_CHUNK_SIZE == mem_heap.heap_size); + JERRY_ASSERT(allocated_sum == mem_heap.allocated_bytes); JERRY_ASSERT(is_last_block_was_met); bool is_first_block_was_met = false; diff --git a/jerry-core/mem/mem-heap.h b/jerry-core/mem/mem-heap.h index 203b147a33..b43e9afb8c 100644 --- a/jerry-core/mem/mem-heap.h +++ b/jerry-core/mem/mem-heap.h @@ -42,7 +42,6 @@ typedef enum extern void mem_heap_init (uint8_t *heap_start, size_t heap_size); extern void mem_heap_finalize (void); extern void* mem_heap_alloc_block (size_t size_in_bytes, mem_heap_alloc_term_t alloc_term); -extern bool mem_heap_try_resize_block (void *ptr, size_t size_in_bytes); extern void mem_heap_free_block (void *ptr); extern void* mem_heap_get_block_start (void *ptr); extern size_t __attr_pure___ mem_heap_recommend_allocation_size (size_t minimum_allocation_size); diff --git a/jerry-core/mem/mem-pool.h b/jerry-core/mem/mem-pool.h index 64cdfbca02..c37bef11d8 100644 --- a/jerry-core/mem/mem-pool.h +++ b/jerry-core/mem/mem-pool.h @@ -59,10 +59,10 @@ typedef uint8_t mem_pool_chunk_index_t; typedef struct __attribute__ ((aligned (MEM_ALIGNMENT))) mem_pool_state_t { /** Offset of first free chunk from the beginning of the pool (mem_pool_chunk_index_t) */ - unsigned int first_free_chunk : MEM_POOL_MAX_CHUNKS_NUMBER_LOG; + mem_pool_chunk_index_t first_free_chunk : MEM_POOL_MAX_CHUNKS_NUMBER_LOG; /** Number of free chunks (mem_pool_chunk_index_t) */ - unsigned int free_chunks_number : MEM_POOL_MAX_CHUNKS_NUMBER_LOG; + mem_pool_chunk_index_t free_chunks_number : MEM_POOL_MAX_CHUNKS_NUMBER_LOG; /** Pointer to the next pool with same chunk size */ mem_cpointer_t next_pool_cp : MEM_COMPRESSED_POINTER_WIDTH; diff --git a/jerry-core/parser/js/lexer.h b/jerry-core/parser/js/lexer.h index 40b3e4009b..b9b0f5345f 100644 --- a/jerry-core/parser/js/lexer.h +++ b/jerry-core/parser/js/lexer.h @@ -19,7 +19,7 @@ #include "literal.h" #define INVALID_VALUE 255 -#define INVALID_LITERAL UINT32_MAX +#define INVALID_LITERAL ((uint32_t) -1) /* Keywords. */ typedef enum __attr_packed___ diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 846c0d0c3a..4cf9b073db 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -439,6 +439,10 @@ run_int_loop (int_data_t *int_data) completion = __opfuncs[curr->op_idx] (*curr, int_data); +#ifdef CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE + ecma_gc_run (); +#endif /* CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE */ + #ifdef MEM_STATS interp_mem_stats_opcode_exit (int_data, opcode_pos, diff --git a/main-linux.cpp b/main-linux.cpp index 8a49cec643..26430470f5 100644 --- a/main-linux.cpp +++ b/main-linux.cpp @@ -143,6 +143,14 @@ main (int argc, { flags |= JERRY_FLAG_MEM_STATS; } + else if (!strcmp ("--mem-stats-per-opcode", argv[i])) + { + flags |= JERRY_FLAG_MEM_STATS_PER_OPCODE; + } + else if (!strcmp ("--mem-stats-separate", argv[i])) + { + flags |= JERRY_FLAG_MEM_STATS_SEPARATE; + } else if (!strcmp ("--parse-only", argv[i])) { flags |= JERRY_FLAG_PARSE_ONLY; diff --git a/main-nuttx.cpp b/main-nuttx.cpp index 04fbaf9453..bfc0661b1f 100644 --- a/main-nuttx.cpp +++ b/main-nuttx.cpp @@ -150,6 +150,14 @@ int jerry_main (int argc, char *argv[]) { flags |= JERRY_FLAG_MEM_STATS; } + else if (!strcmp ("--mem-stats-per-opcode", argv[i])) + { + flags |= JERRY_FLAG_MEM_STATS_PER_OPCODE; + } + else if (!strcmp ("--mem-stats-separate", argv[i])) + { + flags |= JERRY_FLAG_MEM_STATS_SEPARATE; + } else if (!strcmp ("--parse-only", argv[i])) { flags |= JERRY_FLAG_PARSE_ONLY; diff --git a/tests/unit/test_heap.cpp b/tests/unit/test_heap.cpp index e26d36eba8..b775e85870 100644 --- a/tests/unit/test_heap.cpp +++ b/tests/unit/test_heap.cpp @@ -118,28 +118,6 @@ main (int __attr_unused___ argc, // mem_heap_print (true); - for (uint32_t j = 0; j < test_sub_iters; j++) - { - if (ptrs[j] != NULL && (rand () % 2) == 0) - { - for (size_t k = 0; k < sizes[j]; k++) - { - JERRY_ASSERT(ptrs[j][k] == 0); - } - - size_t new_size = (size_t) rand () % (test_threshold_block_size); - - if (mem_heap_try_resize_block (ptrs[j], new_size)) - { - sizes[j] = new_size; - memset (ptrs[j], 0, sizes[j]); - } - - JERRY_ASSERT (sizes [j] == 0 - || mem_heap_get_block_start (ptrs[j] + (size_t) rand () % sizes [j]) == ptrs[j]); - } - } - for (uint32_t j = 0; j < test_sub_iters; j++) { if (ptrs[j] != NULL) diff --git a/tools/git-scripts/log.sh b/tools/git-scripts/log.sh index d7c59555bd..6987cab724 100755 --- a/tools/git-scripts/log.sh +++ b/tools/git-scripts/log.sh @@ -17,6 +17,7 @@ git log --graph --branches --decorate \ --show-notes=perf --show-notes=mem --show-notes=test_build_env \ --show-notes=arm-linux-perf \ + --show-notes=arm-linux-memory-consumption \ --show-notes=arm-linux-binary-size \ --show-notes=stm32f4-binary-size diff --git a/tools/mem_stats.sh b/tools/mem_stats.sh new file mode 100755 index 0000000000..eb64d393f4 --- /dev/null +++ b/tools/mem_stats.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# Copyright 2015 Samsung Electronics Co., Ltd. +# +# 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. + +# Choosing table or semicolon-separated output mode +if [ "$1" == "-d" ] +then + TABLE="no" + PRINT_TEST_NAME_AWK_SCRIPT='{printf "%s;", $1}' + PRINT_TOTAL_AWK_SCRIPT='{printf "%d;%d;%d;%d\n", $1, $2, $4, $5 * 1024}' + + shift +else + PRINT_TEST_NAME_AWK_SCRIPT='{printf "%30s", $1}' + PRINT_TOTAL_AWK_SCRIPT='{printf "%25d%35d%35d%20d\n", $1, $2, $4, $5 * 1024}' + TABLE="yes" +fi + +function fail_msg +{ + echo "$1" + exit 1 +} + +# Engine + +# Check if the specified build supports memory statistics options +function is_mem_stats_build +{ + [ -x "$1" ] || fail_msg "Engine '$1' is not executable" + + tmpfile=`mktemp` + "$1" --mem-stats $tmpfile | grep -- "Ignoring memory statistics option because of '!MEM_STATS' build configuration." 2>&1 > /dev/null + code=$? + rm $tmpfile + + return $code +} + +JERRY="$1" +shift +is_mem_stats_build "$JERRY" || fail_msg "First engine specified should be built without memory statistics support" + +JERRY_MEM_STATS="$1" +shift +is_mem_stats_build "$JERRY_MEM_STATS" && fail_msg "Second engine specified should be built with memory statistics support" + +# Benchmarks list +BENCHMARKS="" + +while [ $# -ne 0 ] +do + BENCHMARKS="$BENCHMARKS $1" + shift +done + +# Running +if [ "$TABLE" == "yes" ] +then + awk 'BEGIN {printf "%30s%25s%35s%35s%20s\n", "Test name", "Heap (byte-code)", "Heap (byte-code + parser)", "Heap (byte-code + execution)", "Maximum RSS"}' + echo +fi + +for bench in $BENCHMARKS +do + test=`basename -s '.js' $bench` + + echo "$test" | awk "$PRINT_TEST_NAME_AWK_SCRIPT" + MEM_STATS=$("$JERRY_MEM_STATS" --mem-stats --mem-stats-separate $bench | grep -e "Peak allocated=" -e "Allocated =" | grep -o "[0-9]*") + RSS=$(./tools/rss-measure.sh "$JERRY" $bench | tail -n 1 | grep -e "Rss" | grep -o "[0-9]*") + echo $MEM_STATS $RSS | xargs | awk "$PRINT_TOTAL_AWK_SCRIPT" +done diff --git a/tools/rss-measure.sh b/tools/rss-measure.sh index b590acdba2..a9dd85f736 100755 --- a/tools/rss-measure.sh +++ b/tools/rss-measure.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2014 Samsung Electronics Co., Ltd. +# Copyright 2014-2015 Samsung Electronics Co., Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,27 +16,16 @@ JERRY=$1 TEST=$2 -RAW_OUTPUT=$3 -SLEEP=0.3 -REPEATS=5 - +SLEEP=0.1 Rss_OUT="" -Pss_OUT="" -Private_Dirty_OUT="" -Private_Clean_OUT="" -Shared_Dirty_OUT="" -Shared_Clean_OUT="" -Share_OUT="" -Size_OUT="" -Swap_OUT="" function collect_entry() { OUT_NAME="$1_OUT"; OUT=$OUT_NAME; - SUM=$(cat /proc/"$PID"/smaps 2>/dev/null | grep "$1" | awk '{sum += $2;} END { if (sum != 0) { print sum; }; }'); + SUM=$(grep -o -e "^[0-9a-f][0-9a-f]*.*" -e "^Rss.*" /proc/$PID/smaps 2>/dev/null | grep -A 1 -- "r[w-]-p " | grep "^Rss"|awk '{s += $2;} END {print s;}') if [ "$SUM" != "" ]; then @@ -49,7 +38,7 @@ function print_entry() OUT_NAME="$1_OUT"; OUT=$OUT_NAME; - eval "echo -e \"\$$OUT\"" | awk -v entry="$1" '{ if ($1 != "") { sum += $1; n += 1; if ($1 > max) { max = $1; } } } END { if (n == 0) { exit; }; printf "%19s:%8d Kb%19s:%8d Kb\n", entry, sum / n, entry, max; }'; + eval "echo -e \"\$$OUT\"" | awk -v entry="$1" '{ if ($1 != "") { n += 1; if ($1 > max) { max = $1; } } } END { if (n == 0) { exit; }; printf "%s:%8d Kb\n", entry, max; }'; } function run_test() @@ -60,68 +49,11 @@ function run_test() while kill -0 "$PID" > /dev/null 2>&1; do collect_entry Rss - collect_entry Pss - collect_entry Private_Dirty - collect_entry Private_Clean - collect_entry Shared_Dirty - collect_entry Shared_Clean - collect_entry Share - collect_entry Size - collect_entry Swap sleep $SLEEP - done } +run_test -ITERATIONS=`seq 1 $REPEATS` -START=$(date +%s.%N) - -for i in $ITERATIONS -do - run_test -done - -FINISH=$(date +%s.%N) - -echo - -if [ "$RAW_OUTPUT" != "" ]; -then - echo -e "$Rss_OUT"; - echo -e "$Pss_OUT"; - echo -e "$Private_Dirty_OUT"; - echo -e "$Private_Clean_OUT"; - echo -e "$Shared_Dirty_OUT"; - echo -e "$Shared_Clean_OUT"; - echo -e "$Share_OUT"; - echo -e "$Size_OUT"; - echo -e "$Swap_OUT"; -fi; - -if [ "$Size_OUT" == "" ] -then - echo "===================" - echo "Test failed." - echo "===================" - exit 1 -fi; - -TIME=$(echo "scale=3;($FINISH - $START) / 1.0" | bc ); -AVG_TIME=$(echo "scale=3;$TIME / $REPEATS" | bc ); - -echo "===================" -printf "%24sAVERAGE%28sMAX\n" "" ""; print_entry Rss -print_entry Pss -print_entry Private_Dirty -print_entry Private_Clean -print_entry Shared_Dirty -print_entry Shared_Clean -print_entry Share -print_entry Size -print_entry Swap -echo -e "---" -echo -e "Exec time / average:\t$TIME / $AVG_TIME secs" -echo "==================="