source: rtems/cpukit/libmisc/stackchk/check.c @ 4da36c1a

4.104.114.84.9
Last change on this file since 4da36c1a was 4da36c1a, checked in by Joel Sherrill <joel.sherrill@…>, on May 11, 2007 at 8:01:37 PM

2007-05-11 Joel Sherrill <joel.sherrill@…>

  • libmisc/cpuuse/cpuuse.c, libmisc/stackchk/check.c, libmisc/stackchk/stackchk.h: Clean up as side-effect of making them suitable for inclusion in the Users Guide.
  • Property mode set to 100644
File size: 11.1 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-2007.
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 <assert.h>
37#include <string.h>
38#include <stdlib.h>
39
40#include <rtems/bspIo.h>
41#include <rtems/stackchk.h>
42#include "internal.h"
43
44/*
45 *  Variable to indicate when the stack checker has been initialized.
46 */
47static int   Stack_check_Initialized = 0;
48
49/*
50 *  The "magic pattern" used to mark the end of the stack.
51 */
52Stack_check_Control Stack_check_Pattern;
53
54/*
55 * Helper function to report if the actual stack pointer is in range.
56 *
57 * NOTE: This uses a GCC specific method.
58 */
59static inline boolean Stack_check_Frame_pointer_in_range(
60  Stack_Control *the_stack
61)
62{
63  void *sp = __builtin_frame_address(0);
64
65  #if defined(__GNUC__)
66    if ( sp < the_stack->area ) {
67      printk( "Stack Pointer Too Low!\n" );
68      return FALSE;
69    }
70    if ( sp > (the_stack->area + the_stack->size) ) {
71      printk( "Stack Pointer Too High!\n" );
72      return FALSE;
73    }
74  #else
75    #error "How do I check stack bounds on a non-GNU compiler?"
76  #endif
77  return TRUE;
78}
79
80/*
81 *  Where the pattern goes in the stack area is dependent upon
82 *  whether the stack grow to the high or low area of the memory.
83 */
84#if (CPU_STACK_GROWS_UP == TRUE)
85  #define Stack_check_Get_pattern_area( _the_stack ) \
86    ((Stack_check_Control *) ((char *)(_the_stack)->area + \
87         (_the_stack)->size - sizeof( Stack_check_Control ) ))
88
89  #define Stack_check_Calculate_used( _low, _size, _high_water ) \
90      ((char *)(_high_water) - (char *)(_low))
91
92  #define Stack_check_usable_stack_start(_the_stack) \
93    ((_the_stack)->area)
94
95#else
96  #define Stack_check_Get_pattern_area( _the_stack ) \
97    ((Stack_check_Control *) ((char *)(_the_stack)->area + HEAP_OVERHEAD))
98
99  #define Stack_check_Calculate_used( _low, _size, _high_water) \
100      ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
101
102  #define Stack_check_usable_stack_start(_the_stack) \
103      ((char *)(_the_stack)->area + sizeof(Stack_check_Control))
104
105#endif
106
107/*
108 *  The assumption is that if the pattern gets overwritten, the task
109 *  is too close.  This defines the usable stack memory.
110 */
111#define Stack_check_usable_stack_size(_the_stack) \
112    ((_the_stack)->size - sizeof(Stack_check_Control))
113
114/*
115 * Do we have an interrupt stack?
116 * XXX it would sure be nice if the interrupt stack were also
117 *     stored in a "stack" structure!
118 */
119Stack_Control Stack_check_Interrupt_stack;
120
121/*
122 *  Fill an entire stack area with BYTE_PATTERN.  This will be used
123 *  to check for amount of actual stack used.
124 */
125#define Stack_check_Dope_stack(_stack) \
126  memset((_stack)->area, BYTE_PATTERN, (_stack)->size)
127
128/*
129 *  Stack_check_Initialize
130 */
131void Stack_check_Initialize( void )
132{
133  uint32_t *p;
134
135  if (Stack_check_Initialized)
136    return;
137
138  /*
139   * Dope the pattern and fill areas
140   */
141
142  for ( p = Stack_check_Pattern.pattern;
143        p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS];
144        p += 4
145      ) {
146      p[0] = 0xFEEDF00D;          /* FEED FOOD to BAD DOG */
147      p[1] = 0x0BAD0D06;
148      p[2] = 0xDEADF00D;          /* DEAD FOOD GOOD DOG */
149      p[3] = 0x600D0D06;
150  }
151 
152  /*
153   * If appropriate, setup the interrupt stack for high water testing
154   * also.
155   */
156  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
157    if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) {
158      Stack_check_Interrupt_stack.area = _CPU_Interrupt_stack_low;
159      Stack_check_Interrupt_stack.size = (char *) _CPU_Interrupt_stack_high -
160                                  (char *) _CPU_Interrupt_stack_low;
161      Stack_check_Dope_stack(&Stack_check_Interrupt_stack);
162  }
163  #endif
164
165  Stack_check_Initialized = 1;
166}
167
168/*
169 *  rtems_stack_checker_create_extension
170 */
171boolean rtems_stack_checker_create_extension(
172  Thread_Control *running,
173  Thread_Control *the_thread
174)
175{
176  Stack_check_Initialize();
177
178  if (the_thread)
179    Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
180
181  return TRUE;
182}
183
184/*
185 *  rtems_stack_checker_Begin_extension
186 */
187void rtems_stack_checker_begin_extension(
188  Thread_Control *the_thread
189)
190{
191  Stack_check_Control  *the_pattern;
192
193  if ( the_thread->Object.id == 0 )        /* skip system tasks */
194    return;
195
196  the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
197
198  *the_pattern = Stack_check_Pattern;
199}
200
201/*
202 *  Stack_check_report_blown_task
203 *
204 *  Report a blown stack.  Needs to be a separate routine
205 *  so that interrupt handlers can use this too.
206 *
207 *  NOTE: The system is in a questionable state... we may not get
208 *        the following message out.
209 */
210void Stack_check_report_blown_task(
211  Thread_Control *running,
212  boolean         pattern_ok
213)
214{
215  Stack_Control *stack = &running->Start.Initial_stack;
216
217  printk(
218    "BLOWN STACK!!! Offending task(%p): id=0x%08" PRIx32 "; name=0x%08" PRIx32,
219    running,
220    running->Object.id,
221    (uint32_t) running->Object.name
222  );
223
224  #if defined(RTEMS_MULTIPROCESSING)
225    if (rtems_configuration_get_user_multiprocessing_table()) {
226      printk(
227        "; node=%d\n",
228        rtems_configuration_get_user_multiprocessing_table()->node
229      );
230    }
231  #else
232      printk( "\n" );
233  #endif
234
235  printk(
236    "  stack covers range %p - %p (%d bytes)\n",
237    stack->area,
238    stack->area + stack->size - 1,
239    stack->size
240  );
241
242  if ( !pattern_ok ) {
243    printk(
244      "  Damaged pattern begins at 0x%08lx and is %ld bytes long\n",
245      (unsigned long) Stack_check_Get_pattern_area(stack),
246      (long) PATTERN_SIZE_BYTES);
247  }
248
249  rtems_fatal_error_occurred( 0x81 );
250}
251
252/*
253 *  rtems_stack_checker_switch_extension
254 */
255void rtems_stack_checker_switch_extension(
256  Thread_Control *running,
257  Thread_Control *heir
258)
259{
260  Stack_Control *the_stack = &running->Start.Initial_stack;
261  void          *pattern;
262  boolean        sp_ok;
263  boolean        pattern_ok = TRUE;
264
265  pattern = (void *) Stack_check_Get_pattern_area(the_stack)->pattern;
266
267  /*
268   *  Check for an out of bounds stack pointer and then an overwrite
269   */
270
271  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
272  pattern_ok = (!memcmp( pattern,
273            (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES));
274
275  if ( !sp_ok || !pattern_ok ) {
276    Stack_check_report_blown_task( running, pattern_ok );
277  }
278}
279
280/*
281 *  Check if blown
282 */
283boolean rtems_stack_checker_is_blown( void )
284{
285  Stack_Control *the_stack = &_Thread_Executing->Start.Initial_stack;
286  void          *pattern;
287  boolean        sp_ok;
288  boolean        pattern_ok = TRUE;
289
290  pattern = (void *) Stack_check_Get_pattern_area(the_stack)->pattern;
291
292  /*
293   *  Check for an out of bounds stack pointer and then an overwrite
294   */
295
296  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
297  pattern_ok = (!memcmp( pattern,
298            (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES));
299
300  if ( !sp_ok || !pattern_ok ) {
301    return TRUE;
302  }
303  return FALSE;
304}
305
306/*
307 * Stack_check_find_high_water_mark
308 */
309void *Stack_check_find_high_water_mark(
310  const void *s,
311  size_t      n
312)
313{
314  const uint32_t   *base, *ebase;
315  uint32_t   length;
316
317  base = s;
318  length = n/4;
319
320  #if ( CPU_STACK_GROWS_UP == TRUE )
321    /*
322     * start at higher memory and find first word that does not
323     * match pattern
324     */
325
326    base += length - 1;
327    for (ebase = s; base > ebase; base--)
328        if (*base != U32_PATTERN)
329            return (void *) base;
330  #else
331    /*
332     * start at lower memory and find first word that does not
333     * match pattern
334     */
335
336    base += PATTERN_SIZE_WORDS;
337    for (ebase = base + length; base < ebase; base++)
338        if (*base != U32_PATTERN)
339            return (void *) base;
340  #endif
341
342  return (void *)0;
343}
344
345/*
346 *  Report Name
347 */
348
349char *Stack_check_Get_object_name(
350  Objects_Control  *the_object,
351  char            **name
352)
353{
354  Objects_Information *info;
355
356  info = _Objects_Get_information(the_object->id);
357  if ( info->is_string ) {
358    *name = (char *) the_object->name;
359  } else {
360    uint32_t  u32_name = (uint32_t) the_object->name;
361    (*name)[ 0 ] = (u32_name >> 24) & 0xff;
362    (*name)[ 1 ] = (u32_name >> 16) & 0xff;
363    (*name)[ 2 ] = (u32_name >>  8) & 0xff;
364    (*name)[ 3 ] = (u32_name >>  0) & 0xff;
365    (*name)[ 4 ] = '\0';
366  }
367  return *name;
368}
369
370/*
371 *  Stack_check_Dump_threads_usage(
372 *
373 *  Try to print out how much stack was actually used by the task.
374 */
375void Stack_check_Dump_threads_usage(
376  Thread_Control *the_thread
377)
378{
379  uint32_t        size, used;
380  void           *low;
381  void           *high_water_mark;
382  Stack_Control  *stack;
383  uint32_t        u32_name;
384  char            name_str[5];
385  char           *name;
386
387  if ( !the_thread )
388    return;
389
390  /*
391   * XXX HACK to get to interrupt stack
392   */
393
394  if (the_thread == (Thread_Control *) -1) {
395    if (Stack_check_Interrupt_stack.area) {
396      stack = &Stack_check_Interrupt_stack;
397      the_thread = 0;
398    }
399    else
400      return;
401  } else
402    stack = &the_thread->Start.Initial_stack;
403
404  low  = Stack_check_usable_stack_start(stack);
405  size = Stack_check_usable_stack_size(stack);
406
407  high_water_mark = Stack_check_find_high_water_mark(low, size);
408
409  if ( high_water_mark )
410    used = Stack_check_Calculate_used( low, size, high_water_mark );
411  else
412    used = 0;
413
414  name = name_str;
415  if ( the_thread ) {
416    name = Stack_check_Get_object_name( &the_thread->Object, &name );
417  } else {
418    u32_name = rtems_build_name('I', 'N', 'T', 'R');
419    name[ 0 ] = (u32_name >> 24) & 0xff;
420    name[ 1 ] = (u32_name >> 16) & 0xff;
421    name[ 2 ] = (u32_name >>  8) & 0xff;
422    name[ 3 ] = (u32_name >>  0) & 0xff;
423    name[ 4 ] = '\0';
424  }
425
426  printk("0x%08" PRIx32 "  %4s  %p - %p   %8" PRId32 "   %8" PRId32 "\n",
427    the_thread ? the_thread->Object.id : ~0,
428    name,
429    stack->area,
430    stack->area + stack->size - 1,
431    size,
432    used
433  );
434}
435
436/*
437 *  rtems_stack_checker_fatal_extension
438 */
439#ifndef DONT_USE_FATAL_EXTENSION
440  void rtems_stack_checker_fatal_extension(
441    Internal_errors_Source  source,
442    boolean                 is_internal,
443    uint32_t                status
444  )
445  {
446    if (status == 0)
447      rtems_stack_checker_report_usage();
448  }
449#endif
450
451/*PAGE
452 *
453 *  rtems_stack_checker_report_usage
454 */
455
456void rtems_stack_checker_report_usage( void )
457{
458  if (Stack_check_Initialized == 0)
459      return;
460
461  printk("Stack usage by thread\n");
462  printk(
463    "    ID      NAME       LOW        HIGH     AVAILABLE      USED\n"
464  );
465
466  /* iterate over all threads and dump the usage */
467  rtems_iterate_over_all_threads( Stack_check_Dump_threads_usage );
468
469  /* dump interrupt stack info if any */
470  Stack_check_Dump_threads_usage((Thread_Control *) -1);
471}
Note: See TracBrowser for help on using the repository browser.