forked from Imagelibrary/rtems
Initial revision
This commit is contained in:
16
c/src/lib/libmisc/README
Normal file
16
c/src/lib/libmisc/README
Normal file
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
This directory contains for the "miscellaneous" library. Currently
|
||||
the only item in this library is a user extension set which checks
|
||||
for a task "blowing" it's stack.
|
||||
|
||||
The following ideas have been mentioned for items which could go
|
||||
in this library, but this list is not all inclusive:
|
||||
|
||||
+ Workspace Consistency Checker
|
||||
+ Task Execution Time Monitor
|
||||
|
||||
The intent of this library is to provide a home for useful utility routines
|
||||
which are dependent upon RTEMS.
|
||||
7
c/src/lib/libmisc/monitor/README
Normal file
7
c/src/lib/libmisc/monitor/README
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
This is a snapshot of a work in process. It is the beginnings of a
|
||||
debug monitor task and trap handler which is tasking aware.
|
||||
|
||||
307
c/src/lib/libmisc/monitor/mon-monitor.c
Normal file
307
c/src/lib/libmisc/monitor/mon-monitor.c
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* @(#)monitor.c 1.6 - 95/04/24
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* mon-task.c
|
||||
*
|
||||
* Description:
|
||||
* RTEMS monitor task
|
||||
*
|
||||
*
|
||||
*
|
||||
* TODO:
|
||||
* add pause command (monitor sleeps for 'n' ticks, then wakes up)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <rtems.h>
|
||||
/* #include <bsp.h> */
|
||||
|
||||
#include "symbols.h"
|
||||
#include "monitor.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define STREQ(a,b) (strcmp(a,b) == 0)
|
||||
|
||||
/* set by trap handler */
|
||||
extern rtems_tcb *debugger_interrupted_task;
|
||||
extern rtems_context *debugger_interrupted_task_context;
|
||||
extern rtems_unsigned32 debugger_trap;
|
||||
|
||||
/* our task id needs to be public so any debugger can resume us */
|
||||
rtems_unsigned32 rtems_monitor_task_id;
|
||||
|
||||
|
||||
rtems_symbol_table_t *rtems_monitor_symbols;
|
||||
|
||||
|
||||
#ifndef MONITOR_PROMPT
|
||||
#define MONITOR_PROMPT "rtems> "
|
||||
#endif
|
||||
|
||||
#define MONITOR_WAKEUP_EVENT RTEMS_EVENT_0
|
||||
|
||||
/*
|
||||
* Function: rtems_monitor_init
|
||||
*
|
||||
* Description:
|
||||
* Create the RTEMS monitor task
|
||||
*
|
||||
* Parameters:
|
||||
* 'monitor_suspend' arg is passed as initial arg to monitor task
|
||||
* If TRUE, monitor will suspend itself as it starts up. Otherwise
|
||||
* it will begin its command loop.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
*
|
||||
* Side Effects:
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
*
|
||||
* Deficiencies/ToDo:
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* make_argv(cp): token-count
|
||||
* Break up the command line in 'cp' into global argv[] and argc (return
|
||||
* value).
|
||||
*/
|
||||
|
||||
int
|
||||
rtems_monitor_make_argv(
|
||||
char *cp,
|
||||
int *argc_p,
|
||||
char **argv)
|
||||
{
|
||||
int argc = 0;
|
||||
|
||||
while ((cp = strtok(cp, " \t\n\r")))
|
||||
{
|
||||
argv[argc++] = cp;
|
||||
cp = (char *) NULL;
|
||||
}
|
||||
argv[argc] = (char *) NULL; /* end of argv */
|
||||
|
||||
return *argc_p = argc;
|
||||
}
|
||||
|
||||
void
|
||||
rtems_monitor_init(rtems_boolean monitor_suspend)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
status = rtems_task_create(rtems_build_name('R', 'M', 'O', 'N'),
|
||||
1, 0/*stack*/, RTEMS_NO_PREEMPT | RTEMS_INTERRUPT_LEVEL(0), RTEMS_DEFAULT_ATTRIBUTES, &rtems_monitor_task_id);
|
||||
if (status != RTEMS_SUCCESSFUL)
|
||||
{
|
||||
printf("could not create monitor task\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
rtems_monitor_symbols_loadup();
|
||||
|
||||
status = rtems_task_start(rtems_monitor_task_id, rtems_monitor_task, monitor_suspend);
|
||||
if (status != RTEMS_SUCCESSFUL)
|
||||
{
|
||||
printf("could not start monitor!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
}
|
||||
|
||||
rtems_status_code
|
||||
rtems_monitor_suspend(rtems_interval timeout)
|
||||
{
|
||||
rtems_event_set event_set;
|
||||
rtems_status_code status;
|
||||
|
||||
status = rtems_event_receive(MONITOR_WAKEUP_EVENT, RTEMS_DEFAULT_OPTIONS, timeout, &event_set);
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
rtems_monitor_wakeup(void)
|
||||
{
|
||||
rtems_status_code status;
|
||||
|
||||
status = rtems_event_send(rtems_monitor_task_id, MONITOR_WAKEUP_EVENT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read and break up a monitor command
|
||||
*
|
||||
* We have to loop on the gets call, since it will return NULL under UNIX
|
||||
* RTEMS when we get a signal (eg: SIGALRM).
|
||||
*/
|
||||
|
||||
int
|
||||
rtems_monitor_read_command(char *command,
|
||||
int *argc,
|
||||
char **argv)
|
||||
{
|
||||
printf("%s", MONITOR_PROMPT); fflush(stdout);
|
||||
while (gets(command) == (char *) 0)
|
||||
;
|
||||
return rtems_monitor_make_argv(command, argc, argv);
|
||||
}
|
||||
|
||||
void
|
||||
rtems_monitor_task(rtems_task_argument monitor_suspend)
|
||||
{
|
||||
rtems_tcb *debugee = 0;
|
||||
char command[513];
|
||||
rtems_context *rp;
|
||||
rtems_context_fp *fp;
|
||||
char *cp;
|
||||
int argc;
|
||||
char *argv[64];
|
||||
|
||||
if ((rtems_boolean) monitor_suspend)
|
||||
(void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
extern rtems_tcb * _Thread_Executing;
|
||||
debugee = _Thread_Executing;
|
||||
rp = &debugee->Registers;
|
||||
fp = (rtems_context_fp *) debugee->fp_context; /* possibly 0 */
|
||||
|
||||
if (0 == rtems_monitor_read_command(command, &argc, argv))
|
||||
continue;
|
||||
|
||||
if (STREQ(argv[0], "quit"))
|
||||
rtems_monitor_suspend(RTEMS_NO_TIMEOUT);
|
||||
else if (STREQ(argv[0], "pause"))
|
||||
rtems_monitor_suspend(1);
|
||||
|
||||
#ifdef CPU_INVOKE_DEBUGGER
|
||||
else if (STREQ(argv[0], "debug"))
|
||||
{
|
||||
CPU_INVOKE_DEBUGGER;
|
||||
}
|
||||
#endif
|
||||
else if (STREQ(argv[0], "symbol"))
|
||||
{
|
||||
char *symbol;
|
||||
char *value;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("usage: symbol symname symvalue\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
symbol = argv[1];
|
||||
value = argv[2];
|
||||
if (symbol && value)
|
||||
{
|
||||
rtems_symbol_t *sp;
|
||||
sp = rtems_symbol_create(rtems_monitor_symbols,
|
||||
symbol,
|
||||
(rtems_unsigned32) strtoul(value, 0, 16));
|
||||
if (sp)
|
||||
printf("symbol defined is at %p\n", sp);
|
||||
else
|
||||
printf("could not define symbol\n");
|
||||
}
|
||||
else
|
||||
printf("parsing error\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unrecognized command: '%s'\n", argv[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: rtems_monitor_symbols_loadup
|
||||
*
|
||||
* Description:
|
||||
* Create and load the monitor's symbol table.
|
||||
* We are reading the output format of 'gnm' which looks like this:
|
||||
*
|
||||
* 400a7068 ? _Rate_monotonic_Information
|
||||
* 400a708c ? _Thread_Dispatch_disable_level
|
||||
* 400a7090 ? _Configuration_Table
|
||||
*
|
||||
*
|
||||
* We ignore the type field.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
*
|
||||
* Side Effects:
|
||||
* Creates and fills in 'rtems_monitor_symbols' table
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
*
|
||||
* Deficiencies/ToDo:
|
||||
* Someday this should know BFD
|
||||
* Maybe we could get objcopy to just copy the symbol areas
|
||||
* and copy that down.
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
rtems_monitor_symbols_loadup(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char buffer[128];
|
||||
|
||||
rtems_monitor_symbols = rtems_symbol_table_create(10);
|
||||
if (rtems_monitor_symbols == 0)
|
||||
return;
|
||||
|
||||
fp = fdopen(8, "r");
|
||||
if (fp == 0)
|
||||
return;
|
||||
|
||||
while (fgets(buffer, sizeof(buffer) - 1, fp))
|
||||
{
|
||||
char *symbol;
|
||||
char *value;
|
||||
char *ignored_type;
|
||||
|
||||
value = strtok(buffer, " \t\n");
|
||||
ignored_type = strtok(0, " \t\n");
|
||||
symbol = strtok(0, " \t\n");
|
||||
|
||||
if (symbol && ignored_type && value)
|
||||
{
|
||||
rtems_symbol_t *sp;
|
||||
sp = rtems_symbol_create(rtems_monitor_symbols,
|
||||
symbol,
|
||||
(rtems_unsigned32) strtoul(value, 0, 16));
|
||||
if (sp == 0)
|
||||
{
|
||||
printf("could not define symbol\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("parsing error\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
}
|
||||
327
c/src/lib/libmisc/monitor/mon-symbols.c
Normal file
327
c/src/lib/libmisc/monitor/mon-symbols.c
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* @(#)symbols.c 1.3 - 95/04/24
|
||||
*
|
||||
*/
|
||||
|
||||
/* #define qsort _quicksort */
|
||||
|
||||
/*
|
||||
* File: symbols.c
|
||||
*
|
||||
* Description:
|
||||
* Symbol table manager for the RTEMS monitor.
|
||||
* These routines may be used by other system resources also.
|
||||
*
|
||||
*
|
||||
* TODO:
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rtems.h>
|
||||
|
||||
#include "symbols.h"
|
||||
|
||||
extern rtems_symbol_table_t *rtems_monitor_symbols;
|
||||
|
||||
#ifdef RTEMS_DEBUG
|
||||
#define CHK_ADR_PTR(p) \
|
||||
do { \
|
||||
if (((p) < rtems_monitor_symbols->addresses) || \
|
||||
((p) >= (rtems_monitor_symbols->addresses + rtems_monitor_symbols->next))) \
|
||||
{ \
|
||||
printf("bad address pointer %p\n", (p)); \
|
||||
rtems_fatal_error_occurred(RTEMS_INVALID_ADDRESS); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHK_NAME_PTR(p) \
|
||||
do { \
|
||||
if (((p) < rtems_monitor_symbols->symbols) || \
|
||||
((p) >= (rtems_monitor_symbols->symbols + rtems_monitor_symbols->next))) \
|
||||
{ \
|
||||
printf("bad symbol pointer %p\n", (p)); \
|
||||
rtems_fatal_error_occurred(RTEMS_INVALID_ADDRESS); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define CHK_ADR_PTR(p)
|
||||
#define CHK_NAME_PTR(p)
|
||||
#endif
|
||||
|
||||
rtems_symbol_table_t *
|
||||
rtems_symbol_table_create()
|
||||
{
|
||||
rtems_symbol_table_t *table;
|
||||
|
||||
table = (rtems_symbol_table_t *) malloc(sizeof(rtems_symbol_table_t));
|
||||
memset((void *) table, 0, sizeof(*table));
|
||||
|
||||
table->growth_factor = 30; /* 30 percent */
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void
|
||||
rtems_symbol_table_destroy(rtems_symbol_table_t *table)
|
||||
{
|
||||
rtems_symbol_string_block_t *p, *pnext;
|
||||
|
||||
if (table)
|
||||
{
|
||||
if (table->addresses)
|
||||
(void) free(table->addresses);
|
||||
table->addresses = 0;
|
||||
|
||||
if (table->symbols)
|
||||
(void) free(table->symbols);
|
||||
table->symbols = 0;
|
||||
|
||||
p = table->string_buffer_head;
|
||||
while (p)
|
||||
{
|
||||
pnext = p->next;
|
||||
free(p);
|
||||
p = pnext;
|
||||
}
|
||||
table->string_buffer_head = 0;
|
||||
table->string_buffer_current = 0;
|
||||
|
||||
free(table);
|
||||
}
|
||||
}
|
||||
|
||||
rtems_symbol_t *
|
||||
rtems_symbol_create(
|
||||
rtems_symbol_table_t *table,
|
||||
char *name,
|
||||
rtems_unsigned32 value
|
||||
)
|
||||
{
|
||||
int symbol_length;
|
||||
size_t newsize;
|
||||
rtems_symbol_t *sp;
|
||||
|
||||
symbol_length = strlen(name) + 1; /* include '\000' in length */
|
||||
|
||||
/* need to grow the table? */
|
||||
if (table->next >= table->size)
|
||||
{
|
||||
if (table->size == 0)
|
||||
newsize = 100;
|
||||
else
|
||||
newsize = table->size + (table->size / (100 / table->growth_factor));
|
||||
|
||||
table->addresses = (rtems_symbol_t *) realloc((void *) table->addresses, newsize * sizeof(rtems_symbol_t));
|
||||
if (table->addresses == 0) /* blew it; lost orig */
|
||||
goto failed;
|
||||
|
||||
table->symbols = (rtems_symbol_t *) realloc((void *) table->symbols, newsize * sizeof(rtems_symbol_t));
|
||||
if (table->symbols == 0) /* blew it; lost orig */
|
||||
goto failed;
|
||||
|
||||
table->size = newsize;
|
||||
}
|
||||
|
||||
sp = &table->addresses[table->next];
|
||||
sp->value = value;
|
||||
|
||||
/* Have to add it to string pool */
|
||||
/* need to grow pool? */
|
||||
|
||||
if ((table->string_buffer_head == 0) ||
|
||||
(table->strings_next + symbol_length) >= SYMBOL_STRING_BLOCK_SIZE)
|
||||
{
|
||||
rtems_symbol_string_block_t *p;
|
||||
|
||||
p = (rtems_symbol_string_block_t *) malloc(sizeof(rtems_symbol_string_block_t));
|
||||
if (p == 0)
|
||||
goto failed;
|
||||
p->next = 0;
|
||||
if (table->string_buffer_head == 0)
|
||||
table->string_buffer_head = p;
|
||||
else
|
||||
table->string_buffer_current->next = p;
|
||||
table->string_buffer_current = p;
|
||||
|
||||
table->strings_next = 0;
|
||||
}
|
||||
|
||||
sp->name = table->string_buffer_current->buffer + table->strings_next;
|
||||
(void) strcpy(sp->name, name);
|
||||
|
||||
table->strings_next += symbol_length;
|
||||
|
||||
table->symbols[table->next] = *sp;
|
||||
|
||||
table->sorted = 0;
|
||||
table->next++;
|
||||
|
||||
return sp;
|
||||
|
||||
/* XXX Not sure what to do here. We've possibly destroyed the initial
|
||||
symbol table due to realloc failure */
|
||||
failed:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Qsort entry point for compare by address
|
||||
*/
|
||||
|
||||
int
|
||||
rtems_symbol_compare(const void *e1,
|
||||
const void *e2)
|
||||
{
|
||||
rtems_symbol_t *s1, *s2;
|
||||
s1 = (rtems_symbol_t *) e1;
|
||||
s2 = (rtems_symbol_t *) e2;
|
||||
|
||||
CHK_ADR_PTR(s1);
|
||||
CHK_ADR_PTR(s2);
|
||||
|
||||
if (s1->value < s2->value)
|
||||
return -1;
|
||||
if (s1->value > s2->value)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Qsort entry point for compare by string name (case independent)
|
||||
*/
|
||||
|
||||
int
|
||||
rtems_symbol_string_compare(const void *e1,
|
||||
const void *e2)
|
||||
{
|
||||
rtems_symbol_t *s1, *s2;
|
||||
s1 = (rtems_symbol_t *) e1;
|
||||
s2 = (rtems_symbol_t *) e2;
|
||||
|
||||
CHK_NAME_PTR(s1);
|
||||
CHK_NAME_PTR(s2);
|
||||
|
||||
return strcasecmp(s1->name, s2->name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sort the symbol table using qsort
|
||||
*/
|
||||
|
||||
void
|
||||
rtems_symbol_sort(rtems_symbol_table_t *table)
|
||||
{
|
||||
#ifdef simhppa
|
||||
printf("Sorting symbols ... "); /* so slow we need a msg */
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
qsort((void *) table->addresses, (size_t) table->next,
|
||||
sizeof(rtems_symbol_t), rtems_symbol_compare);
|
||||
|
||||
qsort((void *) table->symbols, (size_t) table->next,
|
||||
sizeof(rtems_symbol_t), rtems_symbol_string_compare);
|
||||
|
||||
#ifdef simhppa
|
||||
/* so slow we need a msg */
|
||||
printf("done\n");
|
||||
#endif
|
||||
|
||||
table->sorted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the symbol table by address
|
||||
* This code based on CYGNUS newlib bsearch, but changed
|
||||
* to allow for finding closest symbol <= key
|
||||
*/
|
||||
|
||||
rtems_symbol_t *
|
||||
rtems_symbol_value_lookup(
|
||||
rtems_symbol_table_t *table,
|
||||
rtems_unsigned32 value
|
||||
)
|
||||
{
|
||||
rtems_symbol_t *sp;
|
||||
rtems_symbol_t *base;
|
||||
rtems_symbol_t *best = 0;
|
||||
rtems_unsigned32 distance;
|
||||
rtems_unsigned32 best_distance = ~0;
|
||||
rtems_unsigned32 elements;
|
||||
|
||||
if ((table == 0) || (table->size == 0))
|
||||
return 0;
|
||||
|
||||
if (table->sorted == 0)
|
||||
rtems_symbol_sort(table);
|
||||
|
||||
base = table->addresses;
|
||||
elements = table->next;
|
||||
|
||||
while (elements)
|
||||
{
|
||||
sp = base + (elements / 2);
|
||||
if (value < sp->value)
|
||||
elements /= 2;
|
||||
else if (value > sp->value)
|
||||
{
|
||||
distance = value - sp->value;
|
||||
if (distance < best_distance)
|
||||
{
|
||||
best_distance = distance;
|
||||
best = sp;
|
||||
}
|
||||
base = sp + 1;
|
||||
elements = (elements / 2) - (elements % 2 ? 0 : 1);
|
||||
}
|
||||
else
|
||||
return sp;
|
||||
}
|
||||
|
||||
if (value == base->value)
|
||||
return base;
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the symbol table by string name (case independent)
|
||||
*/
|
||||
|
||||
rtems_symbol_t *
|
||||
rtems_symbol_name_lookup(
|
||||
rtems_symbol_table_t *table,
|
||||
char *name
|
||||
)
|
||||
{
|
||||
rtems_symbol_t *sp = 0;
|
||||
rtems_symbol_t key;
|
||||
|
||||
if ((table == 0) || (name == 0))
|
||||
goto done;
|
||||
|
||||
if (table->sorted == 0)
|
||||
{
|
||||
rtems_symbol_sort(table);
|
||||
}
|
||||
|
||||
/*
|
||||
* dummy up one for bsearch()
|
||||
*/
|
||||
|
||||
key.name = name;
|
||||
key.value = 0;
|
||||
|
||||
sp = (rtems_symbol_t *) bsearch((const void *) &key,
|
||||
(const void *) table->symbols,
|
||||
(size_t) table->next,
|
||||
sizeof(rtems_symbol_t),
|
||||
rtems_symbol_string_compare);
|
||||
|
||||
done:
|
||||
return sp;
|
||||
}
|
||||
|
||||
38
c/src/lib/libmisc/monitor/monitor.h
Normal file
38
c/src/lib/libmisc/monitor/monitor.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* @(#)monitor.h 1.2 - 95/04/24
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* File: monitor.h
|
||||
*
|
||||
* Description:
|
||||
* The RTEMS monitor task include file.
|
||||
*
|
||||
*
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MONITOR_H
|
||||
#define __MONITOR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void rtems_monitor_init(rtems_boolean monitor_suspend);
|
||||
void rtems_monitor_wakeup(void);
|
||||
void rtems_monitor_task(rtems_task_argument monitor_suspend);
|
||||
void rtems_monitor_symbols_loadup(void);
|
||||
|
||||
extern rtems_unsigned32 rtems_monitor_task_id;
|
||||
|
||||
extern rtems_symbol_table_t *rtems_monitor_symbols;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ! __MONITOR_H */
|
||||
80
c/src/lib/libmisc/monitor/symbols.h
Normal file
80
c/src/lib/libmisc/monitor/symbols.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* File: symbols.h
|
||||
*
|
||||
* Description:
|
||||
* Entry points for symbol table routines.
|
||||
*
|
||||
*
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SYMBOLS_H
|
||||
#define _INCLUDE_SYMBOLS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
rtems_unsigned32 value;
|
||||
char *name;
|
||||
} rtems_symbol_t;
|
||||
|
||||
#define SYMBOL_STRING_BLOCK_SIZE 4080
|
||||
typedef struct rtems_symbol_string_block_s {
|
||||
struct rtems_symbol_string_block_s *next;
|
||||
char buffer[SYMBOL_STRING_BLOCK_SIZE];
|
||||
} rtems_symbol_string_block_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
rtems_unsigned32 sorted; /* are symbols sorted right now? */
|
||||
|
||||
rtems_unsigned32 growth_factor; /* % to grow by when needed */
|
||||
|
||||
rtems_unsigned32 next; /* next symbol slot to use when adding */
|
||||
rtems_unsigned32 size; /* max # of symbols */
|
||||
|
||||
/*
|
||||
* Symbol list -- sorted by address (when we do a lookup)
|
||||
*/
|
||||
|
||||
rtems_symbol_t *addresses; /* symbol array by address */
|
||||
|
||||
/*
|
||||
* String list -- sorted by name (when we do a lookup)
|
||||
* This is a duplicate of the info in table->addresses, but it's
|
||||
* pretty small, so I don't worry about it.
|
||||
*/
|
||||
|
||||
rtems_symbol_t *symbols; /* symbol array */
|
||||
|
||||
/*
|
||||
* String pool, unsorted, a list of blocks of string data
|
||||
*/
|
||||
|
||||
rtems_symbol_string_block_t *string_buffer_head;
|
||||
rtems_symbol_string_block_t *string_buffer_current;
|
||||
rtems_unsigned32 strings_next; /* next byte to use in this block */
|
||||
|
||||
} rtems_symbol_table_t;
|
||||
|
||||
void rtems_symbol_table_destroy(rtems_symbol_table_t *table);
|
||||
rtems_symbol_table_t *rtems_symbol_table_create();
|
||||
rtems_symbol_t *rtems_symbol_create(rtems_symbol_table_t *,
|
||||
char *, rtems_unsigned32);
|
||||
rtems_symbol_t *rtems_symbol_value_lookup(rtems_symbol_table_t *,
|
||||
rtems_unsigned32);
|
||||
rtems_symbol_t *rtems_symbol_name_lookup(rtems_symbol_table_t *,
|
||||
char *);
|
||||
|
||||
#define rtems_symbol_name(sp) ((sp)->name)
|
||||
#define rtems_symbol_value(sp) ((sp)->value)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ! _INCLUDE_SYMBOLS_H */
|
||||
41
c/src/lib/libmisc/stackchk/README
Normal file
41
c/src/lib/libmisc/stackchk/README
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
This directory contains a stack bounds checker. It provides two
|
||||
primary features:
|
||||
|
||||
+ check for stack overflow at each context switch
|
||||
+ provides an educated guess at each task's stack usage
|
||||
|
||||
The stack overflow check at context switch works by looking for
|
||||
a 16 byte pattern at the logical end of the stack to be corrupted.
|
||||
The "guesser" assumes that the entire stack was prefilled with a known
|
||||
pattern and assumes that the pattern is still in place if the memory
|
||||
has not been used as a stack.
|
||||
|
||||
Both of these can be fooled by pushing large holes onto the stack
|
||||
and not writing to them... or (much more unlikely) writing the
|
||||
magic patterns into memory.
|
||||
|
||||
This code has not been extensively tested. It is provided as a tool
|
||||
for RTEMS users to catch the most common mistake in multitasking
|
||||
systems ... too little stack space. Suggestions and comments are appreciated.
|
||||
|
||||
NOTES:
|
||||
|
||||
1. Stack usage information is questionable on CPUs which push
|
||||
large holes on stack.
|
||||
|
||||
2. The stack checker has a tendency to generate a fault when
|
||||
trying to print the helpful diagnostic message. If it comes
|
||||
out, congratulations. If not, then the variable Stack_check_Blown_task
|
||||
contains a pointer to the TCB of the offending task. This
|
||||
is usually enough to go on.
|
||||
|
||||
FUTURE:
|
||||
|
||||
1. Determine how/if gcc will generate stack probe calls and support that.
|
||||
|
||||
2. Get accurate stack usage numbers on i960.. it pushes very large
|
||||
holes on the stack.
|
||||
439
c/src/lib/libmisc/stackchk/check.c
Normal file
439
c/src/lib/libmisc/stackchk/check.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Stack Overflow Check User Extension Set
|
||||
*
|
||||
* NOTE: This extension set automatically determines at
|
||||
* initialization time whether the stack for this
|
||||
* CPU grows up or down and installs the correct
|
||||
* extension routines for that direction.
|
||||
*
|
||||
* COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
* All rights assigned to U.S. Government, 1994.
|
||||
*
|
||||
* This material may be reproduced by or for the U.S. Government pursuant
|
||||
* to the copyright license under the clause at DFARS 252.227-7013. This
|
||||
* notice must appear in all copies of this file and its derivatives.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <rtems/system.h>
|
||||
#include <rtems/extension.h>
|
||||
#include <rtems/fatal.h>
|
||||
#include <rtems/heap.h>
|
||||
#include <rtems/stack.h>
|
||||
#include <rtems/thread.h>
|
||||
#ifdef XXX_RTEMS_H_FIXED
|
||||
#include <bsp.h>
|
||||
#else
|
||||
#include <rtems/config.h>
|
||||
extern rtems_configuration_table BSP_Configuration;
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "stackchk.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* This variable contains the name of the task which "blew" the stack.
|
||||
* It is NULL if the system is all right.
|
||||
*/
|
||||
|
||||
Thread_Control *Stack_check_Blown_task;
|
||||
|
||||
/*
|
||||
* The extension table for the stack checker.
|
||||
*/
|
||||
|
||||
rtems_extensions_table Stack_check_Extension_table = {
|
||||
Stack_check_Create_extension, /* rtems_task_create */
|
||||
0, /* rtems_task_start */
|
||||
0, /* rtems_task_restart */
|
||||
0, /* rtems_task_delete */
|
||||
Stack_check_Switch_extension, /* task_switch */
|
||||
Stack_check_Begin_extension, /* task_begin */
|
||||
0, /* task_exitted */
|
||||
Stack_check_Fatal_extension, /* fatal */
|
||||
};
|
||||
|
||||
/*
|
||||
* The "magic pattern" used to mark the end of the stack.
|
||||
*/
|
||||
|
||||
Stack_check_Control Stack_check_Pattern;
|
||||
|
||||
/*
|
||||
* Where the pattern goes in the stack area is dependent upon
|
||||
* whether the stack grow to the high or low area of the memory.
|
||||
*
|
||||
*/
|
||||
|
||||
#if ( CPU_STACK_GROWS_UP == TRUE )
|
||||
|
||||
#define Stack_check_Get_pattern_area( _the_stack ) \
|
||||
((Stack_check_Control *) \
|
||||
((_the_stack)->area + (_the_stack)->size - sizeof( Stack_check_Control ) ))
|
||||
|
||||
#define Stack_check_Calculate_used( _low, _size, _high_water ) \
|
||||
((_high_water) - (_low))
|
||||
|
||||
#define Stack_check_usable_stack_start(_the_stack) \
|
||||
((_the_stack)->area)
|
||||
|
||||
#else
|
||||
|
||||
#define Stack_check_Get_pattern_area( _the_stack ) \
|
||||
((Stack_check_Control *) ((_the_stack)->area + HEAP_OVERHEAD))
|
||||
|
||||
#define Stack_check_Calculate_used( _low, _size, _high_water) \
|
||||
( ((_low) + (_size)) - (_high_water) )
|
||||
|
||||
#define Stack_check_usable_stack_start(_the_stack) \
|
||||
((_the_stack)->area + sizeof(Stack_check_Control))
|
||||
|
||||
#endif
|
||||
|
||||
#define Stack_check_usable_stack_size(_the_stack) \
|
||||
((_the_stack)->size - sizeof(Stack_check_Control))
|
||||
|
||||
|
||||
/*
|
||||
* Do we have an interrupt stack?
|
||||
* XXX it would sure be nice if the interrupt stack were also
|
||||
* stored in a "stack" structure!
|
||||
*/
|
||||
|
||||
|
||||
Stack_Control stack_check_interrupt_stack;
|
||||
|
||||
/*
|
||||
* Fill an entire stack area with BYTE_PATTERN.
|
||||
* This will be used by a Fatal extension to check for
|
||||
* amount of actual stack used
|
||||
*/
|
||||
|
||||
void
|
||||
stack_check_dope_stack(Stack_Control *stack)
|
||||
{
|
||||
memset(stack->area, BYTE_PATTERN, stack->size);
|
||||
}
|
||||
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Initialize
|
||||
*/
|
||||
|
||||
unsigned32 stack_check_initialized = 0;
|
||||
|
||||
void Stack_check_Initialize( void )
|
||||
{
|
||||
rtems_status_code status;
|
||||
Objects_Id id_ignored;
|
||||
unsigned32 *p;
|
||||
|
||||
if (stack_check_initialized)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Dope the pattern and fill areas
|
||||
*/
|
||||
|
||||
for ( p = Stack_check_Pattern.pattern;
|
||||
p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS];
|
||||
p += 4
|
||||
)
|
||||
{
|
||||
p[0] = 0xFEEDF00D; /* FEED FOOD to BAD DOG */
|
||||
p[1] = 0x0BAD0D06;
|
||||
p[2] = 0xDEADF00D; /* DEAD FOOD GOOD DOG */
|
||||
p[3] = 0x600D0D06;
|
||||
};
|
||||
|
||||
status = rtems_extension_create(
|
||||
rtems_build_name( 'S', 'T', 'C', 'K' ),
|
||||
&Stack_check_Extension_table,
|
||||
&id_ignored
|
||||
);
|
||||
assert ( status == RTEMS_SUCCESSFUL );
|
||||
|
||||
Stack_check_Blown_task = 0;
|
||||
|
||||
/*
|
||||
* If installed by a task, that task will not get setup properly
|
||||
* since it missed out on the create hook. This will cause a
|
||||
* failure on first switch out of that task.
|
||||
* So pretend here that we actually ran create and begin extensions.
|
||||
*/
|
||||
|
||||
if (_Thread_Executing)
|
||||
{
|
||||
Stack_check_Create_extension(_Thread_Executing, _Thread_Executing);
|
||||
}
|
||||
|
||||
/*
|
||||
* If appropriate, setup the interrupt stack for high water testing
|
||||
* also.
|
||||
*/
|
||||
if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high)
|
||||
{
|
||||
stack_check_interrupt_stack.area = _CPU_Interrupt_stack_low;
|
||||
stack_check_interrupt_stack.size = _CPU_Interrupt_stack_high -
|
||||
_CPU_Interrupt_stack_low;
|
||||
|
||||
stack_check_dope_stack(&stack_check_interrupt_stack);
|
||||
}
|
||||
|
||||
stack_check_initialized = 1;
|
||||
}
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Create_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Create_extension(
|
||||
Thread_Control *running,
|
||||
Thread_Control *the_thread
|
||||
)
|
||||
{
|
||||
if (the_thread && (the_thread != _Thread_Executing))
|
||||
stack_check_dope_stack(&the_thread->Start.Initial_stack);
|
||||
}
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Begin_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Begin_extension(
|
||||
Thread_Control *the_thread
|
||||
)
|
||||
{
|
||||
Stack_check_Control *the_pattern;
|
||||
|
||||
if ( the_thread->Object.id == 0 ) /* skip system tasks */
|
||||
return;
|
||||
|
||||
the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
|
||||
|
||||
*the_pattern = Stack_check_Pattern;
|
||||
}
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_report_blown_task
|
||||
* Report a blown stack. Needs to be a separate routine
|
||||
* so that interrupt handlers can use this too.
|
||||
*
|
||||
* Caller must have set the Stack_check_Blown_task.
|
||||
*
|
||||
* NOTE: The system is in a questionable state... we may not get
|
||||
* the following message out.
|
||||
*/
|
||||
|
||||
void Stack_check_report_blown_task(void)
|
||||
{
|
||||
Stack_Control *stack;
|
||||
Thread_Control *running;
|
||||
|
||||
running = Stack_check_Blown_task;
|
||||
stack = &running->Start.Initial_stack;
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"BLOWN STACK!!! Offending task(%p): id=0x%08x; name=0x%08x",
|
||||
running,
|
||||
running->Object.id,
|
||||
running->name);
|
||||
fflush(stderr);
|
||||
|
||||
if (BSP_Configuration.User_multiprocessing_table)
|
||||
fprintf(
|
||||
stderr,
|
||||
"; node=%d\n",
|
||||
BSP_Configuration.User_multiprocessing_table->node
|
||||
);
|
||||
else
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
" stack covers range 0x%08x - 0x%08x (%d bytes)\n",
|
||||
(unsigned32) stack->area,
|
||||
(unsigned32) stack->area + stack->size - 1,
|
||||
(unsigned32) stack->size);
|
||||
fflush(stderr);
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
" Damaged pattern begins at 0x%08x and is %d bytes long\n",
|
||||
(unsigned32) Stack_check_Get_pattern_area(stack), PATTERN_SIZE_BYTES);
|
||||
fflush(stderr);
|
||||
|
||||
rtems_fatal_error_occurred( (unsigned32) "STACK BLOWN" );
|
||||
}
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Switch_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Switch_extension(
|
||||
Thread_Control *running,
|
||||
Thread_Control *heir
|
||||
)
|
||||
{
|
||||
if ( running->Object.id == 0 ) /* skip system tasks */
|
||||
return;
|
||||
|
||||
if (0 != memcmp( (void *) Stack_check_Get_pattern_area( &running->Start.Initial_stack)->pattern,
|
||||
(void *) Stack_check_Pattern.pattern,
|
||||
PATTERN_SIZE_BYTES))
|
||||
{
|
||||
Stack_check_Blown_task = running;
|
||||
Stack_check_report_blown_task();
|
||||
}
|
||||
}
|
||||
|
||||
void *Stack_check_find_high_water_mark(
|
||||
const void *s,
|
||||
size_t n
|
||||
)
|
||||
{
|
||||
const unsigned32 *base, *ebase;
|
||||
unsigned32 length;
|
||||
|
||||
base = s;
|
||||
length = n/4;
|
||||
|
||||
#if ( CPU_STACK_GROWS_UP == TRUE )
|
||||
/*
|
||||
* start at higher memory and find first word that does not
|
||||
* match pattern
|
||||
*/
|
||||
|
||||
base += length - 1;
|
||||
for (ebase = s; base > ebase; base--)
|
||||
if (*base != U32_PATTERN)
|
||||
return (void *) base;
|
||||
#else
|
||||
/*
|
||||
* start at lower memory and find first word that does not
|
||||
* match pattern
|
||||
*/
|
||||
|
||||
for (ebase = base + length; base < ebase; base++)
|
||||
if (*base != U32_PATTERN)
|
||||
return (void *) base;
|
||||
#endif
|
||||
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Dump_threads_usage
|
||||
* Try to print out how much stack was actually used by the task.
|
||||
*
|
||||
*/
|
||||
|
||||
void Stack_check_Dump_threads_usage(
|
||||
Thread_Control *the_thread
|
||||
)
|
||||
{
|
||||
unsigned32 size, used;
|
||||
void *low;
|
||||
void *high_water_mark;
|
||||
Stack_Control *stack;
|
||||
|
||||
if ( !the_thread )
|
||||
return;
|
||||
|
||||
/*
|
||||
* XXX HACK to get to interrupt stack
|
||||
*/
|
||||
|
||||
if (the_thread == (Thread_Control *) -1)
|
||||
{
|
||||
if (stack_check_interrupt_stack.area)
|
||||
{
|
||||
stack = &stack_check_interrupt_stack;
|
||||
the_thread = 0;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
else
|
||||
stack = &the_thread->Start.Initial_stack;
|
||||
|
||||
low = Stack_check_usable_stack_start(stack);
|
||||
size = Stack_check_usable_stack_size(stack);
|
||||
|
||||
high_water_mark = Stack_check_find_high_water_mark(low, size);
|
||||
|
||||
if ( high_water_mark )
|
||||
used = Stack_check_Calculate_used( low, size, high_water_mark );
|
||||
else
|
||||
used = 0;
|
||||
|
||||
printf( "0x%08x 0x%08x 0x%08x 0x%08x %8d %8d\n",
|
||||
the_thread ? the_thread->Object.id : ~0,
|
||||
the_thread ? the_thread->name :
|
||||
rtems_build_name('I', 'N', 'T', 'R'),
|
||||
(unsigned32) stack->area,
|
||||
(unsigned32) stack->area + (unsigned32) stack->size - 1,
|
||||
size,
|
||||
used
|
||||
);
|
||||
}
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Fatal_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Fatal_extension( unsigned32 status )
|
||||
{
|
||||
if (status == 0)
|
||||
Stack_check_Dump_usage();
|
||||
}
|
||||
|
||||
|
||||
/*PAGE
|
||||
*
|
||||
* Stack_check_Dump_usage
|
||||
*/
|
||||
|
||||
void Stack_check_Dump_usage( void )
|
||||
{
|
||||
unsigned32 i;
|
||||
Thread_Control *the_thread;
|
||||
unsigned32 hit_running = 0;
|
||||
|
||||
if (stack_check_initialized == 0)
|
||||
return;
|
||||
|
||||
printf(
|
||||
" ID NAME LOW HIGH AVAILABLE USED\n"
|
||||
);
|
||||
for ( i=1 ; i<_Thread_Information.maximum ; i++ ) {
|
||||
the_thread = (Thread_Control *)_Thread_Information.local_table[ i ];
|
||||
Stack_check_Dump_threads_usage( the_thread );
|
||||
if ( the_thread == _Thread_Executing )
|
||||
hit_running = 1;
|
||||
}
|
||||
|
||||
if ( !hit_running )
|
||||
Stack_check_Dump_threads_usage( _Thread_Executing );
|
||||
|
||||
/* dump interrupt stack info if any */
|
||||
Stack_check_Dump_threads_usage((Thread_Control *) -1);
|
||||
}
|
||||
|
||||
94
c/src/lib/libmisc/stackchk/internal.h
Normal file
94
c/src/lib/libmisc/stackchk/internal.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* internal.h
|
||||
*
|
||||
* This include file contains internal information
|
||||
* for the RTEMS stack checker.
|
||||
*
|
||||
* COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
* All rights assigned to U.S. Government, 1994.
|
||||
*
|
||||
* This material may be reproduced by or for the U.S. Government pursuant
|
||||
* to the copyright license under the clause at DFARS 252.227-7013. This
|
||||
* notice must appear in all copies of this file and its derivatives.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef __INTERNAL_STACK_CHECK_h
|
||||
#define __INTERNAL_STACK_CHECK_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This structure is used to fill in and compare the "end of stack"
|
||||
* marker pattern.
|
||||
* pattern area must be a multiple of 4 words.
|
||||
*/
|
||||
|
||||
#ifdef CPU_STACK_CHECK_SIZE
|
||||
#define PATTERN_SIZE_WORDS (((CPU_STACK_CHECK_SIZE / 4) + 3) & ~0x3)
|
||||
#else
|
||||
#define PATTERN_SIZE_WORDS 4
|
||||
#endif
|
||||
|
||||
#define PATTERN_SIZE_BYTES (PATTERN_SIZE_WORDS * 4)
|
||||
|
||||
typedef struct {
|
||||
unsigned32 pattern[ PATTERN_SIZE_WORDS ];
|
||||
} Stack_check_Control;
|
||||
|
||||
/*
|
||||
* The pattern used to fill the entire stack.
|
||||
*/
|
||||
|
||||
#define BYTE_PATTERN 0xA5
|
||||
#define U32_PATTERN 0xA5A5A5A5
|
||||
|
||||
/*
|
||||
* Stack_check_Create_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Create_extension(
|
||||
Thread_Control *running,
|
||||
Thread_Control *the_thread
|
||||
);
|
||||
|
||||
/*
|
||||
* Stack_check_Begin_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Begin_extension(
|
||||
Thread_Control *the_thread
|
||||
);
|
||||
|
||||
/*
|
||||
* Stack_check_Switch_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Switch_extension(
|
||||
Thread_Control *running,
|
||||
Thread_Control *heir
|
||||
);
|
||||
|
||||
/*
|
||||
* Stack_check_Fatal_extension
|
||||
*/
|
||||
|
||||
void Stack_check_Fatal_extension(
|
||||
unsigned32
|
||||
);
|
||||
|
||||
/*
|
||||
* Stack_check_Dump_usage
|
||||
*/
|
||||
|
||||
void Stack_check_Dump_usage( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/* end of include file */
|
||||
41
c/src/lib/libmisc/stackchk/stackchk.h
Normal file
41
c/src/lib/libmisc/stackchk/stackchk.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* stackchk.h
|
||||
*
|
||||
* This include file contains information necessary to utilize
|
||||
* and install the stack checker mechanism.
|
||||
*
|
||||
* COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
* All rights assigned to U.S. Government, 1994.
|
||||
*
|
||||
* This material may be reproduced by or for the U.S. Government pursuant
|
||||
* to the copyright license under the clause at DFARS 252.227-7013. This
|
||||
* notice must appear in all copies of this file and its derivatives.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef __STACK_CHECK_h
|
||||
#define __STACK_CHECK_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Stack_check_Initialize
|
||||
*/
|
||||
|
||||
void Stack_check_Initialize( void );
|
||||
|
||||
/*
|
||||
* Stack_check_Dump_usage
|
||||
*/
|
||||
|
||||
void Stack_check_Dump_usage( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/* end of include file */
|
||||
Reference in New Issue
Block a user