summaryrefslogblamecommitdiffstats
path: root/c/src/libmisc/stackchk/check.c
blob: 8e811e493d048a5c81ac1972551807c55ecf31b5 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                         
                            
                                                    
  

                                                           
                                              




        

                  











                                                                       




                   
                           




















                                                                       


                                                      
                                                      
      
















                                                                 

                                                            

                                                                
                                            






                                                    
                                                                        

                                                               
                                                          

                                                    
                                                              
















                                                             





                                              




















                                                      
     

                                  
      
                         
     



                                   
      


















                                                             
     





                                           
      









                                                                      





                                                                         



                                                                         














                                                                     







                                                                     

                                                                                



                                                           










                                                         







                                
                                     



                            


                                 
                                                                   
                                                                 

                












                                    


                               
































                                                                               
                                           
      

                   
                                                             


                        
                                                                    






































































                                                                                                  
                             






















                                                                  


                           






























                                                                    











                                                      
                                                  
               











                                                                  
                                 



                                        
 
                                

                                 
      









                                   




                                       



                                   
                                    
         
                                                                      
    





                                                            
                                                     





                                                                     








                                                        
/*
 *  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-1999.
 *  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.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 *
 */

#include <rtems.h>

/*
 * 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

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <rtems/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 )
{
#if 0
  rtems_status_code    status;
  Objects_Id           id_ignored;
#endif
  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;
  };

#if 0
  status = rtems_extension_create(
    rtems_build_name( 'S', 'T', 'C', 'K' ),
    &Stack_check_Extension_table,
    &id_ignored
  );
  assert ( status == RTEMS_SUCCESSFUL );
#endif

  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 (!stack_check_initialized)
      Stack_check_Initialize();

    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 (!stack_check_initialized)
    Stack_check_Initialize();

  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 (rtems_configuration_get_user_multiprocessing_table())
        fprintf(
          stderr,
          "; node=%d\n",
          rtems_configuration_get_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;
  unsigned32      u32_name;
  char            name[5];


  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;

  if ( the_thread )
    u32_name = *(unsigned32 *)the_thread->Object.name;
  else
    u32_name = rtems_build_name('I', 'N', 'T', 'R');

  name[ 0 ] = (u32_name >> 24) & 0xff;
  name[ 1 ] = (u32_name >> 16) & 0xff;
  name[ 2 ] = (u32_name >>  8) & 0xff;
  name[ 3 ] = (u32_name >>  0) & 0xff;
  name[ 4 ] = '\0';

  printf( "0x%08x  %4s  0x%08x  0x%08x   %8d   %8d\n",
          the_thread ? the_thread->Object.id : ~0,
          name,
          (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);
}