summaryrefslogtreecommitdiffstats
path: root/cpukit/score/src/heapwalk.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/score/src/heapwalk.c')
-rw-r--r--cpukit/score/src/heapwalk.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/cpukit/score/src/heapwalk.c b/cpukit/score/src/heapwalk.c
new file mode 100644
index 0000000000..fae0bd6330
--- /dev/null
+++ b/cpukit/score/src/heapwalk.c
@@ -0,0 +1,421 @@
+/**
+ * @file
+ *
+ * @ingroup ScoreHeap
+ *
+ * @brief Heap Handler implementation.
+ */
+
+/*
+ * COPYRIGHT ( c ) 1989-2007.
+ * On-Line Applications Research Corporation ( OAR ).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/system.h>
+#include <rtems/score/address.h>
+#include <rtems/score/sysstate.h>
+#include <rtems/score/heap.h>
+#include <rtems/score/interr.h>
+#include <rtems/bspIo.h>
+
+typedef void (*Heap_Walk_printer)(int, bool, const char*, ...);
+
+static void _Heap_Walk_print_nothing(
+ int source,
+ bool error,
+ const char *fmt,
+ ...
+)
+{
+ /* Do nothing */
+}
+
+static void _Heap_Walk_print( int source, bool error, const char *fmt, ... )
+{
+ va_list ap;
+
+ if ( error ) {
+ printk( "FAIL[%d]: ", source );
+ } else {
+ printk( "PASS[%d]: ", source );
+ }
+
+ va_start( ap, fmt );
+ vprintk( fmt, ap );
+ va_end( ap );
+}
+
+static bool _Heap_Walk_check_free_list(
+ int source,
+ Heap_Walk_printer printer,
+ Heap_Control *heap
+)
+{
+ uintptr_t const page_size = heap->page_size;
+ const Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap );
+ const Heap_Block *const first_free_block = _Heap_Free_list_first( heap );
+ const Heap_Block *prev_block = free_list_tail;
+ const Heap_Block *free_block = first_free_block;
+
+ while ( free_block != free_list_tail ) {
+ if ( !_Heap_Is_block_in_heap( heap, free_block ) ) {
+ (*printer)(
+ source,
+ true,
+ "free block 0x%08x: not in heap\n",
+ free_block
+ );
+
+ return false;
+ }
+
+ if (
+ !_Heap_Is_aligned( _Heap_Alloc_area_of_block( free_block ), page_size )
+ ) {
+ (*printer)(
+ source,
+ true,
+ "free block 0x%08x: alloc area not page aligned\n",
+ free_block
+ );
+
+ return false;
+ }
+
+ if ( _Heap_Is_used( free_block ) ) {
+ (*printer)(
+ source,
+ true,
+ "free block 0x%08x: is used\n",
+ free_block
+ );
+
+ return false;
+ }
+
+ if ( free_block->prev != prev_block ) {
+ (*printer)(
+ source,
+ true,
+ "free block 0x%08x: invalid previous block 0x%08x\n",
+ free_block,
+ free_block->prev
+ );
+
+ return false;
+ }
+
+ prev_block = free_block;
+ free_block = free_block->next;
+ }
+
+ return true;
+}
+
+static bool _Heap_Walk_is_in_free_list(
+ Heap_Control *heap,
+ Heap_Block *block
+)
+{
+ const Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap );
+ const Heap_Block *free_block = _Heap_Free_list_first( heap );
+
+ while ( free_block != free_list_tail ) {
+ if ( free_block == block ) {
+ return true;
+ }
+ free_block = free_block->next;
+ }
+
+ return false;
+}
+
+static bool _Heap_Walk_check_control(
+ int source,
+ Heap_Walk_printer printer,
+ Heap_Control *heap
+)
+{
+ uintptr_t const page_size = heap->page_size;
+ uintptr_t const min_block_size = heap->min_block_size;
+ Heap_Block *const first_free_block = _Heap_Free_list_first( heap );
+ Heap_Block *const last_free_block = _Heap_Free_list_last( heap );
+ Heap_Block *const first_block = heap->first_block;
+ Heap_Block *const last_block = heap->last_block;
+
+ (*printer)(
+ source,
+ false,
+ "page size %u, min block size %u\n"
+ "\tarea begin 0x%08x, area end 0x%08x\n"
+ "\tfirst block 0x%08x, last block 0x%08x\n"
+ "\tfirst free 0x%08x, last free 0x%08x\n",
+ page_size, min_block_size,
+ heap->area_begin, heap->area_end,
+ first_block, last_block,
+ first_free_block, last_free_block
+ );
+
+ if ( page_size == 0 ) {
+ (*printer)( source, true, "page size is zero\n" );
+
+ return false;
+ }
+
+ if ( !_Addresses_Is_aligned( (void *) page_size ) ) {
+ (*printer)(
+ source,
+ true,
+ "page size %u not CPU aligned\n",
+ page_size
+ );
+
+ return false;
+ }
+
+ if ( !_Heap_Is_aligned( min_block_size, page_size ) ) {
+ (*printer)(
+ source,
+ true,
+ "min block size %u not page aligned\n",
+ min_block_size
+ );
+
+ return false;
+ }
+
+ if (
+ !_Heap_Is_aligned( _Heap_Alloc_area_of_block( first_block ), page_size )
+ ) {
+ (*printer)(
+ source,
+ true,
+ "first block 0x%08x: alloc area not page aligned\n",
+ first_block
+ );
+
+ return false;
+ }
+
+ if ( !_Heap_Is_prev_used( first_block ) ) {
+ (*printer)(
+ source,
+ true,
+ "first block: HEAP_PREV_BLOCK_USED is cleared\n"
+ );
+
+ return false;
+ }
+
+ if ( _Heap_Is_free( last_block ) ) {
+ (*printer)(
+ source,
+ true,
+ "last block: is free\n"
+ );
+
+ return false;
+ }
+
+ if (
+ _Heap_Block_at( last_block, _Heap_Block_size( last_block ) ) != first_block
+ ) {
+ (*printer)(
+ source,
+ true,
+ "last block: next block is not the first block\n"
+ );
+
+ return false;
+ }
+
+ return _Heap_Walk_check_free_list( source, printer, heap );
+}
+
+static bool _Heap_Walk_check_free_block(
+ int source,
+ Heap_Walk_printer printer,
+ Heap_Control *heap,
+ Heap_Block *block
+)
+{
+ Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap );
+ Heap_Block *const free_list_head = _Heap_Free_list_head( heap );
+ Heap_Block *const first_free_block = _Heap_Free_list_first( heap );
+ Heap_Block *const last_free_block = _Heap_Free_list_last( heap );
+ bool const prev_used = _Heap_Is_prev_used( block );
+ uintptr_t const block_size = _Heap_Block_size( block );
+ Heap_Block *const next_block = _Heap_Block_at( block, block_size );
+
+ (*printer)(
+ source,
+ false,
+ "block 0x%08x: size %u, prev 0x%08x%s, next 0x%08x%s\n",
+ block,
+ block_size,
+ block->prev,
+ block->prev == first_free_block ?
+ " (= first free)"
+ : (block->prev == free_list_head ? " (= head)" : ""),
+ block->next,
+ block->next == last_free_block ?
+ " (= last free)"
+ : (block->next == free_list_tail ? " (= tail)" : "")
+ );
+
+ if ( block_size != next_block->prev_size ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: size %u != size %u (in next block 0x%08x)\n",
+ block,
+ block_size,
+ next_block->prev_size,
+ next_block
+ );
+
+ return false;
+ }
+
+ if ( !prev_used ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: two consecutive blocks are free\n",
+ block
+ );
+
+ return false;
+ }
+
+ if ( !_Heap_Walk_is_in_free_list( heap, block ) ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: free block not in free list\n",
+ block
+ );
+
+ return false;
+ }
+
+ return true;
+}
+
+bool _Heap_Walk(
+ Heap_Control *heap,
+ int source,
+ bool dump
+)
+{
+ uintptr_t const page_size = heap->page_size;
+ uintptr_t const min_block_size = heap->min_block_size;
+ Heap_Block *const first_block = heap->first_block;
+ Heap_Block *const last_block = heap->last_block;
+ Heap_Block *block = first_block;
+ Heap_Walk_printer printer = dump ?
+ _Heap_Walk_print : _Heap_Walk_print_nothing;
+
+ if ( !_System_state_Is_up( _System_state_Get() ) ) {
+ return true;
+ }
+
+ if ( !_Heap_Walk_check_control( source, printer, heap ) ) {
+ return false;
+ }
+
+ do {
+ uintptr_t const block_begin = (uintptr_t) block;
+ uintptr_t const block_size = _Heap_Block_size( block );
+ bool const prev_used = _Heap_Is_prev_used( block );
+ Heap_Block *const next_block = _Heap_Block_at( block, block_size );
+ uintptr_t const next_block_begin = (uintptr_t) next_block;
+ bool const is_not_last_block = block != last_block;
+
+ if ( !_Heap_Is_block_in_heap( heap, next_block ) ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: next block 0x%08x not in heap\n",
+ block,
+ next_block
+ );
+
+ return false;
+ }
+
+ if ( !_Heap_Is_aligned( block_size, page_size ) && is_not_last_block ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: block size %u not page aligned\n",
+ block,
+ block_size
+ );
+
+ return false;
+ }
+
+ if ( block_size < min_block_size && is_not_last_block ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: size %u < min block size %u\n",
+ block,
+ block_size,
+ min_block_size
+ );
+
+ return false;
+ }
+
+ if ( next_block_begin <= block_begin && is_not_last_block ) {
+ (*printer)(
+ source,
+ true,
+ "block 0x%08x: next block 0x%08x is not a successor\n",
+ block,
+ next_block
+ );
+
+ return false;
+ }
+
+ if ( !_Heap_Is_prev_used( next_block ) ) {
+ if ( !_Heap_Walk_check_free_block( source, printer, heap, block ) ) {
+ return false;
+ }
+ } else if (prev_used) {
+ (*printer)(
+ source,
+ false,
+ "block 0x%08x: size %u\n",
+ block,
+ block_size
+ );
+ } else {
+ (*printer)(
+ source,
+ false,
+ "block 0x%08x: size %u, prev_size %u\n",
+ block,
+ block_size,
+ block->prev_size
+ );
+ }
+
+ block = next_block;
+ } while ( block != first_block );
+
+ return true;
+}