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

4.104.114.84.9
Last change on this file since dc2a1750 was 8583f82, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 8, 2007 at 9:43:05 PM

2007-03-08 Joel Sherrill <joel@…>

  • libmisc/stackchk/check.c, libmisc/stackchk/internal.h, libmisc/stackchk/stackchk.h: Change dump usage to report usage.
  • Property mode set to 100644
File size: 13.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-2006.
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 * HACK
29 * the stack dump information should be printed by a "fatal" extension.
30 * Fatal extensions only get called via rtems_fatal_error_occurred()
31 * and not when rtems_shutdown_executive() is called.
32 * I hope/think this is changing so that fatal extensions are renamed
33 * to "shutdown" extensions.
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 <assert.h>
40#include <stdio.h>
41#include <string.h>
42#include <stdlib.h>
43
44#include <rtems/stackchk.h>
45#include "internal.h"
46
47/*
48 *  This variable contains the name of the task which "blew" the stack.
49 *  It is NULL if the system is all right.
50 */
51
52Thread_Control *Stack_check_Blown_task;
53
54/*
55 *  The extension table for the stack checker.
56 */
57
58rtems_extensions_table rtems_stack_checker_extension_table = {
59  rtems_stack_checker_create_extension,     /* rtems_task_create  */
60  0,                                        /* rtems_task_start   */
61  0,                                        /* rtems_task_restart */
62  0,                                        /* rtems_task_delete  */
63  rtems_stack_checker_switch_extension,     /* task_switch  */
64  rtems_stack_checker_begin_extension,      /* task_begin   */ 0,                                /* task_exitted */
65#ifdef DONT_USE_FATAL_EXTENSION
66  0,                                        /* fatal        */
67#else
68  rtems_stack_checker_fatal_extension,      /* fatal        */
69#endif
70};
71
72/*
73 *  The "magic pattern" used to mark the end of the stack.
74 */
75
76Stack_check_Control Stack_check_Pattern;
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 */
83
84#if ( CPU_STACK_GROWS_UP == TRUE )
85
86#define Stack_check_Get_pattern_area( _the_stack ) \
87  ((Stack_check_Control *) ((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#define Stack_check_Get_pattern_area( _the_stack ) \
99  ((Stack_check_Control *) ((char *)(_the_stack)->area + HEAP_OVERHEAD))
100
101#define Stack_check_Calculate_used( _low, _size, _high_water) \
102    ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
103
104#define Stack_check_usable_stack_start(_the_stack) \
105    ((char *)(_the_stack)->area + sizeof(Stack_check_Control))
106
107#endif
108
109#define Stack_check_usable_stack_size(_the_stack) \
110    ((_the_stack)->size - sizeof(Stack_check_Control))
111
112
113/*
114 * Do we have an interrupt stack?
115 * XXX it would sure be nice if the interrupt stack were also
116 *     stored in a "stack" structure!
117 */
118
119
120Stack_Control stack_check_interrupt_stack;
121
122/*
123 * Fill an entire stack area with BYTE_PATTERN.
124 * This will be used by a Fatal extension to check for
125 * amount of actual stack used
126 */
127
128void
129stack_check_dope_stack(Stack_Control *stack)
130{
131    memset(stack->area, BYTE_PATTERN, stack->size);
132}
133
134
135/*PAGE
136 *
137 *  rtems_stack_checker_initialize
138 */
139
140static int   stack_check_initialized = 0;
141
142void rtems_stack_checker_initialize( void )
143{
144#if 0
145  rtems_status_code    status;
146  Objects_Id           id_ignored;
147#endif
148  uint32_t            *p;
149#if 0
150  uint32_t             i;
151  uint32_t             api_index;
152  Thread_Control      *the_thread;
153  Objects_Information *information;
154#endif
155
156  if (stack_check_initialized)
157      return;
158
159  /*
160   * Dope the pattern and fill areas
161   */
162
163  for ( p = Stack_check_Pattern.pattern;
164        p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS];
165        p += 4
166      )
167  {
168      p[0] = 0xFEEDF00D;          /* FEED FOOD to BAD DOG */
169      p[1] = 0x0BAD0D06;
170      p[2] = 0xDEADF00D;          /* DEAD FOOD GOOD DOG */
171      p[3] = 0x600D0D06;
172  };
173
174#if 0
175  status = rtems_extension_create(
176    rtems_build_name( 'S', 'T', 'C', 'K' ),
177    &Stack_check_Extension_table,
178    &id_ignored
179  );
180  assert ( status == RTEMS_SUCCESSFUL );
181#endif
182
183  Stack_check_Blown_task = 0;
184
185  /*
186   * If installed by a task, that task will not get setup properly
187   * since it missed out on the create hook.  This will cause a
188   * failure on first switch out of that task.
189   * So pretend here that we actually ran create and begin extensions.
190   */
191
192  /* XXX
193   *
194   *  Technically this has not been done for any task created before this
195   *  happened.  So just run through them and fix the situation.
196   */
197#if 0
198  if (_Thread_Executing) {
199      rtems_stack_checker_create_extension(
200        _Thread_Executing,
201        _Thread_Executing
202      );
203  }
204#endif
205
206#if 0
207  for ( api_index = 1;
208        api_index <= OBJECTS_APIS_LAST ;
209        api_index++ ) {
210    if ( !_Objects_Information_table[ api_index ] )
211      continue;
212    information = _Objects_Information_table[ api_index ][ 1 ];
213    if ( information ) {
214      for ( i=1 ; i <= information->maximum ; i++ ) {
215        the_thread = (Thread_Control *)information->local_table[ i ];
216        rtems_stack_checker_create_extension( the_thread, the_thread );
217      }
218    }
219  }
220#endif
221
222  /*
223   * If appropriate, setup the interrupt stack for high water testing
224   * also.
225   */
226#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
227  if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high)
228  {
229      stack_check_interrupt_stack.area = _CPU_Interrupt_stack_low;
230      stack_check_interrupt_stack.size = (char *) _CPU_Interrupt_stack_high -
231                                              (char *) _CPU_Interrupt_stack_low;
232
233      stack_check_dope_stack(&stack_check_interrupt_stack);
234  }
235#endif
236
237#ifdef DONT_USE_FATAL_EXTENSION
238#ifdef RTEMS_DEBUG
239    /*
240     * this would normally be called by a fatal extension
241     * handler, but we don't run fatal extensions unless
242     * we fatal error.
243     */
244  atexit(rtems_stack_checker_report_usage);
245#endif
246#endif
247
248  stack_check_initialized = 1;
249}
250
251/*PAGE
252 *
253 *  rtems_stack_checker_create_extension
254 */
255
256boolean rtems_stack_checker_create_extension(
257  Thread_Control *running,
258  Thread_Control *the_thread
259)
260{
261    if (!stack_check_initialized)
262      rtems_stack_checker_initialize();
263
264    if (the_thread /* XXX && (the_thread != _Thread_Executing) */ )
265        stack_check_dope_stack(&the_thread->Start.Initial_stack);
266
267    return TRUE;
268}
269
270/*PAGE
271 *
272 *  rtems_stack_checker_Begin_extension
273 */
274
275void rtems_stack_checker_begin_extension(
276  Thread_Control *the_thread
277)
278{
279  Stack_check_Control  *the_pattern;
280
281  if (!stack_check_initialized)
282    rtems_stack_checker_initialize();
283
284  if ( the_thread->Object.id == 0 )        /* skip system tasks */
285    return;
286
287  the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
288
289  *the_pattern = Stack_check_Pattern;
290}
291
292/*PAGE
293 *
294 *  Stack_check_report_blown_task
295 *  Report a blown stack.  Needs to be a separate routine
296 *  so that interrupt handlers can use this too.
297 *
298 *  Caller must have set the Stack_check_Blown_task.
299 *
300 *  NOTE: The system is in a questionable state... we may not get
301 *        the following message out.
302 */
303
304void Stack_check_report_blown_task(void)
305{
306    Stack_Control *stack;
307    Thread_Control *running;
308
309    running = Stack_check_Blown_task;
310    stack = &running->Start.Initial_stack;
311
312    fprintf(
313        stderr,
314        "BLOWN STACK!!! Offending task(%p): id=0x%08" PRIx32
315             "; name=0x%08" PRIx32,
316        running,
317        running->Object.id,
318        (uint32_t) running->Object.name
319    );
320    fflush(stderr);
321
322    if (rtems_configuration_get_user_multiprocessing_table())
323        fprintf(
324          stderr,
325          "; node=%" PRId32 "\n",
326          rtems_configuration_get_user_multiprocessing_table()->node
327       );
328    else
329        fprintf(stderr, "\n");
330    fflush(stderr);
331
332    fprintf(
333        stderr,
334        "  stack covers range %p - %p" PRIx32 " (%" PRId32 " bytes)\n",
335        stack->area,
336        stack->area + stack->size - 1,
337        stack->size);
338    fflush(stderr);
339
340    fprintf(
341        stderr,
342        "  Damaged pattern begins at 0x%08lx and is %ld bytes long\n",
343        (unsigned long) Stack_check_Get_pattern_area(stack),
344        (long) PATTERN_SIZE_BYTES);
345    fflush(stderr);
346
347    rtems_fatal_error_occurred( (uint32_t  ) "STACK BLOWN" );
348}
349
350/*PAGE
351 *
352 *  rtems_stack_checker_switch_extension
353 */
354
355void rtems_stack_checker_switch_extension(
356  Thread_Control *running,
357  Thread_Control *heir
358)
359{
360  void *pattern;
361  if ( running->Object.id == 0 )        /* skip system tasks */
362    return;
363  pattern = (void *)
364    Stack_check_Get_pattern_area(&running->Start.Initial_stack)->pattern;
365
366  if (0 != memcmp( pattern,
367            (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES)) {
368      Stack_check_Blown_task = running;
369      Stack_check_report_blown_task();
370  }
371}
372
373void *Stack_check_find_high_water_mark(
374  const void *s,
375  size_t n
376)
377{
378  const uint32_t   *base, *ebase;
379  uint32_t   length;
380
381  base = s;
382  length = n/4;
383
384#if ( CPU_STACK_GROWS_UP == TRUE )
385  /*
386   * start at higher memory and find first word that does not
387   * match pattern
388   */
389
390  base += length - 1;
391  for (ebase = s; base > ebase; base--)
392      if (*base != U32_PATTERN)
393          return (void *) base;
394#else
395  /*
396   * start at lower memory and find first word that does not
397   * match pattern
398   */
399
400  base += PATTERN_SIZE_WORDS;
401  for (ebase = base + length; base < ebase; base++)
402      if (*base != U32_PATTERN)
403          return (void *) base;
404#endif
405
406  return (void *)0;
407}
408
409/*PAGE
410 *
411 *  Stack_check_Dump_threads_usage(
412 *  Try to print out how much stack was actually used by the task.
413 *
414 */
415
416void Stack_check_Dump_threads_usage(
417  Thread_Control *the_thread
418)
419{
420  uint32_t        size, used;
421  void           *low;
422  void           *high_water_mark;
423  Stack_Control  *stack;
424  uint32_t        u32_name;
425  char            name_str[5];
426  char            *name;
427  Objects_Information *info;
428
429  if ( !the_thread )
430    return;
431
432  /*
433   * XXX HACK to get to interrupt stack
434   */
435
436  if (the_thread == (Thread_Control *) -1)
437  {
438      if (stack_check_interrupt_stack.area)
439      {
440          stack = &stack_check_interrupt_stack;
441          the_thread = 0;
442      }
443      else
444          return;
445  }
446  else
447      stack = &the_thread->Start.Initial_stack;
448
449  low  = Stack_check_usable_stack_start(stack);
450  size = Stack_check_usable_stack_size(stack);
451
452  high_water_mark = Stack_check_find_high_water_mark(low, size);
453
454  if ( high_water_mark )
455    used = Stack_check_Calculate_used( low, size, high_water_mark );
456  else
457    used = 0;
458
459  name = name_str;
460  if ( the_thread ) {
461    info = _Objects_Get_information(the_thread->Object.id);
462    if ( info->is_string ) {
463      name = (char *) the_thread->Object.name;
464    } else {
465      u32_name = (uint32_t )the_thread->Object.name;
466      name[ 0 ] = (u32_name >> 24) & 0xff;
467      name[ 1 ] = (u32_name >> 16) & 0xff;
468      name[ 2 ] = (u32_name >>  8) & 0xff;
469      name[ 3 ] = (u32_name >>  0) & 0xff;
470      name[ 4 ] = '\0';
471    }
472  } else {
473    u32_name = rtems_build_name('I', 'N', 'T', 'R');
474    name[ 0 ] = (u32_name >> 24) & 0xff;
475    name[ 1 ] = (u32_name >> 16) & 0xff;
476    name[ 2 ] = (u32_name >>  8) & 0xff;
477    name[ 3 ] = (u32_name >>  0) & 0xff;
478    name[ 4 ] = '\0';
479  }
480
481  fprintf(stdout, "0x%08" PRIx32 "  %4s  %p - %p"
482             "   %8" PRId32 "   %8" PRId32 "\n",
483          the_thread ? the_thread->Object.id : ~0,
484          name,
485          stack->area,
486          stack->area + stack->size - 1,
487          size,
488          used
489  );
490}
491
492/*PAGE
493 *
494 *  rtems_stack_checker_fatal_extension
495 */
496
497void rtems_stack_checker_fatal_extension(
498    Internal_errors_Source  source,
499    boolean                 is_internal,
500    uint32_t                status
501)
502{
503#ifndef DONT_USE_FATAL_EXTENSION
504    if (status == 0)
505        rtems_stack_checker_report_usage();
506#endif
507}
508
509
510/*PAGE
511 *
512 *  rtems_stack_checker_report_usage
513 */
514
515void rtems_stack_checker_report_usage( void )
516{
517  uint32_t             i;
518  uint32_t             api_index;
519  Thread_Control      *the_thread;
520  uint32_t             hit_running = 0;
521  Objects_Information *information;
522
523  if (stack_check_initialized == 0)
524      return;
525
526  fprintf(stdout,"Stack usage by thread\n");
527  fprintf(stdout,
528    "    ID      NAME       LOW        HIGH     AVAILABLE      USED\n"
529  );
530
531  for ( api_index = 1 ;
532        api_index <= OBJECTS_APIS_LAST ;
533        api_index++ ) {
534    if ( !_Objects_Information_table[ api_index ] )
535      continue;
536    information = _Objects_Information_table[ api_index ][ 1 ];
537    if ( information ) {
538      for ( i=1 ; i <= information->maximum ; i++ ) {
539        the_thread = (Thread_Control *)information->local_table[ i ];
540        Stack_check_Dump_threads_usage( the_thread );
541        if ( the_thread == _Thread_Executing )
542          hit_running = 1;
543      }
544    }
545  }
546
547  if ( !hit_running )
548    Stack_check_Dump_threads_usage( _Thread_Executing );
549
550  /* dump interrupt stack info if any */
551  Stack_check_Dump_threads_usage((Thread_Control *) -1);
552}
Note: See TracBrowser for help on using the repository browser.