/* * This file was written by Bill Cox, originally in 1991, and maintained since. It is hereby * placed into the public domain. */ #include #include #include "ddutil.h" struct utMem_ *utMems; uint32 utfFreeMem, utfVirgMem_, utmMem; struct utStack_ *utStacks; uint32 utsStack, utmStack; static utStackRef utBotStack; static uint32 utMaxMem; uint32 utUsedMem; uint32 utMemSeed = 42; /*-------------------------------------------------------------------------------------------------- Create a new mem. --------------------------------------------------------------------------------------------------*/ utMemRef utcMem (void) { utMemRef mem; if (uttMemExists(utfFreeMem())) { mem = utfFreeMem(); utfFreeMem() = utnFreeMem(mem); } else { if (utfVirgMem() == utmMem()) { utmMem() += utmMem()/2; utMems = (struct utMem_ *)realloc((void *)utMems, utmMem()*sizeof(struct utMem_)); if (!utMems) { utExit("utcMem: Can't allocate memory handle"); } } mem = utfVirgMem()++; } uttMemUsed(mem) = true; return mem; } /*-------------------------------------------------------------------------------------------------- Delete a mem (put it on the free list). --------------------------------------------------------------------------------------------------*/ void utdMem ( utMemRef mem, bool farHeap, char *fileName, uint32 line) { utStackRef stack = utlStack(); uint8 rightPicket = *(((uint8 *)utgMemPtr(mem)) + utgMemSize(mem) - 1); utUsedMem -= utgMemSize(mem); if (!uttMemUsed(mem)) { utExit("utdMem: Memory allocated in %s on line %u freed second time " "in %s on line %u", utgMemName(mem), utgMemLine(mem), fileName, line); } if (utgMemPicket(mem) != rightPicket) { utExit("utdMem: Memory allocated in %s on line %u and freed in %s " "line %u lost right picket", utgMemName(mem), utgMemLine(mem), fileName, line); } if (uttMemFarHeap(mem) && !farHeap) { utExit("utdMem: Far memory allocated in %s on line %u is freed in %s " "line %u to the near heap", utgMemName(mem), utgMemLine(mem), fileName, line); } if (!uttMemFarHeap(mem) && farHeap) { utExit("utdMem: Near memory allocated in %s on line %u is freed in %s " "line %u to the far heap", utgMemName(mem), utgMemLine(mem), fileName, line); } utdStackMem(stack, mem, fileName, line); utnFreeMem(mem) = utfFreeMem(); utfFreeMem() = mem; uttMemUsed(mem) = false; free(utgMemName(mem)); } /*-------------------------------------------------------------------------------------------------- Set the seed. Set the signature to 0; --------------------------------------------------------------------------------------------------*/ void utMemInitSeed ( uint32 seed) { if (seed == 0) { utMemSeed = 1; } else { utMemSeed = seed; } } /*-------------------------------------------------------------------------------------------------- Return a random number between 1 and 2^23 - 1. Specific to utMem functions so that users utSeed is not affected. --------------------------------------------------------------------------------------------------*/ uint32 utMemRand (void) { utMemSeed = (((((utMemSeed << 5) ^ utMemSeed) & 0x7FFF80) >> 7) | (utMemSeed << 16)) & 0x7FFFFF; return utMemSeed; } /*-------------------------------------------------------------------------------------------------- Create a new mem, filling in all fields. Note that the calling routine is responsible for putting the pickets in memory. --------------------------------------------------------------------------------------------------*/ utMemRef utBuildMem ( void *memPtr, uint32 size, bool farHeap, char *file, uint32 line) { utMemRef mem = utcMem(); utAssert(size > 1); utAssert(file != NULL && *file != '\0'); utgMemPtr(mem) = memPtr; utgMemSize(mem) = size; utUsedMem += size; if (utUsedMem > utMaxMem) { utMaxMem = utUsedMem; } utgMemLine(mem) = line; utgMemName(mem) = malloc((strlen(file) + 1)*sizeof(char)); strcpy(utgMemName(mem), file); utaStackMem(utlStack(), mem); utgMemPicket(mem) = (uint8)utMemRand(); *(((uint8 *)memPtr) + size - 1) = utgMemPicket(mem); uttMemFarHeap(mem) = farHeap; return mem; } /*-------------------------------------------------------------------------------------------------- Find a mem with the same memory pointer. --------------------------------------------------------------------------------------------------*/ utMemRef utqMemPtr ( void *memPtr) { utStackRef stack = utlStack(); utMemRef mem; for (mem = utfStackMem(stack); uttMemExists(mem); mem = utnStackMem(stack, mem)) { if (utgMemPtr(mem) == memPtr) { return mem; } } return ut0Mem; } /*-------------------------------------------------------------------------------------------------- Create a new stack. --------------------------------------------------------------------------------------------------*/ utStackRef utcStackTrace ( char *fileName, uint32 line) { utStackRef stack; if (utsStack() == utmStack()) { utmStack() += utmStack()/2; utStacks = (struct utStack_ *)realloc((void *)utStacks, utmStack()*sizeof(struct utStack_)); if (!utStacks) { utExit("utcStackTrace: Can't allocate a new stack"); } } stack = utsStack()++; utfStackMem(stack) = ut0Mem; utgStackName(stack) = malloc((strlen(fileName) + 1)*sizeof(char)); strcpy(utgStackName(stack), fileName); utgStackLine(stack) = line; return stack; } /*-------------------------------------------------------------------------------------------------- Delete a stack. Check for memory errors. --------------------------------------------------------------------------------------------------*/ void utdStackTrace ( utStackRef stack, char *fileName, uint32 line) { utMemRef mem; bool error = false; for (mem = utfStackMem(stack); uttMemExists(mem); mem = utnStackMem(stack, mem)) { error = true; utExit("utdStack: Failed to free memory allocated in %s on line %u", utgMemName(mem), utgMemLine(mem)); } if (stack != utlStack()) { error = true; utExit("utdStack: Stack allocated in %s on line %u is freed out of " "order in %s on line %u", utgStackName(stack), utgStackLine(stack), fileName, line); } if (error) { utExit("utdStack: Punting due to memory errors"); } free(utgStackName(stack)); utAssert(utsStack() > 0); utsStack()--; } /*-------------------------------------------------------------------------------------------------- Add a mem to a stack. --------------------------------------------------------------------------------------------------*/ void utaStackMem ( utStackRef stack, utMemRef mem) { utoStackMem(mem) = stack; utnStackMem(stack, mem) = utfStackMem(stack); utfStackMem(stack) = mem; } /*-------------------------------------------------------------------------------------------------- Remove a mem from a stack. --------------------------------------------------------------------------------------------------*/ void utdStackMem ( utStackRef stack, utMemRef mem, char *fileName, uint32 line) { utMemRef pMem, nMem; pMem = ut0Mem; for (nMem = utfStackMem(stack); uttMemExists(nMem) && nMem != mem; nMem = utnStackMem(stack, nMem)) { pMem = nMem; } if (nMem != mem) { utExit("utdStackMem: Memory allocated in %s on line %u does not " "belong to the stack allocated in %s on line %u. Look at " "%s line %u", utgMemName(mem), utgMemLine(mem), utgStackName(stack), utgStackLine(stack), fileName, line); } if (uttMemExists(pMem)) { utnStackMem(stack, pMem) = utnStackMem(stack, mem); } else { utfStackMem(stack) = utnStackMem(stack, mem); } } /*-------------------------------------------------------------------------------------------------- Allocate initail memory for mems. --------------------------------------------------------------------------------------------------*/ static void allocateMems (void) { utfFreeMem() = ut0Mem; utfVirgMem() = 0; utmMem() = 100; utMems = (struct utMem_ *)calloc(utmMem(), sizeof(struct utMem_)); if (!utMems) { utExit("allocateMems: Out of memory"); } } /*-------------------------------------------------------------------------------------------------- Allocate initail memory for stacks. --------------------------------------------------------------------------------------------------*/ static void allocateStacks (void) { utsStack() = 0; utmStack() = 100; utStacks = (struct utStack_ *)calloc(utmStack(), sizeof(struct utStack_)); if (!utStacks) { utExit("allocateStacks: Out of memory"); } } /*-------------------------------------------------------------------------------------------------- Initialize the memory checker module; --------------------------------------------------------------------------------------------------*/ void utMemStart(void) { allocateMems(); allocateStacks(); utBotStack = utcStack(); utUsedMem = 0; utMaxMem = 0; } /*-------------------------------------------------------------------------------------------------- Free memory used by the memory checker. --------------------------------------------------------------------------------------------------*/ void utMemStop( bool reportMemory) { utdStack(utBotStack); free((void *)utMems); free((void *)utStacks); if(reportMemory) { utLogMessage("Used %s of memory", utMemoryUnits(utMaxMem)); } } /*-------------------------------------------------------------------------------------------------- Perform a memory check. Basically, check all the pickets. --------------------------------------------------------------------------------------------------*/ void utMemCheckTrace ( char *fileName, uint32 line) { utStackRef stack; utMemRef mem; uint8 picket; for (stack = utfStack(); uttStackExists(stack); stack = utnStack(stack)) { for (mem = utfStackMem(stack); uttMemExists(mem); mem = utnStackMem(stack, mem)) { picket = *(((uint8 *)utgMemPtr(mem)) + utgMemSize(mem) - 1); if (picket != utgMemPicket(mem)) { utExit("utMemCheck: Invoked from %s, line %u: Picket hosed for " "memory allocated in %s, " "line %u", fileName, line, utgMemName(mem), utgMemLine(mem)); } } } }