source: rtems/cpukit/libmisc/stackchk/check.c @ 506bfc8

5
Last change on this file since 506bfc8 was 506bfc8, checked in by Sebastian Huber <sebastian.huber@…>, on 06/21/16 at 11:30:15

Move printer initialization to separate header

The RTEMS print user need to know nothing about a particular printer
implementation. In particular get rid of the <stdio.h> include which
would be visible via <rtems.h>.

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