source: rtems/cpukit/libmisc/stackchk/check.c @ 1737e8f

5
Last change on this file since 1737e8f was 1737e8f, checked in by Andrei Chichak <andrei@…>, on Nov 18, 2017 at 6:01:41 AM

libmisc/stackchk/check.c: correct formatting of stack pointers in Stack_check_Dump_threads_usage

Pointers were being printed as 0x<decimal> rather than 0x<hex>. I altered the formatting define used
to give the correct formatting.
Close #3240

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