📄 common.c
字号:
ptr = ((char *) block) + sizeof(memblock_t);
j = 0;
for (i = 0; i < 20 && i < block->d.allocSize; i++) {
if (ptr[i] >= 32 && ptr[i] < 127) {
dump[j++] = ptr[i];
}
else {
dump[j++] = '_';
}
}
dump[j] = '\0';
Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s) [%s]\r\n", block->d.allocSize, block->d.file, block->d.line, block->d.label, dump);
FS_Write(buf, strlen(buf), logfile);
allocSize += block->d.allocSize;
#endif
size += block->size;
numBlocks++;
}
}
#ifdef ZONE_DEBUG
// subtract debug memory
size -= numBlocks * sizeof(zonedebug_t);
#else
allocSize = numBlocks * sizeof(memblock_t); // + 32 bit alignment
#endif
Com_sprintf(buf, sizeof(buf), "%d %s memory in %d blocks\r\n", size, name, numBlocks);
FS_Write(buf, strlen(buf), logfile);
Com_sprintf(buf, sizeof(buf), "%d %s memory overhead\r\n", size - allocSize, name);
FS_Write(buf, strlen(buf), logfile);
}
/*
========================
Z_LogHeap
========================
*/
void Z_LogHeap( void ) {
Z_LogZoneHeap( mainzone, "MAIN" );
Z_LogZoneHeap( smallzone, "SMALL" );
}
// static mem blocks to reduce a lot of small zone overhead
typedef struct memstatic_s {
memblock_t b;
byte mem[2];
} memstatic_t;
// bk001204 - initializer brackets
memstatic_t emptystring =
{ {(sizeof(memblock_t)+2 + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'\0', '\0'} };
memstatic_t numberstring[] = {
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'0', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'1', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'2', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'3', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'4', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'5', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'6', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'7', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'8', '\0'} },
{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'9', '\0'} }
};
/*
========================
CopyString
NOTE: never write over the memory CopyString returns because
memory from a memstatic_t might be returned
========================
*/
char *CopyString( const char *in ) {
char *out;
if (!in[0]) {
return ((char *)&emptystring) + sizeof(memblock_t);
}
else if (!in[1]) {
if (in[0] >= '0' && in[0] <= '9') {
return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t);
}
}
out = S_Malloc (strlen(in)+1);
strcpy (out, in);
return out;
}
/*
==============================================================================
Goals:
reproducable without history effects -- no out of memory errors on weird map to map changes
allow restarting of the client without fragmentation
minimize total pages in use at run time
minimize total pages needed during load time
Single block of memory with stack allocators coming from both ends towards the middle.
One side is designated the temporary memory allocator.
Temporary memory can be allocated and freed in any order.
A highwater mark is kept of the most in use at any time.
When there is no temporary memory allocated, the permanent and temp sides
can be switched, allowing the already touched temp memory to be used for
permanent storage.
Temp memory must never be allocated on two ends at once, or fragmentation
could occur.
If we have any in-use temp memory, additional temp allocations must come from
that side.
If not, we can choose to make either side the new temp side and push future
permanent allocations to the other side. Permanent allocations should be
kept on the side that has the current greatest wasted highwater mark.
==============================================================================
*/
#define HUNK_MAGIC 0x89537892
#define HUNK_FREE_MAGIC 0x89537893
typedef struct {
int magic;
int size;
} hunkHeader_t;
typedef struct {
int mark;
int permanent;
int temp;
int tempHighwater;
} hunkUsed_t;
typedef struct hunkblock_s {
int size;
byte printed;
struct hunkblock_s *next;
char *label;
char *file;
int line;
} hunkblock_t;
static hunkblock_t *hunkblocks;
static hunkUsed_t hunk_low, hunk_high;
static hunkUsed_t *hunk_permanent, *hunk_temp;
static byte *s_hunkData = NULL;
static int s_hunkTotal;
static int s_zoneTotal;
static int s_smallZoneTotal;
/*
=================
Com_Meminfo_f
=================
*/
void Com_Meminfo_f( void ) {
memblock_t *block;
int zoneBytes, zoneBlocks;
int smallZoneBytes, smallZoneBlocks;
int botlibBytes, rendererBytes;
int unused;
zoneBytes = 0;
botlibBytes = 0;
rendererBytes = 0;
zoneBlocks = 0;
for (block = mainzone->blocklist.next ; ; block = block->next) {
if ( Cmd_Argc() != 1 ) {
Com_Printf ("block:%p size:%7i tag:%3i\n",
block, block->size, block->tag);
}
if ( block->tag ) {
zoneBytes += block->size;
zoneBlocks++;
if ( block->tag == TAG_BOTLIB ) {
botlibBytes += block->size;
} else if ( block->tag == TAG_RENDERER ) {
rendererBytes += block->size;
}
}
if (block->next == &mainzone->blocklist) {
break; // all blocks have been hit
}
if ( (byte *)block + block->size != (byte *)block->next) {
Com_Printf ("ERROR: block size does not touch the next block\n");
}
if ( block->next->prev != block) {
Com_Printf ("ERROR: next block doesn't have proper back link\n");
}
if ( !block->tag && !block->next->tag ) {
Com_Printf ("ERROR: two consecutive free blocks\n");
}
}
smallZoneBytes = 0;
smallZoneBlocks = 0;
for (block = smallzone->blocklist.next ; ; block = block->next) {
if ( block->tag ) {
smallZoneBytes += block->size;
smallZoneBlocks++;
}
if (block->next == &smallzone->blocklist) {
break; // all blocks have been hit
}
}
Com_Printf( "%8i bytes total hunk\n", s_hunkTotal );
Com_Printf( "%8i bytes total zone\n", s_zoneTotal );
Com_Printf( "\n" );
Com_Printf( "%8i low mark\n", hunk_low.mark );
Com_Printf( "%8i low permanent\n", hunk_low.permanent );
if ( hunk_low.temp != hunk_low.permanent ) {
Com_Printf( "%8i low temp\n", hunk_low.temp );
}
Com_Printf( "%8i low tempHighwater\n", hunk_low.tempHighwater );
Com_Printf( "\n" );
Com_Printf( "%8i high mark\n", hunk_high.mark );
Com_Printf( "%8i high permanent\n", hunk_high.permanent );
if ( hunk_high.temp != hunk_high.permanent ) {
Com_Printf( "%8i high temp\n", hunk_high.temp );
}
Com_Printf( "%8i high tempHighwater\n", hunk_high.tempHighwater );
Com_Printf( "\n" );
Com_Printf( "%8i total hunk in use\n", hunk_low.permanent + hunk_high.permanent );
unused = 0;
if ( hunk_low.tempHighwater > hunk_low.permanent ) {
unused += hunk_low.tempHighwater - hunk_low.permanent;
}
if ( hunk_high.tempHighwater > hunk_high.permanent ) {
unused += hunk_high.tempHighwater - hunk_high.permanent;
}
Com_Printf( "%8i unused highwater\n", unused );
Com_Printf( "\n" );
Com_Printf( "%8i bytes in %i zone blocks\n", zoneBytes, zoneBlocks );
Com_Printf( " %8i bytes in dynamic botlib\n", botlibBytes );
Com_Printf( " %8i bytes in dynamic renderer\n", rendererBytes );
Com_Printf( " %8i bytes in dynamic other\n", zoneBytes - ( botlibBytes + rendererBytes ) );
Com_Printf( " %8i bytes in small Zone memory\n", smallZoneBytes );
}
/*
===============
Com_TouchMemory
Touch all known used data to make sure it is paged in
===============
*/
void Com_TouchMemory( void ) {
int start, end;
int i, j;
int sum;
memblock_t *block;
Z_CheckHeap();
start = Sys_Milliseconds();
sum = 0;
j = hunk_low.permanent >> 2;
for ( i = 0 ; i < j ; i+=64 ) { // only need to touch each page
sum += ((int *)s_hunkData)[i];
}
i = ( s_hunkTotal - hunk_high.permanent ) >> 2;
j = hunk_high.permanent >> 2;
for ( ; i < j ; i+=64 ) { // only need to touch each page
sum += ((int *)s_hunkData)[i];
}
for (block = mainzone->blocklist.next ; ; block = block->next) {
if ( block->tag ) {
j = block->size >> 2;
for ( i = 0 ; i < j ; i+=64 ) { // only need to touch each page
sum += ((int *)block)[i];
}
}
if ( block->next == &mainzone->blocklist ) {
break; // all blocks have been hit
}
}
end = Sys_Milliseconds();
Com_Printf( "Com_TouchMemory: %i msec\n", end - start );
}
/*
=================
Com_InitZoneMemory
=================
*/
void Com_InitSmallZoneMemory( void ) {
s_smallZoneTotal = 512 * 1024;
// bk001205 - was malloc
smallzone = calloc( s_smallZoneTotal, 1 );
if ( !smallzone ) {
Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal / (1024*1024) );
}
Z_ClearZone( smallzone, s_smallZoneTotal );
return;
}
void Com_InitZoneMemory( void ) {
cvar_t *cv;
// allocate the random block zone
cv = Cvar_Get( "com_zoneMegs", DEF_COMZONEMEGS, CVAR_LATCH | CVAR_ARCHIVE );
if ( cv->integer < 20 ) {
s_zoneTotal = 1024 * 1024 * 16;
} else {
s_zoneTotal = cv->integer * 1024 * 1024;
}
// bk001205 - was malloc
mainzone = calloc( s_zoneTotal, 1 );
if ( !mainzone ) {
Com_Error( ERR_FATAL, "Zone data failed to allocate %i megs", s_zoneTotal / (1024*1024) );
}
Z_ClearZone( mainzone, s_zoneTotal );
}
/*
=================
Hunk_Log
=================
*/
void Hunk_Log( void) {
hunkblock_t *block;
char buf[4096];
int size, numBlocks;
if (!logfile || !FS_Initialized())
return;
size = 0;
numBlocks = 0;
Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk log\r\n================\r\n");
FS_Write(buf, strlen(buf), logfile);
for (block = hunkblocks ; block; block = block->next) {
#ifdef HUNK_DEBUG
Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", block->size, block->file, block->line, block->label);
FS_Write(buf, strlen(buf), logfile);
#endif
size += block->size;
numBlocks++;
}
Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size);
FS_Write(buf, strlen(buf), logfile);
Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks);
FS_Write(buf, strlen(buf), logfile);
}
/*
=================
Hunk_SmallLog
=================
*/
void Hunk_SmallLog( void) {
hunkblock_t *block, *block2;
char buf[4096];
int size, locsize, numBlocks;
if (!logfile || !FS_Initialized())
return;
for (block = hunkblocks ; block; block = block->next) {
block->printed = qfalse;
}
size = 0;
numBlocks = 0;
Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk Small log\r\n================\r\n");
FS_Write(buf, strlen(buf), logfile);
for (block = hunkblocks; block; block = block->next) {
if (block->printed) {
continue;
}
locsize = block->size;
for (block2 = block->next; block2; block2 = block2->next) {
if (block->line != block2->line) {
continue;
}
if (Q_stricmp(block->file, block2->file)) {
continue;
}
size += block2->size;
locsize += block2->size;
block2->printed = qtrue;
}
#ifdef HUNK_DEBUG
Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", locsize, block->file, block->line, block->label);
FS_Write(buf, strlen(buf), logfile);
#endif
size += block->size;
numBlocks++;
}
Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size);
FS_Write(buf, strlen(buf), logfile);
Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks);
FS_Write(buf, strlen(buf), logfile);
}
/*
=================
Com_InitZoneMemory
=================
*/
void Com_InitHunkMemory( void ) {
cvar_t *cv;
int nMinAlloc;
char *pMsg = NULL;
// make sure the file system has allocated and "not" freed any temp blocks
// this allows the config and product id files ( journal files too ) to be loaded
// by the file system without redunant routines in the file system utilizing different
// memory systems
if (FS_LoadStack() != 0) {
Com_Error( ERR_FATAL, "Hunk initialization failed. File system load stack not zero");
}
// allocate the stack based hunk allocator
cv = Cvar_Get( "com_hunkMegs", DEF_COMHUNKMEGS, CVAR_LATCH | CVAR_ARCHIVE );
// if we are not dedicated min allocation is 56, otherwise min is 1
if (com_dedicated && com_dedicated->integer) {
nMinAlloc = MIN_DEDICATED_COMHUNKMEGS;
pMsg = "Minimum com_hunkMegs for a dedicated server is %i, allocating %i megs.\n";
}
else {
nMinAlloc = MIN_COMHUNKMEGS;
pMsg = "Minimum com_hunkMegs is %i, allocating %i megs.\n";
}
if ( cv->integer < nMinAlloc ) {
s_hunkTotal = 1024 * 1024 * nMinAlloc;
Com_Printf(pMsg, nMinAlloc, s_hunkTotal / (1024 * 1024));
} else {
s_hunkTotal = cv->integer * 1024 * 1024;
}
// bk001205 - was malloc
s_hunkData = calloc( s_hunkTotal + 31, 1 );
if ( !s_hunkData ) {
Com_Error( ERR_FATAL, "Hunk data failed to allocate %i megs", s_hunkTotal / (1024*1024) );
}
// cacheline align
s_hunkData = (byte *) ( ( (int)s_hunkData + 31 ) & ~31 );
Hunk_Clear();
Cmd_AddCommand( "meminfo", Com_Meminfo_f );
#ifdef ZONE_DEBUG
Cmd_AddCommand( "zonelog", Z_LogHeap );
#endif
#ifdef HUNK_DEBUG
Cmd_AddCommand( "hunklog", Hunk_Log );
Cmd_AddCommand( "hunksmalllog", Hunk_SmallLog );
#endif
}
/*
====================
Hunk_MemoryRemaining
====================
*/
int Hunk_MemoryRemaining( void ) {
int low, high;
low = hunk_low.permanent > hunk_low.temp ? hunk_low.permanent : hunk_low.temp;
high = hunk_high.permanent > hunk_high.temp ? hunk_high.permanent : hunk_high.temp;
return s_hunkTotal - ( low + high );
}
/*
===================
Hunk_SetMark
The server calls this after the level and game VM have been loaded
===================
*/
void Hunk_SetMark( void ) {
hunk_low.mark = hunk_low.permanent;
hunk_high.mark = hunk_high.permanent;
}
/*
=================
Hunk_ClearToMark
The client calls this before starting a vid_restart or snd_restart
=================
*/
void Hunk_ClearToMark( void ) {
hunk_low.permanent = hunk_low.temp = hunk_low.mark;
hunk_high.permanent = hunk_high.temp = hunk_high.mark;
}
/*
=================
Hunk_CheckMark
=================
*/
qboolean Hunk_CheckMark( void ) {
if( hunk_low.mark || hunk_high.mark ) {
return qtrue;
}
return qfalse;
}
void CL_ShutdownCGame( void );
void CL_ShutdownUI( void );
void SV_ShutdownGameProgs( void );
/*
=================
Hunk_Clear
The server calls this before shutting down or loading a new map
=================
*/
void Hunk_Clear( void ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -