/* * Stack Overflow Check User Extension Set * * NOTE: This extension set automatically determines at * initialization time whether the stack for this * CPU grows up or down and installs the correct * extension routines for that direction. * * COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994. * On-Line Applications Research Corporation (OAR). * All rights assigned to U.S. Government, 1994. * * This material may be reproduced by or for the U.S. Government pursuant * to the copyright license under the clause at DFARS 252.227-7013. This * notice must appear in all copies of this file and its derivatives. * * $Id$ * */ #include /* * HACK * the stack dump information should be printed by a "fatal" extension. * Fatal extensions only get called via rtems_fatal_error_occurred() * and not when rtems_shutdown_executive() is called. * I hope/think this is changing so that fatal extensions are renamed * to "shutdown" extensions. * When that happens, this #define should be deleted and all the code * it marks. */ #define DONT_USE_FATAL_EXTENSION extern rtems_configuration_table BSP_Configuration; #include #include #include #include #include "stackchk.h" #include "internal.h" /* * This variable contains the name of the task which "blew" the stack. * It is NULL if the system is all right. */ Thread_Control *Stack_check_Blown_task; /* * The extension table for the stack checker. */ rtems_extensions_table Stack_check_Extension_table = { Stack_check_Create_extension, /* rtems_task_create */ 0, /* rtems_task_start */ 0, /* rtems_task_restart */ 0, /* rtems_task_delete */ Stack_check_Switch_extension, /* task_switch */ Stack_check_Begin_extension, /* task_begin */ 0, /* task_exitted */ #ifdef DONT_USE_FATAL_EXTENSION 0, /* fatal */ #else Stack_check_Fatal_extension, /* fatal */ #endif }; /* * The "magic pattern" used to mark the end of the stack. */ Stack_check_Control Stack_check_Pattern; /* * Where the pattern goes in the stack area is dependent upon * whether the stack grow to the high or low area of the memory. * */ #if ( CPU_STACK_GROWS_UP == TRUE ) #define Stack_check_Get_pattern_area( _the_stack ) \ ((Stack_check_Control *) ((char *)(_the_stack)->area + \ (_the_stack)->size - sizeof( Stack_check_Control ) )) #define Stack_check_Calculate_used( _low, _size, _high_water ) \ ((char *)(_high_water) - (char *)(_low)) #define Stack_check_usable_stack_start(_the_stack) \ ((_the_stack)->area) #else #define Stack_check_Get_pattern_area( _the_stack ) \ ((Stack_check_Control *) ((char *)(_the_stack)->area + HEAP_OVERHEAD)) #define Stack_check_Calculate_used( _low, _size, _high_water) \ ( ((char *)(_low) + (_size)) - (char *)(_high_water) ) #define Stack_check_usable_stack_start(_the_stack) \ ((char *)(_the_stack)->area + sizeof(Stack_check_Control)) #endif #define Stack_check_usable_stack_size(_the_stack) \ ((_the_stack)->size - sizeof(Stack_check_Control)) /* * Do we have an interrupt stack? * XXX it would sure be nice if the interrupt stack were also * stored in a "stack" structure! */ Stack_Control stack_check_interrupt_stack; /* * Prototypes necessary for forward references */ void Stack_check_Dump_usage( void ); /* * Fill an entire stack area with BYTE_PATTERN. * This will be used by a Fatal extension to check for * amount of actual stack used */ void stack_check_dope_stack(Stack_Control *stack) { memset(stack->area, BYTE_PATTERN, stack->size); } /*PAGE * * Stack_check_Initialize */ unsigned32 stack_check_initialized = 0; void Stack_check_Initialize( void ) { rtems_status_code status; Objects_Id id_ignored; unsigned32 *p; #if 0 unsigned32 i; unsigned32 class_index; Thread_Control *the_thread; Objects_Information *information; #endif if (stack_check_initialized) return; /* * Dope the pattern and fill areas */ for ( p = Stack_check_Pattern.pattern; p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS]; p += 4 ) { p[0] = 0xFEEDF00D; /* FEED FOOD to BAD DOG */ p[1] = 0x0BAD0D06; p[2] = 0xDEADF00D; /* DEAD FOOD GOOD DOG */ p[3] = 0x600D0D06; }; status = rtems_extension_create( rtems_build_name( 'S', 'T', 'C', 'K' ), &Stack_check_Extension_table, &id_ignored ); assert ( status == RTEMS_SUCCESSFUL ); Stack_check_Blown_task = 0; /* * If installed by a task, that task will not get setup properly * since it missed out on the create hook. This will cause a * failure on first switch out of that task. * So pretend here that we actually ran create and begin extensions. */ /* XXX * * Technically this has not been done for any task created before this * happened. So just run through them and fix the situation. */ #if 0 if (_Thread_Executing) { Stack_check_Create_extension(_Thread_Executing, _Thread_Executing); } #endif #if 0 for ( class_index = OBJECTS_CLASSES_FIRST ; class_index <= OBJECTS_CLASSES_LAST ; class_index++ ) { information = _Objects_Information_table[ class_index ]; if ( information && information->is_thread ) { for ( i=1 ; i <= information->maximum ; i++ ) { the_thread = (Thread_Control *)information->local_table[ i ]; Stack_check_Create_extension( the_thread, the_thread ); } } } #endif /* * If appropriate, setup the interrupt stack for high water testing * also. */ if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) { stack_check_interrupt_stack.area = _CPU_Interrupt_stack_low; stack_check_interrupt_stack.size = (char *) _CPU_Interrupt_stack_high - (char *) _CPU_Interrupt_stack_low; stack_check_dope_stack(&stack_check_interrupt_stack); } #ifdef DONT_USE_FATAL_EXTENSION #ifdef RTEMS_DEBUG /* * this would normally be called by a fatal extension * handler, but we don't run fatal extensions unless * we fatal error. */ atexit(Stack_check_Dump_usage); #endif #endif stack_check_initialized = 1; } /*PAGE * * Stack_check_Create_extension */ boolean Stack_check_Create_extension( Thread_Control *running, Thread_Control *the_thread ) { if (the_thread /* XXX && (the_thread != _Thread_Executing) */ ) stack_check_dope_stack(&the_thread->Start.Initial_stack); return TRUE; } /*PAGE * * Stack_check_Begin_extension */ void Stack_check_Begin_extension( Thread_Control *the_thread ) { Stack_check_Control *the_pattern; if ( the_thread->Object.id == 0 ) /* skip system tasks */ return; the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack); *the_pattern = Stack_check_Pattern; } /*PAGE * * Stack_check_report_blown_task * Report a blown stack. Needs to be a separate routine * so that interrupt handlers can use this too. * * Caller must have set the Stack_check_Blown_task. * * NOTE: The system is in a questionable state... we may not get * the following message out. */ void Stack_check_report_blown_task(void) { Stack_Control *stack; Thread_Control *running; running = Stack_check_Blown_task; stack = &running->Start.Initial_stack; fprintf( stderr, "BLOWN STACK!!! Offending task(%p): id=0x%08x; name=0x%08x", running, running->Object.id, *(unsigned32 *)running->Object.name ); fflush(stderr); if (BSP_Configuration.User_multiprocessing_table) fprintf( stderr, "; node=%d\n", BSP_Configuration.User_multiprocessing_table->node ); else fprintf(stderr, "\n"); fflush(stderr); fprintf( stderr, " stack covers range 0x%08x - 0x%08x (%d bytes)\n", (unsigned32) stack->area, (unsigned32) stack->area + stack->size - 1, (unsigned32) stack->size); fflush(stderr); fprintf( stderr, " Damaged pattern begins at 0x%08x and is %d bytes long\n", (unsigned32) Stack_check_Get_pattern_area(stack), PATTERN_SIZE_BYTES); fflush(stderr); rtems_fatal_error_occurred( (unsigned32) "STACK BLOWN" ); } /*PAGE * * Stack_check_Switch_extension */ void Stack_check_Switch_extension( Thread_Control *running, Thread_Control *heir ) { if ( running->Object.id == 0 ) /* skip system tasks */ return; if (0 != memcmp( (void *) Stack_check_Get_pattern_area( &running->Start.Initial_stack)->pattern, (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES)) { Stack_check_Blown_task = running; Stack_check_report_blown_task(); } } void *Stack_check_find_high_water_mark( const void *s, size_t n ) { const unsigned32 *base, *ebase; unsigned32 length; base = s; length = n/4; #if ( CPU_STACK_GROWS_UP == TRUE ) /* * start at higher memory and find first word that does not * match pattern */ base += length - 1; for (ebase = s; base > ebase; base--) if (*base != U32_PATTERN) return (void *) base; #else /* * start at lower memory and find first word that does not * match pattern */ base += PATTERN_SIZE_WORDS; for (ebase = base + length; base < ebase; base++) if (*base != U32_PATTERN) return (void *) base; #endif return (void *)0; } /*PAGE * * Stack_check_Dump_threads_usage * Try to print out how much stack was actually used by the task. * */ void Stack_check_Dump_threads_usage( Thread_Control *the_thread ) { unsigned32 size, used; void *low; void *high_water_mark; Stack_Control *stack; if ( !the_thread ) return; /* * XXX HACK to get to interrupt stack */ if (the_thread == (Thread_Control *) -1) { if (stack_check_interrupt_stack.area) { stack = &stack_check_interrupt_stack; the_thread = 0; } else return; } else stack = &the_thread->Start.Initial_stack; low = Stack_check_usable_stack_start(stack); size = Stack_check_usable_stack_size(stack); high_water_mark = Stack_check_find_high_water_mark(low, size); if ( high_water_mark ) used = Stack_check_Calculate_used( low, size, high_water_mark ); else used = 0; printf( "0x%08x 0x%08x 0x%08x 0x%08x %8d %8d\n", the_thread ? the_thread->Object.id : ~0, the_thread ? *(unsigned32 *)the_thread->Object.name : rtems_build_name('I', 'N', 'T', 'R'), (unsigned32) stack->area, (unsigned32) stack->area + (unsigned32) stack->size - 1, size, used ); } /*PAGE * * Stack_check_Fatal_extension */ void Stack_check_Fatal_extension( Internal_errors_Source source, boolean is_internal, unsigned32 status ) { #ifndef DONT_USE_FATAL_EXTENSION if (status == 0) Stack_check_Dump_usage(); #endif } /*PAGE * * Stack_check_Dump_usage */ void Stack_check_Dump_usage( void ) { unsigned32 i; unsigned32 class_index; Thread_Control *the_thread; unsigned32 hit_running = 0; Objects_Information *information; if (stack_check_initialized == 0) return; printf("Stack usage by thread\n"); printf( " ID NAME LOW HIGH AVAILABLE USED\n" ); for ( class_index = OBJECTS_CLASSES_FIRST ; class_index <= OBJECTS_CLASSES_LAST ; class_index++ ) { information = _Objects_Information_table[ class_index ]; if ( information && information->is_thread ) { for ( i=1 ; i <= information->maximum ; i++ ) { the_thread = (Thread_Control *)information->local_table[ i ]; Stack_check_Dump_threads_usage( the_thread ); if ( the_thread == _Thread_Executing ) hit_running = 1; } } } if ( !hit_running ) Stack_check_Dump_threads_usage( _Thread_Executing ); /* dump interrupt stack info if any */ Stack_check_Dump_threads_usage((Thread_Control *) -1); }