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

5
Last change on this file since bba988d was bba988d, checked in by Sebastian Huber <sebastian.huber@…>, on 09/07/16 at 11:46:40

stackchk: Remove superfluous internal header file

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