diff --git a/loader.c b/loader.c index 67a8cf5..6f4d88e 100644 --- a/loader.c +++ b/loader.c @@ -25,6 +25,13 @@ void init_selector_tables(void); void init_trampolines(void); void objc_send_load_message(Class class); +void log_selector_memory_usage(void); + +static void log_memory_stats(void) +{ + log_selector_memory_usage(); +} + /* Number of threads that are alive. */ int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ @@ -62,6 +69,10 @@ void __objc_exec_class(struct objc_module_abi_8 *module) init_arc(); init_trampolines(); first_run = NO; + if (getenv("LIBOBJC_MEMORY_PROFILE")) + { + atexit(log_memory_stats); + } } // The runtime mutex is held for the entire duration of a load. It does diff --git a/pool.h b/pool.h index ffa8a6a..f0bd545 100644 --- a/pool.h +++ b/pool.h @@ -29,13 +29,17 @@ static mutex_t NAME(_lock); #define UNLOCK_POOL() #endif +static int pool_size = 0; +static int pool_allocs = 0; static inline POOL_TYPE*NAME(_pool_alloc)(void) { LOCK_POOL(); + pool_allocs++; if (0 > NAME(_pool_next_index)) { NAME(_pool) = malloc(PAGE_SIZE); NAME(_pool_next_index) = POOL_SIZE - 1; + pool_size += PAGE_SIZE; } POOL_TYPE* new = &NAME(_pool)[NAME(_pool_next_index)--]; UNLOCK_POOL(); diff --git a/sarray2.c b/sarray2.c index 7e98c91..c3c79d8 100644 --- a/sarray2.c +++ b/sarray2.c @@ -243,3 +243,24 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray) free(sarray); } +PRIVATE int SparseArraySize(SparseArray *sarray) +{ + int size = 0; + if (sarray->shift == 0) + { + return 256*sizeof(void*) + sizeof(SparseArray); + } + size += 256*sizeof(void*) + sizeof(SparseArray); + for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) + { + SparseArray *child = sarray->data[i]; + if (child == &EmptyArray || + child == &EmptyArray8 || + child == &EmptyArray16) + { + continue; + } + size += SparseArraySize(child); + } + return size; +} diff --git a/sarray2.h b/sarray2.h index eb98ccf..cefc521 100644 --- a/sarray2.h +++ b/sarray2.h @@ -136,4 +136,9 @@ void * SparseArrayNext(SparseArray * sarray, uint32_t * index); */ SparseArray *SparseArrayCopy(SparseArray * sarray); +/** + * Returns the total memory usage of a sparse array. + */ +int SparseArraySize(SparseArray *sarray); + #endif //_SARRAY_H_INCLUDED_ diff --git a/selector_table.c b/selector_table.c index ec932ff..13b6ca5 100644 --- a/selector_table.c +++ b/selector_table.c @@ -22,7 +22,6 @@ # define TDD(x) #endif -#define fprintf(...) // Define the pool allocator for selectors. This is a simple bump-the-pointer @@ -43,6 +42,12 @@ static uint32_t selector_count = 1; */ PRIVATE SparseArray *selector_list = NULL; +#ifdef DEBUG_SELECTOR_TABLE +#define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif + // Get the functions for string hashing #include "string_hash.h" @@ -156,7 +161,7 @@ static int selector_identical(const void *k, const SEL value) { SEL key = (SEL)k; - fprintf(stderr, "Comparing %s %s, %s %s\n", sel_getNameNonUnique(key), sel_getNameNonUnique(value), sel_getType_np(key), sel_getType_np(value)); + DEBUG_LOG("Comparing %s %s, %s %s\n", sel_getNameNonUnique(key), sel_getNameNonUnique(value), sel_getType_np(key), sel_getType_np(value)); return string_compare(sel_getNameNonUnique(key), sel_getNameNonUnique(value)) && selector_types_equal(sel_getType_np(key), sel_getType_np(value)); } @@ -226,6 +231,20 @@ static selector_table *sel_table; */ mutex_t selector_table_lock; +static int selector_name_copies; + +PRIVATE void log_selector_memory_usage(void) +{ + fprintf(stderr, "%d bytes in selector name list.\n", SparseArraySize(selector_list)); + fprintf(stderr, "%d bytes in selector names.\n", selector_name_copies); + fprintf(stderr, "%d bytes (%d entries) in selector hash table.\n", (int)(sel_table->table_size * + sizeof(struct selector_table_cell_struct)), sel_table->table_size); + fprintf(stderr, "%d selectors registered.\n", selector_count); + fprintf(stderr, "%d hash table cells per selector (%.2f%% full)\n", sel_table->table_size / selector_count, ((float)selector_count) / sel_table->table_size * 100); +} + + + /** * Resizes the dtables to ensure that they can store as many selectors as @@ -251,7 +270,7 @@ static SEL selector_lookup(const char *name, const char *types) } static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx) { - //fprintf(stderr, "Sel %s uid: %d, idx: %d, hash: %d\n", sel_getNameNonUnique(aSel), uid, idx, hash_selector(aSel)); + DEBUG_LOG("Sel %s uid: %d, idx: %d, hash: %d\n", sel_getNameNonUnique(aSel), uid, idx, hash_selector(aSel)); struct sel_type_list *typeList = (struct sel_type_list *)selector_pool_alloc(); typeList->value = aSel->name; @@ -271,7 +290,7 @@ static inline void register_selector_locked(SEL aSel) uintptr_t idx = selector_count++; if (NULL == aSel->types) { - fprintf(stderr, "Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); + DEBUG_LOG("Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); add_selector_to_table(aSel, idx, idx); objc_resize_dtables(selector_count); return; @@ -283,7 +302,7 @@ static inline void register_selector_locked(SEL aSel) untyped = selector_pool_alloc(); untyped->name = aSel->name; untyped->types = 0; - fprintf(stderr, "Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); + DEBUG_LOG("Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); add_selector_to_table(untyped, idx, idx); // If we are in type dependent dispatch mode, the uid for the typed // and untyped versions will be different @@ -296,7 +315,7 @@ static inline void register_selector_locked(SEL aSel) } uintptr_t uid = (uintptr_t)untyped->name; TDD(uid = idx); - fprintf(stderr, "Registering typed selector %d %s %s\n", (int)uid, sel_getNameNonUnique(aSel), sel_getType_np(aSel)); + DEBUG_LOG("Registering typed selector %d %s %s\n", (int)uid, sel_getNameNonUnique(aSel), sel_getType_np(aSel)); add_selector_to_table(aSel, uid, idx); // Add this set of types to the list. @@ -340,10 +359,10 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs) { // If an identical selector is already registered, return it. SEL copy = selector_lookup(aSel->name, aSel->types); - //fprintf(stderr, "Checking if old selector is registered: %d (%d)\n", NULL != copy ? selector_equal(aSel, copy) : 0, ((NULL != copy) && selector_equal(aSel, copy))); + DEBUG_LOG("Checking if old selector is registered: %d (%d)\n", NULL != copy ? selector_equal(aSel, copy) : 0, ((NULL != copy) && selector_equal(aSel, copy))); if ((NULL != copy) && selector_identical(aSel, copy)) { - //fprintf(stderr, "Not adding new copy\n"); + DEBUG_LOG("Not adding new copy\n"); return copy; } LOCK_FOR_SCOPE(&selector_table_lock); @@ -355,6 +374,7 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs) // Create a copy of this selector. copy = selector_pool_alloc(); copy->name = aSel->name; + copy->types = (NULL == aSel->types) ? NULL : aSel->types; if (copyArgs) { SEL untyped = selector_lookup(aSel->name, 0); @@ -365,10 +385,14 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs) else { copy->name = strdup(aSel->name); + selector_name_copies += strlen(copy->name); + } + if (copy->types != NULL) + { + copy->types = strdup(copy->types); + selector_name_copies += strlen(copy->types); } } - copy->types = (NULL == aSel->types) ? NULL : - (copyArgs ? strdup(aSel->types) : aSel->types); // Try to register the copy as the authoritative version register_selector_locked(copy); return copy;