Files
Mozilla/mozilla/lib/mac/MacMemoryAllocator/src/fastmem.c
gerv%gerv.net 13afb6d0f9 Relicensing Round 1, Take 2. Most C-like NPL files -> NPL/GPL/LGPL. Bug 98089.
git-svn-id: svn://10.0.0.236/trunk@104119 18797224-902f-48f8-a5cc-f745e15eee43
2001-09-28 20:14:13 +00:00

1935 lines
56 KiB
C

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#pragma error "This file is obsolete, but remains for reference reasons"
#include <Memory.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "fastmem.h"
#include "prmacos.h"
#include <OSUtils.h>
#include <Processes.h>
//##############################################################################
//##############################################################################
#pragma mark malloc DEBUG DECLARATIONS
#if STATS_MAC_MEMORY
UInt32 gSmallAllocationTotalCountTable[1025];
UInt32 gSmallAllocationActiveCountTable[1025];
UInt32 gSmallAllocationMaxCountTable[1025];
#endif
#if DEBUG_MAC_MEMORY
enum {
kActiveSiteHashTableSize = 128,
kActiveSizeListSize = 1024
};
enum {
kFreeBlockHeaderTag = 'FREE',
kFreeBlockTrailerTag = 'free',
kUsedBlockHeaderTag = 'USED',
kUsedBlockTrailerTag = 'used',
kRefdBlockHeaderTag = 'REFD',
kRefdBlockTrailerTag = 'refd',
kFreeMemoryFillPattern = 0xEF,
kUsedMemoryFillPattern = 0xDB
};
MemoryBlockHeader *gFirstAllocatedBlock = NULL;
MemoryBlockHeader *gLastAllocatedBlock = NULL;
enum {
kAllocationSiteMalloc = 0,
kAllocationSiteNewHandle = 1,
kAllocationSiteNewPtr = 2
};
typedef struct AllocationSite AllocationSite;
struct AllocationSite {
UInt32 type;
AllocationSite *next;
void *whoAllocated[kRecordingDepthOfStackLevels];
UInt32 mallocs;
UInt32 frees;
UInt32 totalAllocations;
};
AllocationSite *gActiveSizeHashTable[kActiveSiteHashTableSize];
AllocationSite gActiveSizeList[kActiveSizeListSize];
AllocationSite *gCurrentActiveSize = gActiveSizeList;
UInt32 gRemainingSitesInList = kActiveSizeListSize;
AllocationSite *AddAllocationSite(UInt32 allocationType, void **allocatorStack);
void AddBlockToActiveList(void *newBlock, AllocationSite *alocationSite);
void RemoveBlockFromActiveList(void *deadBlock);
UInt32 gOutstandingPointers = 0;
UInt32 gOutstandingHandles = 0;
#endif
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark malloc DEBUG CONTROL VARIABLES
#if DEBUG_MAC_MEMORY
UInt32 gVerifyHeapOnEveryMalloc = 0;
UInt32 gVerifyHeapOnEveryFree = 0;
UInt32 gFillUsedBlocksWithPattern = 0;
UInt32 gFillFreeBlocksWithPattern = 1;
UInt32 gTrackLeaks = 0;
UInt32 gDontActuallyFreeMemory = 0;
UInt32 gOnMallocFailureReturnDEADBEEF = 0;
SInt32 gFailToAllocateAfterNMallocs = -1;
SInt32 gReportActiveBlocks = 1;
SInt32 gTagReferencedBlocks = 0; // for best effect, turn on gFillFreeBlocksWithPattern also
SInt32 gReportLeaks = 1;
#endif
//##############################################################################
//##############################################################################
#if STATS_MAC_MEMORY
UInt32 gTotalFixedSizeCompactBlocksAllocated = 0;
UInt32 gTotalFixedSizeCompactBlocksUsed = 0;
UInt32 gTotalFixedSizeCompactAllocated = 0;
UInt32 gTotalFixedSizeCompactUsed = 0;
UInt32 gTotalFixedSizeFastBlocksAllocated = 0;
UInt32 gTotalFixedSizeFastBlocksUsed = 0;
UInt32 gTotalFixedSizeFastAllocated = 0;
UInt32 gTotalFixedSizeFastUsed = 0;
UInt32 gTotalSmallHeapBlocksAllocated = 0;
UInt32 gTotalSmallHeapBlocksUsed = 0;
UInt32 gTotalSmallHeapAllocated = 0;
UInt32 gTotalSmallHeapUsed = 0;
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark DEBUG malloc AND free
#endif
#if DEBUG_MAC_MEMORY || STATS_MAC_MEMORY
void *gOurApplicationHeapBase;
void *gOurApplicationHeapMax;
#if GENERATINGPOWERPC
asm void *GetCurrentStackPointer()
{
mr r3, sp
lwz r3, 0(r3)
blr
}
#endif
UInt32 gNextBlockNum = 0;
void *malloc(size_t blockSize)
{
MemoryBlockHeader *newBlockHeader;
#if DEBUG_MAC_MEMORY
MemoryBlockTrailer *newBlockTrailer;
#endif
size_t newCompositeSize = blockSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE;
size_t blockSizeCopy = blockSize;
UInt32 stackIterator;
void *newBlock;
void *currentSP;
#if DEBUG_MAC_MEMORY
#if GENERATINGPOWERPC
currentSP = GetCurrentStackPointer();
#endif
if (gVerifyHeapOnEveryMalloc)
VerifyMallocHeapIntegrity();
if (gFailToAllocateAfterNMallocs != -1) {
if (gFailToAllocateAfterNMallocs-- == 0)
return NULL;
}
#endif
#if STATS_MAC_MEMORY
if (blockSizeCopy >= 1024)
blockSizeCopy = 1024;
gSmallAllocationTotalCountTable[blockSizeCopy]++;
gSmallAllocationActiveCountTable[blockSizeCopy]++;
if (gSmallAllocationActiveCountTable[blockSizeCopy] > gSmallAllocationMaxCountTable[blockSizeCopy])
gSmallAllocationMaxCountTable[blockSizeCopy] = gSmallAllocationActiveCountTable[blockSizeCopy];
#endif
if (blockSize <= kMaxTableAllocatedBlockSize) {
AllocMemoryBlockDescriptor *whichAllocator;
whichAllocator = gFastMemSmallSizeAllocators + ((blockSize + 3) >> 2);
newBlock = (char *)(whichAllocator->allocRoutine)(blockSize, whichAllocator->refcon) - sizeof(MemoryBlockHeader);
}
else {
newBlock = (char *)StandardAlloc(blockSize, 0) - sizeof(MemoryBlockHeader);
}
#if DEBUG_MAC_MEMORY
if (((Ptr)newBlock + sizeof(MemoryBlockHeader)) == NULL) {
DebugStr("\pMALLOC FALUIRE");
if (gOnMallocFailureReturnDEADBEEF)
return (void *)0xDEADBEEF;
else
return NULL;
}
// Fill in the blocks header. This includes adding the
// block to the used list.
newBlockHeader = (MemoryBlockHeader *)newBlock;
memset(newBlockHeader->whoAllocated, 0, sizeof(void *) * kRecordingDepthOfStackLevels);
#if GENERATINGPOWERPC
for (stackIterator = 0; stackIterator < kRecordingDepthOfStackLevels; stackIterator++) {
if ((currentSP < gOurApplicationHeapBase) || (currentSP > gOurApplicationHeapMax))
break;
newBlockHeader->whoAllocated[stackIterator] = *((void **)((char *)currentSP + 8));
currentSP = *((void **)currentSP);
}
#endif
newBlockHeader->blockSize = blockSize;
newBlockHeader->headerTag = kUsedBlockHeaderTag;
newBlockHeader->blockNum = ++gNextBlockNum;
newBlockHeader->next = gFirstAllocatedBlock;
newBlockHeader->prev = NULL;
if (gFirstAllocatedBlock != NULL)
gFirstAllocatedBlock->prev = newBlockHeader;
else
gLastAllocatedBlock = newBlockHeader;
gFirstAllocatedBlock = newBlockHeader;
// Fill in the trailer
newBlockTrailer = (MemoryBlockTrailer *)((char *)newBlock + newCompositeSize - sizeof(MemoryBlockTrailer));
newBlockTrailer->trailerTag = kUsedBlockTrailerTag;
// Fill with the used memory pattern
if (gFillUsedBlocksWithPattern)
memset((char *)newBlock + sizeof(MemoryBlockHeader), kUsedMemoryFillPattern, blockSize);
// If leak detection is on, then add this block to the
// tracking list.
if (gTrackLeaks)
AddAllocationSite(kAllocationSiteMalloc, newBlockHeader->whoAllocated);
#endif
return (void *)((char *)newBlock + sizeof(MemoryBlockHeader));
}
void free(void *deadBlock)
{
MemoryBlockHeader *deadBlockHeader;
#if DEBUG_MAC_MEMORY
MemoryBlockTrailer *deadBlockTrailer;
#endif
size_t deadBlockSize;
char *deadBlockBase;
FreeMemoryBlockDescriptor *descriptor;
#if DEBUG_MAC_MEMORY
if (gVerifyHeapOnEveryFree)
VerifyMallocHeapIntegrity();
#endif
if (deadBlock == NULL)
return;
deadBlockBase = ((char *)deadBlock - sizeof(MemoryBlockHeader));
deadBlockHeader = (MemoryBlockHeader *)deadBlockBase;
deadBlockSize = deadBlockHeader->blockSize;
#if DEBUG_MAC_MEMORY
deadBlockTrailer = (MemoryBlockTrailer *)(deadBlockBase + deadBlockSize + sizeof(MemoryBlockHeader));
#endif
#if STATS_MAC_MEMORY
if (deadBlockSize >= 1024)
deadBlockSize = 1024;
gSmallAllocationActiveCountTable[deadBlockSize]--;
#endif
#if DEBUG_MAC_MEMORY
if (deadBlockHeader->headerTag == kFreeBlockHeaderTag)
DebugStr("\pfastmem: double dispose of malloc block");
else
if ((deadBlockHeader->headerTag != kUsedBlockHeaderTag) || (deadBlockTrailer->trailerTag != kUsedBlockTrailerTag))
DebugStr("\pfastmem: attempt to dispose illegal block");
// Remove the block from the list of used blocks.
if (deadBlockHeader->next != NULL)
deadBlockHeader->next->prev = deadBlockHeader->prev;
else
gLastAllocatedBlock = deadBlockHeader->prev;
if (deadBlockHeader->prev != NULL)
deadBlockHeader->prev->next = deadBlockHeader->next;
else
gFirstAllocatedBlock = deadBlockHeader->next;
// Change the header and tailer tags to be the free ones.
deadBlockHeader->headerTag = kFreeBlockHeaderTag;
deadBlockTrailer->trailerTag = kFreeBlockTrailerTag;
// If we are tracking leaks, then decrement this blocks
// usage in the tracking table.
if (gTrackLeaks) {
UInt32 hashBin = ((UInt32)(deadBlockHeader->whoAllocated[0]) >> 2) % kActiveSiteHashTableSize;
AllocationSite *currentSite = gActiveSizeHashTable[hashBin];
while (currentSite) {
UInt32 currentStackLevel;
for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
if (currentSite->whoAllocated[currentStackLevel] != deadBlockHeader->whoAllocated[currentStackLevel])
break;
}
if (currentStackLevel == kRecordingDepthOfStackLevels)
break;
currentSite = currentSite->next;
}
if (currentSite != NULL)
currentSite->frees++;
}
// Fill with the free memory pattern
if (gFillFreeBlocksWithPattern)
memset(deadBlock, kFreeMemoryFillPattern, deadBlockSize);
if (!gDontActuallyFreeMemory) {
descriptor = deadBlockHeader->blockFreeRoutine;
(descriptor->freeRoutine)(deadBlock, descriptor->refcon);
}
#endif
}
#else
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark OPTIMIZED POWERPC malloc AND free
#if (defined(powerc) || defined(_powerc))
void *malloc(size_t blockSize)
{
return AsmMalloc(blockSize);
}
asm void *AsmMalloc(size_t blockSize)
{
cmplwi r3, kMaxTableAllocatedBlockSize
bgt largeAlloc
lwz r6,gFastMemSmallSizeAllocators(RTOC)
addi r4,r3,3
rlwinm r4,r4,1,0,28
add r6,r6,r4
lwz r5,0(r6)
lwz r5,0(r5)
mtctr r5
lwz r4,4(r6)
bcctr 31, 0
largeAlloc:
b StandardAlloc
}
void free(void *block)
{
AsmFree(block);
}
asm void AsmFree(void *)
{
cmplwi r3, 0
beqlr
subi r5,r3,4
lwz r5,0(r5)
lwz r6,0(r5)
lwz r6,0(r6)
mtctr r6
lwz r4,4(r5)
bcctr 31, 0
}
#else
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark STANDARD malloc AND free
void *malloc(size_t blockSize)
{
if (blockSize <= kMaxTableAllocatedBlockSize) {
AllocMemoryBlockDescriptor *whichAllocator;
whichAllocator = gFastMemSmallSizeAllocators + ((blockSize + 3) >> 2);
return (whichAllocator->allocRoutine)(blockSize, whichAllocator->refcon);
}
else {
return StandardAlloc(blockSize, 0);
}
}
void free(void *block)
{
MemoryBlockHeader *blockHeader;
FreeMemoryBlockDescriptor *descriptor;
if (block != NULL) {
blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
descriptor = blockHeader->blockFreeRoutine;
(descriptor->freeRoutine)(block, descriptor->refcon);
}
}
#endif
#endif
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark realloc AND calloc
void* reallocSmaller(void* block, size_t newSize)
{
// This actually doesn't reclaim memory!
#if 0
MemoryBlockHeader *blockHeader;
#if DEBUG_MAC_MEMORY
MemoryBlockTrailer *blockTrailer;
#endif
if (block != NULL) {
blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
if (blockHeader->blockFreeRoutine->freeRoutine == &StandardFree) {
size_t newCompositeSize = newSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE;
newCompositeSize = (newCompositeSize + 3) & 0xFFFFFFFC;
SetPtrSize((Ptr)blockHeader, newCompositeSize);
#if DEBUG_MAC_MEMORY
blockHeader->blockSize = newSize;
blockTrailer = (MemoryBlockTrailer *)((char *)block + newSize + sizeof(MemoryBlockHeader));
blockTrailer->trailerTag = kUsedBlockTrailerTag;
#endif
}
}
#endif
}
void* realloc(void* block, size_t newSize)
{
void *newBlock = NULL;
UInt8 tryAgain = true;
newBlock = malloc(newSize);
if (newBlock != NULL) {
BlockMoveData(block, newBlock, newSize);
free(block);
}
return newBlock;
}
void *calloc(size_t nele, size_t elesize)
{
char *newBlock = NULL;
UInt8 tryAgain = true;
size_t totalSize = nele * elesize;
newBlock = (char *)malloc(totalSize);
if (newBlock != NULL)
memset(newBlock, 0, totalSize);
return newBlock;
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark DEBUG MEMORY UTILS
#if STATS_MAC_MEMORY
FILE *statsFile = NULL;
void DumpCurrentMemoryStatistics(char *operation)
{
UInt32 count;
if (statsFile == NULL)
statsFile = fopen("stats", "w");
fprintf(statsFile, "%s\n", operation);
fprintf(statsFile, "Total Allocations\n");
for (count = 0; count < 1025; count++)
fprintf(statsFile, "%ld\n", gSmallAllocationTotalCountTable[count]);
fprintf(statsFile, "Current Allocation Allocations\n");
for (count = 0; count < 1025; count++)
fprintf(statsFile, "%ld\n", gSmallAllocationActiveCountTable[count]);
}
#endif
#if DEBUG_MAC_MEMORY || STATS_MAC_MEMORY
void PrintHexNumber(UInt32 hexNumber)
{
char hexString[11];
char hexMap[] = "0123456789ABCDEF";
long digits = 8;
hexString[0] = '0';
hexString[1] = 'x';
while (digits) {
hexString[digits + 1] = *((hexNumber & 0x0F) + hexMap);
hexNumber = hexNumber >> 4;
digits--;
}
hexString[10] = 0;
printf(hexString);
}
#endif
#if DEBUG_MAC_MEMORY
AllocationSite *
AddAllocationSite(UInt32 allocationType, void **allocatorStack)
{
UInt32 hashBin = ((UInt32)allocatorStack[0] >> 2) % kActiveSiteHashTableSize;
AllocationSite *currentSite = gActiveSizeHashTable[hashBin];
while (currentSite) {
UInt32 currentStackLevel;
for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
if (currentSite->whoAllocated[currentStackLevel] != allocatorStack[currentStackLevel])
break;
}
if (currentStackLevel == kRecordingDepthOfStackLevels)
break;
currentSite = currentSite->next;
}
// If this is a new site, then create a new entry from the table
if ((currentSite == NULL) && (gRemainingSitesInList != 0)) {
currentSite = gCurrentActiveSize++;
gRemainingSitesInList--;
currentSite->next = gActiveSizeHashTable[hashBin];
gActiveSizeHashTable[hashBin] = currentSite;
BlockMoveData(allocatorStack, currentSite->whoAllocated, sizeof(void *) * kRecordingDepthOfStackLevels);
currentSite->totalAllocations = 0;
currentSite->mallocs = 0;
currentSite->frees = 0;
currentSite->type = allocationType;
}
if (currentSite != NULL) {
currentSite->mallocs++;
currentSite->totalAllocations++;
}
return currentSite;
}
extern void VerifyMallocHeapIntegrity()
{
MemoryBlockHeader *currentBlock = gFirstAllocatedBlock;
// Walk the used block list, checking all of the headers.
while (currentBlock != NULL) {
MemoryBlockTrailer *trailer;
trailer = (MemoryBlockTrailer *)((char *)currentBlock + sizeof(MemoryBlockHeader) + currentBlock->blockSize);
if ((currentBlock->headerTag != kUsedBlockHeaderTag) || (trailer->trailerTag != kUsedBlockTrailerTag))
DebugStr("\pfastmem: malloc heap is corrupt!");
currentBlock = currentBlock->next;
}
}
enum {
kMaxInstructionScanDistance = 4096
};
void PrintRoutineNameFromPC(void *currentPC)
{
UInt32 instructionsToLook = kMaxInstructionScanDistance;
UInt32 *currentPCAsLong = (UInt32 *)currentPC;
UInt32 offset = 0;
char labelBuffer[256];
if (currentPC == NULL)
return;
while (instructionsToLook--) {
if (*currentPCAsLong == 0x4E800020) {
char stringLength = *((char *)currentPCAsLong + 21);
memset(labelBuffer, 0, 256);
BlockMoveData(((char *)currentPCAsLong + 22), labelBuffer, stringLength);
printf(labelBuffer);
break;
}
currentPCAsLong++;
}
instructionsToLook = kMaxInstructionScanDistance;
currentPCAsLong = (UInt32 *)currentPC;
while (instructionsToLook--) {
if (*currentPCAsLong == 0x7C0802A6) {
printf("+");
PrintHexNumber(offset - 4);
break;
}
currentPCAsLong--;
offset += 4;
}
}
#endif
#if STATS_MAC_MEMORY
void PrintEfficiency(UInt32 current, UInt32 total)
{
UInt32 efficiency = current * 1000 / total;
UInt32 efficiencyLow = efficiency / 10;
efficiency = efficiency/10;
printf("%ld.%ld", efficiency, efficiencyLow);
}
#endif
#if DEBUG_MAC_MEMORY
static void TagReferencedBlocks(void)
{
char *start = (char *) ApplicationZone();
char *end = (char *) GetApplLimit();
char *comb = start;
while (comb < end) {
MemoryBlockHeader *curCandidate = (MemoryBlockHeader *) comb;
if (curCandidate->headerTag == kFreeBlockHeaderTag ||
curCandidate->headerTag == kUsedBlockHeaderTag ||
curCandidate->headerTag == kRefdBlockHeaderTag) {
// we are pointing at a block header
// we don't want to consider the next or prev structure members
// therefore, skip ahead 8 bytes = 2 * sizeof(MemoryBlockHeader *)
comb += 8;
} else if ((char *) curCandidate->next > start &&
(char *) curCandidate->next < end) {
MemoryBlockHeader *referencedBlock;
referencedBlock = (MemoryBlockHeader *) ((char *) curCandidate->next - sizeof(MemoryBlockHeader));
if (referencedBlock->headerTag == kUsedBlockHeaderTag) {
// Bingo!!
// Marked the referenced block as being referenced.
// skip ahead 4 bytes (or should we be conservative and just skip 2 bytes?)
referencedBlock->headerTag = kRefdBlockHeaderTag; // TODO: insert kRefdBlockTrailerTag as well
comb += 4;
} else {
// False alarm
// it looked like a pointer, but it didn't point to the beginning of a valid used block.
// move forward two bytes and look some more
comb += 2;
}
} else {
// we aren't looking at anything interesting
// move forward two bytes and look some more.
comb += 2;
}
}
}
#endif
void DumpAllocHeap(void)
{
#if DEBUG_MAC_MEMORY
MemoryBlockHeader *currentBlock = gFirstAllocatedBlock;
#endif
#if STATS_MAC_MEMORY
UInt32 currentItem;
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("USAGE REPORT (MALLOC HEAP)\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("TYPE BLOCKS (A) BLOCKS (U) BLOCKS (F) MEMORY (A) MEMORY (U) EFFICIENCY\n");
printf("---- ---------- ---------- ---------- ---------- ---------- ----------\n");
printf("COMPACT ");
PrintHexNumber(gTotalFixedSizeCompactBlocksAllocated);
printf(" ");
PrintHexNumber(gTotalFixedSizeCompactBlocksUsed);
printf(" ");
PrintHexNumber(gTotalFixedSizeCompactBlocksAllocated - gTotalFixedSizeCompactBlocksUsed);
printf(" ");
PrintHexNumber(gTotalFixedSizeCompactAllocated);
printf(" ");
PrintHexNumber(gTotalFixedSizeCompactUsed);
printf(" ");
PrintEfficiency(gTotalFixedSizeCompactBlocksUsed, gTotalFixedSizeCompactBlocksAllocated);
printf("/");
PrintEfficiency(gTotalFixedSizeCompactUsed, gTotalFixedSizeCompactAllocated);
printf(" \n");
printf("FIXED ");
PrintHexNumber(gTotalFixedSizeFastBlocksAllocated);
printf(" ");
PrintHexNumber(gTotalFixedSizeFastBlocksUsed);
printf(" ");
PrintHexNumber(gTotalFixedSizeFastBlocksAllocated - gTotalFixedSizeFastBlocksUsed);
printf(" ");
PrintHexNumber(gTotalFixedSizeFastAllocated);
printf(" ");
PrintHexNumber(gTotalFixedSizeFastUsed);
printf(" ");
PrintEfficiency(gTotalFixedSizeFastBlocksUsed, gTotalFixedSizeFastBlocksAllocated);
printf("/");
PrintEfficiency(gTotalFixedSizeFastUsed, gTotalFixedSizeFastAllocated);
printf(" \n");
printf("HEAP ");
PrintHexNumber(0);
printf(" ");
PrintHexNumber(gTotalSmallHeapBlocksUsed);
printf(" ");
PrintHexNumber(0);
printf(" ");
PrintHexNumber(gTotalSmallHeapAllocated);
printf(" ");
PrintHexNumber(gTotalSmallHeapUsed);
printf(" ");
PrintEfficiency(gTotalSmallHeapUsed, gTotalSmallHeapAllocated);
printf(" \nTotal efficiency: ");
PrintEfficiency(gTotalSmallHeapUsed + gTotalFixedSizeFastUsed + gTotalFixedSizeCompactUsed,
gTotalSmallHeapAllocated + gTotalFixedSizeFastAllocated + gTotalFixedSizeCompactAllocated);
printf(" \n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("BLOCK DISTRIBUTION (MALLOC HEAP: ACTIVE)\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("SIZE +0x00 +0x01 +0x02 +0x03 TOTAL\n");
printf("---- ---------- ---------- ---------- ---------- ----------\n");
for (currentItem = 0; currentItem < 64; currentItem++) {
PrintHexNumber(currentItem * 4);
printf(" ");
PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4]);
printf(" ");
PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4 + 1]);
printf(" ");
PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4 + 2]);
printf(" ");
PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4 + 3]);
printf(" ");
PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4] +
gSmallAllocationActiveCountTable[currentItem * 4 + 1] +
gSmallAllocationActiveCountTable[currentItem * 4 + 2] +
gSmallAllocationActiveCountTable[currentItem * 4 + 3]);
printf("\n");
}
printf("Blocks Over 1K:");
PrintHexNumber(gSmallAllocationActiveCountTable[1024]);
printf("\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("BLOCK DISTRIBUTION (MALLOC HEAP: MAX)\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("SIZE +0x00 +0x01 +0x02 +0x03 TOTAL\n");
printf("---- ---------- ---------- ---------- ---------- ----------\n");
for (currentItem = 0; currentItem < 64; currentItem++) {
PrintHexNumber(currentItem * 4);
printf(" ");
PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4]);
printf(" ");
PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4 + 1]);
printf(" ");
PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4 + 2]);
printf(" ");
PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4 + 3]);
printf(" ");
PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4] +
gSmallAllocationMaxCountTable[currentItem * 4 + 1] +
gSmallAllocationMaxCountTable[currentItem * 4 + 2] +
gSmallAllocationMaxCountTable[currentItem * 4 + 3]);
printf("\n");
}
printf("Blocks Over 1K:");
PrintHexNumber(gSmallAllocationMaxCountTable[1024]);
printf("\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("BLOCK DISTRIBUTION (MALLOC HEAP: TOTAL)\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("SIZE +0x00 +0x01 +0x02 +0x03 TOTAL\n");
printf("---- ---------- ---------- ---------- ---------- ----------\n");
for (currentItem = 0; currentItem < 64; currentItem++) {
PrintHexNumber(currentItem * 4);
printf(" ");
PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4]);
printf(" ");
PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4 + 1]);
printf(" ");
PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4 + 2]);
printf(" ");
PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4 + 3]);
printf(" ");
PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4] +
gSmallAllocationTotalCountTable[currentItem * 4 + 1] +
gSmallAllocationTotalCountTable[currentItem * 4 + 2] +
gSmallAllocationTotalCountTable[currentItem * 4 + 3]);
printf("\n");
}
printf("Blocks Over 1K:");
PrintHexNumber(gSmallAllocationTotalCountTable[1024]);
printf("\n");
#endif
#if DEBUG_MAC_MEMORY
currentBlock = gFirstAllocatedBlock;
// Report leaks
if (gReportLeaks) {
AllocationSite *scanSite = gActiveSizeList;
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("LEAKS\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("TYPE ALLOCS FREES LEAKS STACK\n");
printf("------ ------ ----- ----- -----\n");
while (scanSite != gCurrentActiveSize) {
if (scanSite->mallocs > (scanSite->frees + 1)) {
UInt32 currentStackLevel;
switch (scanSite->type) {
case kAllocationSiteMalloc:
printf("MALLOC ");
break;
case kAllocationSiteNewHandle:
printf("HANDLE ");
break;
case kAllocationSiteNewPtr:
printf("POINTR ");
break;
default:
break;
}
PrintHexNumber(scanSite->mallocs);
printf("\t");
PrintHexNumber(scanSite->frees);
printf("\t");
PrintHexNumber(scanSite->totalAllocations - scanSite->frees);
printf("\t");
for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
PrintRoutineNameFromPC(scanSite->whoAllocated[currentStackLevel]);
if (currentStackLevel == kRecordingDepthOfStackLevels - 1)
printf("\n");
else
printf(",");
}
}
scanSite++;
}
printf("Outstanding Pointers:");
PrintHexNumber(gOutstandingPointers);
printf("\n");
printf("Outstanding Handles:");
PrintHexNumber(gOutstandingHandles);
printf("\n");
}
// Tag blocks which are referenced from somewhere in the heap.
if (gTagReferencedBlocks) {
TagReferencedBlocks();
}
// Dump out active blocks
if (gReportActiveBlocks) {
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("ACTIVE BLOCKS\n");
printf("********************************************************************************\n");
printf("********************************************************************************\n");
printf("ADDRESS NUMBER SIZE STACK\n");
printf("------- ------ ---- -----\n");
while (currentBlock) {
UInt32 currentStackLevel;
if (!gTagReferencedBlocks || currentBlock->headerTag == kUsedBlockHeaderTag) {
PrintHexNumber((UInt32)currentBlock);
printf("\t");
PrintHexNumber((UInt32)(currentBlock->blockNum));
printf("\t");
PrintHexNumber((UInt32)(currentBlock->blockSize));
printf("\t");
for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
PrintRoutineNameFromPC(currentBlock->whoAllocated[currentStackLevel]);
if (currentStackLevel == kRecordingDepthOfStackLevels - 1)
printf("\n");
else
printf(",");
}
}
currentBlock = currentBlock->next;
}
}
#endif
}
FreeMemoryBlockDescriptor StandardFreeDescriptor = { &StandardFree, 0 };
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark STANDARD ALLOCATOR (SLOW!)
void *StandardAlloc(size_t blockSize, void *refcon)
{
MemoryBlockHeader *newBlockHeader = (MemoryBlockHeader*)AllocateRawMemory(((blockSize + 3) & 0xFFFFFFFC) + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE);
if (newBlockHeader == NULL)
return NULL;
newBlockHeader->blockFreeRoutine = &StandardFreeDescriptor;
return (void *)((char *)newBlockHeader + sizeof(MemoryBlockHeader));
}
void StandardFree(void *block, void *refcon)
{
MemoryBlockHeader *blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
FreeRawMemory((Ptr)blockHeader);
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark FIXED SIZED ALLOCATOR (COMPACT)
UInt32 CountLeadingZeros(UInt32 value);
#if defined(powerc) || defined(_powerc)
asm UInt32 CountLeadingZeros(UInt32 value)
{
cntlzw r3, r3
blr
}
#else
UInt32 CountLeadingZeros(UInt32 value)
{
UInt32 whichZero = 0;
while ((value & 0x80000000) == 0) {
whichZero++;
value = value << 1;
}
return whichZero;
}
#endif
void *FixedSizeCompactAlloc(size_t blockSize, void *refcon)
{
FixedSizeCompactAllocationRoot *sizeRoot = (FixedSizeCompactAllocationRoot *)refcon;
FixedSizeCompactAllocationChunk *currentChunk = sizeRoot->firstChunk;
FreeMemoryBlockDescriptor *whichFreeDescriptor;
UInt32 whichBlockIsFree,
blockOffset;
MemoryBlockHeader *blockHeader;
// Try to find an existing chunk with a free block.
while ((currentChunk != NULL) && (currentChunk->chunkUsage == 0))
currentChunk = currentChunk->next;
// If there is not chunk with a free block, then create
// a new one and put it at the beginning of the chunk list.
if (currentChunk == NULL) {
currentChunk = (FixedSizeCompactAllocationChunk *)AllocateRawMemory(sizeof(FixedSizeCompactAllocationChunk) +
(32 * (sizeRoot->blockSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE)));
if (currentChunk == NULL) {
return NULL;
}
#if STATS_MAC_MEMORY
gTotalFixedSizeCompactAllocated += sizeof(FixedSizeCompactAllocationChunk) +
(32 * (sizeRoot->blockSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE));
gTotalFixedSizeCompactBlocksAllocated += 32;
#endif
currentChunk->root = sizeRoot;
currentChunk->chunkUsage = 0xFFFFFFFF;
currentChunk->next = sizeRoot->firstChunk;
sizeRoot->firstChunk = currentChunk;
}
// Find the block in the chunk that is free.
whichBlockIsFree = CountLeadingZeros(currentChunk->chunkUsage);
// Claim the block
currentChunk->chunkUsage &= ~(0x80000000 >> whichBlockIsFree);
// Install the free descriptor
whichFreeDescriptor = sizeRoot->freeDescriptorTable + whichBlockIsFree;
blockOffset = (UInt32)(whichFreeDescriptor->refcon) & 0x0000FFFF;
blockHeader = (MemoryBlockHeader *)((char *)currentChunk + blockOffset);
blockHeader->blockFreeRoutine = whichFreeDescriptor;
#if STATS_MAC_MEMORY
gTotalFixedSizeCompactUsed += sizeRoot->blockSize;
gTotalFixedSizeCompactBlocksUsed++;
#endif
return (void *)((char *)blockHeader + sizeof(MemoryBlockHeader));
}
void FixedSizeCompactFree(void *block, void *refcon)
{
MemoryBlockHeader *blockHeader;
UInt32 blockNumber;
UInt32 blockOffset;
FixedSizeCompactAllocationChunk *thisChunk;
FixedSizeCompactAllocationChunk *currentChunk,
**previousChunk = NULL;
FixedSizeCompactAllocationRoot *root;
blockNumber = (UInt32)refcon >> 16;
blockOffset = (UInt32)refcon & 0xFFFF;
// Find the block header and the
blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
thisChunk = (FixedSizeCompactAllocationChunk *)((char *)blockHeader - blockOffset);
// Mark the block as unused.
thisChunk->chunkUsage |= (0x80000000 >> blockNumber);
// If the block is completely unused, then reclaim it
root = thisChunk->root;
currentChunk = root->firstChunk;
if ((thisChunk->chunkUsage == 0xFFFFFFFF) && (currentChunk != thisChunk)) {
while (currentChunk != thisChunk) {
previousChunk = &currentChunk->next;
currentChunk = currentChunk->next;
}
*previousChunk = currentChunk->next;
FreeRawMemory((Ptr)thisChunk);
}
#if STATS_MAC_MEMORY
gTotalFixedSizeCompactUsed -= root->blockSize;
gTotalFixedSizeCompactBlocksUsed--;
#endif
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark FIXED SIZED ALLOCATOR (FAST)
void *FixedSizeFastAlloc(size_t blockSize, void *refcon)
{
FixedSizeFastAllocationRoot *sizeRoot = (FixedSizeFastAllocationRoot *)refcon;
FixedSizeFastAllocationChunk *currentChunk;
FixedSizeFastMemoryBlockHeader *ourBlock;
// If the free list is empty, then we have to allocate another chunk.
if (sizeRoot->firstFree == NULL) {
FixedSizeFastMemoryBlockHeader *currentBlock;
UInt32 currentBlockNum;
#if STATS_MAC_MEMORY
gTotalFixedSizeFastAllocated += sizeof(FixedSizeFastAllocationChunk) +
(sizeRoot->blocksPerChunk * (sizeRoot->blockSize + sizeof(FixedSizeFastMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE));
gTotalFixedSizeFastBlocksAllocated += sizeRoot->blocksPerChunk;
#endif
currentChunk = (FixedSizeFastAllocationChunk *)AllocateRawMemory(sizeof(FixedSizeFastAllocationChunk) +
(sizeRoot->blocksPerChunk * (sizeRoot->blockSize + sizeof(FixedSizeFastMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE)));
if (currentChunk == NULL) {
return NULL;
}
currentChunk->next = sizeRoot->firstChunk;
sizeRoot->firstChunk = currentChunk;
// Thread all of the chunks blocks onto the free list.
currentBlock = (FixedSizeFastMemoryBlockHeader *)((char *)currentChunk + sizeof(FixedSizeFastAllocationChunk));
for (currentBlockNum = 0; currentBlockNum < sizeRoot->blocksPerChunk; currentBlockNum++) {
currentBlock->nextFree = sizeRoot->firstFree;
currentBlock->realHeader.blockFreeRoutine = sizeRoot->freeDescriptor;
sizeRoot->firstFree = currentBlock;
currentBlock = (FixedSizeFastMemoryBlockHeader *)((char *)currentBlock +
sizeof(FixedSizeFastMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE + sizeRoot->blockSize);
}
}
// Take the first block off of the free list. It is the one
// we will use.
ourBlock = sizeRoot->firstFree;
sizeRoot->firstFree = ourBlock->nextFree;
#if STATS_MAC_MEMORY
gTotalFixedSizeFastBlocksUsed++;
gTotalFixedSizeFastUsed += sizeRoot->blockSize;
#endif
return (void *)((char *)ourBlock + sizeof(FixedSizeFastMemoryBlockHeader));
}
void FixedSizeFastFree(void *block, void *refcon)
{
FixedSizeFastMemoryBlockHeader *deadBlock;
FixedSizeFastAllocationRoot *sizeRoot = (FixedSizeFastAllocationRoot *)refcon;
deadBlock = (FixedSizeFastMemoryBlockHeader *)((char *)block - sizeof(FixedSizeFastMemoryBlockHeader));
// Put the block on the free list.
deadBlock->nextFree = sizeRoot->firstFree;
sizeRoot->firstFree = deadBlock;
#if STATS_MAC_MEMORY
gTotalFixedSizeFastBlocksUsed--;
gTotalFixedSizeFastUsed -= sizeRoot->blockSize;
#endif
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark SMALL HEAP ALLOCATOR
#define SmallHeapBlockToMemoryPtr(x) ((void *)((Ptr)x + sizeof(SmallHeapBlock)))
#define MemoryPtrToSmallHeapBlock(x) ((SmallHeapBlock *)((Ptr)x - sizeof(SmallHeapBlock)))
#define NextSmallHeapBlock(x) ((SmallHeapBlock *)((Ptr)x + (x->blockSize & ~kBlockInUseFlag) + sizeof(SmallHeapBlock) + MEMORY_BLOCK_TAILER_SIZE))
#define PrevSmallHeapBlock(x) (x->prevBlock)
#define SmallHeapBlockInUse(x) ((x->blockSize & kBlockInUseFlag) != 0)
#define SmallHeapBlockNotInUse(x) ((x->blockSize & kBlockInUseFlag) == 0)
#define TotalSmallHeapBlockUsage(x) ((x->blockSize & ~kBlockInUseFlag) + sizeof(SmallHeapBlock) + MEMORY_BLOCK_TAILER_SIZE)
#define AddSmallHeapBlockToFreeList(r, x) \
{ \
UInt32 blockSize = x->blockSize & ~kBlockInUseFlag; \
UInt32 nextBlockBin; \
SmallHeapBlock *tempBlock; \
x->blockSize = blockSize ; \
nextBlockBin = (blockSize - kDefaultSmallHeadMinSize) >> 2; \
x->info.freeInfo.prevFree = NULL; \
if (blockSize > kMaximumBinBlockSize) { \
tempBlock = r->overflow; \
x->info.freeInfo.nextFree = tempBlock; \
if (tempBlock != NULL) \
tempBlock->info.freeInfo.prevFree = x; \
r->overflow = x; \
} \
else { \
tempBlock = r->bins[nextBlockBin]; \
x->info.freeInfo.nextFree = tempBlock; \
if (tempBlock != NULL) \
tempBlock->info.freeInfo.prevFree = x; \
r->bins[nextBlockBin] = x; \
} \
}
#define RemoveSmallHeapBlockFromFreeList(r, x) \
{ \
SmallHeapBlock *nextFree, \
*prevFree; \
UInt32 blockSize = x->blockSize; \
UInt32 nextBlockBin; \
x->blockSize |= kBlockInUseFlag; \
nextBlockBin = (blockSize - kDefaultSmallHeadMinSize) >> 2; \
nextFree = x->info.freeInfo.nextFree; \
prevFree = x->info.freeInfo.prevFree; \
if (nextFree != NULL) \
nextFree->info.freeInfo.prevFree = prevFree; \
if (prevFree != NULL) \
prevFree->info.freeInfo.nextFree = nextFree; \
else \
if (blockSize > kMaximumBinBlockSize) \
r->overflow = nextFree; \
else \
r->bins[nextBlockBin] = nextFree; \
}
#if DEBUG_MAC_MEMORY
void SmallHeapCheck(SmallHeapBlock *fromBlock)
{
SmallHeapBlock *previousBlock = NULL;
// Go to the first block in the list
while (fromBlock->prevBlock != NULL)
fromBlock = fromBlock->prevBlock;
// Walk the list
while (fromBlock) {
if (fromBlock->blockSize == 0xFFFFFFFF)
break;
if (previousBlock != NULL) {
if (previousBlock != PrevSmallHeapBlock(fromBlock)) {
DebugStr("\pSmall Heap Corrupt");
break;
}
}
previousBlock = fromBlock;
fromBlock = NextSmallHeapBlock(fromBlock);
}
}
#endif
void *SmallHeapAlloc(size_t blockSize, void *refcon)
{
SmallHeapRoot *heapRoot = (SmallHeapRoot *)refcon;
UInt32 startingBinNum,
remainingBins;
SmallHeapBlock **currentBin;
SmallHeapBlock *currentBinValue;
SmallHeapBlock *newFreeOverflowBlock,
*blockToCarve,
*newRawBlockHeader,
*newRawBlockTrailer;
// Round up allocation to nearest 4 bytes.
blockSize = (blockSize + 3) & 0xFFFFFFFC;
// Try to find the best fit in one of the bins.
startingBinNum = (blockSize - kDefaultSmallHeadMinSize) >> 2;
currentBin = heapRoot->bins + startingBinNum;
currentBinValue = *currentBin;
// If the current bin has something in it,
// then use it for our allocation.
if (currentBinValue != NULL) {
RemoveSmallHeapBlockFromFreeList(heapRoot, currentBinValue);
currentBinValue->info.inUseInfo.freeProc.blockFreeRoutine = heapRoot->blockFreeRoutine;
#if STATS_MAC_MEMORY
gTotalSmallHeapBlocksUsed++;
gTotalSmallHeapUsed += TotalSmallHeapBlockUsage(currentBinValue);
#endif
return SmallHeapBlockToMemoryPtr(currentBinValue);
}
// Otherwise, try to carve up an existing larger block.
remainingBins = kDefaultSmallHeapBins - startingBinNum - 1;
blockToCarve = NULL;
while (remainingBins--) {
currentBin++;
currentBinValue = *currentBin;
if (currentBinValue != NULL) {
blockToCarve = currentBinValue;
break;
}
}
// Try carving up up a block from the overflow bin.
if (blockToCarve == NULL)
blockToCarve = heapRoot->overflow;
doCarve:
while (blockToCarve != NULL) {
SInt32 blockToCarveSize = blockToCarve->blockSize;
// If the owerflow block is big enough to house the
// allocation, then use it.
if (blockToCarveSize >= blockSize) {
SInt32 leftovers;
// Remove the block from the free list... we will
// be using it for the allocation...
RemoveSmallHeapBlockFromFreeList(heapRoot, blockToCarve);
// If taking our current allocation out of
// the overflow block would still leave enough
// room for another allocation out of the
// block, then split the block up.
leftovers = blockToCarveSize - sizeof(SmallHeapBlock) - MEMORY_BLOCK_TAILER_SIZE - blockSize;
if (leftovers >= kDefaultSmallHeadMinSize) {
SmallHeapBlock *leftoverBlock;
SmallHeapBlock *nextBlock;
nextBlock = NextSmallHeapBlock(blockToCarve);
// Create a new block out of the leftovers
leftoverBlock = (SmallHeapBlock *)((char *)blockToCarve +
sizeof(SmallHeapBlock) + blockSize + MEMORY_BLOCK_TAILER_SIZE);
// Add the block to linked list of blocks in this raw
// allocation chunk.
nextBlock->prevBlock = leftoverBlock;
blockToCarve->blockSize = blockSize | kBlockInUseFlag;
leftoverBlock->prevBlock = blockToCarve;
leftoverBlock->blockSize = leftovers;
// And add the block to a free list, which will either be
// one of the sized bins or the overflow list, depending
// on its size.
AddSmallHeapBlockToFreeList(heapRoot, leftoverBlock);
}
blockToCarve->info.inUseInfo.freeProc.blockFreeRoutine = heapRoot->blockFreeRoutine;
#if STATS_MAC_MEMORY
gTotalSmallHeapBlocksUsed++;
gTotalSmallHeapUsed += TotalSmallHeapBlockUsage(blockToCarve);
#endif
return SmallHeapBlockToMemoryPtr(blockToCarve);
}
blockToCarve = blockToCarve->info.freeInfo.nextFree;
}
// No existing block was suitable. We need to allocate
// some raw memory to try again.
#if STATS_MAC_MEMORY
gTotalSmallHeapAllocated += kSmallHeapSize;
#endif
newRawBlockHeader = (SmallHeapBlock *)AllocateRawMemory(kSmallHeapSize);
if (newRawBlockHeader == NULL) {
// If all else fails, try allocating out of the NewPtr heap.
return StandardAlloc(blockSize, 0);
}
// The first few bytes of the block are a dummy header
// which is a block of size zero that is always alloacted.
// This allows our coalesce code to work without modification
// on the edge case of coalescing the first real block.
newRawBlockHeader->prevBlock = NULL;
newRawBlockHeader->blockSize = kBlockInUseFlag;
newRawBlockHeader->info.inUseInfo.freeProc.blockFreeRoutine = NULL;
newFreeOverflowBlock = NextSmallHeapBlock(newRawBlockHeader);
newFreeOverflowBlock->prevBlock = newRawBlockHeader;
newFreeOverflowBlock->blockSize = kSmallHeapSize - 3 * sizeof(SmallHeapBlock) - 3 * MEMORY_BLOCK_TAILER_SIZE;
// The same is true for the last few bytes in the block
// as well.
newRawBlockTrailer = (SmallHeapBlock *)(((Ptr)newRawBlockHeader) + kSmallHeapSize - sizeof(SmallHeapBlock) - MEMORY_BLOCK_TAILER_SIZE);
newRawBlockTrailer->prevBlock = newFreeOverflowBlock;
newRawBlockTrailer->blockSize = kBlockInUseFlag | 0xFFFFFFFF;
newRawBlockTrailer->info.inUseInfo.freeProc.blockFreeRoutine = NULL;
AddSmallHeapBlockToFreeList(heapRoot, newFreeOverflowBlock);
blockToCarve = newFreeOverflowBlock;
// Try again.
goto doCarve;
}
void SmallHeapFree(void *address, void *refcon)
{
SmallHeapRoot *heapRoot = (SmallHeapRoot *)refcon;
SmallHeapBlock *deadBlock,
*prevBlock,
*nextBlock;
deadBlock = MemoryPtrToSmallHeapBlock(address);
#if STATS_MAC_MEMORY
gTotalSmallHeapBlocksUsed--;
gTotalSmallHeapUsed -= TotalSmallHeapBlockUsage(deadBlock);
#endif
// If the block after us is free, then coalesce with it.
nextBlock = NextSmallHeapBlock(deadBlock);
prevBlock = PrevSmallHeapBlock(deadBlock);
if (SmallHeapBlockNotInUse(nextBlock)) {
RemoveSmallHeapBlockFromFreeList(heapRoot, nextBlock);
deadBlock->blockSize += TotalSmallHeapBlockUsage(nextBlock);
nextBlock = NextSmallHeapBlock(nextBlock);
}
// If the block before us is free, then coalesce with it.
if (SmallHeapBlockNotInUse(prevBlock)) {
RemoveSmallHeapBlockFromFreeList(heapRoot, prevBlock);
prevBlock->blockSize = ((Ptr)nextBlock - (Ptr)prevBlock) - sizeof(SmallHeapBlock) - MEMORY_BLOCK_TAILER_SIZE;
AddSmallHeapBlockToFreeList(heapRoot, prevBlock);
deadBlock = prevBlock;
}
else {
AddSmallHeapBlockToFreeList(heapRoot, deadBlock);
}
nextBlock->prevBlock = deadBlock;
}
//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark TRACKING MEMORY MANAGER MEMORY
#if DEBUG_MAC_MEMORY
#if GENERATINGPOWERPC
enum {
kFigmentBlockHashTableSize = 128,
kFigmentBlockTotalTrackedBlocks = 8192
};
typedef struct FigmentBlockInformation FigmentBlockInformation;
struct FigmentBlockInformation {
FigmentBlockInformation *next;
void *allocation;
AllocationSite *allocator;
};
enum {
uppNewHandleProcInfo = kRegisterBased
| RESULT_SIZE(SIZE_CODE(sizeof(Handle)))
| REGISTER_RESULT_LOCATION(kRegisterA0)
| REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
| REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(Size))),
uppNewPtrProcInfo = kRegisterBased
| RESULT_SIZE(SIZE_CODE(sizeof(Handle)))
| REGISTER_RESULT_LOCATION(kRegisterA0)
| REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
| REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(Size))),
uppDisposeHandleProcInfo = kRegisterBased
| REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
| REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Handle))),
uppDisposePtrProcInfo = kRegisterBased
| REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
| REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Ptr)))
};
extern pascal Handle NewHandlePatch(UInt16 trapWord, Size byteCount);
extern pascal Ptr NewPtrPatch(UInt16 trapWord, Size byteCount);
extern pascal void DisposeHandlePatch(UInt16 trapWord, Handle deadHandle);
extern pascal void DisposePtrPatch(UInt16 trapWord, Ptr deadPtr);
RoutineDescriptor gNewHandlePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppNewHandleProcInfo, &NewHandlePatch);
RoutineDescriptor gNewPtrPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppNewPtrProcInfo, &NewPtrPatch);
RoutineDescriptor gDisposeHandlePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppDisposeHandleProcInfo, &DisposeHandlePatch);
RoutineDescriptor gDisposePtrPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppDisposePtrProcInfo, &DisposePtrPatch);
UniversalProcPtr gNewHandlePatchCallThru = NULL;
UniversalProcPtr gNewPtrPatchCallThru = NULL;
UniversalProcPtr gDisposeHandlePatchCallThru = NULL;
UniversalProcPtr gDisposePtrPatchCallThru = NULL;
Ptr figmentAllocationList = NULL;
FigmentBlockInformation *figmentAllocationListHash[kFigmentBlockHashTableSize];
FigmentBlockInformation *figmentAllocationFreeList = NULL;
void AddBlockToActiveList(void *newBlock, AllocationSite *alocationSite)
{
// If we don't already have a block to store the list
// of our active figment blocks, then create one.
if (figmentAllocationList == NULL) {
UInt32 currentBlock;
figmentAllocationList = (Ptr)CallOSTrapUniversalProc(gNewPtrPatchCallThru, uppNewPtrProcInfo, 0x031E, kFigmentBlockTotalTrackedBlocks * sizeof(FigmentBlockInformation));
// Thread all of the new blocks onto the free list.
for (currentBlock = 0; currentBlock < kFigmentBlockTotalTrackedBlocks; currentBlock++) {
FigmentBlockInformation *currentBlockPtr;
currentBlockPtr = (FigmentBlockInformation * )figmentAllocationList + currentBlock;
currentBlockPtr->next = figmentAllocationFreeList;
figmentAllocationFreeList = currentBlockPtr;
}
}
// If we have a free block on the free list, use it
// to store the information about this allocation.
if (figmentAllocationFreeList != NULL) {
UInt32 hashKey = ((UInt32)newBlock >> 2) % kFigmentBlockHashTableSize;
FigmentBlockInformation *blockToUse;
// Remove the block from the free list.
blockToUse = figmentAllocationFreeList;
figmentAllocationFreeList = blockToUse->next;
// Add it to the hash table of active blocks in the appropriate place.
blockToUse->next = figmentAllocationListHash[hashKey];
figmentAllocationListHash[hashKey] = blockToUse;
// And fill in its relevant information.
blockToUse->allocation = newBlock;
blockToUse->allocator = alocationSite;
}
}
void RemoveBlockFromActiveList(void *deadBlock)
{
UInt32 hashKey = ((UInt32)deadBlock >> 2) % kFigmentBlockHashTableSize;
FigmentBlockInformation *blockToUse;
FigmentBlockInformation **previousBlockToUse = NULL;
blockToUse = figmentAllocationListHash[hashKey];
previousBlockToUse = figmentAllocationListHash + hashKey;
while (blockToUse) {
if (blockToUse->allocation == deadBlock) {
if (blockToUse->allocator != NULL)
blockToUse->allocator->frees++;
*previousBlockToUse = blockToUse->next;
blockToUse->next = figmentAllocationFreeList;
figmentAllocationFreeList = blockToUse;
break;
}
previousBlockToUse = &(blockToUse->next);
blockToUse = blockToUse->next;
}
}
void GetCurrentNativeStackTrace(void **stackCrawl)
{
void *currentSP;
void *interfaceLibSP;
void *callerFirstStackFrame;
void *callerSP;
UInt32 stackFrameLevel;
UInt8 isPowerPC = true;
memset(stackCrawl, 0, sizeof(void *) * kRecordingDepthOfStackLevels);
#if !GENERATINGPOWERPC
return;
#endif
// If the current SP is not in our heap, then bail (OT is evil).
#if GENERATINGPOWERPC
currentSP = GetCurrentStackPointer();
#endif
if ((currentSP > gOurApplicationHeapMax) || (currentSP < gOurApplicationHeapBase))
return;
interfaceLibSP = *((void **)currentSP);
callerFirstStackFrame = interfaceLibSP;
// Walk up the stack until we get the first frame
// which is actually in our executable's heap... note that
// this only works if VM is OFF!
while (1) {
void *nextFrame;
if ((nextFrame > gOurApplicationHeapMax) || (nextFrame < gOurApplicationHeapBase))
return;
// Walk the stack differently whether we are at a
// PowerPC or 68K frame...
if (isPowerPC) {
void *framePC;
// If we are PowerPC, check to see if the PC
// corresponding to the current frame is in the
// the app's code space. If so, then we are
// done and we break out.
framePC = *((void **)callerFirstStackFrame + 2);
if ((framePC < gOurApplicationHeapMax) && (framePC > gOurApplicationHeapBase))
break;
// Otherwise, check the pointer to the next
// stack frame. If its low bit is set, then
// it is a mixed-mode switch frame, and
// we need to switch to 68K frames.
nextFrame = *((void **)callerFirstStackFrame);
if (((UInt32)nextFrame & 0x00000001) != 0) {
callerFirstStackFrame = (void *)((UInt32)nextFrame & 0xFFFFFFFE);
isPowerPC = false;
} else
callerFirstStackFrame = nextFrame;
}
else {
// 68K case: If the location immediately above the stack
// frame pointer is -1, then we are at a switch frame,
// move back to PowerPC.
if (*((UInt32 *)callerFirstStackFrame - 1) == 0xFFFFFFFF)
isPowerPC = true;
callerFirstStackFrame = *((void **)callerFirstStackFrame);
}
}
callerSP = callerFirstStackFrame;
for (stackFrameLevel = 0; stackFrameLevel < kRecordingDepthOfStackLevels; stackFrameLevel++) {
if ((callerSP > gOurApplicationHeapMax) || (callerSP < gOurApplicationHeapBase))
return;
stackCrawl[stackFrameLevel] = *(((void **)callerSP) + 2);
callerSP = *((void **)callerSP);
}
}
extern pascal Handle NewHandlePatch(UInt16 trapWord, Size byteCount)
{
void *stackCrawl[kRecordingDepthOfStackLevels];
Handle resultHandle;
AllocationSite *allocationSite;
if (gTrackLeaks)
GetCurrentNativeStackTrace(stackCrawl);
resultHandle = (Handle)CallOSTrapUniversalProc(gNewHandlePatchCallThru, uppNewHandleProcInfo, trapWord, byteCount);
if (gTrackLeaks && resultHandle) {
allocationSite = AddAllocationSite(kAllocationSiteNewHandle, stackCrawl);
AddBlockToActiveList((void *)resultHandle, allocationSite);
gOutstandingHandles++;
}
return resultHandle;
}
extern pascal Ptr NewPtrPatch(UInt16 trapWord, Size byteCount)
{
void *stackCrawl[kRecordingDepthOfStackLevels];
Ptr resultPtr;
AllocationSite *allocationSite;
if (gTrackLeaks)
GetCurrentNativeStackTrace(stackCrawl);
resultPtr = (Ptr)CallOSTrapUniversalProc(gNewPtrPatchCallThru, uppNewPtrProcInfo, trapWord, byteCount);
if (gTrackLeaks && resultPtr) {
allocationSite = AddAllocationSite(kAllocationSiteNewPtr, stackCrawl);
AddBlockToActiveList((void *)resultPtr, allocationSite);
gOutstandingPointers++;
}
return resultPtr;
}
pascal void DisposeHandlePatch(UInt16 trapWord, Handle deadHandle)
{
if (gTrackLeaks) {
RemoveBlockFromActiveList(deadHandle);
gOutstandingHandles--;
}
CallOSTrapUniversalProc(gDisposeHandlePatchCallThru, uppDisposeHandleProcInfo, trapWord, deadHandle);
}
pascal void DisposePtrPatch(UInt16 trapWord, Ptr deadPtr)
{
if (gTrackLeaks) {
RemoveBlockFromActiveList(deadPtr);
gOutstandingPointers--;
}
CallOSTrapUniversalProc(gDisposePtrPatchCallThru, uppDisposePtrProcInfo, trapWord, deadPtr);
}
void InstallMemoryManagerPatches(void)
{
ProcessSerialNumber thisProcess = { 0, kCurrentProcess };
ProcessInfoRec processInfo;
processInfo.processInfoLength = sizeof(processInfo);
processInfo.processName = NULL;
processInfo.processAppSpec = NULL;
GetProcessInformation(&thisProcess, &processInfo);
gOurApplicationHeapBase = processInfo.processLocation;
gOurApplicationHeapMax = (Ptr)gOurApplicationHeapBase + processInfo.processSize;
gNewHandlePatchCallThru = GetOSTrapAddress(0x0122);
SetOSTrapAddress(&gNewHandlePatchRD, 0x0122);
gNewPtrPatchCallThru = GetOSTrapAddress(0x011E);
SetOSTrapAddress(&gNewPtrPatchRD, 0x011E);
gDisposeHandlePatchCallThru = GetOSTrapAddress(0x023);
SetOSTrapAddress(&gDisposeHandlePatchRD, 0x023);
gDisposePtrPatchCallThru = GetOSTrapAddress(0x001F);
SetOSTrapAddress(&gDisposePtrPatchRD, 0x001F);
}
#else
void InstallMemoryManagerPatches(void)
{
}
#endif
#endif