source: rtems/cpukit/libmisc/stackchk/check.c @ 65e8f25

4.104.11
Last change on this file since 65e8f25 was 65e8f25, checked in by Joel Sherrill <joel.sherrill@…>, on Feb 11, 2009 at 7:33:44 PM

2009-02-11 Joel Sherrill <joel.sherrill@…>

PR 1374/misc

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