/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ /* File: OSMemory_Server.cpp Contains: Implementation of OSMemory stuff, including all new & delete operators. */ #include #include "OSMemory.h" #if MEMORY_DEBUGGING OSQueue OSMemory::sMemoryQueue; OSQueue OSMemory::sTagQueue; UInt32 OSMemory::sAllocatedBytes = 0; OSMutex OSMemory::sMutex; #endif static SInt32 sMemoryErr = 0; // // OPERATORS #if MEMORY_DEBUGGING void* operator new(size_t s, char* inFile, int inLine) { return OSMemory::DebugNew(s, inFile, inLine, true); } void* operator new[](size_t s, char* inFile, int inLine) { return OSMemory::DebugNew(s, inFile, inLine, false); } #endif void* operator new (size_t s) { return OSMemory::New(s); } void* operator new[](size_t s) { return OSMemory::New(s); } void operator delete(void* mem) { OSMemory::Delete(mem); } void operator delete[](void* mem) { OSMemory::Delete(mem); } void OSMemory::SetMemoryError(SInt32 inErr) { sMemoryErr = inErr; } void* OSMemory::New(size_t inSize) { #if MEMORY_DEBUGGING return OSMemory::DebugNew(inSize, __FILE__, __LINE__, false); #else void *m = malloc(inSize); if (m == NULL) ::exit(sMemoryErr); return m; #endif } void OSMemory::Delete(void* inMemory) { if (inMemory == NULL) return; #if MEMORY_DEBUGGING OSMemory::DebugDelete(inMemory); #else free(inMemory); #endif } #if MEMORY_DEBUGGING void* OSMemory::DebugNew(size_t s, char* inFile, int inLine, Bool16 sizeCheck) { //also allocate enough space for a Q elem and a long to store the length of this //allocation block OSMutexLocker locker(&sMutex); ValidateMemoryQueue(); UInt32 actualSize = s + sizeof(MemoryDebugging) + (2 * sizeof(inLine)); char *m = (char *)malloc(actualSize); if (m == NULL) ::exit(sMemoryErr); char theFileName[kMaxFileNameSize]; strncpy(theFileName, inFile, kMaxFileNameSize); theFileName[kMaxFileNameSize] = '\0'; //mark the beginning and the end with the line number memset(m, 0xfe, actualSize);//mark the block with an easily identifiable pattern memcpy(m, &inLine, sizeof(inLine)); memcpy((m + actualSize) - sizeof(inLine), &inLine, sizeof(inLine)); TagElem* theElem = NULL; //also update the tag queue for (OSQueueIter iter(&sTagQueue); !iter.IsDone(); iter.Next()) { TagElem* elem = (TagElem*)iter.GetCurrent()->GetEnclosingObject(); if ((::strcmp(elem->fileName, theFileName) == 0) && (elem->line == inLine)) { //verify that the size of this allocation is the same as all others //(if requested... some tags are of variable size) if (sizeCheck) Assert(s == elem->tagSize); elem->totMemory += s; elem->numObjects++; theElem = elem; } } if (theElem == NULL) { //if we've gotten here, this tag doesn't exist, so let's add it. theElem = (TagElem*)malloc(sizeof(TagElem)); if (theElem == NULL) ::exit(sMemoryErr); memset(theElem, 0, sizeof(TagElem)); theElem->elem.SetEnclosingObject(theElem); ::strcpy(theElem->fileName, theFileName); theElem->line = inLine; theElem->tagSize = s; theElem->totMemory = s; theElem->numObjects = 1; sTagQueue.EnQueue(&theElem->elem); } //put this chunk on the global chunk queue MemoryDebugging* header = (MemoryDebugging*)(m + sizeof(inLine)); memset(header, 0, sizeof(MemoryDebugging)); header->size = s; header->tagElem = theElem; header->elem.SetEnclosingObject(header); sMemoryQueue.EnQueue(&header->elem); sAllocatedBytes += s; return m + sizeof(inLine) + sizeof(MemoryDebugging); } void OSMemory::DebugDelete(void *mem) { OSMutexLocker locker(&sMutex); ValidateMemoryQueue(); char* memPtr = (char*)mem; MemoryDebugging* memInfo = (MemoryDebugging*)mem; memInfo--;//get a pointer to the MemoryDebugging structure Assert(memInfo->elem.IsMemberOfAnyQueue());//must be on the memory Queue //double check it's on the memory queue Bool16 found = false; for (OSQueueIter iter(&sMemoryQueue); !iter.IsDone(); iter.Next()) { MemoryDebugging* check = (MemoryDebugging*)iter.GetCurrent()->GetEnclosingObject(); if (check == memInfo) { found = true; break; } } Assert(found == true); sMemoryQueue.Remove(&memInfo->elem); Assert(!memInfo->elem.IsMemberOfAnyQueue()); sAllocatedBytes -= memInfo->size; //verify that the tags placed at the very beginning and very end of the //block still exist memPtr += memInfo->size; int* linePtr = (int*)memPtr; Assert(*linePtr == memInfo->tagElem->line); memPtr -= sizeof(MemoryDebugging) + sizeof(int) + memInfo->size; linePtr = (int*)memPtr; Assert(*linePtr == memInfo->tagElem->line); //also update the tag queue Assert(memInfo->tagElem->numObjects > 0); memInfo->tagElem->numObjects--; memInfo->tagElem->totMemory -= memInfo->size; if (memInfo->tagElem->numObjects == 0) { // If this tag has no elements, then delete the tag Assert(memInfo->tagElem->totMemory == 0); sTagQueue.Remove(&memInfo->tagElem->elem); free(memInfo->tagElem); } // delete our memory block memset(mem, 0xfd,memInfo->size); free(memPtr); } void OSMemory::ValidateMemoryQueue() { OSMutexLocker locker(&sMutex); for(OSQueueIter iter(&sMemoryQueue); !iter.IsDone(); iter.Next()) { MemoryDebugging* elem = (MemoryDebugging*)iter.GetCurrent()->GetEnclosingObject(); char* rawmem = (char*)elem; rawmem -= sizeof(int); int* tagPtr = (int*)rawmem; Assert(*tagPtr == elem->tagElem->line); rawmem += sizeof(int) + sizeof(MemoryDebugging) + elem->size; tagPtr = (int*)rawmem; Assert(*tagPtr == elem->tagElem->line); } } #if 0 Bool16 OSMemory::MemoryDebuggingTest() { static char* s20 = "this is 20 characte"; static char* s30 = "this is 30 characters long, o"; static char* s40 = "this is 40 characters long, okey dokeys"; void* victim = DebugNew(20, 'tsta', true); strcpy((char*)victim, s20); MemoryDebugging* victimInfo = (MemoryDebugging*)victim; ValidateMemoryQueue(); victimInfo--; if (victimInfo->tag != 'tsta') return false; if (victimInfo->size != 20) return false; void* victim2 = DebugNew(30, 'tstb', true); strcpy((char*)victim2, s30); ValidateMemoryQueue(); void* victim3 = DebugNew(20, 'tsta', true); strcpy((char*)victim3, s20); ValidateMemoryQueue(); void* victim4 = DebugNew(40, 'tstc', true); strcpy((char*)victim4, s40); ValidateMemoryQueue(); void* victim5 = DebugNew(30, 'tstb', true); strcpy((char*)victim5, s30); ValidateMemoryQueue(); if (sTagQueue.GetLength() != 3) return false; for (OSQueueIter iter(&sTagQueue); !iter.IsDone(); iter.Next()) { TagElem* elem = (TagElem*)iter.GetCurrent()->GetEnclosingObject(); if (*elem->tagPtr == 'tstb') { if (elem->tagSize != 30) return false; if (elem->numObjects != 2) return false; } else if (*elem->tagPtr == 'tsta') { if (elem->tagSize != 20) return false; if (elem->numObjects != 2) return false; } else if (*elem->tagPtr == 'tstc') { if (elem->tagSize != 40) return false; if (elem->numObjects != 1) return false; } else return false; } DebugDelete(victim3); ValidateMemoryQueue(); DebugDelete(victim4); ValidateMemoryQueue(); if (sTagQueue.GetLength() != 3) return false; for (OSQueueIter iter2(&sTagQueue); !iter2.IsDone(); iter2.Next()) { TagElem* elem = (TagElem*)iter2.GetCurrent()->GetEnclosingObject(); if (*elem->tagPtr == 'tstb') { if (elem->tagSize != 30) return false; if (elem->numObjects != 2) return false; } else if (*elem->tagPtr == 'tsta') { if (elem->tagSize != 20) return false; if (elem->numObjects != 1) return false; } else if (*elem->tagPtr == 'tstc') { if (elem->tagSize != 40) return false; if (elem->numObjects != 0) return false; } else return false; } if (sMemoryQueue.GetLength() != 3) return false; DebugDelete(victim); ValidateMemoryQueue(); if (sMemoryQueue.GetLength() != 2) return false; DebugDelete(victim5); ValidateMemoryQueue(); if (sMemoryQueue.GetLength() != 1) return false; DebugDelete(victim2); ValidateMemoryQueue(); if (sMemoryQueue.GetLength() != 0) return false; DebugDelete(victim4); return true; } #endif //0 #endif // MEMORY_DEBUGGING