void *Z_Realloc(void *ptr, size_t n, int mallocTag) { int tag = ptr ? Z_GetTag(ptr) : mallocTag; void *p; lockZone(); n = ALIGNED(n); p = Z_Malloc(n, tag, 0); // User always 0; if (ptr) { size_t bsize; // Has old data; copy it. memblock_t *block = Z_GetBlock(ptr); #ifdef LIBDENG_FAKE_MEMORY_ZONE bsize = block->areaSize; #else bsize = block->size - sizeof(memblock_t); #endif memcpy(p, ptr, MIN_OF(n, bsize)); Z_Free(ptr); } unlockZone(); return p; }
static int Z_GetAlign(const ZoneHeader* header) { if (*header & (1 << 31)) { unsigned char* ptr = (unsigned char*)header; memtag_t tag = Z_GetTag(header); // point to the first alignment block if (Z_IsTagTemp(tag)) { ptr += sizeof(ZoneHeader) + Z_GetSize(header); #ifdef _DEBUG ptr += sizeof(ZoneDebugHeader) + sizeof(ZoneDebugFooter); #endif } else { if (Z_IsTagLinked(tag)) { // skip the link header ptr -= sizeof(ZoneLinkHeader); } ptr -= 1; } return *ptr + 1; } return 0; }
int Z_Size(void *pvAddress) { assert(s_Initialized); #ifdef _DEBUG ZoneDebugHeader* debug = (ZoneDebugHeader*)pvAddress - 1; if (*debug != ZONE_MAGIC) { Com_Error(ERR_FATAL, "Z_Size(): Not a valid zone header!"); return 0; // won't get here } pvAddress = (void*)debug; #endif ZoneHeader* header = (ZoneHeader*)pvAddress - 1; if (Z_GetTag(header) == TAG_STATIC) { return 0; // kind of } return Z_GetSize(header); }
void Z_TagPointers(memtag_t eTag) { assert(s_Initialized); #ifndef _GAMECUBE WaitForSingleObject(s_Mutex, INFINITE); #endif Sys_Log( "pointers.txt", va("Pointers for tag %d:\n", eTag) ); for (ZoneLinkHeader* link = s_LinkBase; link;) { ZoneHeader* header = (ZoneHeader*)(link + 1); link = link->m_Next; if (eTag == TAG_ALL || Z_GetTag(header) == eTag) { #ifdef _DEBUG Sys_Log( "pointers.txt", va("%x - %d\n", ((void*)((char*)header + sizeof(ZoneHeader) + sizeof(ZoneDebugHeader))), Z_Size(((void*)((char*)header + sizeof(ZoneHeader) + sizeof(ZoneDebugHeader)))))); #else Sys_Log( "pointers.txt", va("%x - %d\n", (void*)(header + 1), Z_Size((void*)(header + 1)))); #endif } } #ifndef _GAMECUBE ReleaseMutex(s_Mutex); #endif }
void W_RemoveLumpsWithHandle(DFILE * handle) { int i, k, first, len; for(i = 0, first = -1; i < numlumps; i++) { if(first < 0 && lumpinfo[i].handle == handle) { // Start a region. first = i; continue; } // Does a region end? if(first >= 0) if(lumpinfo[i].handle != handle || i == numlumps - 1 || MarkerForGroup(lumpinfo[i].name, true) || MarkerForGroup(lumpinfo[i].name, false)) { if(lumpinfo[i].handle == handle && i == numlumps - 1) i++; // Also free the last one. // The length of the region. len = i - first; // Free the memory allocated for the region. for(k = first; k < i; k++) { // Con_Message("removing lump: %s (%d)\n", lumpinfo[k].name, lumpinfo[k].handle); if(lumpcache[k]) { // If the block has a user, it must be explicitly freed. /*if((unsigned int)Z_GetUser(lumpcache[k]) > 0x100) Z_Free(lumpcache[k]); else if(Z_GetTag(lumpcache[k]) < PU_LEVEL) Z_ChangeTag(lumpcache[k], PU_LEVEL); */ //Z_ChangeTag(lumpcache[k], PU_CACHE); //Z_ChangeUser(lumpcache[k], NULL); if(Z_GetTag(lumpcache[k]) < PU_LEVEL) Z_ChangeTag(lumpcache[k], PU_LEVEL); // Mark the memory pointer in use, but unowned. Z_ChangeUser(lumpcache[k], (void *) 0x2); } } // Move the data in the lump storage. W_MoveLumps(i, numlumps - i, -len); numlumps -= len; i -= len; // Make it possible to begin a new region. first = -1; } } }
void Z_TagFree(memtag_t eTag) { assert(s_Initialized); for (ZoneLinkHeader* link = s_LinkBase; link;) { ZoneHeader* header = (ZoneHeader*)(link + 1); link = link->m_Next; if (eTag == TAG_ALL || Z_GetTag(header) == eTag) { #ifdef _DEBUG Z_Free((void*)((char*)header + sizeof(ZoneHeader) + sizeof(ZoneDebugHeader))); #else Z_Free((void*)(header + 1)); #endif } } }
void *Z_Recalloc(void *ptr, size_t n, int callocTag) { memblock_t *block; void *p; size_t bsize; lockZone(); n = ALIGNED(n); if (ptr) // Has old data. { p = Z_Malloc(n, Z_GetTag(ptr), NULL); block = Z_GetBlock(ptr); #ifdef LIBDENG_FAKE_MEMORY_ZONE bsize = block->areaSize; #else bsize = block->size - sizeof(memblock_t); #endif if (bsize <= n) { memcpy(p, ptr, bsize); memset((char *) p + bsize, 0, n - bsize); } else { // New block is smaller. memcpy(p, ptr, n); } Z_Free(ptr); } else { // Totally new allocation. p = Z_Calloc(n, callocTag, NULL); } unlockZone(); return p; }
qboolean Z_IsFromZone(void *pvAddress, memtag_t eTag) { assert(s_Initialized); #ifdef _DEBUG ZoneDebugHeader* debug = (ZoneDebugHeader*)pvAddress - 1; if (*debug != ZONE_MAGIC) { return qfalse; } pvAddress = (void*)debug; #endif ZoneHeader* header = (ZoneHeader*)pvAddress - 1; if (Z_GetTag(header) != eTag) { return qfalse; } return Z_GetSize(header); }
void Z_FindLeak(void) { assert(s_Initialized); static int cycle_count = 0; const memtag_t tag = TAG_NEWDEL; struct PointerInfo { void* data; int counter; bool mark; }; const int max_pointers = 32768; static PointerInfo pointers[max_pointers]; static int num_pointers = 0; // Clear pointer existance for (int i = 0; i < num_pointers; ++i) { pointers[i].mark = false; } // Add all known pointers int start_num = num_pointers; for (ZoneLinkHeader* link = s_LinkBase; link;) { ZoneHeader* header = (ZoneHeader*)(link + 1); link = link->m_Next; if (Z_GetTag(header) == tag) { // See if the pointer already is in the array bool found = false; for (int k = start_num; k < num_pointers; ++k) { if (pointers[k].data == header) { ++pointers[k].counter; pointers[k].mark = true; found = true; break; } } // If the pointer is not in the array, add it if (!found) { assert(num_pointers < max_pointers); pointers[num_pointers].data = header; pointers[num_pointers].counter = 0; pointers[num_pointers].mark = true; ++num_pointers; } } } // Remove pointers that are no longer used for (int j = 0; j < num_pointers; ++j) { if (pointers[j].mark) { if (pointers[j].counter != cycle_count && pointers[j].counter != cycle_count - 1 && pointers[j].counter != 0) { Com_Printf("Memory leak: %p\n", pointers[j].data); } } else { int k; for (k = j; k < num_pointers; ++k) { if (pointers[k].mark) break; } if (k == num_pointers) break; memmove(pointers + j, pointers + k, (num_pointers - k) * sizeof(PointerInfo)); num_pointers -= k - j; } } ++cycle_count; }
int Z_Free(void *pvAddress) #define Z_FREE_RETURN(x) return (x) #endif { #ifdef _WINDOWS if (!s_Initialized) return; #endif assert(s_Initialized); #ifdef _DEBUG // check the header magic ZoneDebugHeader* debug_header = (ZoneDebugHeader*)pvAddress - 1; if (*debug_header != ZONE_MAGIC) { Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone header!"); Z_FREE_RETURN( 0 ); } ZoneHeader* header = (ZoneHeader*)debug_header - 1; // check the footer magic ZoneDebugFooter* debug_footer = (ZoneDebugFooter*)((char*)pvAddress + Z_GetSize(header)); if (*debug_footer != ZONE_MAGIC) { Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone footer!"); Z_FREE_RETURN( 0 ); } #else ZoneHeader* header = (ZoneHeader*)pvAddress - 1; #endif memtag_t tag = Z_GetTag(header); if (tag != TAG_STATIC) { #ifndef _GAMECUBE WaitForSingleObject(s_Mutex, INFINITE); #endif // Determine size of header and footer int header_size = sizeof(ZoneHeader); int align_size = Z_GetAlign(header); int footer_size = 0; int data_size = Z_GetSize(header); if (Z_IsTagLinked(tag)) { header_size += sizeof(ZoneLinkHeader); } if (Z_IsTagTemp(tag)) { footer_size += align_size; } else { header_size += align_size; } #ifdef _DEBUG header_size += sizeof(ZoneDebugHeader); footer_size += sizeof(ZoneDebugFooter); #endif int real_size = data_size + header_size + footer_size; // Update the stats s_Stats.m_SizeAlloc -= data_size; s_Stats.m_OverheadAlloc -= header_size + footer_size; s_Stats.m_SizesPerTag[tag] -= data_size; s_Stats.m_CountAlloc--; s_Stats.m_CountsPerTag[tag]--; // Delink block if (Z_IsTagLinked(tag)) { ZoneLinkHeader* linked = (ZoneLinkHeader*)header - 1; if (linked == s_LinkBase) { s_LinkBase = linked->m_Next; if (s_LinkBase) { s_LinkBase->m_Prev = NULL; } } else { if (linked->m_Next) { linked->m_Next->m_Prev = linked->m_Prev; } linked->m_Prev->m_Next = linked->m_Next; } assert(Z_ValidateLinks()); } // Clear the block header for safety *header = 0; // Add block to free list ZoneFreeBlock* nblock = NULL; if (real_size < sizeof(ZoneFreeBlock)) { // Not enough space in block to put free information -- // use overflow buffer. nblock = Z_GetOverflowBlock(); if (nblock == NULL) { Z_Details_f(); Com_Error(ERR_FATAL, "Zone free overflow buffer overflowed!"); } } else { // Place free information in block nblock = (ZoneFreeBlock*)((char*)pvAddress - header_size); } nblock->m_Address = (unsigned int)pvAddress - header_size; nblock->m_Size = real_size; Z_LinkFreeBlock(nblock); // Coalesce any adjacent free blocks Z_Coalasce(nblock); #ifndef _GAMECUBE ReleaseMutex(s_Mutex); #endif } Z_FREE_RETURN( 0 ); }