source: rtems/cpukit/libmisc/stackchk/check.c @ d22cd4a

4.104.115
Last change on this file since d22cd4a was d22cd4a, checked in by Joel Sherrill <joel.sherrill@…>, on 01/19/10 at 01:56:28

2010-01-18 Joel Sherrill <joel.sherrill@…>

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