source: rtems/cpukit/libmisc/stackchk/check.c @ 143696a

5
Last change on this file since 143696a was 143696a, checked in by Sebastian Huber <sebastian.huber@…>, on 10/16/15 at 06:15:03

basedefs.h: Add and use RTEMS_NO_RETURN

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