source: rtems/cpukit/libmisc/stackchk/check.c @ 16b1546e

4.115
Last change on this file since 16b1546e was 16b1546e, checked in by Joel Sherrill <joel.sherrill@…>, on 11/04/10 at 22:22:49

2010-11-04 Joel Sherrill <joel.sherrill@…>

  • libmisc/stackchk/check.c: Address casting issue highlighted by clang. There was no need for an intermediate cast to a structure pointer.
  • Property mode set to 100644
File size: 12.5 KB
Line 
1/*
2 *  Stack Overflow Check User Extension Set
3 *
4 *  NOTE:  This extension set automatically determines at
5 *         initialization time whether the stack for this
6 *         CPU grows up or down and installs the correct
7 *         extension routines for that direction.
8 *
9 *  COPYRIGHT (c) 1989-2010.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.com/license/LICENSE.
15 *
16 *  $Id$
17 *
18 */
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include <rtems.h>
25#include <inttypes.h>
26
27/*
28 * The stack dump information may be printed by a "fatal" extension.
29 * Fatal extensions only get called via rtems_fatal_error_occurred()
30 * and not when rtems_shutdown_executive() is called.
31 * When that happens, this #define should be deleted and all the code
32 * it marks.
33 */
34#define DONT_USE_FATAL_EXTENSION
35
36#include <string.h>
37#include <stdlib.h>
38
39#include <rtems/bspIo.h>
40#include <rtems/stackchk.h>
41#include "internal.h"
42
43/*
44 *  Variable to indicate when the stack checker has been initialized.
45 */
46static int   Stack_check_Initialized = 0;
47
48/*
49 *  The "magic pattern" used to mark the end of the stack.
50 */
51Stack_check_Control Stack_check_Pattern;
52
53/*
54 * Helper function to report if the actual stack pointer is in range.
55 *
56 * NOTE: This uses a GCC specific method.
57 */
58static inline bool Stack_check_Frame_pointer_in_range(
59  Stack_Control *the_stack
60)
61{
62  #if defined(__GNUC__)
63    void *sp = __builtin_frame_address(0);
64
65    if ( sp < the_stack->area ) {
66      return false;
67    }
68    if ( sp > (the_stack->area + the_stack->size) ) {
69      return false;
70    }
71  #else
72    #error "How do I check stack bounds on a non-GNU compiler?"
73  #endif
74  return true;
75}
76
77/*
78 *  Where the pattern goes in the stack area is dependent upon
79 *  whether the stack grow to the high or low area of the memory.
80 */
81#if (CPU_STACK_GROWS_UP == TRUE)
82  #define Stack_check_Get_pattern( _the_stack ) \
83    ((char *)(_the_stack)->area + \
84         (_the_stack)->size - sizeof( Stack_check_Control ) ))
85
86  #define Stack_check_Calculate_used( _low, _size, _high_water ) \
87      ((char *)(_high_water) - (char *)(_low))
88
89  #define Stack_check_usable_stack_start(_the_stack) \
90    ((_the_stack)->area)
91
92#else
93  /*
94   * We need this magic offset because during a task delete the task stack will
95   * be freed before we enter the task switch extension which checks the stack.
96   * The task stack free operation will write the next and previous pointers
97   * for the free list into this area.
98   */
99  #define Stack_check_Get_pattern( _the_stack ) \
100    ((char *)(_the_stack)->area \
101      + sizeof(Heap_Block) - HEAP_BLOCK_HEADER_SIZE))
102
103  #define Stack_check_Calculate_used( _low, _size, _high_water) \
104      ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
105
106  #define Stack_check_usable_stack_start(_the_stack) \
107      ((char *)(_the_stack)->area + sizeof(Stack_check_Control))
108
109#endif
110
111/*
112 *  Obtain a properly typed pointer to the area to check.
113 */
114#define Stack_check_Get_pattern_area( _the_stack ) \
115  ((Stack_check_Control *) Stack_check_Get_pattern( _the_stack )
116
117/*
118 *  The assumption is that if the pattern gets overwritten, the task
119 *  is too close.  This defines the usable stack memory.
120 */
121#define Stack_check_usable_stack_size(_the_stack) \
122    ((_the_stack)->size - sizeof(Stack_check_Control))
123
124#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
125  /*
126   *  Did RTEMS allocate the interrupt stack? If so, put it in
127   *  Stack_Control format.
128   */
129  Stack_Control Stack_check_Interrupt_stack;
130#endif
131
132/*
133 *  Fill an entire stack area with BYTE_PATTERN.  This will be used
134 *  to check for amount of actual stack used.
135 */
136#define Stack_check_Dope_stack(_stack) \
137  memset((_stack)->area, BYTE_PATTERN, (_stack)->size)
138
139/*
140 *  Stack_check_Initialize
141 */
142void Stack_check_Initialize( void )
143{
144  int       i;
145  uint32_t *p;
146  static    uint32_t pattern[ 4 ] = {
147    0xFEEDF00D, 0x0BAD0D06,  /* FEED FOOD to  BAD DOG */
148    0xDEADF00D, 0x600D0D06   /* DEAD FOOD but GOOD DOG */
149  };
150
151  if ( Stack_check_Initialized )
152    return;
153
154  /*
155   * Dope the pattern and fill areas
156   */
157  p = Stack_check_Pattern.pattern;
158  for ( i = 0; i < PATTERN_SIZE_WORDS; i++ ) {
159      p[i] = pattern[ i%4 ];
160  }
161
162  /*
163   * If appropriate, setup the interrupt stack for high water testing
164   * also.
165   */
166  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
167    if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) {
168      Stack_check_Interrupt_stack.area = _CPU_Interrupt_stack_low;
169      Stack_check_Interrupt_stack.size = (char *) _CPU_Interrupt_stack_high -
170                                  (char *) _CPU_Interrupt_stack_low;
171      Stack_check_Dope_stack(&Stack_check_Interrupt_stack);
172   }
173  #endif
174
175  Stack_check_Initialized = 1;
176}
177
178/*
179 *  rtems_stack_checker_create_extension
180 */
181bool rtems_stack_checker_create_extension(
182  Thread_Control *running __attribute__((unused)),
183  Thread_Control *the_thread
184)
185{
186  Stack_check_Initialize();
187
188  if (the_thread)
189    Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
190
191  return true;
192}
193
194/*
195 *  rtems_stack_checker_Begin_extension
196 */
197void rtems_stack_checker_begin_extension(
198  Thread_Control *the_thread
199)
200{
201  Stack_check_Control  *the_pattern;
202
203  if ( the_thread->Object.id == 0 )        /* skip system tasks */
204    return;
205
206  the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
207
208  *the_pattern = Stack_check_Pattern;
209}
210
211/*
212 *  Stack_check_report_blown_task
213 *
214 *  Report a blown stack.  Needs to be a separate routine
215 *  so that interrupt handlers can use this too.
216 *
217 *  NOTE: The system is in a questionable state... we may not get
218 *        the following message out.
219 */
220void Stack_check_report_blown_task(
221  Thread_Control *running,
222  bool pattern_ok
223) RTEMS_COMPILER_NO_RETURN_ATTRIBUTE;
224
225void Stack_check_report_blown_task(Thread_Control *running, bool pattern_ok)
226{
227  Stack_Control *stack = &running->Start.Initial_stack;
228  void          *pattern_area = Stack_check_Get_pattern(stack);
229  char           name[32];
230
231  printk("BLOWN STACK!!!\n");
232  printk("task control block: 0x%08" PRIxPTR "\n", running);
233  printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
234  printk(
235    "task name: 0x%08" PRIx32 "\n",
236    running->Object.name.name_u32
237  );
238  printk(
239    "task name string: %s\n",
240    rtems_object_get_name(running->Object.id, sizeof(name), name)
241  );
242  printk(
243    "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
244    (unsigned long) stack->size,
245    stack->area,
246    ((char *) stack->area + stack->size)
247  );
248  if (!pattern_ok) {
249    printk(
250      "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
251      (unsigned long) PATTERN_SIZE_BYTES,
252      pattern_area,
253      (pattern_area + PATTERN_SIZE_BYTES)
254    );
255  }
256
257  #if defined(RTEMS_MULTIPROCESSING)
258    if (rtems_configuration_get_user_multiprocessing_table()) {
259      printk(
260        "node: 0x%08" PRIxPTR "\n",
261          rtems_configuration_get_user_multiprocessing_table()->node
262      );
263    }
264  #endif
265
266  rtems_fatal_error_occurred(0x81);
267}
268
269/*
270 *  rtems_stack_checker_switch_extension
271 */
272void rtems_stack_checker_switch_extension(
273  Thread_Control *running __attribute__((unused)),
274  Thread_Control *heir __attribute__((unused))
275)
276{
277  Stack_Control *the_stack = &running->Start.Initial_stack;
278  void          *pattern;
279  bool           sp_ok;
280  bool           pattern_ok = true;
281
282  pattern = Stack_check_Get_pattern_area(the_stack);
283
284  /*
285   *  Check for an out of bounds stack pointer or an overwrite
286   */
287  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
288
289  pattern_ok = (!memcmp( pattern,
290            (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES));
291
292  if ( !sp_ok || !pattern_ok ) {
293    Stack_check_report_blown_task( running, pattern_ok );
294  }
295}
296
297/*
298 *  Check if blown
299 */
300bool rtems_stack_checker_is_blown( void )
301{
302  Stack_Control *the_stack = &_Thread_Executing->Start.Initial_stack;
303  bool           sp_ok;
304  bool           pattern_ok = true;
305
306  /*
307   *  Check for an out of bounds stack pointer
308   */
309
310  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
311
312  /*
313   * The stack checker must be initialized before the pattern is there
314   * to check.
315   */
316  if ( Stack_check_Initialized ) {
317    pattern_ok = (!memcmp(
318      Stack_check_Get_pattern(the_stack),
319      (void *) Stack_check_Pattern.pattern,
320      PATTERN_SIZE_BYTES
321    ));
322  }
323
324
325  /*
326   * Let's report as much as we can.
327   */
328  if ( !sp_ok || !pattern_ok ) {
329    Stack_check_report_blown_task( _Thread_Executing, pattern_ok );
330    /* DOES NOT RETURN */
331  }
332
333  /*
334   * The Stack Pointer and the Pattern Area are OK so return false.
335   */
336  return false;
337}
338
339/*
340 * Stack_check_find_high_water_mark
341 */
342static inline void *Stack_check_find_high_water_mark(
343  const void *s,
344  size_t      n
345)
346{
347  const uint32_t   *base, *ebase;
348  uint32_t   length;
349
350  base = s;
351  length = n/4;
352
353  #if ( CPU_STACK_GROWS_UP == TRUE )
354    /*
355     * start at higher memory and find first word that does not
356     * match pattern
357     */
358
359    base += length - 1;
360    for (ebase = s; base > ebase; base--)
361      if (*base != U32_PATTERN)
362        return (void *) base;
363  #else
364    /*
365     * start at lower memory and find first word that does not
366     * match pattern
367     */
368
369    base += PATTERN_SIZE_WORDS;
370    for (ebase = base + length; base < ebase; base++)
371      if (*base != U32_PATTERN)
372        return (void *) base;
373  #endif
374
375  return (void *)0;
376}
377
378/*
379 *  Stack_check_Dump_threads_usage
380 *
381 *  Try to print out how much stack was actually used by the task.
382 */
383static void                   *print_context;
384static rtems_printk_plugin_t   print_handler;
385
386void Stack_check_Dump_threads_usage(
387  Thread_Control *the_thread
388)
389{
390  uint32_t        size, used;
391  void           *low;
392  void           *high_water_mark;
393  void           *current;
394  Stack_Control  *stack;
395  char            name[5];
396
397  /*
398   *  The pointer passed in for the_thread is guaranteed to be non-NULL from
399   *  rtems_iterate_over_all_threads() so no need to check it here.
400   */
401
402  /*
403   *  Obtain interrupt stack information
404   */
405  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
406    if (the_thread == (Thread_Control *) -1) {
407      if (!Stack_check_Interrupt_stack.area)
408        return;
409      stack = &Stack_check_Interrupt_stack;
410      the_thread = 0;
411      current = 0;
412    } else
413  #endif
414    {
415      stack  = &the_thread->Start.Initial_stack;
416      current = (void *)_CPU_Context_Get_SP( &the_thread->Registers );
417    }
418
419  low  = Stack_check_usable_stack_start(stack);
420  size = Stack_check_usable_stack_size(stack);
421
422  high_water_mark = Stack_check_find_high_water_mark(low, size);
423
424  if ( high_water_mark )
425    used = Stack_check_Calculate_used( low, size, high_water_mark );
426  else
427    used = 0;
428
429
430  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
431    if ( the_thread )
432  #endif
433    {
434      (*print_handler)(
435        print_context,
436        "0x%08" PRIx32 "  %4s",
437        the_thread->Object.id,
438        rtems_object_get_name( the_thread->Object.id, sizeof(name), name )
439      );
440    }
441    #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
442      else {
443        (*print_handler)( print_context, "0x%08" PRIx32 "  INTR", ~0 );
444      }
445    #endif
446
447  (*print_handler)(
448    print_context,
449    " %010p - %010p %010p  %8" PRId32 "   ",
450    stack->area,
451    stack->area + stack->size - 1,
452    current,
453    size
454  );
455
456  if (Stack_check_Initialized == 0) {
457    (*print_handler)( print_context, "Unavailable\n" );
458  } else {
459    (*print_handler)( print_context, "%8" PRId32 "\n", used );
460  }
461
462
463}
464
465/*
466 *  rtems_stack_checker_fatal_extension
467 */
468#ifndef DONT_USE_FATAL_EXTENSION
469  void rtems_stack_checker_fatal_extension(
470    Internal_errors_Source  source,
471    bool                    is_internal,
472    uint32_t                status
473  )
474  {
475    if (status == 0)
476      rtems_stack_checker_report_usage();
477  }
478#endif
479
480/*PAGE
481 *
482 *  rtems_stack_checker_report_usage
483 */
484
485void rtems_stack_checker_report_usage_with_plugin(
486  void                  *context,
487  rtems_printk_plugin_t  print
488)
489{
490  if ( !print )
491    return;
492
493  print_context = context;
494  print_handler = print;
495
496  (*print)( context, "Stack usage by thread\n");
497  (*print)( context,
498"    ID      NAME    LOW          HIGH     CURRENT     AVAILABLE     USED\n"
499  );
500
501  /* iterate over all threads and dump the usage */
502  rtems_iterate_over_all_threads( Stack_check_Dump_threads_usage );
503
504  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
505    /* dump interrupt stack info if any */
506    Stack_check_Dump_threads_usage((Thread_Control *) -1);
507  #endif
508
509  print_context = NULL;
510  print_handler = NULL;
511}
512
513void rtems_stack_checker_report_usage( void )
514{
515  rtems_stack_checker_report_usage_with_plugin( NULL, printk_plugin );
516}
Note: See TracBrowser for help on using the repository browser.