first commit

This commit is contained in:
murdle
2026-03-01 02:38:58 +02:00
commit 19250b9db4
19111 changed files with 4358159 additions and 0 deletions

View File

@@ -0,0 +1,390 @@
/* SCE CONFIDENTIAL
PlayStation(R)Vita Programmer Tool Runtime Library Release 03.000.061
* Copyright (C) 2012 Sony Computer Entertainment Inc.
* All Rights Reserved.
*/
// AP - this is a modified version of the basic sony memory overide functions.
// It was found from profiling that threads were stalling far too much waiting for their turn to malloc/free
// This manager will keep memory bound to a thread once allocated so it can be reused by the same thread later.
// It's only interested in chunks up to 1036 bytes in size, anything greater is not retained
// Todo : I may need to put in a memory flusher if I find threads holding onto memory too much.
#include <stdlib.h>
#include <mspace.h>
#include <kernel.h>
//#include <libdbg.h>
#include <assert.h>
#define HEAP_SIZE (164 * 1024 * 1024)
#define HEAP_ERROR1 1
#define HEAP_ERROR2 2
#define HEAP_ERROR3 3
static SceUID s_heapUid;
static mspace s_mspace;
void user_malloc_init(void);
void user_malloc_finalize(void);
void *user_malloc(size_t size);
void user_free(void *ptr);
void *user_calloc(size_t nelem, size_t size);
void *user_realloc(void *ptr, size_t size);
void *user_memalign(size_t boundary, size_t size);
void *user_reallocalign(void *ptr, size_t size, size_t boundary);
int user_malloc_stats(struct malloc_managed_size *mmsize);
int user_malloc_stats_fast(struct malloc_managed_size *mmsize);
size_t user_malloc_usable_size(void *ptr);
// this is our basic node for managing stacks
typedef struct Block
{
void* Memory;
struct Block *Next;
} Block;
#define MaxRetainedBytes 1036
#define Malloc_BlocksMemorySize 1024
typedef struct
{
Block **Malloc_MemoryPool; // this is an array of available memory allocations up to 1036 bytes in size
Block *Malloc_Blocks; // this is a stack of available block nodes used to store memory chunks
Block *Malloc_BlocksMemory[Malloc_BlocksMemorySize]; // this is a pool of block nodes allocated in large chunks (256 blocks at a time)
int Malloc_BlocksAlloced; // this shows how many block node chunks have been allocated in Malloc_BlocksMemory
} SThreadStorage;
__thread SThreadStorage *Malloc_ThreadStorage = NULL;
/**E Replace _malloc_init function. */
/**J _malloc_init 関数と置き換わる */
void user_malloc_init(void)
{
int res;
void *base = NULL;
/**E Allocate a memory block from the kernel */
/**J カーネルからメモリブロックを確保する */
s_heapUid = sceKernelAllocMemBlock("UserAllocator", SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA, HEAP_SIZE, SCE_NULL);
if (s_heapUid < SCE_OK) {
/**E Error handling */
/**J エラー処理 */
sceLibcSetHeapInitError(HEAP_ERROR1);
} else {
/**E Obtain the address of the allocated memory block */
/**J 確保したメモリブロックのアドレスを取得する */
res = sceKernelGetMemBlockBase(s_heapUid, &base);
if (res < SCE_OK) {
/**E Error handling */
/**J エラー処理 */
sceLibcSetHeapInitError(HEAP_ERROR2);
} else {
/**E Generate mspace */
/**J mspace を生成する */
s_mspace = mspace_create(base, HEAP_SIZE);
if (s_mspace == NULL) {
/**E Error handling */
/**J エラー処理 */
sceLibcSetHeapInitError(HEAP_ERROR3);
}
}
}
}
/**E Replace _malloc_finalize function. */
/**J _malloc_finalize 関数と置き換わる */
void user_malloc_finalize(void)
{
int res;
if (s_mspace != NULL) {
/**E Free mspace */
/**J mspace を解放する */
res = mspace_destroy(s_mspace);
if (res != 0) {
/**E Error handling */
/**J エラー処理 */
__breakpoint(0);
}
s_mspace = NULL;
}
if (SCE_OK <= s_heapUid) {
/**E Free the memory block */
/**J メモリブロックを解放する */
#if 0
res = sceKernelFreeMemBlock(s_heapUid);
if (res < SCE_OK) {
/**E Error handling */
/**J エラー処理 */
__breakpoint(0);
}
#endif
}
}
// before a thread can use the memory system it should register itself
void user_registerthread()
{
Malloc_ThreadStorage = mspace_malloc(s_mspace, sizeof(SThreadStorage));
Malloc_ThreadStorage->Malloc_Blocks = NULL;
Malloc_ThreadStorage->Malloc_BlocksAlloced = 0;
Malloc_ThreadStorage->Malloc_MemoryPool = NULL;
}
// before a thread is destroyed make sure we free any space it might be holding on to
void user_removethread()
{
SThreadStorage *psStorage = Malloc_ThreadStorage;
if( psStorage )
{
if( psStorage->Malloc_MemoryPool )
{
for( int j = 0;j < 1037;j += 1 )
{
Block *OldBlock = psStorage->Malloc_MemoryPool[j];
while( OldBlock )
{
mspace_free(s_mspace, OldBlock->Memory);
OldBlock = OldBlock->Next;
}
}
mspace_free(s_mspace, psStorage->Malloc_MemoryPool);
}
for( int j = 0;j < psStorage->Malloc_BlocksAlloced;j += 1 )
{
free(psStorage->Malloc_BlocksMemory[j]);
}
mspace_free(s_mspace, psStorage);
Malloc_ThreadStorage = NULL;
}
}
/**E Replace malloc function. */
/**J malloc 関数と置き換わる */
void *user_malloc(size_t size)
{
void *p = NULL;
SThreadStorage *psStorage = Malloc_ThreadStorage;
if( psStorage )
{
// is this the first time we've malloced
if( psStorage->Malloc_MemoryPool == NULL )
{
// create an array of pointers to Block nodes, one pointer for each memory bytes size up to 1036
psStorage->Malloc_MemoryPool = mspace_malloc(s_mspace, (MaxRetainedBytes+1) * 4);
for( int i = 0;i < (MaxRetainedBytes+1);i += 1 )
{
psStorage->Malloc_MemoryPool[i] = NULL;
}
}
// are we interested in retaining this size of memory (less 4 bytes to store the associated thread ID)
if( size < (MaxRetainedBytes-4) )
{
// add on space for the thread ID
size += 4;
// round to the nearest malloc boundary. This is what happens internally in the malloc library
if( size <= 12 )
size = 12;
else
size = ((size - 12) & 0xfffffff0) + 16 + 12;
// do we have any memory of this size retained
if( psStorage->Malloc_MemoryPool[size] )
{
// pop it from the retained pool
Block * OldBlock = psStorage->Malloc_MemoryPool[size];
psStorage->Malloc_MemoryPool[size] = OldBlock->Next;
p = OldBlock->Memory;
// push the block storage onto the stack
OldBlock->Next = psStorage->Malloc_Blocks;
psStorage->Malloc_Blocks = OldBlock;
}
else
{
// create some new memory
p = mspace_malloc(s_mspace, size);
// store the thread ID in the last 4 bytes
unsigned int threadID = sceKernelGetThreadId();
unsigned int *ThreadAddr = (unsigned int *) ((unsigned char*) p + (size - 4));
ThreadAddr[0] = threadID;
}
}
else
{
p = mspace_malloc(s_mspace, size);
}
}
else
{
p = mspace_malloc(s_mspace, size);
}
// SCE_DBG_LOG_TRACE("Called malloc(%u)", size);
return p;
}
/**E Replace free function. */
/**J free 関数と置き換わる */
void user_free(void *ptr)
{
if( !ptr || !s_mspace )
{
return;
}
SThreadStorage *psStorage = Malloc_ThreadStorage;
if( psStorage )
{
// calc the size of this chunk rounding to a valid size
unsigned int size = ((unsigned int*)ptr)[-1] - 7;
unsigned int RoundedSize = (size & 0xfffffff0) + 12;
if( RoundedSize < (MaxRetainedBytes+1) )
{
// grab the thread ID from the last 4 bytes
unsigned int *ThreadAddr = (unsigned int *) ((unsigned char*) ptr + (RoundedSize - 4));
// did this thread create this memory
if( sceKernelGetThreadId() != ThreadAddr[0] )
{
// don't worry about retaining memory allocated from a different thread. too much mucking about
mspace_free(s_mspace, ptr);
return;
}
// we need a block node to retain the memory on our stack. do we have any block nodes available
if( psStorage->Malloc_Blocks == NULL )
{
if( psStorage->Malloc_BlocksAlloced == Malloc_BlocksMemorySize )
{
// Max blocks allocated
// The worst case for this running out is when the player has explored a large amount of the map in a single session and then quits. All the allocations
// are suddenly freed up so we need lots of space to store them.
printf("oh nos, max blocks allocated. increase Malloc_BlocksMemorySize");
assert(0);
}
// allocate some more block space in a large chunk
int chunkSize = 256;
psStorage->Malloc_BlocksMemory[psStorage->Malloc_BlocksAlloced] = mspace_malloc(s_mspace, chunkSize * sizeof(Block));
// push all the new blocks onto the pool
for(int i = 0;i < chunkSize;i += 1 )
{
Block *NewBlock = &psStorage->Malloc_BlocksMemory[psStorage->Malloc_BlocksAlloced][i];
NewBlock->Next = psStorage->Malloc_Blocks;
psStorage->Malloc_Blocks = NewBlock;
}
psStorage->Malloc_BlocksAlloced++;
}
// pop a block node off the stack
Block *NewBlock = psStorage->Malloc_Blocks;
psStorage->Malloc_Blocks = NewBlock->Next;
// set up the block data and retain it to the correct slot. We can now reuse this memory on the next malloc
NewBlock->Memory = ptr;
NewBlock->Next = psStorage->Malloc_MemoryPool[RoundedSize];
psStorage->Malloc_MemoryPool[RoundedSize] = NewBlock;
}
else
{
mspace_free(s_mspace, ptr);
}
}
else
{
mspace_free(s_mspace, ptr);
}
}
/**E Replace calloc function. */
/**J calloc 関数と置き換わる */
void *user_calloc(size_t nelem, size_t size)
{
// SCE_DBG_LOG_TRACE("Called calloc(%u, %u)", nelem, size);
return mspace_calloc(s_mspace, nelem, size);
}
/**E Replace realloc function. */
/**J realloc 関数と置き換わる */
void *user_realloc(void *ptr, size_t size)
{
void* p = NULL;
if( Malloc_ThreadStorage )
{
// make sure we use malloc/memcpy/free instead of realloc
p = user_malloc(size);
unsigned int OldSize;
unsigned int OldRoundedSize;
if( ptr )
{
// calc the size of this chunk rounding to a valid size
OldSize = ((unsigned int*)ptr)[-1] - 7;
OldRoundedSize = (OldSize & 0xfffffff0) + 12;
memcpy(p, ptr, OldRoundedSize);
user_free(ptr);
}
}
else
{
p = mspace_realloc(s_mspace, ptr, size);
}
return p;
// SCE_DBG_LOG_TRACE("Called realloc(%p, %u)", ptr, size);
// return mspace_realloc(s_mspace, ptr, size);
}
/**E Replace memalign function. */
/**J memalign 関数と置き換わる */
void *user_memalign(size_t boundary, size_t size)
{
// SCE_DBG_LOG_TRACE("Called memalign(%u, %u)", boundary, size);
return mspace_memalign(s_mspace, boundary, size);
}
/**E Replace reallocalign function. */
/**J reallocalign 関数と置き換わる */
void *user_reallocalign(void *ptr, size_t size, size_t boundary)
{
// SCE_DBG_LOG_TRACE("Called reallocalign(%p, %u, %u)", ptr, size, boundary);
return mspace_reallocalign(s_mspace, ptr, size, boundary);
}
/**E Replace malloc_stats function. */
/**J malloc_stats 関数と置き換わる */
int user_malloc_stats(struct malloc_managed_size *mmsize)
{
// SCE_DBG_LOG_TRACE("Called malloc_stats");
return mspace_malloc_stats(s_mspace, mmsize);
}
/**E Replace malloc_stats_fast function. */
/**J malloc_stata_fast 関数と置き換わる */
int user_malloc_stats_fast(struct malloc_managed_size *mmsize)
{
// SCE_DBG_LOG_TRACE("Called malloc_stats_fast");
return mspace_malloc_stats_fast(s_mspace, mmsize);
}
/**E Replace malloc_usable_size function. */
/**J malloc_usable_size 関数と置き換わる */
size_t user_malloc_usable_size(void *ptr)
{
// SCE_DBG_LOG_TRACE("Called malloc_usable_size");
return mspace_malloc_usable_size(ptr);
}