#include <mm_virt.h>
#include <heap_int.h>
#include <limits.h>
+#include <debug_hooks.h>
#define WARNINGS 1 // Warn and dump on heap errors
#define DEBUG_TRACE 0 // Enable tracing of allocations
#define VERBOSE_DUMP 0 // Set to 1 to enable a verbose dump when heap errors are encountered
+#define VALIDATE_ON_ALLOC 1 // Set to 1 to enable validation of the heap on all malloc() calls
// === CONSTANTS ===
#define HEAP_INIT_SIZE 0x8000 // 32 KiB
void Heap_Install(void);
void *Heap_Extend(size_t Bytes);
void *Heap_Merge(tHeapHead *Head);
+static const size_t Heap_int_GetBlockSize(size_t AllocSize);
//void *Heap_Allocate(const char *File, int Line, size_t Bytes);
//void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
//void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
//void Heap_Deallocate(const char *File, int Line, void *Ptr);
-void Heap_Dump(int bVerbose);
-void Heap_Stats(void);
+ int Heap_int_ApplyWatchpont(void *Addr, bool Enabled);
+void Heap_int_WatchBlock(tHeapHead *Head, bool Enabled);
+//void Heap_Dump(void);
+void Heap_ValidateDump(int bVerbose);
+//void Heap_Stats(void);
// === GLOBALS ===
tMutex glHeap;
*/
void *Heap_Extend(size_t Bytes)
{
-
- Debug("Heap_Extend(0x%x)", Bytes);
+ //Debug("Heap_Extend(0x%x)", Bytes);
// Bounds Check
if( gHeapEnd == (tHeapHead*)MM_KHEAP_MAX ) {
if( new_end > (tHeapHead*)MM_KHEAP_MAX )
{
Log_Error("Heap", "Heap limit exceeded (%p)", (void*)new_end);
- // TODO: Clip allocation to avaliable space, and have caller check returned block
+ // TODO: Clip allocation to available space, and have caller check returned block
return NULL;
}
// Heap expands in pages
for( Uint i = 0; i < pages; i ++ )
{
- if( !MM_Allocate( (tVAddr)gHeapEnd+(i<<12) ) )
+ if( !MM_Allocate( (tPage*)gHeapEnd + i ) )
{
Warning("OOM - Heap_Extend (%i bytes)");
- Heap_Dump(1);
+ Heap_Dump();
return NULL;
}
}
}
/**
- * \brief Merges two ajacent heap blocks
+ * \brief Merges two adjacent heap blocks
*/
void *Heap_Merge(tHeapHead *Head)
{
- tHeapFoot *foot;
- tHeapFoot *thisFoot;
- tHeapHead *head;
-
//Log("Heap_Merge: (Head=%p)", Head);
+ tHeapFoot *thisFoot = Heap_ThisFoot(Head);
- thisFoot = Heap_ThisFoot(Head);
- if( (void*)thisFoot > (void*)gHeapEnd )
- return NULL;
+ ASSERT( Heap_NextHead(Head) <= gHeapEnd );
// Merge Left (Down)
- foot = Heap_PrevFoot(Head);
- if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart)
- && foot->Head->Magic == MAGIC_FREE) {
+ tHeapFoot *foot = Heap_PrevFoot(Head);
+ if( Head > gHeapStart && foot->Head->Magic == MAGIC_FREE)
+ {
foot->Head->Size += Head->Size; // Increase size
thisFoot->Head = foot->Head; // Change backlink
Head->Magic = 0; // Clear old head
}
// Merge Right (Upwards)
- head = Heap_NextHead(Head);
- if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE)
+ tHeapHead *nexthead = Heap_NextHead(Head);
+ if(nexthead < gHeapEnd && nexthead->Magic == MAGIC_FREE)
{
- Head->Size += head->Size;
+ Head->Size += nexthead->Size;
foot = Heap_ThisFoot(Head);
foot->Head = Head; // Update Backlink
thisFoot->Head = NULL; // Clear old footer
thisFoot->Magic = 0;
- head->Size = 0; // Clear old header
- head->Magic = 0;
+ nexthead->Size = 0; // Clear old header
+ nexthead->Magic = 0;
}
// Return new address
return Head;
}
+static const size_t Heap_int_GetBlockSize(size_t AllocSize)
+{
+ size_t Bytes;
+ #if POW2_SIZES
+ Bytes = AllocSize + sizeof(tHeapHead) + sizeof(tHeapFoot);
+ Bytes = 1UUL << LOG2(Bytes);
+ #else
+ Bytes = (AllocSize + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
+ #endif
+ return Bytes;
+}
+
/**
* \param File Allocating source file
* \param Line Source line
return NULL; // TODO: Return a known un-mapped range.
// return INVLPTR;
}
+
+ #if VALIDATE_ON_ALLOC
+ Heap_Validate();
+ #endif
// Get required size
- #if POW2_SIZES
- Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot);
- Bytes = 1UUL << LOG2(__Bytes);
- #else
- Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
- #endif
+ Bytes = Heap_int_GetBlockSize(__Bytes);
// Lock Heap
Mutex_Acquire(&glHeap);
Log_Warning("Heap", "Size of heap address %p is invalid"
" - not aligned (0x%x) [at paddr 0x%x]",
head, head->Size, MM_GetPhysAddr(&head->Size));
- Heap_Dump(VERBOSE_DUMP);
+ Heap_ValidateDump(VERBOSE_DUMP);
#endif
return NULL;
}
if( head->Size < MIN_SIZE ) {
Mutex_Release(&glHeap);
+ #if WARNINGS
Log_Warning("Heap", "Size of heap address %p is invalid"
" - Too small (0x%x) [at paddr 0x%x]",
head, head->Size, MM_GetPhysAddr(&head->Size));
- Heap_Dump(VERBOSE_DUMP);
+ Heap_ValidateDump(VERBOSE_DUMP);
+ #endif
return NULL;
}
if( head->Size > (2<<30) ) {
Mutex_Release(&glHeap);
+ #if WARNINGS
Log_Warning("Heap", "Size of heap address %p is invalid"
" - Over 2GiB (0x%x) [at paddr 0x%x]",
head, head->Size, MM_GetPhysAddr(&head->Size));
- Heap_Dump(VERBOSE_DUMP);
+ Heap_ValidateDump(VERBOSE_DUMP);
+ #endif
return NULL;
}
#if WARNINGS
Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)",
head, &head->Magic, head->Magic);
- Heap_Dump(VERBOSE_DUMP);
+ Heap_ValidateDump(VERBOSE_DUMP);
#endif
return NULL;
}
// INVLPTR is returned from Heap_Allocate when the allocation
// size is zero.
if( Ptr == INVLPTR ) return;
+ // free(NULL) is a no-op
+ if( Ptr == NULL ) return;
// Alignment Check
if( (tVAddr)Ptr % sizeof(void*) != 0 ) {
}
// Check memory block - Header
- tHeapHead *head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
+ tHeapHead *head = (tHeapHead*)Ptr - 1;
if(head->Magic == MAGIC_FREE) {
Log_Warning("Heap", "free - Passed a freed block (%p) by %s:%i (was freed by %s:%i)",
head, File, Line,
head->File, head->Line);
+ Proc_PrintBacktrace();
return;
}
if(head->Magic != MAGIC_USED) {
// Lock
Mutex_Acquire( &glHeap );
-
+
+ Heap_int_WatchBlock(head, false);
+
// Mark as free
head->Magic = MAGIC_FREE;
head->File = File;
*/
void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
{
- tHeapHead *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) );
+ tHeapHead *head = (tHeapHead*)__ptr - 1;
tHeapHead *nextHead;
tHeapFoot *foot;
- Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1);
+ Uint newSize = Heap_int_GetBlockSize(__size);
// Check for reallocating NULL
- if(__ptr == NULL) return Heap_Allocate(File, Line, __size);
+ if(__ptr == NULL)
+ return Heap_Allocate(File, Line, __size);
+
+ if( !Heap_IsHeapAddr(__ptr)) {
+ Log_Error("Heap", "%s:%i passed non-heap address %p when reallocating to %zi",
+ File, Line, __ptr, __size);
+ return NULL;
+ }
// Check if resize is needed
- if(newSize <= head->Size) return __ptr;
+ // TODO: Reduce size of block if needed
+ if(newSize <= head->Size) {
+ #if DEBUG_TRACE
+ Log_Debug("Heap", "realloc maintain %p (0x%x >= 0x%x), returning to %s:%i",
+ head->Data, head->Size, newSize, File, Line);
+ #endif
+ return __ptr;
+ }
+
+ #if VALIDATE_ON_ALLOC
+ Heap_Validate();
+ #endif
+
+ Heap_int_WatchBlock(head, false);
// Check if next block is free
nextHead = Heap_NextHead(head);
// Extend into next block
if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
{
+ #if DEBUG_TRACE
+ Log_Debug("Heap", "realloc expand %p (0x%x to 0x%x), returning to %s:%i",
+ head->Data, head->Size, newSize, File, Line);
+ #endif
Uint size = nextHead->Size + head->Size;
foot = Heap_ThisFoot(nextHead);
// Exact Fit
nextHead->Size = size - newSize;
nextHead->Magic = MAGIC_FREE;
// - Update old footer
- foot->Head = nextHead;
+ Heap_ThisFoot(nextHead)->Head = nextHead;
return __ptr;
}
Warning("[Heap ] TODO: Space efficient realloc when new size is smaller");
}
+ #if DEBUG_TRACE
+ Log_Debug("Heap", "realloc expand down %p (0x%x to 0x%x), returning to %s:%i",
+ head->Data, head->Size, newSize, File, Line);
+ #endif
+
// Exact fit
Uint oldDataSize;
// Set 1st (new/lower) header
nextHead->Line = Line;
nextHead->ValidSize = __size;
+ ASSERTC(head->Size, <, nextHead->Size);
+ ASSERTC(__ptr, ==, head->Data);
memcpy(
nextHead->Data,
__ptr,
);
free(__ptr);
-
+ Heap_Validate();
+
return nextHead->Data;
}
return 1;
}
+int Heap_int_ApplyWatchpont(void *Word, bool enabled)
+{
+ #if ARCHDIR_IS_x86
+ static void *active_wps[4];
+ unsigned int dr;
+ for( dr = 2; dr < 4; dr ++ )
+ {
+ if( (enabled && active_wps[dr] == NULL) || active_wps[dr] == Word)
+ break;
+ }
+ if(dr == 4) {
+ return 1;
+ }
+ if( enabled )
+ {
+ active_wps[dr] = Word;
+ switch(dr)
+ {
+ //case 0: ASM("mov %0, %%dr0" : : "r" (Word)); break;
+ //case 1: ASM("mov %0, %%dr1" : : "r" (Word)); break;
+ case 2: ASM("mov %0, %%dr2" : : "r" (Word)); break;
+ case 3: ASM("mov %0, %%dr3" : : "r" (Word)); break;
+ default: ASSERTC(dr,<,4); return 1;
+ }
+ }
+ else
+ {
+ active_wps[dr] = NULL;
+ }
+ Uint32 dr7flag;
+ ASM("MOV %%dr7, %0" : "=r" (dr7flag));
+ dr7flag &= ~(0x2 << (dr*2));
+ dr7flag &= ~(0xF000 << (dr*4));
+ if( enabled ) {
+ dr7flag |= 0x2 << (dr*2);
+ dr7flag |= 0xD000 << (dr*4); // 4 bytes, write
+ Debug("Heap_int_ApplyWatchpont: Watchpoint #%i %p ENABLED", dr, Word);
+ }
+ else {
+ Debug("Heap_int_ApplyWatchpont: Watchpoint #%i %p disabled", dr, Word);
+ }
+ ASM("MOV %0, %%dr7" : : "r" (dr7flag));
+ return 0;
+ #else
+ return 1;
+ #endif
+}
+
+void Heap_int_WatchBlock(tHeapHead *Head, bool Enabled)
+{
+ int rv;
+ rv = Heap_int_ApplyWatchpont( &Head->Size, Enabled );
+ //rv = Heap_int_ApplyWatchpont( &Head->Magic, Enabled );
+ rv = rv + Heap_int_ApplyWatchpont( &Heap_ThisFoot(Head)->Head, Enabled );
+ if(rv && Enabled) {
+ Warning("Can't apply watch on %p", Head);
+ }
+}
+
+int Heap_WatchBlock(void *Ptr)
+{
+ //Heap_int_ApplyWatchpont();
+ tHeapHead *head;
+ if((Uint)Ptr < (Uint)gHeapStart) return 0;
+ if((Uint)Ptr > (Uint)gHeapEnd) return 0;
+ if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
+
+ head = (tHeapHead*)Ptr - 1;
+
+ Heap_int_WatchBlock( head, true );
+
+ return 0;
+}
+
/**
*/
void Heap_Validate(void)
{
// Call dump non-verbosely.
// - If a heap error is detected, it'll print
- Heap_Dump(0);
+ Heap_ValidateDump(0);
+}
+
+void Heap_Dump(void)
+{
+ Heap_ValidateDump(1);
}
-void Heap_Dump(int bVerbose)
+void Heap_ValidateDump(int bVerbose)
{
tHeapHead *head, *badHead;
tHeapFoot *foot = NULL;
in_heap_dump = 0;
return ;
}
-
+
// If not verbose, we need to dump the failing block
if( !bVerbose )
{
}
Log_Log("Heap", "");
}
-
-
+
badHead = head;
// Work backwards
Log_Debug("Heap", "head=%p", head);
}
- Panic("Heap_Dump - Heap is corrupted, kernel panic!");
+ Panic("Heap_Dump - Heap is corrupted, kernel panic! (%p)", badHead);
}
void Heap_Stats(void)