Files
ac-decomp/src/static/libc64/__osMalloc.c
T

637 lines
20 KiB
C

#include "libc64/__osMalloc.h"
#include <dolphin/os.h>
#include "libultra/libultra.h"
#include "terminal.h"
#define OS_MALLOC_MAGIC (s16)'ss' // magic number for an OSMemBlock
#define OS_MALLOC_BLOCK_OK(block) ((block) != NULL && (block)->magic == OS_MALLOC_MAGIC) // check if OSMemBlock structure is OK
#define OS_MALLOC_DATA2BLOCK(data) ((OSMemBlock*)((u32)(data) - sizeof(OSMemBlock))) // get memblock data pointer from OSMemBlock
#define OS_MALLOC_BLOCK2DATA(block) ((u8*)(block) + sizeof(OSMemBlock)) // get OSMemBlock pointer from data
// Gets the pointer to the next OSMemBlock immediately following this block in RAM, may or may not be a valid OSMemBlock
#define OS_MALLOC_NEXTMEMBLOCK(block) ((OSMemBlock*)((u32)(block) + sizeof(OSMemBlock) + (block)->size))
int __osMalloc_FreeBlockTest_Enable = FALSE;
static void setDebugInfo(OSMemBlock* block, const char* file, s32 line, OSArena* arena) {
block->file = file;
block->line = line;
block->threadId = osGetThreadId(NULL);
block->arena = arena;
block->time = osGetTime();
}
static void arena_lock_init(OSArena* arena) {
static OSMesg arena_lock_msg;
osCreateMesgQueue(&arena->lockQueue, &arena_lock_msg, OS_MESG_BLOCK);
}
static void arena_lock(OSArena* arena) {
osSendMesg(&arena->lockQueue, NULL, OS_MESG_BLOCK);
}
static void arena_unlock(OSArena* arena) {
osRecvMesg(&arena->lockQueue, NULL, OS_MESG_BLOCK);
}
static OSMemBlock* get_block_next(OSMemBlock* block) {
if (block->next != NULL && !OS_MALLOC_BLOCK_OK(block->next)) {
// Emergency! Memory leak discovered!
OSReport(VT_COL(RED, WHITE) "緊急事態!メモリリーク発見! (block=%08x)\n" VT_RST, block->next);
OSPanic(__FILE__, 133, "");
block->next = NULL; // @BUG - OSPanic halts CPU execution, this is pointless
}
return block->next;
}
static OSMemBlock* get_block_prev(OSMemBlock* block) {
// @BUG - this shouldn't check for block->prev != NULL
if (block->prev != NULL && !OS_MALLOC_BLOCK_OK(block->prev)) {
// Emergency! Memory leak discovered!
OSReport(VT_COL(RED, WHITE) "緊急事態!メモリリーク発見! (block=%08x)\n" VT_RST, block->prev);
OSPanic(__FILE__, 144, "");
block->prev = NULL; // @BUG - OSPanic halts CPU execution, this is pointless
}
return block->prev;
}
static OSMemBlock* search_last_block(OSArena* arena) {
OSMemBlock* block = NULL;
if (arena != NULL) {
OSMemBlock* current = arena->head;
if (OS_MALLOC_BLOCK_OK(current)) {
while (current != NULL) {
block = current;
current = get_block_next(current);
}
}
}
return block;
}
extern void __osMallocInit(OSArena* arena, u8* base, s32 size) {
bzero(arena, sizeof(OSArena));
arena_lock_init(arena);
__osMallocAddBlock(arena, base, size);
arena->initialized = TRUE;
}
extern void __osMallocAddBlock(OSArena* arena, u8* base, s32 size) {
s32 align_size;
OSMemBlock* block;
OSMemBlock* last;
if (base != NULL) {
block = (OSMemBlock*)ALIGN_NEXT((u32)base, 32);
align_size = ALIGN_PREV(size - ((u32)block - (u32)base), 32);
if (align_size > (int)sizeof(OSMemBlock)) {
memset(block, 0xAB, align_size);
block->next = NULL;
block->prev = NULL;
block->size = align_size - sizeof(OSMemBlock);
block->free = TRUE;
block->magic = OS_MALLOC_MAGIC;
arena_lock(arena);
last = search_last_block(arena);
if (last == NULL) {
arena->head = block;
arena->base = base;
} else {
block->prev = last;
last->next = block;
}
arena_unlock(arena);
}
}
}
static void destroy_all_block(OSArena* arena) {
OSMemBlock* block;
OSMemBlock* next;
arena_lock(arena);
block = arena->head;
while (block != NULL) {
next = get_block_next(block);
memset(block, 0xAB, block->size + sizeof(OSMemBlock));
block = next;
}
arena_unlock(arena);
}
extern void __osMallocCleanup(OSArena* arena) {
destroy_all_block(arena);
bzero(arena, sizeof(OSArena));
}
extern BOOL __osMallocIsInitalized(OSArena* arena) {
return arena->initialized;
}
static void __osMalloc_FreeBlockTest(OSArena* arena, OSMemBlock* block) {
if (__osMalloc_FreeBlockTest_Enable) {
u32* s = (u32*)(OS_MALLOC_BLOCK2DATA(block));
u32* e = (u32*)(OS_MALLOC_BLOCK2DATA(block) + block->size);
u32* p;
for (p = s; p < e; p++) {
u32 v = *p;
if (v != 0xABABABAB && v != 0xEFEFEFEF) {
// Emergency! Memory leak detected!
OSReport(VT_COL(RED, WHITE) "緊急事態!メモリリーク検出! (block=%08x s=%08x e=%08x p=%08x)\n" VT_RST, block, s, e, p);
__osDisplayArena(arena);
OSPanic(__FILE__, 300, "");
break;
}
}
}
}
static void* __osMallocAlign_NoLock(OSArena* arena, u32 size, u32 align) {
OSMemBlock* aligned_block;
OSMemBlock* new_next;
int alignment_bytes;
OSMemBlock* block;
u8* data_p = NULL;
OSMemBlock* next;
u32 full_size;
u32 mask;
int remain;
size = ALIGN_NEXT(size, 32);
full_size = ALIGN_NEXT(size, 32) + sizeof(OSMemBlock);
if (align <= 16) {
align = 16;
} else if (align <= 32) {
align = 32;
} else if (align <= 64) {
align = 64;
} else if (align <= 128) {
align = 128;
} else if (align <= 256) {
align = 256;
} else if (align <= 1024) {
align = 1024;
} else {
align = 8;
}
block = arena->head;
mask = align - 1;
while (block != NULL) {
if (block->free) {
remain = ((u32)block + sizeof(OSMemBlock)) & mask;
alignment_bytes = remain == 0 ? 0 : align - remain;
aligned_block = (OSMemBlock*)((u32)block + alignment_bytes);
if (block->size - alignment_bytes >= size) {
if (arena->flags & OSArena_FLAG_FREE_BLOCK_TEST) {
__osMalloc_FreeBlockTest(arena, block);
}
if (block != aligned_block) {
memmove(aligned_block, block, sizeof(OSMemBlock));
block = aligned_block;
block->size -= alignment_bytes;
if (block->prev != NULL) {
block->prev->next = block;
block->prev->size += alignment_bytes; // do not orphan alignment bytes
} else {
arena->head = block;
}
if (block->next != NULL) {
block->next->prev = block;
}
}
if (block->size > full_size) {
new_next = (OSMemBlock*)((u32)block + full_size);
new_next->next = get_block_next(block);
new_next->prev = block;
new_next->size = block->size - full_size;
new_next->free = TRUE;
new_next->magic = OS_MALLOC_MAGIC;
block->next = new_next;
block->size = size;
next = get_block_next(new_next);
if (next != NULL) {
next->prev = new_next;
}
}
block->free = FALSE;
setDebugInfo(block, NULL, 0, arena);
data_p = OS_MALLOC_BLOCK2DATA(block);
if (arena->flags & OSArena_FLAG_CLEAR_MEM_ON_ALLOC) {
memset(data_p, 0xCD, size);
}
break;
}
}
block = get_block_next(block);
}
return data_p;
}
static void* __osMalloc_NoLock(OSArena* arena, u32 size) {
return __osMallocAlign_NoLock(arena, size, 0);
}
extern void* __osMallocAlign(OSArena* arena, u32 size, u32 align) {
void* ret;
arena_lock(arena);
ret = __osMallocAlign_NoLock(arena, size, align);
arena_unlock(arena);
return ret;
}
extern void* __osMalloc(OSArena* arena, u32 size) {
return __osMallocAlign(arena, size, 0);
}
extern void* __osMallocR(OSArena* arena, u32 size) {
OSMemBlock* block;
OSMemBlock* next;
OSMemBlock* n;
u8* ret = NULL;
u32 full_size;
size = ALIGN_NEXT(size, 32);
full_size = ALIGN_NEXT(size, 32) + sizeof(OSMemBlock);
arena_lock(arena);
block = search_last_block(arena);
while (block != NULL) {
if (block->free && block->size >= size) {
if (arena->flags & OSArena_FLAG_FREE_BLOCK_TEST) {
__osMalloc_FreeBlockTest(arena, block);
}
if (block->size > full_size) {
next = (OSMemBlock*)((u32)block + block->size - size);
next->next = get_block_next(block);
next->prev = block;
next->size = size;
next->magic = OS_MALLOC_MAGIC;
block->next = next;
block->size -= full_size;
n = get_block_next(next);
if (n != NULL) {
n->prev = next;
}
block = next;
}
block->free = FALSE;
setDebugInfo(block, NULL, 0, arena);
ret = OS_MALLOC_BLOCK2DATA(block);
if (arena->flags & OSArena_FLAG_CLEAR_MEM_ON_ALLOC) {
memset(ret, 0xCD, size);
}
break;
}
block = get_block_prev(block);
}
arena_unlock(arena);
return ret;
}
static void __osFree_NoLock(OSArena* arena, void* ptr) {
OSMemBlock* block = OS_MALLOC_DATA2BLOCK(ptr);
OSMemBlock* next;
OSMemBlock* prev;
OSMemBlock* temp;
if (ptr != NULL) {
if (!OS_MALLOC_BLOCK_OK(block)) {
OSReport(VT_COL(RED, WHITE) "__osFree:不正解放(%08x)\n" VT_RST, ptr); // __osFree: irregular deallocation
OSPanic(__FILE__, 738, "");
return;
}
if (block->free) {
OSReport(VT_COL(RED, WHITE) "__osFree:二重解放(%08x)\n" VT_RST, ptr); // __osFree: double deallocation
OSPanic(__FILE__, 743, "");
return;
}
if (block->arena != arena && arena != NULL) {
OSReport(VT_COL(RED, WHITE) "__osFree:確保時と違う方法で解放しようとした (%08x:%08x)\n" VT_RST, arena, block->arena); // __osFree: attempt to release memory in a different arena from where it was allocated
OSPanic(__FILE__, 750, "");
return;
}
next = get_block_next(block);
prev = get_block_prev(block);
block->free = TRUE;
setDebugInfo(block, NULL, 0, arena);
if (arena->flags & OSArena_FLAG_CLEAR_MEM_ON_FREE) {
memset(OS_MALLOC_BLOCK2DATA(block), 0xEF, block->size);
}
if (next == OS_MALLOC_NEXTMEMBLOCK(block)) {
if (next->free) {
temp = get_block_next(next);
if (temp != NULL) {
temp->prev = block;
}
block->size += next->size + sizeof(OSMemBlock);
if (arena->flags & OSArena_FLAG_CLEAR_MEM_ON_FREE) {
memset(next, 0xEF, sizeof(OSMemBlock));
}
block->next = temp;
next = temp;
}
}
if (prev != NULL && prev->free && block == OS_MALLOC_NEXTMEMBLOCK(prev)) {
if (next != NULL) {
next->prev = prev;
}
prev->next = next;
prev->size += block->size + sizeof(OSMemBlock);
if (arena->flags & OSArena_FLAG_CLEAR_MEM_ON_FREE) {
memset(block, 0xEF, sizeof(OSMemBlock));
}
}
}
}
extern void __osFree(OSArena* arena, void* ptr) {
arena_lock(arena);
__osFree_NoLock(arena, ptr);
arena_unlock(arena);
}
// @fabricated, most likely suspect from DnM+'s symbol map
static void* __osFree_NoLock_DEBUG(OSArena* arena, void* ptr) {
// __osFree: irregular deallocation
OSReport(VT_COL(RED, WHITE) "__osFree:不正解放(%08x) [%s:%d ]\n" VT_RST);
// __osFree: double deallocation
OSReport(VT_COL(RED, WHITE) "__osFree:二重解放(%08x) [%s:%d ]\n" VT_RST);
}
#pragma force_active on
extern void* __osRealloc(OSArena* arena, void* ptr, u32 size) {
void* new_ptr;
OSMemBlock* orig_block;
OSMemBlock* next;
OSMemBlock* temp;
OSMemBlock* new_next;
u32 full_size;
u32 need_size;
orig_block = OS_MALLOC_DATA2BLOCK(ptr);
size = ALIGN_NEXT(size, 32);
full_size = ALIGN_NEXT(size, 32) + sizeof(OSMemBlock);
arena_lock(arena);
if (ptr == NULL) {
ptr = __osMalloc_NoLock(arena, size);
} else if (size == 0) {
__osFree_NoLock(arena, ptr);
ptr = NULL;
} else if (size != orig_block->size) {
if (size > orig_block->size) {
next = get_block_next(orig_block);
need_size = size - orig_block->size;
if (next == OS_MALLOC_NEXTMEMBLOCK(orig_block) && next->free && next->size >= need_size) {
OSMemBlock* new_next = (OSMemBlock*)((u32)next + need_size);
next->size -= need_size;
temp = get_block_next(next);
if (temp != NULL) {
temp->prev = new_next;
}
orig_block->next = new_next;
orig_block->size = size;
memmove(new_next, next, sizeof(OSMemBlock));
} else {
new_ptr = __osMalloc_NoLock(arena, size);
if (new_ptr != NULL) {
memmove(new_ptr, ptr, orig_block->size);
__osFree_NoLock(arena, ptr);
}
ptr = new_ptr;
}
} else if (size < orig_block->size) {
next = get_block_next(orig_block);
if (next != NULL && next->free) {
OSMemBlock* new_next = (OSMemBlock*)((u32)orig_block + full_size);
*new_next = *next;
new_next->size += orig_block->size - size;
orig_block->next = new_next;
orig_block->size = size;
temp = get_block_next(new_next);
if (temp != NULL) {
temp->prev = new_next;
}
} else if (size + sizeof(OSMemBlock) < orig_block->size) {
new_next = (OSMemBlock*)((u32)orig_block + full_size);
new_next->next = get_block_next(orig_block);
new_next->prev = orig_block;
new_next->size = orig_block->size - full_size;
new_next->free = TRUE;
new_next->magic = OS_MALLOC_MAGIC;
orig_block->next = new_next;
orig_block->size = size;
temp = get_block_next(new_next);
if (temp != NULL) {
temp->prev = new_next;
}
} else {
ptr = NULL;
}
}
}
arena_unlock(arena);
return ptr;
}
#pragma force_active reset
static int __osAnalyzeArena(OSArena* arena, u32* ptr) {
OSMemBlock* block;
u32 max_free_block_size = 0;
u32 free_blocks_size = 0;
u32 free_blocks = 0;
u32 used_blocks_size = 0;
u32 used_blocks = 0;
if (arena == NULL || ptr == NULL) {
return -1;
}
arena_lock(arena);
for (block = arena->head; block != NULL; block = get_block_next(block)) {
if (block->free) {
free_blocks++;
free_blocks_size += block->size;
if (max_free_block_size < block->size) {
max_free_block_size = block->size;
}
} else {
used_blocks++;
used_blocks_size += block->size;
}
}
ptr[0] = max_free_block_size;
ptr[1] = free_blocks_size;
ptr[2] = free_blocks;
ptr[3] = used_blocks_size;
ptr[4] = used_blocks;
arena_unlock(arena);
return 0;
}
extern void __osGetFreeArena(OSArena* arena, u32* max_free_block_size, u32* free_blocks_size, u32* used_blocks_size) {
u32 data[5];
if (__osAnalyzeArena(arena, data) == 0) {
if (max_free_block_size != NULL) {
*max_free_block_size = data[0];
}
if (free_blocks_size != NULL) {
*free_blocks_size = data[1];
}
if (used_blocks_size != NULL) {
*used_blocks_size = data[3];
}
}
}
extern u32 __osGetTotalFreeSize(OSArena* arena) {
u32 total_free_size;
__osGetFreeArena(arena, NULL, &total_free_size, NULL);
return total_free_size;
}
#pragma force_active on
extern u32 __osGetFreeSize(OSArena* arena) {
u32 free_size;
__osGetFreeArena(arena, &free_size, NULL, NULL);
return free_size;
}
#pragma force_active reset
extern s32 __osGetMemBlockSize(OSArena* arena, void* ptr) {
OSMemBlock* block;
if (ptr == NULL) {
return -1;
}
block = OS_MALLOC_DATA2BLOCK(ptr);
if (OS_MALLOC_BLOCK_OK(block)) {
return block->size;
}
return -1;
}
extern void __osDisplayArena(OSArena* arena) {
OSMemBlock* block;
OSMemBlock* next;
u32 max_free;
u32 total_free;
u32 total_used;
if (__osMallocIsInitalized(arena)) {
arena_lock(arena);
max_free = 0;
total_free = 0;
total_used = 0;
// Arena Contents
OSReport("アリーナの内容 (0x%08x)\n", arena);
// Memory block span | status | size | [time s ms us ns: TID:src:line]
OSReport("メモリブロック範囲 status サイズ [時刻 s ms us ns: TID:src:行]\n");
block = arena->head;
while (block != NULL) {
if (OS_MALLOC_BLOCK_OK(block)) {
next = block->next;
OSReport("%08x-%08x%c %s %08x", (u32)block, (u32)block + sizeof(OSMemBlock) + block->size, next == NULL ? '$' : (next->prev != block ? '!' : ' '), block->free ? "空き" : "確保", block->size);
if (!block->free) {
// 空き = free, 確保 = used
OSReport(" [%016llu:%2d:%s:%d]", OSTicksToMicroseconds((u64)block->time * 1000), block->threadId, block->file != NULL ? block->file : "**NULL**", block->line);
}
OSReport("\n");
if (block->free) {
total_free += block->size;
if (max_free < block->size) {
max_free = block->size;
}
} else {
total_used += block->size;
}
} else {
OSReport("%08x Block Invalid\n", block);
next = NULL;
}
block = next;
}
// Total used memblock size: 0x%08x bytes
OSReport("確保ブロックサイズの合計 0x%08x バイト\n", total_used);
// Total free memblock size: 0x%08x bytes
OSReport("空きブロックサイズの合計 0x%08x バイト\n", total_free);
// Largest free block size: %08x bytes
OSReport("最大空きブロックサイズ 0x%08x バイト\n", max_free);
arena_unlock(arena);
}
}
#pragma force_active on
extern int __osCheckArena(OSArena* arena) {
int ret = FALSE;
OSMemBlock* block;
arena_lock(arena);
block = arena->head;
while (block != NULL) {
if (!OS_MALLOC_BLOCK_OK(block)) {
// Oops!!
OSReport(VT_COL(RED, WHITE) "おおっと!! (%08x %08x)\n" VT_RST, block, block->magic);
OSPanic(__FILE__, 1307, "");
ret = TRUE;
break;
}
block = get_block_next(block);
}
arena_unlock(arena);
return ret;
}
#pragma force_active reset