mem.c

Go to the documentation of this file.
00001 
00006 /*
00007 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 Original file from Quake 2 v3.21: quake2-2.31/game/q_shared.c
00010 Copyright (C) 1997-2001 Id Software, Inc.
00011 
00012 This program is free software; you can redistribute it and/or
00013 modify it under the terms of the GNU General Public License
00014 as published by the Free Software Foundation; either version 2
00015 of the License, or (at your option) any later version.
00016 
00017 This program is distributed in the hope that it will be useful,
00018 but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021 See the GNU General Public License for more details.
00022 
00023 You should have received a copy of the GNU General Public License
00024 along with this program; if not, write to the Free Software
00025 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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 POOL MANAGEMENT
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     /* Check name */
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     /* See if it already exists */
00079     pool = Mem_FindPool(name);
00080     if (pool)
00081         return pool;
00082 
00083     /* Nope, create a slot */
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     /* Store values */
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     /* Release all allocated memory */
00120     size = _Mem_FreePool(pool, fileName, fileLine);
00121 
00122     /* Simple, yes? */
00123     pool->inUse = qfalse;
00124     pool->name[0] = '\0';
00125 
00126     return size;
00127 }
00128 
00129 
00130 /*==============================================================================
00131 POOL AND TAG MEMORY ALLOCATION
00132 ==============================================================================*/
00133 
00134 static void _Mem_CheckSentinels (void *ptr, const char *fileName, const int fileLine)
00135 {
00136     memBlock_t *mem;
00137 
00138     /* Check sentinels */
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     /* Decrement counters */
00181     mem->pool->blockCount--;
00182     mem->pool->byteCount -= mem->size;
00183     size = mem->size;
00184 
00185     /* De-link it */
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     /* Free it */
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     /* Check pool */
00267     if (!pool)
00268         Sys_Error("Mem_Alloc: Error - no pool given\n" "alloc: %s:#%i\n", fileName, fileLine);
00269 
00270     /* Check size */
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     /* Add header and round to cacheline */
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     /* Zero fill */
00284     if (zeroFill)
00285         memset(mem, 0, size);
00286 
00287     /* Fill in the header */
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     /* Fill in the footer */
00300     mem->footer->sentinel = MEM_FOOT_SENTINEL;
00301 
00302     SDL_mutexP(z_lock);
00303 
00304     /* For integrity checking and stats */
00305     pool->blockCount++;
00306     pool->byteCount += size;
00307 
00308     /* Link it in to the appropriate pool */
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     /* if size matches, do nothing */
00334     if (mem->memSize == size)
00335         return ptr;
00336 
00337     pool = mem->pool;
00338 
00339     /* allocate memory for the new size */
00340     newPtr = _Mem_Alloc(size, qfalse, pool, mem->tagNum, fileName, fileLine);
00341 
00342     /* copy old data */
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     /* if there was old data, free it */
00350     _Mem_Free(ptr, fileName, fileLine);
00351 
00352     _Mem_CheckSentinels(newPtr, fileName, fileLine);
00353 
00354     return newPtr;
00355 }
00356 
00357 /*==============================================================================
00358 MISC FUNCTIONS
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     /* Check sentinels */
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     /* Check block/byte counts */
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     /* Cycle through the blocks */
00544     for (j = 0; j < MEM_HASH; j++) {
00545         for (mem = pool->blocks[j]; mem; mem = mem->next) {
00546             /* Touch each page */
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     /* if it's in the pool, it must be in THIS block */
00567     mem = pool->blocks[(uintptr_t)pointer % MEM_HASH];
00568     if (!mem)       /* the block might be in initialized state */
00569         return NULL;
00570     /* Cycle through the blocks */
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     /* Touch every pool */
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 CONSOLE COMMANDS
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     /* don't use cvars, debug print or anything else inside of this loop
00705      * the mem you are trying to use here might already be wiped away */
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 }

Generated by  doxygen 1.6.2