#include <stdio.h>
#include "pure.h"
#include <windows.h>
// Forward declaration
void func_that_uses_custom_alloc();
int main()
{
func_that_uses_custom_alloc();
sreturn 0;
}
// Size of a virtual memory region that will contain blocks not managed by
// the windows heap
#define REGIONSIZE 0x10000
// Pointer to the virtual memory region itself
void *lpRegion;
// Structure forming an ordered list of allocated memory blocks
typedef struct Block
{
unsigned int base;
unsigned int limit;
struct Block *next;
} Block;
Block * FirstBlock;
// A really basic allocator
void *custom_alloc(size_t size)
{
void *retVal = NULL;
static unsigned int allocCount;
Block *NewBlock, *ThisBlock, *NextBlock;
unsigned int lastLimit = (unsigned int) lpRegion;
unsigned int newBase;
if (!allocCount)
{
FirstBlock = NULL;
}
if (FirstBlock)
{
// Find the first space big enough for the new block.
ThisBlock = FirstBlock;
NextBlock = ThisBlock->next;
lastLimit = ((unsigned int) lpRegion) - 1;
newBase = 0;
while (ThisBlock)
{
if (ThisBlock->base - lastLimit > size)
{
// Fit the new block in before this one.
newBase = lastLimit + 1;
break;
}
else if (!NextBlock)
{
// See if the new block will fit at the end.
if (size < (((unsigned int) lpRegion) +
REGIONSIZE - ThisBlock->limit))
{
newBase = ThisBlock->limit + 1;
}
break;
}
lastLimit = ThisBlock->limit;
ThisBlock = NextBlock;
NextBlock = ThisBlock->next;
}
}
else if (size < ((unsigned int) lpRegion) + REGIONSIZE)
{
newBase = (unsigned int) lpRegion;
NextBlock = NULL;
}
if (newBase)
{
// Remember the base and extent of the new block.
NewBlock = (Block *) malloc(sizeof(Block));
NewBlock->base = newBase;
NewBlock->limit = newBase + size - 1;
NewBlock->next = NextBlock;
if (!FirstBlock)
{
// The list was empty.
FirstBlock = NewBlock;
}
else if (NewBlock->base < FirstBlock->base)
{
// The new block appears at the head of the list.
FirstBlock = NewBlock;
FirstBlock->next = ThisBlock;
}
else
{
// The new block belongs anywhere else in the list.
ThisBlock->next = NewBlock;
}
allocCount++;
retVal = (void *) newBase;
}
return retVal;
}
// A really basic deallocator
void custom_dealloc(void *address)
{
Block *ThisBlock, *LastBlock;
if ((unsigned int) address < ((unsigned int) lpRegion) ||
(unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)
{
printf("Bad dealloc at ", (unsigned int) address);
}
if (FirstBlock && FirstBlock->base)
{
// Walk through the blocks, starting at the head of the list.
ThisBlock = FirstBlock;
while (ThisBlock)
{
if (ThisBlock->base == (unsigned int) address)
{
if (ThisBlock == FirstBlock)
{
// Free the first block.
FirstBlock = ThisBlock->next;
}
else
{
// Free this one.
LastBlock->next = ThisBlock->next;
}
free(ThisBlock);
break;
}
LastBlock = ThisBlock;
ThisBlock = ThisBlock->next;
}
}
}
// A really basic reallocator
void * custom_realloc(void *address, size_t size)
{
void *retVal = NULL;
Block *NewBlock, *OldBlock, *LastOldBlock;
unsigned int newBase;
size_t bytesToCopy;
if ((unsigned int) address < ((unsigned int) lpRegion) ||
(unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)
{
printf("Bad realloc at ", (unsigned int) address);
}
if (FirstBlock && FirstBlock->base)
{
// Find the specified old block in the list.
OldBlock = FirstBlock;
while (OldBlock)
{
if (OldBlock->base == (unsigned int) address)
{
break;
}
LastOldBlock = OldBlock;
OldBlock = OldBlock->next;
}
if (OldBlock)
{
// Allocate the new block (this may update OldBlock->next).
newBase = (unsigned int) custom_alloc(size);
// Copy as many bytes as we have in the smaller of these blocks.
bytesToCopy = OldBlock->limit - OldBlock->base + 1;
if (bytesToCopy > size)
{
bytesToCopy = size;
}
memcpy((void *) newBase, (void *) OldBlock->base, bytesToCopy);
// Clean up the old block.
if (OldBlock == FirstBlock)
{
OldBlock = OldBlock->next; // Free the first block.
}
else
{
LastOldBlock->next = OldBlock->next; // Free this one.
}
free(OldBlock);
retVal = (void *) newBase;
}
}
reurn retVal;
}
// A really basic "get block size" routine
size_t custom_getsize(void *address)
{
int retVal = 0;
Block *ThisBlock;
if ((unsigned int) address < ((unsigned int) lpRegion) ||
(unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)
{
printf("Bad getsize at ", (unsigned int) address);
}
if (FirstBlock && FirstBlock->base)
{
// Get the size of the specified block in the list.
ThisBlock = FirstBlock;
while (ThisBlock)
{
if (ThisBlock->base == (unsigned int) address)
{
retVal = ThisBlock->limit - ThisBlock->base + 1;
break;
}
ThisBlock = ThisBlock->next;
}
}
return retVal;
}
// Returns non-zero if the address is in a block, zero otherwise.
int custom_didalloc(void *address)
{
int retVal = 0;
Block *ThisBlock;
if ((unsigned int) address < ((unsigned int) lpRegion) ||
(unsigned int) address > ((unsigned int) lpRegion) + REGIONSIZE)
{
printf("Bad didalloc at ", (unsigned int) address);
}
if (FirstBlock && FirstBlock->base)
{
// Get the size of the specified block in the list.
ThisBlock = FirstBlock;
while (ThisBlock)
{
if (ThisBlock->base <= (unsigned int) address &&
ThisBlock->base + ThisBlock->limit >= (unsigned int) address)
{
retVal = (unsigned int) address - ThisBlock->base;
break;
}
ThisBlock = ThisBlock->next;
}
}
return retVal;
}
// Not implemented
void custom_compact(void)
{
return;
}
// The following functions are used to "wrap" the above custom memory
// allocation routines when Purify is running. They are written to be
// understandable. In production code, the PurifyPre.../Post... API calls
// could be placed in the custom memory allocation routines themselves.
// When Purify is running, these calls adjust for red zones and enable
// Purify to track the allocated blocks for error reporting purposes.
// The Purify API calls do not adjust the passed-in and returned values
// unless Purify is running.
//
void *Allocate(size_t userSize)
{
size_t adjustedSize = PurifyPreAlloc(userSize, /* dwFlags = */ 0);
void * lpBlock = (char *) custom_alloc(adjustedSize);
return PurifyPostAlloc(lpBlock, /* dwFlags = */ 0);
}
void *Reallocate(void *userAddress, size_t userSize)
{
void *adjustedAddress = userAddress;
size_t adjustedSize = PurifyPreRealloc(userAddress, userSize, (void **) &adjustedAddress, /* dwFlags = */ 0);
adjustedAddress = (char *) custom_realloc(adjustedAddress, adjustedSize);
return (void *) PurifyPostRealloc(adjustedAddress, /* dwFlags = */ 0);
}
void Deallocate(void *userAddress)
{
void *adjustedAddress = PurifyPreFree(userAddress, /* dwFlags = */ 0);
custom_dealloc(adjustedAddress);
PurifyPostFree(/* dwFlags = */ 0);
return;
}
size_t GetBlockSize(void *userAddress)
{
void *adjustedAddress = PurifyPreGetSize(userAddress, /* dwFlags = */ 0);
size_t adjustedSize = custom_getsize(adjustedAddress);
return PurifyPostGetSize(adjustedSize, /* dwFlags = */ 0);
}
int DidAlloc(void *userAddress)
{
void *adjustedAddress = PurifyPreDidAlloc(userAddress, /* dwFlags = */ 0);
int fActual = custom_didalloc(adjustedAddress);
return PurifyPostDidAlloc(adjustedAddress, fActual, /* dwFlags = */ 0);
}
void Compact(void)
{
PurifyPreHeapMinimize();
custom_compact();
PurifyPostHeapMinimize();
}
// This is an example function that uses the above Purify-aware custom memory
// allocation routines.
//
void func_that_uses_custom_alloc()
{
char *lpvBlock, *lpvTrueBlock;
size_t vBlockSize, vBlockSavedSize;
int indicator;
lpRegion = VirtualAlloc(/* lpAddress = */ NULL, REGIONSIZE, MEM_COMMIT, PAGE_READWRITE);
// Allocate a block (note that Purify will add red zones).
vBlockSize = 0x20;
lpvBlock = (char *) Allocate(vBlockSize);
// Leak that first block and create a second one.
lpvBlock = (char *) Allocate(vBlockSize);
// Initialize part of the second block.
if (DidAlloc(lpvBlock))
{
lpvBlock[0] = 't'; lpvBlock[1] = 'e'; lpvBlock[2] = 's'; lpvBlock[3] = 't';
}
PurifyDescribe(lpvBlock);
PurifyWhatColors(lpvBlock, 8);
// Enlarge it.
vBlockSize = vBlockSavedSize = 0x180;
lpvBlock = (char *) Reallocate(lpvBlock, vBlockSize);
// Check its size.
vBlockSize = GetBlockSize(lpvBlock);
if (vBlockSize != vBlockSavedSize)
{
printf("Block size mismatch for block at ", (unsigned int) lpvBlock);
}
PurifyWhatColors(lpvBlock, 8);
// Deallocate it.
Deallocate(lpvBlock);
// Check for leaks.
indicator = PurifyNewLeaks(); // Report a leak of 0x20 bytes.
}
(C) Copyright IBM Corporation 1992, 2010.