mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-09 00:53:16 +00:00
604 lines
14 KiB
C
604 lines
14 KiB
C
/*
|
|
* RTEMS monitor main body
|
|
*
|
|
* TODO:
|
|
* add stuff to RTEMS api
|
|
* rtems_get_name(id)
|
|
* rtems_get_type(id)
|
|
* rtems_build_id(node, type, num)
|
|
* Add a command to dump out info about an arbitrary id when
|
|
* types are added to id's
|
|
* rtems> id idnum
|
|
* idnum: node n, object: whatever, id: whatever
|
|
* allow id's to be specified as n:t:id, where 'n:t' is optional
|
|
* should have a separate monitor FILE stream (ala the debugger)
|
|
* remote request/response stuff should be cleaned up
|
|
* maybe we can use real rpc??
|
|
* 'info' command to print out:
|
|
* interrupt stack location, direction and size
|
|
* floating point config stuff
|
|
* interrupt config stuff
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <rtems.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include <rtems/monitor.h>
|
|
|
|
/* set by trap handler */
|
|
extern rtems_tcb *debugger_interrupted_task;
|
|
extern rtems_context *debugger_interrupted_task_context;
|
|
extern rtems_unsigned32 debugger_trap;
|
|
|
|
/*
|
|
* Various id's for the monitor
|
|
* They need to be public variables for access by other agencies
|
|
* such as debugger and remote servers'
|
|
*/
|
|
|
|
rtems_id rtems_monitor_task_id;
|
|
|
|
unsigned32 rtems_monitor_node; /* our node number */
|
|
unsigned32 rtems_monitor_default_node; /* current default for commands */
|
|
|
|
/*
|
|
* The rtems symbol table
|
|
*/
|
|
|
|
rtems_symbol_table_t *rtems_monitor_symbols;
|
|
|
|
/*
|
|
* User registered commands.
|
|
*/
|
|
|
|
rtems_monitor_command_entry_t rtems_registered_commands;
|
|
|
|
/*
|
|
* The top-level commands
|
|
*/
|
|
|
|
rtems_monitor_command_entry_t rtems_monitor_commands[] = {
|
|
{ "config",
|
|
"Show the system configuration.",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_CONFIG,
|
|
&rtems_monitor_commands[1],
|
|
},
|
|
{ "itask",
|
|
"List init tasks for the system",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_INIT_TASK,
|
|
&rtems_monitor_commands[2],
|
|
},
|
|
{ "mpci",
|
|
"Show the MPCI system configuration, if configured.",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_MPCI,
|
|
&rtems_monitor_commands[3],
|
|
},
|
|
{ "pause",
|
|
"Monitor goes to \"sleep\" for specified ticks (default is 1). "
|
|
"Monitor will resume at end of period or if explicitly awakened\n"
|
|
" pause [ticks]",
|
|
0,
|
|
rtems_monitor_pause_cmd,
|
|
0,
|
|
&rtems_monitor_commands[4],
|
|
},
|
|
{ "continue",
|
|
"Put the monitor to sleep waiting for an explicit wakeup from the "
|
|
"program running.\n",
|
|
0,
|
|
rtems_monitor_continue_cmd,
|
|
0,
|
|
&rtems_monitor_commands[5],
|
|
},
|
|
{ "go",
|
|
"Alias for 'continue'",
|
|
0,
|
|
rtems_monitor_continue_cmd,
|
|
0,
|
|
&rtems_monitor_commands[6],
|
|
},
|
|
{ "node",
|
|
"Specify default node number for commands that take id's.\n"
|
|
" node [ node number ]",
|
|
0,
|
|
rtems_monitor_node_cmd,
|
|
0,
|
|
&rtems_monitor_commands[7],
|
|
},
|
|
{ "symbol",
|
|
"Display value associated with specified symbol. "
|
|
"Defaults to displaying all known symbols.\n"
|
|
" symbol [ symbolname [symbolname ... ] ]",
|
|
0,
|
|
rtems_monitor_symbol_cmd,
|
|
#if defined(RTEMS_CPU_HAS_16_BIT_ADDRESSES)
|
|
0, /* XXX find a way to fix the compile time error on h8 */
|
|
#else
|
|
(unsigned32) &rtems_monitor_symbols,
|
|
#endif
|
|
&rtems_monitor_commands[8],
|
|
},
|
|
{ "extension",
|
|
"Display information about specified extensions. "
|
|
"Default is to display information about all extensions on this node.\n"
|
|
" extension [id [id ...] ]",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_EXTENSION,
|
|
&rtems_monitor_commands[9],
|
|
},
|
|
{ "task",
|
|
"Display information about the specified tasks. "
|
|
"Default is to display information about all tasks on this node.\n"
|
|
" task [id [id ...] ]",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_TASK,
|
|
&rtems_monitor_commands[10],
|
|
},
|
|
{ "queue",
|
|
"Display information about the specified message queues. "
|
|
"Default is to display information about all queues on this node.\n"
|
|
" queue [id [id ... ] ]",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_QUEUE,
|
|
&rtems_monitor_commands[11],
|
|
},
|
|
{ "object",
|
|
"Display information about specified RTEMS objects. "
|
|
"Object id's must include 'type' information. "
|
|
"(which may normally be defaulted)\n"
|
|
" object [id [id ...] ]",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_INVALID,
|
|
&rtems_monitor_commands[12],
|
|
},
|
|
{ "driver",
|
|
"Display the RTEMS device driver table.\n"
|
|
" driver [ major [ major ... ] ]",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_DRIVER,
|
|
&rtems_monitor_commands[13],
|
|
},
|
|
{ "dname",
|
|
"Displays information about named drivers.\n",
|
|
0,
|
|
rtems_monitor_object_cmd,
|
|
RTEMS_MONITOR_OBJECT_DNAME,
|
|
&rtems_monitor_commands[14],
|
|
},
|
|
{ "exit",
|
|
"Invoke 'rtems_fatal_error_occurred' with 'status' "
|
|
"(default is RTEMS_SUCCESSFUL)\n"
|
|
" exit [status]",
|
|
0,
|
|
rtems_monitor_fatal_cmd,
|
|
RTEMS_SUCCESSFUL,
|
|
&rtems_monitor_commands[15],
|
|
},
|
|
{ "fatal",
|
|
"'exit' with fatal error; default error is RTEMS_TASK_EXITTED\n"
|
|
" fatal [status]",
|
|
0,
|
|
rtems_monitor_fatal_cmd,
|
|
RTEMS_TASK_EXITTED, /* exit value */
|
|
&rtems_monitor_commands[16],
|
|
},
|
|
{ "quit",
|
|
"Alias for 'exit'\n",
|
|
0,
|
|
rtems_monitor_fatal_cmd,
|
|
RTEMS_SUCCESSFUL, /* exit value */
|
|
&rtems_monitor_commands[17],
|
|
},
|
|
{ "help",
|
|
"Provide information about commands. "
|
|
"Default is show basic command summary.\n"
|
|
"help [ command [ command ] ]",
|
|
0,
|
|
rtems_monitor_help_cmd,
|
|
#if defined(RTEMS_CPU_HAS_16_BIT_ADDRESSES)
|
|
0, /* XXX find a way to fix the compile time error on h8 */
|
|
#else
|
|
(unsigned32) rtems_monitor_commands,
|
|
#endif
|
|
&rtems_monitor_commands[18],
|
|
},
|
|
#ifdef CPU_INVOKE_DEBUGGER
|
|
{ "debugger",
|
|
"Enter the debugger, if possible. "
|
|
"A continue from the debugger will return to the monitor.\n",
|
|
0,
|
|
rtems_monitor_debugger_cmd,
|
|
0,
|
|
&rtems_monitor_commands[19],
|
|
},
|
|
#endif
|
|
{ 0, 0, 0, 0, 0, &rtems_registered_commands },
|
|
};
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
rtems_monitor_debugger_cmd(
|
|
int argc,
|
|
char **argv,
|
|
unsigned32 command_arg,
|
|
boolean verbose
|
|
)
|
|
{
|
|
#ifdef CPU_INVOKE_DEBUGGER
|
|
CPU_INVOKE_DEBUGGER;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
rtems_monitor_pause_cmd(
|
|
int argc,
|
|
char **argv,
|
|
unsigned32 command_arg,
|
|
boolean verbose
|
|
)
|
|
{
|
|
if (argc == 1)
|
|
rtems_monitor_suspend(1);
|
|
else
|
|
rtems_monitor_suspend(strtoul(argv[1], 0, 0));
|
|
}
|
|
|
|
void
|
|
rtems_monitor_fatal_cmd(
|
|
int argc,
|
|
char **argv,
|
|
unsigned32 command_arg,
|
|
boolean verbose
|
|
)
|
|
{
|
|
if (argc == 1)
|
|
rtems_fatal_error_occurred(command_arg);
|
|
else
|
|
rtems_fatal_error_occurred(strtoul(argv[1], 0, 0));
|
|
}
|
|
|
|
void
|
|
rtems_monitor_continue_cmd(
|
|
int argc,
|
|
char **argv,
|
|
unsigned32 command_arg,
|
|
boolean verbose
|
|
)
|
|
{
|
|
rtems_monitor_suspend(RTEMS_NO_TIMEOUT);
|
|
}
|
|
|
|
void
|
|
rtems_monitor_node_cmd(
|
|
int argc,
|
|
char **argv,
|
|
unsigned32 command_arg,
|
|
boolean verbose
|
|
)
|
|
{
|
|
unsigned32 new_node = rtems_monitor_default_node;
|
|
|
|
switch (argc)
|
|
{
|
|
case 1: /* no node, just set back to ours */
|
|
new_node = rtems_monitor_node;
|
|
break;
|
|
|
|
case 2:
|
|
new_node = strtoul(argv[1], 0, 0);
|
|
break;
|
|
|
|
default:
|
|
printf("invalid syntax, try 'help node'\n");
|
|
break;
|
|
}
|
|
|
|
if ((new_node >= 1) &&
|
|
_Configuration_MP_table &&
|
|
(new_node <= _Configuration_MP_table->maximum_nodes))
|
|
rtems_monitor_default_node = new_node;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* Side Effects:
|
|
* Creates and fills in 'rtems_monitor_symbols' table
|
|
*
|
|
* TODO
|
|
* there should be a BSP #define or something like that
|
|
* to do this; Assuming stdio is crazy.
|
|
* 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];
|
|
|
|
if (rtems_monitor_symbols)
|
|
rtems_symbol_table_destroy(rtems_monitor_symbols);
|
|
|
|
rtems_monitor_symbols = rtems_symbol_table_create(10);
|
|
if (rtems_monitor_symbols == 0)
|
|
return;
|
|
|
|
fp = fopen("symbols", "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 '%s'\n", symbol);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("parsing error on '%s'\n", buffer);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* User registered commands.
|
|
*/
|
|
|
|
int
|
|
rtems_monitor_insert_cmd (
|
|
rtems_monitor_command_entry_t *command
|
|
)
|
|
{
|
|
rtems_monitor_command_entry_t *p = rtems_registered_commands.next;
|
|
|
|
command->next = 0;
|
|
|
|
if (rtems_registered_commands.next)
|
|
{
|
|
for (; p->next; p = p->next)
|
|
{
|
|
if (STREQ(command->command, p->command))
|
|
return 0;
|
|
}
|
|
p->next = command;
|
|
}
|
|
else
|
|
rtems_registered_commands.next = command;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
rtems_monitor_erase_cmd (
|
|
rtems_monitor_command_entry_t *command
|
|
)
|
|
{
|
|
rtems_monitor_command_entry_t *p;
|
|
rtems_monitor_command_entry_t **p_prev = &rtems_registered_commands.next;
|
|
|
|
if (rtems_registered_commands.next)
|
|
{
|
|
for (p = rtems_registered_commands.next; p->next; p = p->next)
|
|
{
|
|
if (STREQ(command->command, p->command))
|
|
{
|
|
*p_prev = p->next;
|
|
return 1;
|
|
}
|
|
p_prev = &p->next;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Main monitor command loop
|
|
*/
|
|
|
|
void
|
|
rtems_monitor_task(
|
|
rtems_task_argument monitor_flags
|
|
)
|
|
{
|
|
rtems_tcb *debugee = 0;
|
|
rtems_context *rp;
|
|
rtems_context_fp *fp;
|
|
char command_buffer[513];
|
|
int argc;
|
|
char *argv[64];
|
|
boolean verbose = FALSE;
|
|
struct termios term;
|
|
|
|
/*
|
|
* Make the stdin stream characte not line based.
|
|
*/
|
|
|
|
if (tcgetattr (STDIN_FILENO, &term) < 0)
|
|
{
|
|
printf("rtems-monitor: cannot get terminal attributes.\n");
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* No echo, no canonical processing.
|
|
*/
|
|
|
|
term.c_lflag &= ~(ECHO | ICANON | IEXTEN);
|
|
|
|
/*
|
|
* No sigint on BREAK, CR-to-NL off, input parity off,
|
|
* don't strip 8th bit on input, output flow control off
|
|
*/
|
|
|
|
term.c_lflag &= ~(INPCK | ISTRIP | IXON);
|
|
term.c_cc[VMIN] = 1;
|
|
term.c_cc[VTIME] = 0;
|
|
|
|
if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0)
|
|
{
|
|
printf("cannot set terminal attributes\n");
|
|
}
|
|
}
|
|
|
|
if (monitor_flags & RTEMS_MONITOR_SUSPEND)
|
|
(void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT);
|
|
|
|
for (;;)
|
|
{
|
|
extern rtems_tcb * _Thread_Executing;
|
|
rtems_monitor_command_entry_t *command;
|
|
|
|
debugee = _Thread_Executing;
|
|
rp = &debugee->Registers;
|
|
#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE)
|
|
fp = (rtems_context_fp *) debugee->fp_context; /* possibly 0 */
|
|
#else
|
|
fp = 0;
|
|
#endif
|
|
|
|
if (0 == rtems_monitor_command_read(command_buffer, &argc, argv))
|
|
continue;
|
|
if ((command = rtems_monitor_command_lookup(rtems_monitor_commands,
|
|
argc,
|
|
argv)) == 0)
|
|
{
|
|
/* no command */
|
|
printf("Unrecognised command; try 'help'\n");
|
|
continue;
|
|
}
|
|
|
|
command->command_function(argc, argv, command->command_arg, verbose);
|
|
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
rtems_monitor_kill(void)
|
|
{
|
|
if (rtems_monitor_task_id)
|
|
rtems_task_delete(rtems_monitor_task_id);
|
|
rtems_monitor_task_id = 0;
|
|
|
|
rtems_monitor_server_kill();
|
|
}
|
|
|
|
void
|
|
rtems_monitor_init(
|
|
unsigned32 monitor_flags
|
|
)
|
|
{
|
|
rtems_status_code status;
|
|
|
|
rtems_monitor_kill();
|
|
|
|
status = rtems_task_create(RTEMS_MONITOR_NAME,
|
|
1,
|
|
RTEMS_MINIMUM_STACK_SIZE * 2,
|
|
RTEMS_INTERRUPT_LEVEL(0),
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&rtems_monitor_task_id);
|
|
if (status != RTEMS_SUCCESSFUL)
|
|
{
|
|
rtems_error(status, "could not create monitor task");
|
|
return;
|
|
}
|
|
|
|
rtems_monitor_node = rtems_get_node(rtems_monitor_task_id);
|
|
rtems_monitor_default_node = rtems_monitor_node;
|
|
|
|
rtems_monitor_symbols_loadup();
|
|
|
|
if (monitor_flags & RTEMS_MONITOR_GLOBAL)
|
|
rtems_monitor_server_init(monitor_flags);
|
|
|
|
/*
|
|
* Start the monitor task itself
|
|
*/
|
|
|
|
status = rtems_task_start(rtems_monitor_task_id,
|
|
rtems_monitor_task,
|
|
monitor_flags);
|
|
if (status != RTEMS_SUCCESSFUL)
|
|
{
|
|
rtems_error(status, "could not start monitor");
|
|
return;
|
|
}
|
|
}
|