364 lines
10 KiB
C++
364 lines
10 KiB
C++
/*
|
|
*
|
|
* @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 <string.h>
|
|
#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
|
|
|