/* * 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$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include /* set by trap handler */ extern rtems_tcb *debugger_interrupted_task; extern rtems_context *debugger_interrupted_task_context; extern uint32_t 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; uint32_t rtems_monitor_node; /* our node number */ uint32_t rtems_monitor_default_node; /* current default for commands */ /* * The rtems symbol table */ rtems_symbol_table_t *rtems_monitor_symbols; /* * The top-level commands */ static const 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, { .symbol_table = &rtems_monitor_symbols }, &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], }, { "sema", "sema [id [id ... ] ]\n" " display information about the specified semaphores\n" " Default is to display information about all semaphores on this node\n" , 0, rtems_monitor_object_cmd, { RTEMS_MONITOR_OBJECT_SEMAPHORE }, &rtems_monitor_commands[12], }, { "region", "region [id [id ... ] ]\n" " display information about the specified regions\n" " Default is to display information about all regions on this node\n" , 0, rtems_monitor_object_cmd, { RTEMS_MONITOR_OBJECT_REGION }, &rtems_monitor_commands[13], }, { "part", "part [id [id ... ] ]\n" " display information about the specified partitions\n" " Default is to display information about all partitions on this node\n" , 0, rtems_monitor_object_cmd, { RTEMS_MONITOR_OBJECT_PARTITION }, &rtems_monitor_commands[14], }, { "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[15], }, { "driver", "Display the RTEMS device driver table.\n" " driver [ major [ major ... ] ]", 0, rtems_monitor_object_cmd, { RTEMS_MONITOR_OBJECT_DRIVER }, &rtems_monitor_commands[16], }, { "dname", "Displays information about named drivers.\n", 0, rtems_monitor_object_cmd, { RTEMS_MONITOR_OBJECT_DNAME }, &rtems_monitor_commands[17], }, { "exit", "Invoke 'rtems_fatal_error_occurred' with 'status' " "(default is RTEMS_SUCCESSFUL)\n" " exit [status]", 0, rtems_monitor_fatal_cmd, { .status_code = RTEMS_SUCCESSFUL }, &rtems_monitor_commands[18], }, { "fatal", "'exit' with fatal error; default error is RTEMS_TASK_EXITTED\n" " fatal [status]", 0, rtems_monitor_fatal_cmd, { .status_code = RTEMS_TASK_EXITTED }, /* exit value */ &rtems_monitor_commands[19], }, { "quit", "Alias for 'exit'\n", 0, rtems_monitor_fatal_cmd, { .status_code = RTEMS_SUCCESSFUL }, /* exit value */ &rtems_monitor_commands[20], }, #ifdef RTEMS_POSIX_API { "pthread", "Display information about the specified pthreads. " "Default is to display information about all pthreads on this node.\n" " pthread [id [id ...] ]", 0, rtems_monitor_object_cmd, { RTEMS_MONITOR_OBJECT_PTHREAD }, &rtems_monitor_commands[21], }, #define RTEMS_MONITOR_DEBUGGER_NEXT 22 #else #define RTEMS_MONITOR_DEBUGGER_NEXT 21 #endif #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[RTEMS_MONITOR_DEBUGGER_NEXT], }, #endif { "help", "Provide information about commands. " "Default is show basic command summary.\n" "help [ command [ command ] ]", 0, rtems_monitor_help_cmd, { .monitor_command_entry = rtems_monitor_commands }, NULL } }; /* * All registered commands. */ static const rtems_monitor_command_entry_t *rtems_monitor_registered_commands = &rtems_monitor_commands [0]; 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 __attribute__((unused)), char **argv __attribute__((unused)), const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), bool verbose __attribute__((unused)) ) { #ifdef CPU_INVOKE_DEBUGGER CPU_INVOKE_DEBUGGER; #endif } void rtems_monitor_pause_cmd( int argc, char **argv, const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), bool verbose __attribute__((unused)) ) { 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, const rtems_monitor_command_arg_t *command_arg, bool verbose __attribute__((unused)) ) { if (argc == 1) rtems_fatal_error_occurred(command_arg->status_code); else rtems_fatal_error_occurred(strtoul(argv[1], 0, 0)); } void rtems_monitor_continue_cmd( int argc __attribute__((unused)), char **argv __attribute__((unused)), const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), bool verbose __attribute__((unused)) ) { rtems_monitor_suspend(RTEMS_NO_TIMEOUT); } void rtems_monitor_node_cmd( int argc, char **argv, const rtems_monitor_command_arg_t *command_arg __attribute__((unused)), bool verbose __attribute__((unused)) ) { uint32_t 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: fprintf(stdout,"invalid syntax, try 'help node'\n"); break; } #if defined(RTEMS_MULTIPROCESSING) if ((new_node >= 1) && _Configuration_MP_table && (new_node <= _Configuration_MP_table->maximum_nodes)) rtems_monitor_default_node = new_node; #endif } /* * 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(); 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, (uint32_t) strtoul(value, 0, 16)); if (sp == 0) { fprintf(stdout,"could not define symbol '%s'\n", symbol); goto done; } } else { fprintf(stdout,"parsing error on '%s'\n", buffer); goto done; } } done: return; } /* * User registered commands. */ int rtems_monitor_insert_cmd ( rtems_monitor_command_entry_t *command ) { const rtems_monitor_command_entry_t *e = rtems_monitor_registered_commands; /* Reject empty commands */ if (command->command == NULL) { return 0; } /* Reject command if already present */ while (e->next != NULL) { if (e->command != NULL && strcmp(command->command, e->command) == 0) { return 0; } e = e->next; } /* Prepend new command */ command->next = rtems_monitor_registered_commands; rtems_monitor_registered_commands = command; return 1; } /** * @brief Iterates through all registerd commands. * * For each command the interation routine @a routine is called with the * command entry and the user provided argument @a arg. It is guaranteed that * the command name and function are not NULL. */ void rtems_monitor_command_iterate( rtems_monitor_per_command_routine routine, void *arg ) { const rtems_monitor_command_entry_t *e = rtems_monitor_registered_commands; while (e != NULL) { if (e->command != NULL && e->command_function != NULL) { if (!routine(e, arg)) { break; } } e = e->next; } }