00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "common.h"
00030 #include <SDL_thread.h>
00031
00032 #define MEM_HEAD_SENTINEL_TOP 0xFEBDFAED
00033 #define MEM_HEAD_SENTINEL_BOT 0xD0BAF0FF
00034 #define MEM_FOOT_SENTINEL 0xF00DF00D
00035
00036 static SDL_mutex *z_lock;
00037
00038 #define MEM_MAX_POOLCOUNT 32
00039
00040 static memPool_t m_poolList[MEM_MAX_POOLCOUNT];
00041 static uint32_t m_numPools;
00042
00043
00044
00045
00046
00047 static memPool_t *Mem_FindPool (const char *name)
00048 {
00049 memPool_t *pool;
00050 uint32_t i;
00051
00052 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00053 if (!pool->inUse)
00054 continue;
00055 if (strcmp(name, pool->name))
00056 continue;
00057
00058 return pool;
00059 }
00060
00061 return NULL;
00062 }
00063
00067 memPool_t *_Mem_CreatePool (const char *name, const char *fileName, const int fileLine)
00068 {
00069 memPool_t *pool;
00070 uint32_t i;
00071
00072
00073 if (!name || !name[0])
00074 Sys_Error("Mem_CreatePool: NULL name %s:#%i", fileName, fileLine);
00075 if (strlen(name) + 1 >= MEM_MAX_POOLNAME)
00076 Com_Printf("Mem_CreatePoole: name '%s' too long, truncating!\n", name);
00077
00078
00079 pool = Mem_FindPool(name);
00080 if (pool)
00081 return pool;
00082
00083
00084 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00085 if (!pool->inUse)
00086 break;
00087 }
00088 if (i == m_numPools) {
00089 if (m_numPools + 1 >= MEM_MAX_POOLCOUNT)
00090 Sys_Error("Mem_CreatePool: MEM_MAX_POOLCOUNT");
00091 pool = &m_poolList[m_numPools++];
00092 }
00093
00094
00095 for (i = 0; i < MEM_HASH; i++)
00096 pool->blocks[i] = NULL;
00097 pool->blockCount = 0;
00098 pool->byteCount = 0;
00099 pool->createFile = fileName;
00100 pool->createLine = fileLine;
00101 pool->inUse = qtrue;
00102 Q_strncpyz(pool->name, name, sizeof(pool->name));
00103
00104 return pool;
00105 }
00106
00112 uint32_t _Mem_DeletePool (struct memPool_s *pool, const char *fileName, const int fileLine)
00113 {
00114 uint32_t size;
00115
00116 if (!pool)
00117 return 0;
00118
00119
00120 size = _Mem_FreePool(pool, fileName, fileLine);
00121
00122
00123 pool->inUse = qfalse;
00124 pool->name[0] = '\0';
00125
00126 return size;
00127 }
00128
00129
00130
00131
00132
00133
00134 static void _Mem_CheckSentinels (void *ptr, const char *fileName, const int fileLine)
00135 {
00136 memBlock_t *mem;
00137
00138
00139 mem = (memBlock_t *)((byte *)ptr - sizeof(memBlock_t));
00140 if (mem->topSentinel != MEM_HEAD_SENTINEL_TOP) {
00141 Sys_Error("Mem_Free: bad memory header top sentinel [buffer underflow]\n"
00142 "free: %s:#%i", fileName, fileLine);
00143 } else if (mem->botSentinel != MEM_HEAD_SENTINEL_BOT) {
00144 Sys_Error("Mem_Free: bad memory header bottom sentinel [buffer underflow]\n"
00145 "free: %s:#%i", fileName, fileLine);
00146 } else if (!mem->footer) {
00147 Sys_Error("Mem_Free: bad memory footer [buffer overflow]\n"
00148 "pool: %s\n"
00149 "alloc: %s:#%i\n"
00150 "free: %s:#%i",
00151 mem->pool ? mem->pool->name : "UNKNOWN", mem->allocFile, mem->allocLine, fileName, fileLine);
00152 } else if (mem->footer->sentinel != MEM_FOOT_SENTINEL) {
00153 Sys_Error("Mem_Free: bad memory footer sentinel [buffer overflow]\n"
00154 "pool: %s\n"
00155 "alloc: %s:#%i\n"
00156 "free: %s:#%i",
00157 mem->pool ? mem->pool->name : "UNKNOWN", mem->allocFile, mem->allocLine, fileName, fileLine);
00158 }
00159 }
00160
00164 uint32_t _Mem_Free (void *ptr, const char *fileName, const int fileLine)
00165 {
00166 memBlock_t *mem;
00167 memBlock_t *search;
00168 memBlock_t **prev;
00169 uint32_t size;
00170
00171 if (!ptr)
00172 return 0;
00173
00174 _Mem_CheckSentinels(ptr, fileName, fileLine);
00175
00176 mem = (memBlock_t *)((byte *)ptr - sizeof(memBlock_t));
00177
00178 SDL_mutexP(z_lock);
00179
00180
00181 mem->pool->blockCount--;
00182 mem->pool->byteCount -= mem->size;
00183 size = mem->size;
00184
00185
00186 prev = &mem->pool->blocks[(uintptr_t)mem % MEM_HASH];
00187 for (;;) {
00188 search = *prev;
00189 if (!search)
00190 break;
00191
00192 if (search == mem) {
00193 *prev = search->next;
00194 break;
00195 }
00196 prev = &search->next;
00197 }
00198
00199 SDL_mutexV(z_lock);
00200
00201
00202 free(mem);
00203 return size;
00204 }
00205
00209 uint32_t _Mem_FreeTag (struct memPool_s *pool, const int tagNum, const char *fileName, const int fileLine)
00210 {
00211 memBlock_t *mem, *next;
00212 uint32_t size;
00213 int j = 0;
00214
00215 if (!pool)
00216 return 0;
00217
00218 size = 0;
00219 for (j = 0; j < MEM_HASH; j++) {
00220 for (mem = pool->blocks[j]; mem; mem = next) {
00221 next = mem->next;
00222 if (mem->tagNum == tagNum)
00223 size += _Mem_Free(mem->memPointer, fileName, fileLine);
00224 }
00225 }
00226
00227 return size;
00228 }
00229
00230
00236 uint32_t _Mem_FreePool (struct memPool_s *pool, const char *fileName, const int fileLine)
00237 {
00238 memBlock_t *mem, *next;
00239 uint32_t size;
00240 int j = 0;
00241
00242 if (!pool)
00243 return 0;
00244
00245 size = 0;
00246 for (j = 0; j < MEM_HASH; j++) {
00247 for (mem = pool->blocks[j]; mem; mem = next) {
00248 next = mem->next;
00249 size += _Mem_Free(mem->memPointer, fileName, fileLine);
00250 }
00251 }
00252
00253 assert(pool->blockCount == 0);
00254 assert(pool->byteCount == 0);
00255 return size;
00256 }
00257
00258
00262 void *_Mem_Alloc (size_t size, qboolean zeroFill, memPool_t *pool, const int tagNum, const char *fileName, const int fileLine)
00263 {
00264 memBlock_t *mem;
00265
00266
00267 if (!pool)
00268 Sys_Error("Mem_Alloc: Error - no pool given\n" "alloc: %s:#%i\n", fileName, fileLine);
00269
00270
00271 if (size <= 0)
00272 Sys_Error("Mem_Alloc: Attempted allocation of '"UFO_SIZE_T"' memory ignored\n" "alloc: %s:#%i\n", size, fileName, fileLine);
00273
00274 if (size > 0x40000000)
00275 Sys_Error("Mem_Alloc: Attempted allocation of '"UFO_SIZE_T"' bytes!\n" "alloc: %s:#%i\n", size, fileName, fileLine);
00276
00277
00278 size = (size + sizeof(memBlock_t) + sizeof(memBlockFoot_t) + 31) & ~31;
00279 mem = malloc(size);
00280 if (!mem)
00281 Sys_Error("Mem_Alloc: failed on allocation of '"UFO_SIZE_T"' bytes\n" "alloc: %s:#%i", size, fileName, fileLine);
00282
00283
00284 if (zeroFill)
00285 memset(mem, 0, size);
00286
00287
00288 mem->topSentinel = MEM_HEAD_SENTINEL_TOP;
00289 mem->tagNum = tagNum;
00290 mem->size = size;
00291 mem->memPointer = (void *)(mem + 1);
00292 mem->memSize = size - sizeof(memBlock_t) - sizeof(memBlockFoot_t);
00293 mem->pool = pool;
00294 mem->allocFile = fileName;
00295 mem->allocLine = fileLine;
00296 mem->footer = (memBlockFoot_t *)((byte *)mem->memPointer + mem->memSize);
00297 mem->botSentinel = MEM_HEAD_SENTINEL_BOT;
00298
00299
00300 mem->footer->sentinel = MEM_FOOT_SENTINEL;
00301
00302 SDL_mutexP(z_lock);
00303
00304
00305 pool->blockCount++;
00306 pool->byteCount += size;
00307
00308
00309 mem->next = pool->blocks[(uintptr_t)mem % MEM_HASH];
00310 pool->blocks[(uintptr_t)mem % MEM_HASH] = mem;
00311
00312 SDL_mutexV(z_lock);
00313
00314 return mem->memPointer;
00315 }
00316
00317 void* _Mem_ReAlloc (void *ptr, size_t size, const char *fileName, const int fileLine)
00318 {
00319 memBlock_t *mem;
00320 memPool_t *pool;
00321 void *newPtr;
00322
00323 if (!size)
00324 Sys_Error("Use Mem_Free instead");
00325
00326 if (!ptr)
00327 Sys_Error("Use Mem_Alloc instead");
00328
00329 _Mem_CheckSentinels(ptr, fileName, fileLine);
00330
00331 mem = (memBlock_t *)((byte *)ptr - sizeof(memBlock_t));
00332
00333
00334 if (mem->memSize == size)
00335 return ptr;
00336
00337 pool = mem->pool;
00338
00339
00340 newPtr = _Mem_Alloc(size, qfalse, pool, mem->tagNum, fileName, fileLine);
00341
00342
00343 memcpy(newPtr, ptr, min(mem->memSize, size));
00344 if (mem->memSize < size) {
00345 const size_t delta = size - mem->memSize;
00346 memset((byte*)newPtr + mem->memSize, 0, delta);
00347 }
00348
00349
00350 _Mem_Free(ptr, fileName, fileLine);
00351
00352 _Mem_CheckSentinels(newPtr, fileName, fileLine);
00353
00354 return newPtr;
00355 }
00356
00357
00358
00359
00360
00364 size_t Mem_Size (const void *ptr)
00365 {
00366 const memBlock_t *mem = (const memBlock_t *)((const byte *)ptr - sizeof(memBlock_t));
00367 return mem->size;
00368 }
00369
00379 char *_Mem_PoolStrDupTo (const char *in, char **out, struct memPool_s *pool, const int tagNum, const char *fileName, const int fileLine)
00380 {
00381 if (!out)
00382 return NULL;
00383 *out = _Mem_PoolStrDup(in, pool, tagNum, fileName, fileLine);
00384 return *out;
00385 }
00386
00387 void *_Mem_PoolDup (const void *in, size_t size, struct memPool_s *pool, const int tagNum, const char *fileName, const int fileLine)
00388 {
00389 void *copy;
00390
00391 assert(in != NULL);
00392 assert(size > 0);
00393
00394 copy = _Mem_Alloc(size, qfalse, pool, tagNum, fileName, fileLine);
00395 memcpy(copy, in, size);
00396 return copy;
00397 }
00398
00407 char *_Mem_PoolStrDup (const char *in, struct memPool_s *pool, const int tagNum, const char *fileName, const int fileLine)
00408 {
00409 char *out;
00410
00411 out = _Mem_Alloc((size_t)(strlen(in) + 1), qtrue, pool, tagNum, fileName, fileLine);
00412 strcpy(out, in);
00413
00414 return out;
00415 }
00416
00420 uint32_t _Mem_PoolSize (struct memPool_s *pool)
00421 {
00422 if (!pool)
00423 return 0;
00424
00425 return pool->byteCount;
00426 }
00427
00428
00429 uint32_t _Mem_TagSize (struct memPool_s *pool, const int tagNum)
00430 {
00431 memBlock_t *mem;
00432 uint32_t size;
00433 int j = 0;
00434
00435 if (!pool)
00436 return 0;
00437
00438 size = 0;
00439 for (j = 0; j < MEM_HASH; j++) {
00440 for (mem = pool->blocks[j]; mem; mem = mem->next) {
00441 if (mem->tagNum == tagNum)
00442 size += mem->size;
00443 }
00444 }
00445
00446 return size;
00447 }
00448
00449
00450 uint32_t _Mem_ChangeTag (struct memPool_s *pool, const int tagFrom, const int tagTo)
00451 {
00452 memBlock_t *mem;
00453 uint32_t numChanged;
00454 int j = 0;
00455
00456 if (!pool)
00457 return 0;
00458
00459 numChanged = 0;
00460
00461 for (j = 0; j < MEM_HASH; j++) {
00462 for (mem = pool->blocks[j]; mem; mem = mem->next) {
00463 if (mem->tagNum == tagFrom) {
00464 mem->tagNum = tagTo;
00465 numChanged++;
00466 }
00467 }
00468 }
00469
00470 return numChanged;
00471 }
00472
00473
00474 void _Mem_CheckPoolIntegrity (struct memPool_s *pool, const char *fileName, const int fileLine)
00475 {
00476 memBlock_t *mem;
00477 uint32_t blocks;
00478 uint32_t size;
00479 int j = 0;
00480
00481 assert(pool);
00482 if (!pool)
00483 return;
00484
00485
00486 for (j = 0, blocks = 0, size = 0; j < MEM_HASH; j++) {
00487 for (mem = pool->blocks[j]; mem; blocks++, mem = mem->next) {
00488 size += mem->size;
00489 if (mem->topSentinel != MEM_HEAD_SENTINEL_TOP) {
00490 Sys_Error("Mem_CheckPoolIntegrity: bad memory head top sentinel [buffer underflow]\n"
00491 "check: %s:#%i", fileName, fileLine);
00492 } else if (mem->botSentinel != MEM_HEAD_SENTINEL_BOT) {
00493 Sys_Error("Mem_CheckPoolIntegrity: bad memory head bottom sentinel [buffer underflow]\n"
00494 "check: %s:#%i", fileName, fileLine);
00495 } else if (!mem->footer) {
00496 Sys_Error("Mem_CheckPoolIntegrity: bad memory footer [buffer overflow]\n"
00497 "pool: %s\n"
00498 "alloc: %s:#%i\n"
00499 "check: %s:#%i",
00500 mem->pool ? mem->pool->name : "UNKNOWN", mem->allocFile, mem->allocLine, fileName, fileLine);
00501 } else if (mem->footer->sentinel != MEM_FOOT_SENTINEL) {
00502 Sys_Error("Mem_CheckPoolIntegrity: bad memory foot sentinel [buffer overflow]\n"
00503 "pool: %s\n"
00504 "alloc: %s:#%i\n"
00505 "check: %s:#%i",
00506 mem->pool ? mem->pool->name : "UNKNOWN", mem->allocFile, mem->allocLine, fileName, fileLine);
00507 }
00508 }
00509 }
00510
00511
00512 if (pool->blockCount != blocks)
00513 Sys_Error("Mem_CheckPoolIntegrity: bad block count\n" "check: %s:#%i", fileName, fileLine);
00514 if (pool->byteCount != size)
00515 Sys_Error("Mem_CheckPoolIntegrity: bad pool size\n" "check: %s:#%i", fileName, fileLine);
00516 }
00517
00518
00519 void _Mem_CheckGlobalIntegrity (const char *fileName, const int fileLine)
00520 {
00521 memPool_t *pool;
00522 uint32_t i;
00523
00524 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00525 if (pool->inUse)
00526 _Mem_CheckPoolIntegrity(pool, fileName, fileLine);
00527 }
00528 }
00529
00530
00531 void _Mem_TouchPool (struct memPool_s *pool, const char *fileName, const int fileLine)
00532 {
00533 memBlock_t *mem;
00534 uint32_t i;
00535 int sum;
00536 int j = 0;
00537
00538 if (!pool)
00539 return;
00540
00541 sum = 0;
00542
00543
00544 for (j = 0; j < MEM_HASH; j++) {
00545 for (mem = pool->blocks[j]; mem; mem = mem->next) {
00546
00547 for (i = 0; i < mem->memSize; i += 128) {
00548 sum += ((byte *)mem->memPointer)[i];
00549 }
00550 }
00551 }
00552 }
00553
00559 void* _Mem_AllocatedInPool (struct memPool_s *pool, const void *pointer)
00560 {
00561 memBlock_t *mem;
00562
00563 if (!pool)
00564 return NULL;
00565
00566
00567 mem = pool->blocks[(uintptr_t)pointer % MEM_HASH];
00568 if (!mem)
00569 return NULL;
00570
00571 for ( ; mem; mem = mem->next) {
00572 if (mem->memPointer == pointer)
00573 return mem->memPointer;
00574 }
00575
00576 return NULL;
00577 }
00578
00579 void _Mem_TouchGlobal (const char *fileName, const int fileLine)
00580 {
00581 memPool_t *pool;
00582 uint32_t i, num;
00583
00584
00585 num = 0;
00586 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00587 if (pool->inUse) {
00588 _Mem_TouchPool(pool, fileName, fileLine);
00589 num++;
00590 }
00591 }
00592 }
00593
00594 #ifdef COMPILE_UFO
00595
00596
00597
00598
00599 static void Mem_Check_f (void)
00600 {
00601 Mem_CheckGlobalIntegrity();
00602 }
00603
00604 static void Mem_Stats_f (void)
00605 {
00606 uint32_t totalBlocks, totalBytes;
00607 memPool_t *pool;
00608 uint32_t poolNum, i;
00609 int j = 0;
00610
00611 if (Cmd_Argc() > 1) {
00612 memPool_t *best;
00613 memBlock_t *mem;
00614
00615 best = NULL;
00616 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00617 if (!pool->inUse)
00618 continue;
00619 if (strstr(pool->name, Cmd_Args())) {
00620 if (best) {
00621 Com_Printf("Too many matches for '%s'...\n", Cmd_Args());
00622 return;
00623 }
00624 best = pool;
00625 }
00626 }
00627 if (!best) {
00628 Com_Printf("No matches for '%s'...\n", Cmd_Args());
00629 return;
00630 }
00631
00632 Com_Printf("Pool stats for '%s':\n", best->name);
00633 Com_Printf("block line file size \n");
00634 Com_Printf("----- ----- -------------------- ---------- \n");
00635
00636 totalBytes = 0;
00637 for (j = 0; j < MEM_HASH; j++) {
00638 for (i = 0, mem = best->blocks[j]; mem; mem = mem->next, i++) {
00639 if (i & 1)
00640 Com_Printf(COLORED_GREEN);
00641
00642 Com_Printf("%5i %5i %20s "UFO_SIZE_T"B\n", i + 1, mem->allocLine, mem->allocFile, mem->size);
00643
00644 totalBytes += mem->size;
00645 }
00646 }
00647
00648 Com_Printf("----------------------------------------\n");
00649 Com_Printf("Total: %i blocks, %i bytes (%6.3fMB)\n", i, totalBytes, totalBytes/1048576.0f);
00650 return;
00651 }
00652
00653 Com_Printf("Memory stats:\n");
00654 Com_Printf(" blocks size name\n");
00655 Com_Printf("--- ------ ---------- ---------- --------\n");
00656
00657 totalBlocks = 0;
00658 totalBytes = 0;
00659 poolNum = 0;
00660 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00661 if (!pool->inUse)
00662 continue;
00663
00664 poolNum++;
00665 if (poolNum & 1)
00666 Com_Printf(COLORED_GREEN);
00667
00668 Com_Printf("#%2i %6i %9iB (%6.3fMB) %s\n", poolNum, pool->blockCount, pool->byteCount, pool->byteCount/1048576.0f, pool->name);
00669
00670 totalBlocks += pool->blockCount;
00671 totalBytes += pool->byteCount;
00672 }
00673
00674 Com_Printf("----------------------------------------\n");
00675 Com_Printf("Total: %i pools, %i blocks, %i bytes (%6.3fMB)\n", i, totalBlocks, totalBytes, totalBytes/1048576.0f);
00676 }
00677 #endif
00678
00683 void Mem_Init (void)
00684 {
00685 #ifdef COMPILE_UFO
00686 Cmd_AddCommand("mem_stats", Mem_Stats_f, "Prints out current internal memory statistics");
00687 Cmd_AddCommand("mem_check", Mem_Check_f, "Checks global memory integrity");
00688 #endif
00689
00690 z_lock = SDL_CreateMutex();
00691 }
00692
00698 uint32_t Mem_Shutdown (void)
00699 {
00700 memPool_t *pool;
00701 uint32_t size = 0;
00702 int i;
00703
00704
00705
00706 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
00707 if (!pool->inUse)
00708 continue;
00709 size += Mem_DeletePool(pool);
00710 }
00711
00712 SDL_DestroyMutex(z_lock);
00713
00714 return size;
00715 }