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