auto_impl_utilities.c [plain text]
#include "auto_impl_utilities.h"
#include "auto_tester.h"
#include <malloc/malloc.h>
#include <mach/mach.h>
#include <libc.h>
#include <stdarg.h>
__private_extern__ char *__crashreporter_info__ = NULL;
vm_address_t auto_get_sp() {
#if __GNUC__ >= 4
return (vm_address_t)__builtin_frame_address(0);
#else
register vm_address_t result;
#if defined(__ppc__)
__asm__ volatile ("lwz r3, 0(r1)" : : : "r3");
__asm__ volatile ("lwz %[result], 0(r3)" : [result] "=r" (result));
return result;
#elif defined(__ppc64__)
__asm__ volatile ("ld r3, 0(r1)" : : : "r3");
__asm__ volatile ("ld %[result], 0(r3)" : [result] "=r" (result));
return result;
#elif defined(__i386__)
__asm__ volatile ("movl %%esp,%%eax" : : : "eax");
#else
#error architecture unsupported
#endif
#endif
}
size_t auto_round_page(size_t size) {
if (!size) return vm_page_size;
return (size + vm_page_size - 1) & ~ (vm_page_size - 1);
}
const char *auto_prelude(void) {
static char buf[32] = { 0 };
if (!buf[0]) {
snprintf(buf, sizeof(buf), "auto malloc[%d]", getpid());
}
return (const char *)buf;
}
void auto_error(void *azone, const char *msg, const void *ptr) {
if (ptr) {
malloc_printf("*** %s: error for object %p: %s\n", auto_prelude(), ptr, msg);
} else {
malloc_printf("*** %s: error: %s\n", auto_prelude(), msg);
}
#if 0 && DEBUG
malloc_printf("*** Sleeping to help debug\n");
sleep(3600); #endif
}
void auto_fatal(const char *format, ...) {
static char buffer[512];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
__crashreporter_info__ = buffer;
malloc_printf("%s", buffer);
__builtin_trap();
}
__private_extern__ malloc_zone_t *aux_zone = NULL;
__private_extern__ void aux_init(void) {
if (!aux_zone) {
aux_zone = malloc_create_zone(4096, 0); malloc_set_zone_name(aux_zone, "aux_zone");
#if !DEBUG
malloc_zone_unregister(aux_zone); #endif
}
}
double auto_time_interval(auto_date_t after, auto_date_t before) {
#if 0
static double rate = 0.0;
if (rate == 0.0) {
struct mach_timebase_info info;
mach_timebase_info(&info);
rate = (double)info.numer / (1.0E9 * (double)info.denom);
}
return (after - before) * rate;
#else
return (after - before) / 1.0E6;
#endif
}
malloc_zone_t *auto_debug_zone(void)
{
static malloc_zone_t *z = NULL;
if (!z) {
z = malloc_create_zone(4096, 0);
malloc_set_zone_name(z, "auto debug");
}
return z;
}
#define CHECK_ADDR(n) \
entry->stack[n] = (vm_address_t)(__builtin_return_address(n + 1) - 4); \
if ((__builtin_frame_address(n + 3) == NULL) || (__builtin_return_address(n + 2) == NULL)) { \
goto done; \
}
#define CHECK_ADDR2(n) \
CHECK_ADDR(n) \
CHECK_ADDR(n + 1)
#define CHECK_ADDR4(n) \
CHECK_ADDR2(n) \
CHECK_ADDR2(n + 2)
#define CHECK_ADDR8(n) \
CHECK_ADDR4(n) \
CHECK_ADDR4(n + 4)
#define UNUSED 0xffffffff
#define ENTRY_COUNT 32
typedef struct {
vm_address_t stack[8];
unsigned int oldCount; unsigned int newCount; } auto_refcount_history_entry_t;
typedef struct {
vm_address_t address;
int nextEntry;
auto_refcount_history_entry_t entries[ENTRY_COUNT];
} auto_refcount_history_t;
static auto_refcount_history_t *history_list = NULL;
static int history_allocated = 0;
static int history_used = 0;
static int history_hash(vm_address_t target)
{
int index = (target >> 2) % history_allocated;
while (history_list[index].address != 0 &&
history_list[index].address != target)
{
index++;
if (index == history_allocated) index = 0;
}
return index;
}
static spin_lock_t refcount_stacks_lock;
#define auto_refcount_stacks_lock() spin_lock(&refcount_stacks_lock)
#define auto_refcount_stacks_unlock() spin_unlock(&refcount_stacks_lock)
void auto_record_refcount_stack(auto_zone_t *azone, void *ptr, int delta)
{
int h, e;
vm_address_t addr = (vm_address_t)ptr;
auto_refcount_history_t *history = NULL;
auto_refcount_history_entry_t *entry;
auto_refcount_stacks_lock();
if (history_used >= history_allocated * 3 / 4) {
auto_refcount_history_t *old_list = history_list;
int old_allocated = history_allocated;
history_allocated = old_allocated ? old_allocated * 2 : 10000;
history_list =
malloc_zone_calloc(auto_debug_zone(),
history_allocated, sizeof(*history_list));
for (h = 0; h < history_used; h++) {
if (old_list[h].address) {
history_list[history_hash(old_list[h].address)] = old_list[h];
}
}
malloc_zone_free(auto_debug_zone(), old_list);
}
history = &history_list[history_hash(addr)];
if (history->address != addr) {
history->address = (vm_address_t)ptr;
history->nextEntry = 0;
for (e = 0; e < ENTRY_COUNT; e++) {
history->entries[e].oldCount = UNUSED;
}
history_used++;
}
entry = &history->entries[history->nextEntry++];
if (history->nextEntry == ENTRY_COUNT) history->nextEntry = 0;
bzero(entry, sizeof(*entry));
CHECK_ADDR8(0);
done:
entry->oldCount = auto_zone_retain_count((void *)azone, ptr);
entry->newCount = entry->oldCount + delta;
auto_refcount_stacks_unlock();
}
void auto_print_refcount_stacks(void *ptr)
{
int e;
vm_address_t addr = (vm_address_t)ptr;
auto_refcount_history_t *history = NULL;
auto_refcount_history_entry_t *entry;
auto_refcount_stacks_lock();
history = &history_list[history_hash(addr)];
if (history->address != addr) {
malloc_printf("No refcount history for address %p\n", ptr);
return;
}
fprintf(stderr, "\nREFCOUNT HISTORY FOR %p\n\n", ptr);
e = history->nextEntry;
do {
entry = &history->entries[e];
if (entry->oldCount != UNUSED) {
int s;
if (entry->oldCount == entry->newCount) {
fprintf(stderr, "\nrefcount %d (new)\tadecodestack ", entry->newCount);
} else {
fprintf(stderr, "refcount %d -> %d \tadecodestack ",
entry->oldCount, entry->newCount);
}
for (s = 0; s < 8 && entry->stack[s]; s++) {
fprintf(stderr, "%p ", (void *)entry->stack[s]);
}
fprintf(stderr, "\n");
}
e++;
if (e == ENTRY_COUNT) e = 0;
} while (e != history->nextEntry);
auto_refcount_stacks_unlock();
fprintf(stderr, "\ndone\n");
}
#define PTR_SET_DEPTH 4
#define PTR_SET_GROWTH 333
struct ptr_set {
spin_lock_t lock;
unsigned length; void **members; void **end; };
static void **ptr_set_find_slot(ptr_set *set, void *ptr);
static void ptr_set_grow(ptr_set *set);
static inline void **ptr_set_next(ptr_set *set, void **slot) { return ++slot == set->end ? set->members : slot; }
static inline intptr_t ptr_set_hash(ptr_set *set, void *ptr) { return (((intptr_t)ptr >> 2) * 2654435761u) % set->length; }
static boolean_t ptr_set_add_no_lock_did_grow(ptr_set *set, void *ptr) {
boolean_t didGrow = false;
void **slot;
if (ptr == NULL) return false;
if ((intptr_t)ptr & 0x3) {
malloc_printf("*** %s: ptr_set_add(ptr=%p) not pointer aligned\n", auto_prelude(), ptr);
return false;
}
while (1) {
slot = ptr_set_find_slot(set, ptr);
if (slot != NULL) break;
ptr_set_grow(set);
didGrow = true;
}
*slot = ptr;
return didGrow;
}
static void ptr_set_grow(ptr_set *set) {
void **slot;
unsigned old_length = set->length;
void **old_members = set->members;
void **old_end = set->end;
set->length = (old_length + PTR_SET_GROWTH) | 1;
set->members = (void **)aux_calloc(set->length, sizeof(void *));
set->end = set->members + set->length;
for (slot = old_members; slot < old_end; slot++) ptr_set_add_no_lock_did_grow(set, *slot);
aux_free(old_members);
}
static void **ptr_set_find_slot(ptr_set *set, void *ptr) {
void **slot;
unsigned depth;
unsigned hash = ptr_set_hash(set, ptr);
for (depth = 0, slot = set->members + hash; depth < PTR_SET_DEPTH; depth++, slot = ptr_set_next(set, slot)) {
void *old_member = *slot;
if (old_member == NULL || old_member == ptr) return slot;
if (hash != ptr_set_hash(set, old_member)) return NULL;
}
return NULL;
}
__private_extern__ ptr_set *ptr_set_new() {
ptr_set *set = aux_malloc(sizeof(ptr_set));
set->lock = 0;
set->length = PTR_SET_GROWTH;
set->members = (void **)aux_calloc(PTR_SET_GROWTH, sizeof(void *));
set->end = set->members + PTR_SET_GROWTH;
return set;
}
__private_extern__ void ptr_set_dispose(ptr_set *set) {
aux_free(set->members);
aux_free(set);
}
__private_extern__ void ptr_set_add(ptr_set *set, void *ptr) {
spin_lock(&set->lock);
ptr_set_add_no_lock_did_grow(set, ptr);
spin_unlock(&set->lock);
}
__private_extern__ int ptr_set_is_member_no_lock(ptr_set *set, void *ptr) {
void **slot = ptr_set_find_slot(set, ptr);
return (slot != NULL && *slot == ptr);
}
__private_extern__ int ptr_set_is_member(ptr_set *set, void *ptr) {
spin_lock(&set->lock);
void **slot = ptr_set_find_slot(set, ptr);
boolean_t result = slot != NULL && *slot == ptr;
spin_unlock(&set->lock);
return result;
}
__private_extern__ void ptr_set_remove(ptr_set *set, void *ptr) {
spin_lock(&set->lock);
void **slot = ptr_set_find_slot(set, ptr);
if (slot != NULL && *slot == ptr) {
unsigned hash = ptr_set_hash(set, ptr);
void **next_slot;
for (next_slot = ptr_set_next(set, slot); 1; next_slot = ptr_set_next(set, next_slot)) {
void *old_member = *next_slot;
if (old_member == NULL || hash != ptr_set_hash(set, old_member)) {
*slot = NULL;
break;
}
*slot = *next_slot;
slot = next_slot;
}
}
spin_unlock(&set->lock);
}
struct ptr_map {
spin_lock_t lock;
unsigned length; void **members; void **end; };
static void **ptr_map_find_slot(ptr_map *map, void *ptr);
static void ptr_map_grow(ptr_map *map);
static inline void **ptr_map_next(ptr_map *map, void **slot) { return ++slot == map->end ? map->members : slot; }
static inline intptr_t ptr_map_hash(ptr_map *map, void *ptr) { return (((uintptr_t)ptr >> 4) * 2654435761u) % map->length; }
static boolean_t ptr_map_add_no_lock_did_grow(ptr_map *map, void *ptr, void *value) {
boolean_t didGrow = false;
void **slot;
if (ptr == NULL) return false;
if ((intptr_t)ptr & 15) {
malloc_printf("*** %s: ptr_map_add(ptr=%p) not object aligned\n", auto_prelude(), ptr);
return false;
}
while (1) {
slot = ptr_map_find_slot(map, ptr);
if (slot != NULL) break;
ptr_map_grow(map);
didGrow = true;
}
*slot = ptr;
*(slot + map->length) = value;
return didGrow;
}
static void ptr_map_grow(ptr_map *map) {
void **slot;
unsigned old_length = map->length;
void **old_members = map->members;
void **old_end = map->end;
map->length = (old_length + PTR_SET_GROWTH) | 1;
size_t size = map->length * sizeof(void *);
map->members = (void **)aux_calloc(2, size); map->end = map->members + map->length;
for (slot = old_members; slot < old_end; slot++) ptr_map_add_no_lock_did_grow(map, *slot, *(slot+old_length));
aux_free(old_members);
}
static void **ptr_map_find_slot(ptr_map *map, void *ptr) {
void **slot;
unsigned depth;
unsigned hash = ptr_map_hash(map, ptr);
for (depth = 0, slot = map->members + hash; depth < PTR_SET_DEPTH; depth++, slot = ptr_map_next(map, slot)) {
void *old_member = *slot;
if (old_member == NULL || old_member == ptr) return slot;
if (hash != ptr_map_hash(map, old_member)) return NULL;
}
return NULL;
}
__private_extern__ ptr_map * ptr_map_new() {
ptr_map *map = aux_malloc(sizeof(ptr_map));
map->lock = 0;
map->length = PTR_SET_GROWTH;
size_t size = PTR_SET_GROWTH * sizeof(void *);
map->members = (void **)aux_calloc(2, size);
map->end = map->members + PTR_SET_GROWTH;
return map;
}
__private_extern__ void ptr_map_dispose(ptr_map *map) {
aux_free(map->members);
aux_free(map);
}
__private_extern__ void ptr_map_set(ptr_map *map, void *ptr, void *value) {
spin_lock(&map->lock);
ptr_map_add_no_lock_did_grow(map, ptr, value);
spin_unlock(&map->lock);
}
__private_extern__ void * ptr_map_get(ptr_map *map, void *ptr) {
spin_lock(&map->lock);
void **slot = ptr_map_find_slot(map, ptr);
void *result = (slot != NULL && *slot == ptr) ? *(slot + map->length) : NULL;
spin_unlock(&map->lock);
return result;
}
__private_extern__ void *ptr_map_remove(ptr_map *map, void *ptr) {
void *result = NULL;
spin_lock(&map->lock);
void **slot = ptr_map_find_slot(map, ptr);
if (slot != NULL && *slot == ptr) {
result = *(slot + map->length);
unsigned hash = ptr_map_hash(map, ptr);
void **next_slot;
for (next_slot = ptr_map_next(map, slot); 1; next_slot = ptr_map_next(map, next_slot)) {
void *old_member = *next_slot;
if (old_member == NULL || hash != ptr_map_hash(map, old_member)) {
*slot = NULL;
break;
}
*slot = *next_slot; *(slot+map->length) = *(next_slot+map->length);
slot = next_slot;
}
}
spin_unlock(&map->lock);
return result;
}
__private_extern__ void auto_refcount_underflow_error(void *block) { }
__private_extern__ void auto_zone_resurrection_error()
{
}
__private_extern__ void auto_zone_thread_local_error(void) { }
__private_extern__ void auto_zone_thread_registration_error()
{
AUTO_PROBE(auto_probe_unregistered_thread_error());
}
__private_extern__ void auto_zone_global_data_memmove_error() { }
__private_extern__ void auto_zone_association_error(void *address) { }