source: rtems/c/src/libmisc/stackchk/check.c @ 29310016

4.104.114.84.95
Last change on this file since 29310016 was 29310016, checked in by Joel Sherrill <joel.sherrill@…>, on 01/19/96 at 22:21:16

Added condition compile flag for dump of stack usage information on
system exit.

  • Property mode set to 100644
File size: 11.5 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, 1990, 1991, 1992, 1993, 1994.
10 *  On-Line Applications Research Corporation (OAR).
11 *  All rights assigned to U.S. Government, 1994.
12 *
13 *  This material may be reproduced by or for the U.S. Government pursuant
14 *  to the copyright license under the clause at DFARS 252.227-7013.  This
15 *  notice must appear in all copies of this file and its derivatives.
16 *
17 *  $Id$
18 *
19 */
20
21#include <rtems.h>
22
23extern rtems_configuration_table BSP_Configuration;
24
25#include <assert.h>
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29
30#include "stackchk.h"
31#include "internal.h"
32
33/*
34 *  This variable contains the name of the task which "blew" the stack.
35 *  It is NULL if the system is all right.
36 */
37
38Thread_Control *Stack_check_Blown_task;
39
40/*
41 *  The extension table for the stack checker.
42 */
43
44rtems_extensions_table Stack_check_Extension_table = {
45  Stack_check_Create_extension,     /* rtems_task_create  */
46  0,                                /* rtems_task_start   */
47  0,                                /* rtems_task_restart */
48  0,                                /* rtems_task_delete  */
49  Stack_check_Switch_extension,     /* task_switch  */
50  0,                                /* task_post_switch  */
51  Stack_check_Begin_extension,      /* task_begin   */
52  0,                                /* task_exitted */
53  Stack_check_Fatal_extension,      /* fatal        */
54};
55
56/*
57 *  The "magic pattern" used to mark the end of the stack.
58 */
59
60Stack_check_Control Stack_check_Pattern;
61
62/*
63 *  Where the pattern goes in the stack area is dependent upon
64 *  whether the stack grow to the high or low area of the memory.
65 *
66 */
67
68#if ( CPU_STACK_GROWS_UP == TRUE )
69
70#define Stack_check_Get_pattern_area( _the_stack ) \
71  ((Stack_check_Control *) \
72    ((_the_stack)->area + (_the_stack)->size - sizeof( Stack_check_Control ) ))
73
74#define Stack_check_Calculate_used( _low, _size, _high_water ) \
75    ((_high_water) - (_low))
76
77#define Stack_check_usable_stack_start(_the_stack) \
78    ((_the_stack)->area)
79
80#else
81
82#define Stack_check_Get_pattern_area( _the_stack ) \
83  ((Stack_check_Control *) ((_the_stack)->area + HEAP_OVERHEAD))
84
85#define Stack_check_Calculate_used( _low, _size, _high_water) \
86    ( ((_low) + (_size)) - (_high_water) )
87
88#define Stack_check_usable_stack_start(_the_stack) \
89    ((_the_stack)->area + sizeof(Stack_check_Control))
90
91#endif
92
93#define Stack_check_usable_stack_size(_the_stack) \
94    ((_the_stack)->size - sizeof(Stack_check_Control))
95
96
97/*
98 * Do we have an interrupt stack?
99 * XXX it would sure be nice if the interrupt stack were also
100 *     stored in a "stack" structure!
101 */
102
103
104Stack_Control stack_check_interrupt_stack;
105
106/*
107 * Prototypes necessary for forward references
108 */
109
110void Stack_check_Dump_usage( void );
111
112/*
113 * Fill an entire stack area with BYTE_PATTERN.
114 * This will be used by a Fatal extension to check for
115 * amount of actual stack used
116 */
117
118void
119stack_check_dope_stack(Stack_Control *stack)
120{
121    memset(stack->area, BYTE_PATTERN, stack->size);
122}
123
124
125/*PAGE
126 *
127 *  Stack_check_Initialize
128 */
129
130unsigned32 stack_check_initialized = 0;
131
132void Stack_check_Initialize( void )
133{
134  rtems_status_code    status;
135  Objects_Id           id_ignored;
136  unsigned32          *p;
137#if 0
138  unsigned32           i;
139  unsigned32           class_index;
140  Thread_Control      *the_thread;
141  Objects_Information *information;
142#endif
143
144  if (stack_check_initialized)
145      return;
146
147  /*
148   * Dope the pattern and fill areas
149   */
150
151  for ( p = Stack_check_Pattern.pattern;
152        p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS];
153        p += 4
154      )
155  {
156      p[0] = 0xFEEDF00D;          /* FEED FOOD to BAD DOG */
157      p[1] = 0x0BAD0D06;
158      p[2] = 0xDEADF00D;          /* DEAD FOOD GOOD DOG */
159      p[3] = 0x600D0D06;
160  };
161
162  status = rtems_extension_create(
163    rtems_build_name( 'S', 'T', 'C', 'K' ),
164    &Stack_check_Extension_table,
165    &id_ignored
166  );
167  assert ( status == RTEMS_SUCCESSFUL );
168
169  Stack_check_Blown_task = 0;
170
171#ifdef STACK_CHECKER_REPORT_USAGE
172  atexit( Stack_check_Dump_usage );
173#endif
174
175  /*
176   * If installed by a task, that task will not get setup properly
177   * since it missed out on the create hook.  This will cause a
178   * failure on first switch out of that task.
179   * So pretend here that we actually ran create and begin extensions.
180   */
181
182  /* XXX
183   *
184   *  Technically this has not been done for any task created before this
185   *  happened.  So just run through them and fix the situation.
186   */
187#if 0
188  if (_Thread_Executing)
189  {
190      Stack_check_Create_extension(_Thread_Executing, _Thread_Executing);
191  }
192#endif
193
194#if 0
195  for ( class_index = OBJECTS_CLASSES_FIRST ;
196        class_index <= OBJECTS_CLASSES_LAST ;
197        class_index++ ) {
198    information = _Objects_Information_table[ class_index ];
199    if ( information && information->is_thread ) {
200      for ( i=1 ; i <= information->maximum ; i++ ) {
201        the_thread = (Thread_Control *)information->local_table[ i ];
202        Stack_check_Create_extension( the_thread, the_thread );
203      }
204    }
205  }
206#endif
207
208  /*
209   * If appropriate, setup the interrupt stack for high water testing
210   * also.
211   */
212  if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high)
213  {
214      stack_check_interrupt_stack.area = _CPU_Interrupt_stack_low;
215      stack_check_interrupt_stack.size = _CPU_Interrupt_stack_high -
216                                               _CPU_Interrupt_stack_low;
217
218      stack_check_dope_stack(&stack_check_interrupt_stack);
219  }
220
221  stack_check_initialized = 1;
222}
223
224/*PAGE
225 *
226 *  Stack_check_Create_extension
227 */
228
229boolean Stack_check_Create_extension(
230  Thread_Control *running,
231  Thread_Control *the_thread
232)
233{
234    if (the_thread /* XXX && (the_thread != _Thread_Executing) */ )
235        stack_check_dope_stack(&the_thread->Start.Initial_stack);
236
237    return TRUE;
238}
239
240/*PAGE
241 *
242 *  Stack_check_Begin_extension
243 */
244
245void Stack_check_Begin_extension(
246  Thread_Control *the_thread
247)
248{
249  Stack_check_Control  *the_pattern;
250
251  if ( the_thread->Object.id == 0 )        /* skip system tasks */
252    return;
253
254  the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
255
256  *the_pattern = Stack_check_Pattern;
257}
258
259/*PAGE
260 *
261 *  Stack_check_report_blown_task
262 *  Report a blown stack.  Needs to be a separate routine
263 *  so that interrupt handlers can use this too.
264 *
265 *  Caller must have set the Stack_check_Blown_task.
266 *
267 *  NOTE: The system is in a questionable state... we may not get
268 *        the following message out.
269 */
270
271void Stack_check_report_blown_task(void)
272{
273    Stack_Control *stack;
274    Thread_Control *running;
275
276    running = Stack_check_Blown_task;
277    stack = &running->Start.Initial_stack;
278
279    fprintf(
280        stderr,
281        "BLOWN STACK!!! Offending task(%p): id=0x%08x; name=0x%08x",
282        running,
283        running->Object.id,
284        *(unsigned32 *)running->Object.name
285    );
286    fflush(stderr);
287
288    if (BSP_Configuration.User_multiprocessing_table)
289        fprintf(
290          stderr,
291          "; node=%d\n",
292          BSP_Configuration.User_multiprocessing_table->node
293       );
294    else
295        fprintf(stderr, "\n");
296    fflush(stderr);
297
298    fprintf(
299        stderr,
300        "  stack covers range 0x%08x - 0x%08x (%d bytes)\n",
301        (unsigned32) stack->area,
302        (unsigned32) stack->area + stack->size - 1,
303        (unsigned32) stack->size);
304    fflush(stderr);
305
306    fprintf(
307        stderr,
308        "  Damaged pattern begins at 0x%08x and is %d bytes long\n",
309        (unsigned32) Stack_check_Get_pattern_area(stack), PATTERN_SIZE_BYTES);
310    fflush(stderr);
311
312    rtems_fatal_error_occurred( (unsigned32) "STACK BLOWN" );
313}
314
315/*PAGE
316 *
317 *  Stack_check_Switch_extension
318 */
319
320void Stack_check_Switch_extension(
321  Thread_Control *running,
322  Thread_Control *heir
323)
324{
325  if ( running->Object.id == 0 )        /* skip system tasks */
326    return;
327
328  if (0 != memcmp( (void *) Stack_check_Get_pattern_area( &running->Start.Initial_stack)->pattern,
329                  (void *) Stack_check_Pattern.pattern,
330                  PATTERN_SIZE_BYTES))
331  {
332      Stack_check_Blown_task = running;
333      Stack_check_report_blown_task();
334  }
335}
336
337void *Stack_check_find_high_water_mark(
338  const void *s,
339  size_t n
340)
341{
342  const unsigned32 *base, *ebase;
343  unsigned32 length;
344
345  base = s;
346  length = n/4;
347
348#if ( CPU_STACK_GROWS_UP == TRUE )
349  /*
350   * start at higher memory and find first word that does not
351   * match pattern
352   */
353
354  base += length - 1;
355  for (ebase = s; base > ebase; base--)
356      if (*base != U32_PATTERN)
357          return (void *) base;
358#else
359  /*
360   * start at lower memory and find first word that does not
361   * match pattern
362   */
363
364  base += PATTERN_SIZE_WORDS;
365  for (ebase = base + length; base < ebase; base++)
366      if (*base != U32_PATTERN)
367          return (void *) base;
368#endif
369
370  return (void *)0;
371}
372
373/*PAGE
374 *
375 *  Stack_check_Dump_threads_usage
376 *  Try to print out how much stack was actually used by the task.
377 *
378 */
379
380void Stack_check_Dump_threads_usage(
381  Thread_Control *the_thread
382)
383{
384  unsigned32      size, used;
385  void           *low;
386  void           *high_water_mark;
387  Stack_Control  *stack;
388
389  if ( !the_thread )
390    return;
391
392  /*
393   * XXX HACK to get to interrupt stack
394   */
395
396  if (the_thread == (Thread_Control *) -1)
397  {
398      if (stack_check_interrupt_stack.area)
399      {
400          stack = &stack_check_interrupt_stack;
401          the_thread = 0;
402      }
403      else
404          return;
405  }
406  else
407      stack = &the_thread->Start.Initial_stack;
408
409  low  = Stack_check_usable_stack_start(stack);
410  size = Stack_check_usable_stack_size(stack);
411
412  high_water_mark = Stack_check_find_high_water_mark(low, size);
413
414  if ( high_water_mark )
415    used = Stack_check_Calculate_used( low, size, high_water_mark );
416  else
417    used = 0;
418
419  printf( "0x%08x  0x%08x  0x%08x  0x%08x   %8d   %8d\n",
420          the_thread ? the_thread->Object.id : ~0,
421          the_thread ? *(unsigned32 *)the_thread->Object.name :
422                       rtems_build_name('I', 'N', 'T', 'R'),
423          (unsigned32) stack->area,
424          (unsigned32) stack->area + (unsigned32) stack->size - 1,
425          size,
426          used
427  );
428}
429
430/*PAGE
431 *
432 *  Stack_check_Fatal_extension
433 */
434
435void Stack_check_Fatal_extension(
436    Internal_errors_Source  source,
437    boolean                 is_internal,
438    unsigned32              status
439)
440{
441    if (status == 0)
442        Stack_check_Dump_usage();
443}
444
445
446/*PAGE
447 *
448 *  Stack_check_Dump_usage
449 */
450
451void Stack_check_Dump_usage( void )
452{
453  unsigned32           i;
454  unsigned32           class_index;
455  Thread_Control      *the_thread;
456  unsigned32           hit_running = 0;
457  Objects_Information *information;
458
459  if (stack_check_initialized == 0)
460      return;
461
462  printf("Stack usage by thread\n");
463  printf(
464    "   ID          NAME         LOW        HIGH      AVAILABLE     USED\n"
465  );
466
467  for ( class_index = OBJECTS_CLASSES_FIRST ;
468        class_index <= OBJECTS_CLASSES_LAST ;
469        class_index++ ) {
470    information = _Objects_Information_table[ class_index ];
471    if ( information && information->is_thread ) {
472      for ( i=1 ; i <= information->maximum ; i++ ) {
473        the_thread = (Thread_Control *)information->local_table[ i ];
474        Stack_check_Dump_threads_usage( the_thread );
475        if ( the_thread == _Thread_Executing )
476          hit_running = 1;
477      }
478    }
479  }
480
481  if ( !hit_running )
482    Stack_check_Dump_threads_usage( _Thread_Executing );
483
484  /* dump interrupt stack info if any */
485  Stack_check_Dump_threads_usage((Thread_Control *) -1);
486}
487
Note: See TracBrowser for help on using the repository browser.