This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 13/18] Add new "wtx" target.
- From: Joel Brobecker <brobecker at adacore dot com>
- To: gdb-patches at sourceware dot org
- Cc: Joel Brobecker <brobecker at adacore dot com>
- Date: Thu, 24 Feb 2011 12:49:18 -0500
- Subject: [PATCH 13/18] Add new "wtx" target.
- References: <1298569763-18784-1-git-send-email-brobecker@adacore.com>
This adds the bulk of the support for VxWorks systems through a new
target: wtx.
gdb/ChangeLog:
* remote-wtx.h, remote-wtx.c: New files.
---
gdb/remote-wtx.c | 3278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/remote-wtx.h | 25 +
2 files changed, 3303 insertions(+), 0 deletions(-)
create mode 100644 gdb/remote-wtx.c
create mode 100644 gdb/remote-wtx.h
diff --git a/gdb/remote-wtx.c b/gdb/remote-wtx.c
new file mode 100644
index 0000000..95b02ef
--- /dev/null
+++ b/gdb/remote-wtx.c
@@ -0,0 +1,3278 @@
+/* Support for the WTX protocol.
+
+ Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+ Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "remote-wtx.h"
+#include "ada-lang.h"
+#include "language.h"
+#include "remote-wtxapi.h"
+#include "remote-wtx-opt.h"
+#include "remote-wtx-pd.h"
+#include "target.h"
+#include "exceptions.h"
+#include "symfile.h"
+#include "filenames.h"
+#include "command.h"
+#include "objfiles.h"
+#include "remote-wtx-utils.h"
+#include "remote-wtx-bp.h"
+#include "remote-wtx-tasks.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "remote-wtx-hw.h"
+#include "regcache.h"
+#include "gdb_select.h"
+#include "observer.h"
+#include "gdbcmd.h"
+
+#include <signal.h>
+
+/* The target_ops vectors for the WTX protocol support.
+ There is one vector per mode of operation:
+ - Connected to the target server, but not attached;
+ - Attached to a task ("task" and "multi-tasks" modes);
+ - Attached to the system ("system" mode). */
+
+static struct target_ops wtx_ops;
+static struct target_ops wtx_task_mode_ops;
+static struct target_ops wtx_system_mode_ops;
+
+/* The directory name extracted from from the boot-line info.
+ This directory is initialized when we connect to the target server,
+ and will mosly be used to locate the boot image. */
+static char *kernel_directory = NULL;
+
+/* The PID of the task we are currently attached to. This is used
+ to identify the CTX_EXIT event of the task we are debugging from
+ the same event for other tasks. */
+static int attached_context_id = 0;
+
+/* Set to non-zero if we need to fake an "attach" event in the wait loop. */
+static int fake_attach_event = 0;
+
+/* Status of the inferior after an asynchronous interruption (attach,
+ to_stop...) It is used to determine whether to use the "cont" or
+ the "resume" request to resume the execution of the inferior. */
+static enum wtx_task_status inferior_status = WTX_TASK_STATUS_UNKNOWN;
+
+/* A variable that is set to non-zero when the inferior was stopped
+ by a watchpoint. Contains the address of the data being watched. */
+static CORE_ADDR watchpoint_data_address = 0;
+
+/* Some function advance declarations. */
+
+static void unload_module_symbols (const char *module_filename, int from_tty);
+static int wtx_context_alive (int context_id);
+
+/* Return non-zero if we are currently running in system-mode. */
+
+static int
+system_mode_p (void)
+{
+ return (target_shortname == wtx_system_mode_ops.to_shortname);
+}
+
+/* Build a ptid from the given TASK_ID. */
+
+static ptid_t
+context_id_to_ptid (WTX_CONTEXT_ID_T context_id)
+{
+ /* Build the ptid by using the CONTEXT_ID as the pid, and leave
+ the thread & lwp ids set to 0. However, in system mode, the
+ system context ID is -1, which would normally translate to
+ a ptid that GDB treats specially. So, for the system context id,
+ we use a special PID of 1. */
+ if (context_id == SYSTEM_CID)
+ return ptid_build (1, 0, 1);
+
+ return ptid_build (1, 0, context_id);
+}
+
+/* Return the task context ID of the given PTID. */
+
+static WTX_CONTEXT_ID_T
+ptid_get_context_id (ptid_t ptid)
+{
+ int tid = ptid_get_tid (ptid);
+
+ if (tid == 1)
+ /* This is the special PID we use in system mode for the system ptid
+ (see context_id_to_ptid above). Return the system context ID. */
+ return SYSTEM_CID;
+
+ return tid;
+}
+
+/* Record PTID as a new inferior. ATTACHED should be non-zero if
+ we just attached to this process, as oppposed to creating it. */
+
+static void
+wtx_add_inferior (ptid_t ptid, int attached)
+{
+ struct inferior *inf;
+
+ /* For now, we use a model where we only have one inferior at a time.
+ So we simply bind the inferior to the current inferior. */
+ inf = current_inferior ();
+ inferior_appeared (inf, ptid_get_pid (ptid));
+ inf->attach_flag = attached;
+
+ /* This inferior is also the main thread, so add it as a thread. Reset
+ thread list in order to clean up leftovers from previous attach/detach
+ operations. */
+ init_thread_list ();
+ add_thread_silent (ptid);
+}
+
+/* Add PTID to the thread list (if needed). */
+
+static void
+wtx_add_thread (ptid_t ptid)
+{
+ if (!in_thread_list (ptid))
+ add_thread (ptid);
+}
+
+/* Perform a WTX_CONTEXT_CONT operation for the given context type
+ and ID.
+
+ FIXME: brobecker/2007-07-06: This function is not completely
+ implemented yet. The handling of expected error situations
+ is still missing. See the vxworks-5.3 code, and in particular
+ the little tap-dance with current_wtx_operation, and its use
+ in errorHandler. For now, this function is basically a simple
+ wrapper around wtxapi_context_cont. */
+
+static int
+wtx_context_continue (WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id)
+{
+ return wtxapi_context_cont (context_type, context_id);
+}
+
+/* Resume the execution of the inferior given its CONTEXT_TYPE and
+ CONTEXT_ID.
+
+ On 653, the system makes the distinction between the STOPPED state
+ which occurs after the thread has hit a breakpoint, and the SUSPEND
+ state, which happens after we attach to the system. As a result,
+ the debugger needs to use either the "cont" or "resume" WTX request
+ to resume the thread execution.
+
+ On Tornado 2, either request will work on all cases, because
+ the system only provides the SUSPEND mode. However, the documentation
+ explains that it is better to use the "cont" request for breakpoint
+ events on T2 as well. This is very convenient as the same code
+ can be shared for all Tornado versions. */
+
+static int
+continue_inferior (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+ int result = 0;
+
+ switch (inferior_status)
+ {
+ case WTX_TASK_STATUS_CREATED:
+ result = wtxapi_context_resume (context_type, context_id);
+ break;
+ case WTX_TASK_STATUS_STOPPED:
+ if (multi_task_mode_is_on ())
+ result = continue_all_ada_tasks ();
+ else
+ result = wtx_context_continue (context_type, context_id);
+ break;
+ case WTX_TASK_STATUS_SUSPENDED:
+ if (multi_task_mode_is_on ())
+ result = continue_all_ada_tasks ();
+ else
+ result = wtxapi_context_resume (context_type, context_id);
+ break;
+ default:
+ result = wtxapi_context_resume (context_type, context_id);
+ break;
+ }
+
+ if (!result)
+ inferior_status = WTX_TASK_STATUS_UNKNOWN;
+ else
+ inferior_status = WTX_TASK_STATUS_RUNNING;
+ return result;
+}
+
+/* Perform a "suspend" operation on the inferior, given its
+ CONTEXT_TYPE and its CONTEXT_ID. inferior_status is updated
+ accordingly. */
+
+static int
+suspend_inferior (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+ int result = wtxapi_context_suspend (context_type, context_id);
+ if (!result)
+ inferior_status = WTX_TASK_STATUS_UNKNOWN;
+ else
+ inferior_status = WTX_TASK_STATUS_SUSPENDED;
+ return result;
+}
+
+/* Perform a "stop" operation on the inferior, given its
+ CONTEXT_TYPE and its CONTEXT_ID. inferior_status is updated
+ accordingly. */
+
+static int
+stop_inferior (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+ int result = wtxapi_context_stop (context_type, context_id);
+ if (!result)
+ inferior_status = WTX_TASK_STATUS_UNKNOWN;
+ else
+ inferior_status = WTX_TASK_STATUS_STOPPED;
+ return result;
+}
+
+/* A function that is meant to be used as the callback function for
+ bfd_map_over_sections, an whose purpose is to make a shallow
+ duplication of the array of sections for the given BFD object. */
+
+static void
+collect_bfd_sections (bfd *abfd, asection *sect, void *ptr)
+{
+ asection **sections = (asection **) ptr;
+
+ sections[sect->index] = sect;
+}
+
+/* Return nonzero if the the given section SECT is "loadable" (as indicated
+ by its section flags). Loadable here means either that the section is
+ loaded in memory, or that it triggers the creation of a memory region. */
+
+static int
+section_is_loadable (asection *sect)
+{
+ flagword flags = bfd_get_section_flags (NULL, sect);
+
+ return ((flags & SEC_ALLOC) || (flags & SEC_LOAD));
+}
+
+/* Remove any section entry in SECTION_ADDRS if the associated section
+ is not "loadable" (see section_is_loadable above). The removal is
+ performend in-pace, and SECTION_ADDRS->NUM_SECTIONS is adjusted to
+ reflect the new number of sections. */
+
+static void
+remove_non_loadable_sections (struct section_addr_info *section_addrs,
+ asection **bfd_sections)
+{
+ int i;
+ int new_num_sections = 0;
+
+ for (i = 0; i < section_addrs->num_sections; i++)
+ if (section_is_loadable (bfd_sections[section_addrs->other[i].sectindex]))
+ {
+ if (i != new_num_sections)
+ section_addrs->other[new_num_sections] = section_addrs->other[i];
+ new_num_sections++;
+ }
+ section_addrs->num_sections = new_num_sections;
+}
+
+/* Build the SECTION_ADDRS field of the give MODULE_INFO using
+ the module segment base addresses.
+
+ We compute the section addresses by:
+ 1. Getting the list of sections and their attribute from
+ the object file (using bfd)
+ 2. Iterate in order over each section and:
+ a. Determine to which segment it belongs
+ b. Compute its address as being just after the previous
+ section that lives in the same segment (modulo alignment)
+
+ This function assumes that MODULE_INFO->SEGMENTS is not NULL,
+ or will abort with a failed assertion. */
+
+static void
+build_module_info_section_addrs (struct wtxapi_module_info *module_info,
+ bfd *abfd)
+{
+ CORE_ADDR text_addr;
+ CORE_ADDR data_addr;
+ CORE_ADDR bss_addr;
+ int num_sections;
+ int i;
+ asection **bfd_sections;
+
+ gdb_assert (module_info->segments != NULL);
+
+ text_addr = module_info->segments->text_addr;
+ data_addr = module_info->segments->data_addr;
+ bss_addr = module_info->segments->bss_addr;
+
+ num_sections = bfd_count_sections (abfd);
+ module_info->section_addrs = alloc_section_addr_info (num_sections);
+
+ bfd_sections = (asection **) xzalloc (num_sections * sizeof (asection *));
+ bfd_map_over_sections (abfd, collect_bfd_sections, (void *) bfd_sections);
+
+ for (i = 0; i < num_sections; i++)
+ {
+ CORE_ADDR *addr_ptr = NULL;
+ asection *sect = bfd_sections[i];
+ flagword flags = bfd_get_section_flags (NULL, sect)
+ & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA);
+
+ switch (flags)
+ {
+ case (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE): /* text */
+ case (SEC_ALLOC | SEC_LOAD | SEC_CODE): /* writable text */
+ addr_ptr = &text_addr;
+ break;
+
+ case (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA): /* rodata */
+ if (strcmp (sect->name, ".rodata") == 0)
+ addr_ptr = &text_addr;
+ break;
+ case (SEC_ALLOC | SEC_LOAD | SEC_DATA): /* data */
+ addr_ptr = &data_addr;
+ break;
+
+ default:
+ if ((flags & SEC_ALLOC) && !(flags & SEC_LOAD)) /* bss */
+ addr_ptr = &bss_addr;
+ break;
+ }
+
+ if (addr_ptr != NULL)
+ {
+ module_info->section_addrs->other[i].addr =
+ align_power (*addr_ptr, bfd_get_section_alignment (abfd, sect));
+ *addr_ptr = module_info->section_addrs->other[i].addr
+ + bfd_section_size (abfd, sect);
+ }
+ module_info->section_addrs->other[i].name =
+ xstrdup (bfd_get_section_name (abfd, sect));
+ module_info->section_addrs->other[i].sectindex = sect->index;
+
+ /* FIMXE:brobecker/2007-05-18: On VxWorks systems, the offset
+ of each section of our object file seems to be set to zero.
+ This isn't a problem when we have one section per segment,
+ but it actually causes trouble when we have more than one
+ section per segment (eg when building with -ffunction-sections).
+ What happens in that case is that the relocation module thinks
+ that all these sections are located at the beginning of the
+ associated segment, and ends up computing wrong addresses.
+ This is particularly visible in the relocation of the debugging
+ info sections.
+
+ In my opinion, this is a linker issue. But we can cover
+ this issue by fixing up the section offsets, at least until
+ the linker is updated. As far as we know, this is only useful
+ for ".text.*" sections, we do the fixup only for these; it just
+ makes it easier because the base address for the offset is
+ always the text segment base address. */
+ if (sect->name != NULL
+ && strncmp (bfd_sections[i]->name, ".text.", 6) == 0)
+ bfd_set_section_vma
+ (abfd, sect,
+ module_info->section_addrs->other[i].addr
+ - module_info->segments->text_addr);
+ }
+
+ /* We only expect to find the sections that have been loaded in memory
+ in MODULE_INFO->SECTION_ADDRS, so remove any section which are not
+ loadable. It seemed easy enough at first to create the array without
+ those sections to start with. But, in practice, the BFD API made it
+ easier to do it this way. */
+ remove_non_loadable_sections (module_info->section_addrs, bfd_sections);
+
+ xfree (bfd_sections);
+}
+
+/* Read the symbols from the given MODULE_FILENAME, using the information
+ available in MODULE_INFO.
+
+ This function assumes that the current PD is equal to the module PD. */
+
+static int
+read_module_symbols_1 (struct wtxapi_module_info *module_info,
+ char *module_filename)
+{
+ struct gdb_exception e;
+
+
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ bfd *abfd = symfile_bfd_open (module_filename);
+
+ if (module_info->section_addrs == NULL)
+ build_module_info_section_addrs (module_info, abfd);
+
+ symbol_file_add_from_bfd (abfd, 0, module_info->section_addrs,
+ 0 /* flags */);
+ }
+ if (e.reason < 0)
+ {
+ printf_filtered (_("Unable to load symbols for module %s: %s\n"),
+ module_info->module_name, e.message);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Load the symbols from the given module (identified by its MODULE_ID).
+
+ This function handles the case when the module PD is different from
+ the current PD by temporarily switching to the module PD during
+ the symbol reading phase. */
+
+static void
+read_module_symbols (module_id_t module_id, int ignored_verbose)
+{
+ const pd_id_t current_pd = wtxapi_pd_current_get ();
+ const pd_id_t module_pd = wtx_pd_get_module_pd (module_id);
+ struct cleanup *old_chain = NULL;
+ struct wtxapi_module_info *module_info;
+ struct gdb_exception e;
+ char *module_filename;
+
+ if (module_pd != current_pd)
+ {
+ wtx_pd_switch_to_pd (module_pd);
+ old_chain = wtx_pd_make_switch_to_pd_cleanup (current_pd);
+ }
+
+ module_info = wtxapi_obj_module_info_get (module_pd, module_id);
+ if (module_info == NULL)
+ error (_("Cannot get info for module 0x%x: %s"),
+ (unsigned int) module_id, wtxapi_err_msg_get ());
+
+ printf_filtered ("\t%s: ", module_info->module_name);
+ gdb_flush (gdb_stdout);
+
+ /* The module name is a basename, not an absolute filename.
+ If this file is not present in the current directory, then
+ try to see if it is in the kernel_directory. */
+
+ module_filename = module_info->module_name;
+ if (!is_regular_file (module_filename) && kernel_directory != NULL)
+ {
+ char *filename = concat (kernel_directory, module_filename, NULL);
+
+ make_cleanup (xfree, filename);
+ if (is_regular_file (filename))
+ module_filename = filename;
+ }
+
+ /* Try reading the symbols from the given module. */
+ if (read_module_symbols_1 (module_info, module_filename))
+ printf_filtered (_("ok\n"));
+
+ free_wtxapi_module_info (module_info);
+ if (old_chain)
+ do_cleanups (old_chain);
+}
+
+/* Query the target about the list of modules currently loaded,
+ and load the symbols from each module. */
+
+static void
+read_symbols_from_all_modules (int from_tty)
+{
+ struct wtxapi_module_list *module_list;
+ int i;
+
+ if (from_tty)
+ printf_filtered (_("Looking for all loaded modules:\n"));
+
+ module_list = wtxapi_obj_module_list_get (WTX_MOD_FIND_IN_ALL_PD);
+ if (module_list == NULL)
+ error (_("Could not get target module list: %s"), wtxapi_err_msg_get ());
+
+ make_cleanup (cleanup_wtxapi_module_list, module_list);
+
+ for (i = 0; i < module_list->num_obj_mod; i++)
+ {
+ read_module_symbols (module_list->mod_id_array[i], from_tty);
+ QUIT;
+ }
+
+ if (from_tty)
+ printf_filtered (_("Done.\n"));
+
+ /* Now that we have some object files, GDB should recompute the gdbarch
+ vector using them. VxWorks does not really have a concept of "the"
+ executable, but we know that all the modules (aka objfiles) are on
+ the same target and are using the same architecture. So we just
+ pick the first objfile we have and use that to recompute the gdbarch
+ vector. */
+ if (object_files != NULL && object_files->obfd != NULL)
+ set_gdbarch_from_file (object_files->obfd);
+}
+
+/* Compute the name of the directory where the boot image was loaded
+ from.
+
+ When successful, save the result in KERNEL_DIRECTORY. When not
+ successful, then KERNEL_DIRECTORY is set to NULL. */
+
+static void
+cache_kernel_directory_name (void)
+{
+ const char *boot_line = wtxapi_target_bootline_get ();
+ char *separator;
+ char *basename = NULL;
+ char *tmp;
+
+ /* If kernel_directory was previous set, free it. We want to recompute it
+ each time, to make sure it's correct (we may have switched from
+ one board to the other, for instance). */
+ if (kernel_directory != NULL)
+ {
+ xfree (kernel_directory);
+ kernel_directory = NULL;
+ }
+
+ if (boot_line == NULL)
+ return; /* No boot-line information, so nothing much we can do. */
+
+ /* The format of the boot path follows the following syntax:
+ [<hostname> ":"]<kernel module full path> */
+
+ /* Skip the hostname if specified, being careful of not stripping
+ the drive letter on Windows hosts... */
+
+ separator = strchr (boot_line, ':');
+ if (separator != NULL
+ && !(have_dos_based_filesystem ()
+ && (separator - boot_line == 1)))
+ boot_line = separator + 1;
+
+ /* wtxapi_target_bootline_get returns a pointer into temporary memory,
+ so we need to duplicate kernel_directory for our own usage. */
+ kernel_directory = xstrdup (boot_line);
+
+ /* Strip the module name from the path.
+
+ Keep the trailing directory separator, as this directory will be
+ concatenated with filenames to form full paths. So no need to strip
+ it here only to add it back later. */
+
+ tmp = kernel_directory;
+ while (*tmp != '\0')
+ {
+ if (IS_DIR_SEPARATOR (*tmp))
+ basename = tmp + 1;
+ tmp++;
+ }
+
+ if (basename != NULL)
+ *basename = '\0';
+}
+
+/* Implement the to_open method of the target_ops structure
+ for the WTX protocol. */
+
+void
+wtx_open (char *args, int from_tty)
+{
+ char **argv = buildargv (args);
+ char *target_server_name;
+ WTX_AGENT_MODE_TYPE target_agent_mode;
+ struct gdb_exception ex;
+
+ make_cleanup_freeargv (argv);
+
+ if (argv[0] == NULL)
+ error (_("target name is missing"));
+ target_server_name = argv[0];
+
+ printf_filtered (_("Connecting to target server: %s...\n"),
+ target_server_name);
+ unpush_target (&wtx_ops);
+
+ if (!wtxapi_tool_attach (target_server_name, wtx_opt_tool_name ()))
+ error (_("Could not connect to target server."));
+
+ push_target (&wtx_ops);
+
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ /* Make sure that we can communicate with the target server.
+ If the attempt failed and WTX reports that the target server
+ is not attached (target server locked by another user, server
+ is up but target rebooted, server is up but not attached to
+ the target agent, ...), then print a warning and try attaching
+ again. */
+ if (!wtxapi_target_server_available_p ()
+ && wtxapi_err_get () == WTX_ERR_SVR_TARGET_NOT_ATTACHED)
+ {
+ warning (_("target not attached to target server, reattaching..."));
+
+ if (!wtxapi_target_attach ())
+ error (_("Failed to attach to target: %s"), wtxapi_err_msg_get ());
+ }
+
+ /* Make sure that we can communicate with the target server now.
+ If all our previous attemps to connect properly failed, then
+ abort. */
+ if (!wtxapi_target_server_available_p ())
+ error (_("Connection to target server is not usable: %s."),
+ wtxapi_err_msg_get ());
+
+ target_agent_mode = wtxapi_agent_mode_get ();
+ if (target_agent_mode == invalid_agent_mode)
+ error (_("Could not get agent mode: %s"), wtxapi_err_msg_get ());
+
+ if (!wtxapi_register_for_event (".*"))
+ error (_("Could not register for target events: %s"),
+ wtxapi_err_msg_get ());
+
+ printf_filtered (_("Connected to %s\n"), wtxapi_ts_name_get ());
+
+ cache_kernel_directory_name ();
+ read_symbols_from_all_modules (from_tty);
+ ada_language_auto_set ();
+
+ wtx_hw_initialize ();
+ wtx_opt_init_task_options ();
+ wtxapi_notify_connection_established ();
+ }
+ if (ex.reason < 0)
+ {
+ printf_filtered ("%s\n", ex.message);
+ unpush_target (&wtx_ops);
+ }
+}
+
+/* Implement the to_close method of the target_ops for the WTX protocol. */
+
+static void
+wtx_close (int quitting)
+{
+ /* We don't need to remove all the breakpoints from the inferior,
+ because GDB is handling their insertion and removal for us. */
+
+ if (!wtxapi_tool_detach ())
+ warning (_("error detected while detaching from target (ignored): %s"),
+ wtxapi_err_msg_get ());
+}
+
+/* Implement the to_close method when in "task" mode. */
+
+static void
+wtx_task_mode_close (int quitting)
+{
+ /* Nothing to do. */
+}
+
+/* Implement the to_close method when in "system" mode. */
+
+static void
+wtx_system_mode_close (int quitting)
+{
+ /* Nothing to do. */
+}
+
+/* Attach to the "system". In other words, start debugging in
+ the target in system mode. */
+
+static void
+wtx_system_mode_attach (int from_tty)
+{
+ if (from_tty)
+ printf_filtered (_("Entering system-mode.\n"));
+
+ if (!wtxapi_system_mode_support_p ())
+ error (_("system-mode not supported."));
+
+ if (!wtxapi_agent_mode_set (WTX_AGENT_MODE_EXTERN))
+ error (_("Failed to enter system mode: %s"), wtxapi_err_msg_get ());
+
+ if (!suspend_inferior (WTX_CONTEXT_SYSTEM, SYSTEM_CID))
+ error (_("Failed to suspend system context: %s"), wtxapi_err_msg_get ());
+
+ push_target (&wtx_system_mode_ops);
+
+ inferior_ptid =
+ context_id_to_ptid (wtxapi_system_mode_get_current_context_id ());
+
+ /* If the user turned the multi-tasks-mode on, then warn the user that
+ we are turning it off now. This mode doesn't make sense in system
+ mode.
+
+ We could silently ignore the multi-task-mode user setting, but
+ it makes the code clearer to turn it off, because it allows us
+ to determine that if we are in multi-tasks mode iff the user
+ setting is non-zero (without having to check which mode (task-mode
+ vs system-mode we are using). */
+ if (multi_task_mode_is_on ())
+ {
+ warning (_("The multi-tasks mode can not be used in system mode.\n\
+multi-tasks mode turned off."));
+ turn_multi_task_mode_off ();
+ }
+}
+
+/* Register for exit notification for the given task ID. */
+
+static int
+register_for_exit_event (int context_id)
+{
+ evtpt_id_t evtpt_id;
+
+ evtpt_id = wtxapi_context_exit_notify_add (WTX_CONTEXT_TASK, context_id);
+ if (evtpt_id == invalid_evtpt_id)
+ return 0;
+
+ attached_context_id = context_id;
+ return 1;
+}
+
+/* If NAME is the name of a task currently running on the system,
+ return its ID. Return zero otherwize. */
+
+static int
+wtx_context_id_from_name (char *name)
+{
+ struct wtxapi_thread_info *threads = wtxapi_get_thread_list ();
+ struct wtxapi_thread_info *this_thread = threads;
+ int pid = 0;
+
+ while (this_thread != NULL)
+ {
+ if (strcmp (this_thread->name, name) == 0)
+ {
+ pid = this_thread->id;
+ break;
+ }
+ this_thread = this_thread->next;
+ }
+
+ free_wtxapi_thread_info (threads);
+ return pid;
+}
+
+/* Implement the "attach" method for the task mode. */
+
+static void
+wtx_task_mode_attach (char *args, int from_tty)
+{
+ int context_id;
+
+ /* Check to see if the argument was a task name. */
+
+ context_id = wtx_context_id_from_name (args);
+
+ /* If not a valid task name, then try parsing the argument as a task ID. */
+ if (context_id == 0)
+ context_id = parse_and_eval_address (args);
+
+ if (from_tty)
+ printf_filtered (_("Attaching to task %#x.\n"), context_id);
+
+ if (multi_task_mode_is_on ())
+ {
+ start_multi_task_mode ();
+ if (!stop_all_ada_tasks (&inferior_status))
+ error (_("Failed to stop task: %s"), wtxapi_err_msg_get ());
+ }
+ else
+ {
+ if (!stop_inferior (WTX_CONTEXT_TASK, context_id))
+ error (_("Failed to stop task: %s"), wtxapi_err_msg_get ());
+ }
+
+ push_target (&wtx_task_mode_ops);
+
+ wtx_pd_switch_to_task_pd (context_id);
+ inferior_ptid = context_id_to_ptid (context_id);
+
+ if (multi_task_mode_is_on ())
+ {
+ /* In multi-tasks mode, we want to receive the EXIT event
+ of the MAIN task, which is not necessarily the task that
+ we attached too. Otherwise, we'll end up reporting that
+ the program exited when in fact it was just one task that
+ exited. So get the main task context ID. */
+ struct ada_task_info *main_task = ada_get_environment_task ();
+
+ context_id = ptid_get_context_id (main_task->ptid);
+ }
+
+ if (!register_for_exit_event (context_id))
+ error (_("Failed to register exit event: %s"), wtxapi_err_msg_get ());
+
+}
+
+/* Implement the "to_attach" target_ops method. */
+
+static void
+wtx_attach (struct target_ops *ops, char *args, int from_tty)
+{
+ dont_repeat ();
+
+ if (args == NULL || args[0] == '\0')
+ error_no_arg ("task-id | task-name | system");
+
+ if (strcasecmp (args, "system") == 0)
+ wtx_system_mode_attach (from_tty);
+ else
+ wtx_task_mode_attach (args, from_tty);
+ wtx_add_inferior (inferior_ptid, 1);
+
+ /* GDB is expecting that attaching to the inferior will cause
+ some event to be generated (most likely a SIGTRAP). To purge
+ that event, GDB will soon call the to_wait target method.
+ Since VxWorks does not generate any event when suspending a task,
+ we need to inform our event loop that the next event-wait should
+ fake a SIGTRAP.
+
+ brobecker/2007-10-12: It is actually possible configure GDB
+ in a way that it will not expect that event, but it involves
+ the definition of the ATTACH_NO_WAIT macro, which we used
+ to do. But doing so deactivates as a side-effect the printing
+ of the current frame that tells the user where his program is.
+ Given that this macro is currently not multiarched either,
+ and only used by one target, it seems clear that it's preferable
+ to just fake the expected event. */
+ fake_attach_event = 1;
+}
+
+/* Implement the "detach" command for the tasks mode. */
+
+static void
+wtx_task_mode_detach (struct target_ops *ops, char *args, int from_tty)
+{
+ int success;
+ WTX_CONTEXT_ID_T context_id = ptid_get_context_id (inferior_ptid);
+
+ printf_filtered (_("Detaching from task 0x%lx...\n"), context_id);
+
+ /* Resume the execution of the inferior. */
+ success = continue_inferior (WTX_CONTEXT_TASK, context_id);
+ if (!success)
+ warning (_("Failed to resume task: %s"), wtxapi_err_msg_get ());
+
+ if (multi_task_mode_is_on ())
+ stop_multi_task_mode ();
+
+ /* And last, revert back to the basic WTX mode. */
+ pop_target ();
+}
+
+/* Implement the "detach" command for the system mode. */
+
+static void
+wtx_system_mode_detach (struct target_ops *ops, char *args, int from_tty)
+{
+ int success;
+
+ printf_filtered (_("Leaving system-mode...\n"));
+
+ /* switch back to task mode. Does this resume the target execution? */
+ success = wtxapi_agent_mode_set (WTX_AGENT_MODE_TASK);
+ if (!success)
+ warning (_("Operation failed, target may still be in EXTERN mode: %s"),
+ wtxapi_err_msg_get ());
+
+ pop_target ();
+}
+
+/* Perform a step operation in the given CONTEXT_ID & CONTEXT_TYPE.
+
+ Whether a single-instruction step of a multi-instruction step
+ is to be done is decided based on the value of the STEP_RANGE_START
+ and STEP_RANGE_END global variables. */
+
+static void
+step_inferior (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+ struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+ gdb_assert (tp);
+
+ if (tp->control.step_range_end
+ && (tp->control.step_range_end > tp->control.step_range_start))
+ wtxapi_context_step (context_type, context_id,
+ tp->control.step_range_start,
+ tp->control.step_range_end);
+ else /* Step only exactly one instruction. */
+ wtxapi_context_step (context_type, context_id, 0, 0);
+ inferior_status = WTX_TASK_STATUS_UNKNOWN;
+}
+
+/* Implement the "to_resume" command in task mode. */
+
+static void
+wtx_task_mode_resume (struct target_ops *ops, ptid_t ptid, int step,
+ enum target_signal signo)
+{
+ WTX_CONTEXT_ID_T context_id;
+ struct thread_info *tp;
+
+ /* Find the context_id of the task to resume. If GDB wants to
+ step a specific task, then it will give us its ptid, but
+ otherwise, we should be resuming the inferior_ptid task. */
+ context_id = ptid_get_context_id (ptid);
+ if (ptid_equal (ptid, minus_one_ptid))
+ {
+ context_id = ptid_get_context_id (inferior_ptid);
+ tp = find_thread_ptid (inferior_ptid);
+ }
+ else
+ {
+ tp = find_thread_ptid (ptid);
+ }
+
+ gdb_assert (tp);
+
+ if (signo != 0 && signo != tp->suspend.stop_signal)
+ error (_("The WTX protocol does not support sending signals"));
+
+ /* Clear any WTX error status before resuming. */
+ wtxapi_err_clear ();
+
+ if (step)
+ step_inferior (WTX_CONTEXT_TASK, context_id);
+ else
+ continue_inferior (WTX_CONTEXT_TASK, context_id);
+
+ /* Make sure that all went well. */
+
+ if (wtxapi_err_get () != WTX_ERR_NONE)
+ error (_("wtx_task_mode_resume (step = %d, context_id = 0x%lx) failed: %s"),
+ step, context_id, wtxapi_err_msg_get ());
+}
+
+/* Implement the "to_resume" command in system mode. */
+
+static void
+wtx_system_mode_resume (struct target_ops *ops,
+ ptid_t ptid, int step, enum target_signal signo)
+{
+ const int resume_all = ptid_equal (ptid, minus_one_ptid);
+ const WTX_CONTEXT_ID_T context_id = ptid_get_context_id (ptid);
+ struct thread_info *tp = find_thread_ptid (ptid);
+
+ /* If GDB wants to step a specific task:
+ - its thread info should be valid (non null);
+ - it won't support sending signals to this specific task.
+
+ In the other case (resume whole system):
+ - the context_id will be 0;
+ - tp will not be valid.
+ */
+
+ gdb_assert (resume_all || tp);
+ if (!resume_all && signo != 0 && signo != tp->suspend.stop_signal)
+ error (_("The WTX protocol does not support sending signals"));
+
+ /* If the debugger asks for a specific task to be stepped, verify
+ that this task is in fact the current task. In system mode,
+ only this task can be stepped (or in other words, one cannot
+ switch to a different task, and step that task). In practice,
+ this is not a real limitation, since other task are usually
+ executing system calls for which there is little need for next/step
+ operations. */
+ if (step && !resume_all)
+ {
+ const WTX_CONTEXT_ID_T current_context_id =
+ wtxapi_system_mode_get_current_context_id ();
+
+ if (context_id != current_context_id)
+ error (_("task 0x%lx cannot be stepped"), context_id);
+ }
+
+ /* Clear any WTX error status before resuming. */
+ wtxapi_err_clear ();
+
+ if (step)
+ step_inferior (WTX_CONTEXT_SYSTEM, SYSTEM_CID);
+ else
+ continue_inferior (WTX_CONTEXT_SYSTEM, SYSTEM_CID);
+
+ /* Make sure that all went well. */
+
+ if (wtxapi_err_get () != WTX_ERR_NONE)
+ error (_("wtx_system_mode_resume (step = %d) failed: %s"),
+ step, wtxapi_err_msg_get ());
+}
+
+static int
+wtx_event_is_foreign (WTX_CONTEXT_ID_T context_id,
+ WTX_CONTEXT_TYPE context_type)
+{
+ /* In system mode, we are debugging the entire system, and we should
+ handle all events. */
+ if (system_mode_p ())
+ return 0;
+
+ /* In multi-tasks mode, we are debugging an application that has
+ more than one task, and we should logically only process the events
+ from one of these tasks. But because we restrict the user to one
+ debugger per partition when debugging in multi-tasks mode, we can
+ simplify this condition by simply verifying that the event was
+ triggered by a task running in the current partition. */
+ if (multi_task_mode_is_on ())
+ {
+ pd_id_t context_pd;
+ int success;
+
+ success = wtxapi_get_task_pd (context_id, &context_pd);
+ if (!success)
+ {
+ /* Probably a CTX_EXIT event from a task we're not watching,
+ maybe in a different partition. Treat as a foreign event. */
+ return 1;
+ }
+
+ return (context_pd != wtxapi_pd_current_get ());
+ }
+
+ /* In single-task mode, we should only handle events triggered
+ inside the task we are debugging. */
+ return (context_id != ptid_get_context_id (inferior_ptid));
+}
+
+/* Interrupt handling during event polling.
+
+ There are two parts involved in interrupt-handling:
+
+ 1. Stopping the inferior:
+ This part is performed by the SIGINT handler that we install
+ during the event queue polling loop.
+
+ 2. Telling GDB that the inferior stopped:
+ This part is handled by the target "wait" routine.
+ On most systems, the interruption performed in (1)
+ will result in an associated event that can be processed.
+ However, this is not the case with WTX, and so we must
+ maintain a global variable that will be set in the SIGINT
+ handler to notify the event polling loop to stop polling,
+ and let the wait routine fake a SIGINT event. */
+
+/* The old SIGINT handler that we save during the period when
+ it is replaced by our own SIGINT handler. */
+static void (*ofunc) (int);
+
+/* Set to non-zero if the inferior has been interrupted while
+ we were polling for events. */
+static int inferior_interrupted = 0;
+
+/* To be called when reiceiving a SIGINT signal while polling for
+ WTX events. This routine interrupts the inferior, and sets
+ TARGET_WAIT_INTERRUPTED to 1. */
+
+static void
+handle_target_wait_interrupt (int signo)
+{
+ target_stop (inferior_ptid);
+ inferior_interrupted = 1;
+}
+
+/* Setup the interrupt-handler to be used during the event polling
+ phase. As soon as we receive an event, the old handler should
+ be restored.
+
+ INFERIOR_INTERRUPTED is also set to zero. */
+
+static void
+set_target_wait_interrupt_handler (void)
+{
+ inferior_interrupted = 0;
+ ofunc = signal (SIGINT, handle_target_wait_interrupt);
+}
+
+/* Restore the old handler set by set_target_wait_interrupt_handler. */
+
+static void
+unset_target_wait_interrupt_handler (void)
+{
+ signal (SIGINT, ofunc);
+}
+
+/* Poll the WTX event queue for event.
+
+ This routine is blocking until either:
+ 1. We receive an event;
+ 2. Or the user requested that the inferior be interrupted.
+
+ In the first case, a non-NULL wtxapi_event_desc object will
+ be returned. In the second case, NULL is returned. */
+
+static struct wtxapi_event_desc *
+wtx_event_poll (void)
+{
+ /* An event that we fetched from the event queue the previous
+ time this function was called, but could not be processed
+ because the user interrupted the inferior at the same time. */
+ static struct wtxapi_event_desc *postponed_event = NULL;
+ struct wtxapi_event_desc *event_desc;
+
+ /* If there is a postponed event, then return it now. */
+ if (postponed_event != NULL)
+ {
+ event_desc = postponed_event;
+ postponed_event = NULL;
+ return event_desc;
+ }
+
+ set_target_wait_interrupt_handler ();
+
+ /* Wait either for the user to interrupt the inferior, or for
+ a WTX event. */
+ while (1)
+ {
+ struct timeval timeout;
+
+ event_desc = wtxapi_event_get ();
+
+ if (inferior_interrupted || event_desc != NULL)
+ break;
+
+ /* There was no event available on the queue. Wait 10ms before
+ each try to avoid eating up all the CPU. Note that we cannot
+ use usleep because it's not portable. */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ gdb_select (0, 0, 0, 0, &timeout);
+ }
+
+ unset_target_wait_interrupt_handler ();
+
+ if (inferior_interrupted && event_desc != NULL)
+ {
+ /* The user interrupted the inferior, but we also received
+ an event at the same time. Store that event for later
+ processing, and pretend we haven't received it yet. */
+ postponed_event = event_desc;
+ return NULL;
+ }
+
+ return event_desc;
+}
+
+/* Process a TEXT_ACCESS or DATA_ACCESS event triggered by the given
+ TASK_ID, and fill in STATUS appropriately. CONTEXT_ID and CONTEXT_TYPE
+ are the context id & type of the breakpoint that triggered this event.
+
+ This routines assumes that we have already verified that this event
+ was meant for us (ie is not a foreign event).
+
+ Return TASK_ID. */
+
+static int
+handle_access_event (WTX_CONTEXT_ID_T task_id,
+ WTX_CONTEXT_ID_T context_id,
+ WTX_CONTEXT_TYPE context_type,
+ struct target_waitstatus *status)
+{
+ /* Update the status. */
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_TRAP;
+
+ /* When in system-mode, chances are the task_id will be equal to
+ SYSTEM_CID, but this is not precise enough for our needs: We are
+ interested in telling the user which task actually triggered
+ the event. This piece of information is actually not part of
+ the WTX event (at least not with VxWorks 5.5), but since the task
+ that triggered the event is necessarily the current task, we can
+ derive this piece of information from there. */
+ if (context_type == WTX_CONTEXT_SYSTEM)
+ task_id = wtxapi_system_mode_get_current_context_id ();
+
+ /* Switch to the task that triggered that event. */
+ inferior_ptid = context_id_to_ptid (task_id);
+
+ /* If the task is running in a different partition than the current one,
+ automatically switch to the task partition. */
+ wtx_pd_switch_to_task_pd (task_id);
+
+ return task_id;
+}
+
+/* Process the given context-exit EVENT, and fill in STATUS appropriately.
+
+ If the event was not meant for us (the task that exited is not the
+ one we're attached to), then ignore the event and return -1.
+ Otherwise, return the ID of the task that exited. */
+
+static int
+handle_ctx_exit_event (struct wtxapi_ctx_exit_event event,
+ struct target_waitstatus *status)
+{
+ /* If the task that exited is not the main task of our application,
+ then ignore this event. Otherwise, we end up reporting the completion
+ of our program prematurely. */
+ if (event.context_id != attached_context_id)
+ return -1;
+
+ status->kind = TARGET_WAITKIND_EXITED;
+ status->value.integer = event.exit_code;
+
+ return event.context_id;
+}
+
+/* Process the given data-access (watchpoint) EVENT, and fill in
+ STATUS appropriately.
+
+ If the event was not meant for us, then ignore the event and
+ return -1. */
+
+static int
+handle_data_access_event (struct wtxapi_data_access_event event,
+ struct target_waitstatus *status)
+{
+ int pid;
+
+ if (wtx_event_is_foreign (event.context_id, event.context_type))
+ return -1;
+
+ /* Record the location that triggered the watchpoint. */
+ watchpoint_data_address = event.data_addr;
+
+ return handle_access_event (event.task_id, event.context_id,
+ event.context_type, status);
+}
+
+/* Process the given exception EVENT, and fill in STATUS appropriately.
+
+ If the event was not meant for us, then ignore the event and
+ return -1. */
+
+static int
+handle_exception_event (struct wtxapi_exception_event event,
+ struct target_waitstatus *status)
+{
+ WTX_CONTEXT_ID_T context_id = event.context_id;
+
+ if (wtx_event_is_foreign (event.context_id, event.context_type))
+ return -1;
+
+ /* Similarly to when we handle access events in system mode,
+ we need to find the ID of the current task, because the
+ context_id returned by the event is equal to SYSTEM_CID
+ which is not precise enough. We want to tell the user
+ which task is currently executing and triggered that event. */
+ if (context_id == SYSTEM_CID)
+ context_id = wtxapi_system_mode_get_current_context_id ();
+
+ /* FIXME: brobecker/2007-08-31: The current event handling in GDB
+ doesn't really include support for exceptions. For now, treat
+ this as an "unknown signal". Later on, we might want to improve
+ a bit, and in particular display the exception value to the user. */
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_UNKNOWN;
+
+ if (multi_task_mode_is_on ())
+ stop_all_ada_tasks (&inferior_status);
+
+ return context_id;
+}
+
+/* Process the given obj-loaded event by loading the symbols from
+ that new module. */
+
+static void
+handle_obj_loaded_event (struct wtxapi_obj_loaded_event event)
+{
+ printf_filtered ("New module on %s\n", wtxapi_ts_name_get ());
+
+ read_module_symbols (event.module_id, 0);
+}
+
+/* Process the given obj-unloaded event by unloading the symbols from
+ that old module. */
+
+static void
+handle_obj_unloaded_event (struct wtxapi_obj_unloaded_event event)
+{
+ printf_filtered ("Module unloaded on %s\n", wtxapi_ts_name_get ());
+
+ unload_module_symbols (event.module_filename, 0);
+}
+
+/* Process the given text-access (breakpoint) EVENT, and fill in
+ STATUS appropriately.
+
+ If the event was not meant for us, then ignore the event and
+ return -1. */
+
+static int
+handle_text_access_event (struct wtxapi_text_access_event event,
+ struct target_waitstatus *status)
+{
+ if (wtx_event_is_foreign (event.context_id, event.context_type))
+ return -1;
+
+ return handle_access_event (event.task_id, event.context_id,
+ event.context_type, status);
+}
+
+/* Process the given vio-write event. */
+
+static void
+handle_vio_write_event (struct wtxapi_vio_write_event event)
+{
+ /* The DATA field in EVENT contains the output that our inferior
+ just printed, so simply relay it to our standard output. */
+ printf_unfiltered ("%s", event.data);
+}
+
+/* Implement the "to_wait" target_ops method. */
+
+static ptid_t
+wtx_wait (struct target_ops *ops, ptid_t ptid,
+ struct target_waitstatus *status, int options)
+{
+ int event_context_id = -1;
+ int done_waiting = 0;
+
+ /* Reset the WATCHPOINT_DATA_ADDRESS value. Now that the system
+ has been restarted, the watchpoint address is no longer relevant. */
+ watchpoint_data_address = 0;
+
+ if (fake_attach_event)
+ {
+ fake_attach_event = 0;
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_TRAP;
+
+ return inferior_ptid;
+ }
+
+ while (!done_waiting)
+ {
+ struct wtxapi_event_desc *event_desc = wtx_event_poll ();
+
+ if (event_desc == NULL)
+ {
+ /* The event polling was interrupted on user request. The target
+ has already been stopped by the interrupt handler, but there
+ will be no associated WTX event. So we just fake a SIGINT
+ event received from the current inferior. */
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_INT;
+
+ return inferior_ptid;
+ }
+
+ switch (event_desc->event_type)
+ {
+ case WTX_EVENT_CTX_EXIT:
+ event_context_id =
+ handle_ctx_exit_event (event_desc->desc.ctx_exit, status);
+ if (event_context_id != -1)
+ done_waiting = 1;
+ break;
+
+ case WTX_EVENT_DATA_ACCESS:
+ event_context_id =
+ handle_data_access_event (event_desc->desc.data_access, status);
+ if (event_context_id != -1)
+ done_waiting = 1;
+ inferior_status = WTX_TASK_STATUS_STOPPED;
+ break;
+
+ case WTX_EVENT_EXCEPTION:
+ event_context_id =
+ handle_exception_event (event_desc->desc.exception, status);
+ if (event_context_id != -1)
+ done_waiting = 1;
+ inferior_status = WTX_TASK_STATUS_STOPPED;
+ break;
+
+ case WTX_EVENT_OBJ_LOADED:
+ handle_obj_loaded_event (event_desc->desc.obj_loaded);
+ break;
+
+ case WTX_EVENT_OBJ_UNLOADED:
+ handle_obj_unloaded_event (event_desc->desc.obj_unloaded);
+ break;
+
+ case WTX_EVENT_TEXT_ACCESS:
+ event_context_id =
+ handle_text_access_event (event_desc->desc.text_access, status);
+ if (event_context_id != -1)
+ done_waiting = 1;
+ inferior_status = WTX_TASK_STATUS_STOPPED;
+ break;
+
+ case WTX_EVENT_VIO_WRITE:
+ handle_vio_write_event (event_desc->desc.vio_write);
+ break;
+ }
+
+ free_wtxapi_event_desc (event_desc);
+ }
+
+ wtx_add_thread (context_id_to_ptid (event_context_id));
+ return context_id_to_ptid (event_context_id);
+}
+
+/* Fetch the value of one register identified by its register number. */
+
+static void
+wtx_fetch_one_register (struct regcache *regcache,
+ WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id,
+ int regnum)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int result;
+ gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+
+ gdb_assert (regnum != -1);
+
+ memset (buf, 0, sizeof (buf));
+ result = wtx_hw_fetch_register (gdbarch, context_type, context_id,
+ regnum, buf);
+ if (result)
+ regcache_raw_supply (regcache, regnum, buf);
+}
+
+/* Fetch the register REGNUM using the given CONTEXT_TYPE and CONTEXT_ID. */
+
+static
+void
+wtx_fetch_registers (struct regcache *regcache,
+ WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id,
+ int regnum)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int total_regs = gdbarch_num_regs (gdbarch)
+ + gdbarch_num_pseudo_regs (gdbarch);
+ int i;
+
+ if (regnum != -1)
+ wtx_fetch_one_register (regcache, context_type, context_id, regnum);
+ else
+ for (i = 0; i < total_regs; i++)
+ wtx_fetch_one_register (regcache, context_type, context_id, i);
+}
+
+/* Implement the "to_fetch_registers" target_ops method when
+ in "task" or "multi-tasks" mode. */
+
+static void
+wtx_task_mode_fetch_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
+{
+ wtx_fetch_registers (regcache, WTX_CONTEXT_TASK,
+ ptid_get_context_id (inferior_ptid),
+ regnum);
+}
+
+/* Fetch the value of one register identified by its register number.
+
+ REGS_ADDR should be set to the address where the general purpose
+ registers are stored for the task we want to read the registers from.
+
+ FP_REGS_ADDR should be set to the address where the FP registers
+ are stored for the task we want to read the registers from.
+ If this address cannot be computed, then it should be set to zero.
+
+ This function should be called only when currently in system mode. */
+
+static void
+wtx_system_mode_fetch_one_register_in_memory (struct regcache *regcache,
+ CORE_ADDR regs_addr,
+ CORE_ADDR fp_regs_addr,
+ int regnum)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int result;
+ gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+ memset (buf, 0, sizeof (buf));
+
+ gdb_assert (regnum != -1);
+
+ result = wtx_hw_fetch_register_in_memory (gdbarch,
+ WTX_CONTEXT_SYSTEM, SYSTEM_CID,
+ regs_addr, fp_regs_addr, regnum,
+ buf);
+ if (result)
+ regcache_raw_supply (regcache, regnum, buf);
+}
+
+/* For the given context ID:
+ - Set REGS_ADDR to the address where the GP registers are stored.
+ - Set FP_REGS_ADDR to the address where the FP registers are stored.
+
+ Any address that could not be computed is set to zero. */
+
+static void
+wtx_get_context_register_block_addrs (WTX_CONTEXT_ID_T context_id,
+ CORE_ADDR *regs_addr,
+ CORE_ADDR *fp_regs_addr)
+{
+ struct wtxapi_thread_info *threads = wtxapi_get_thread_list ();
+ struct wtxapi_thread_info *this_thread = threads;
+
+ *regs_addr = 0;
+ *fp_regs_addr = 0;
+
+ while (this_thread != NULL)
+ {
+ if (this_thread->id == context_id)
+ {
+ *regs_addr = this_thread->regs_addr;
+ *fp_regs_addr = this_thread->fp_regs_addr;
+ break;
+ }
+ this_thread = this_thread->next;
+ }
+
+ free_wtxapi_thread_info (threads);
+}
+
+/* Implement the "to_fetch_registers" target_ops method when
+ in "system" mode. */
+
+static void
+wtx_system_mode_fetch_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
+{
+ const WTX_CONTEXT_ID_T current_system_context_id =
+ wtxapi_system_mode_get_current_context_id ();
+ const WTX_CONTEXT_ID_T gdb_context_id = ptid_get_context_id (inferior_ptid);
+ CORE_ADDR regs_addr;
+ CORE_ADDR fp_regs_addr;
+
+ if (gdb_context_id == SYSTEM_CID
+ || gdb_context_id == current_system_context_id)
+ {
+ wtx_fetch_registers (regcache, WTX_CONTEXT_SYSTEM, SYSTEM_CID, regnum);
+ return;
+ }
+
+ wtx_get_context_register_block_addrs (gdb_context_id, ®s_addr,
+ &fp_regs_addr);
+ gdb_assert (regs_addr != 0); /* But fp_regs_addr may be null. */
+
+ if (regnum != -1)
+ wtx_system_mode_fetch_one_register_in_memory (regcache, regs_addr,
+ fp_regs_addr, regnum);
+ else
+ {
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ const int total_regs = gdbarch_num_regs (gdbarch)
+ + gdbarch_num_pseudo_regs (gdbarch);
+ int i;
+
+ /* Implementation note: The code below causes a succession
+ of small memory reads, which we could optimize by making
+ one large memory read right from the start, and then fetching
+ the register from there. However, this complicates a bit
+ the implementation, so we'll stick to this simple approach
+ for now. The delay should not be noticeable. */
+ for (i = 0; i < total_regs; i++)
+ wtx_system_mode_fetch_one_register_in_memory (regcache, regs_addr,
+ fp_regs_addr, i);
+ }
+}
+
+/* Store the value of one register identified by its register number. */
+
+static void
+wtx_store_one_register (struct regcache *regcache,
+ WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id,
+ int regnum)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+
+ regcache_raw_collect (regcache, regnum, buf);
+ wtx_hw_store_register (gdbarch, context_type, context_id, regnum, buf);
+}
+
+static void
+wtx_store_registers (struct regcache *regcache,
+ WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id,
+ int regnum)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int total_regs = gdbarch_num_regs (gdbarch)
+ + gdbarch_num_pseudo_regs (gdbarch);
+ int i;
+
+ if (regnum != -1)
+ wtx_store_one_register (regcache, context_type, context_id, regnum);
+ else
+ for (i = 0; i < total_regs; i++)
+ wtx_store_one_register (regcache, context_type, context_id, i);
+}
+
+/* Implement the "to_store_registers" target_ops method when
+ in "task" or "multi-tasks" mode. */
+
+static void
+wtx_task_mode_store_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
+{
+ wtx_store_registers (regcache, WTX_CONTEXT_TASK,
+ ptid_get_context_id (inferior_ptid),
+ regnum);
+}
+
+/* Store the value of one register identified by its register number.
+
+ REGS_ADDR should be set to the address where the general purpose
+ registers are stored for the task whose register is being written.
+
+ FP_REGS_ADDR should be set to the address where the FP registers
+ are stored for the task we want to read the registers from.
+ If this address cannot be computed, then it should be set to zero.
+
+ This function should be called only when currently in system mode. */
+
+static void
+wtx_system_mode_store_one_register_in_memory (struct regcache *regcache,
+ CORE_ADDR regs_addr,
+ CORE_ADDR fp_regs_addr,
+ int regnum)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+ memset (buf, 0, sizeof (buf));
+
+ regcache_raw_collect (regcache, regnum, buf);
+ wtx_hw_store_register_in_memory (gdbarch, WTX_CONTEXT_SYSTEM, SYSTEM_CID,
+ regs_addr, fp_regs_addr, regnum, buf);
+}
+
+/* Implement the "to_store_registers" target_ops method when
+ in "system" mode. */
+
+static void
+wtx_system_mode_store_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
+{
+ const WTX_CONTEXT_ID_T current_system_context_id =
+ wtxapi_system_mode_get_current_context_id ();
+ const WTX_CONTEXT_ID_T gdb_context_id = ptid_get_context_id (inferior_ptid);
+ CORE_ADDR regs_addr;
+ CORE_ADDR fp_regs_addr;
+
+ if (gdb_context_id == SYSTEM_CID
+ || gdb_context_id == current_system_context_id)
+ {
+ wtx_store_registers (regcache, WTX_CONTEXT_SYSTEM, SYSTEM_CID, regnum);
+ return;
+ }
+
+ wtx_get_context_register_block_addrs (gdb_context_id, ®s_addr,
+ &fp_regs_addr);
+ gdb_assert (regs_addr != 0); /* But fp_regs_addr may be null. */
+
+ if (regnum != -1)
+ wtx_system_mode_store_one_register_in_memory (regcache, regs_addr,
+ fp_regs_addr, regnum);
+ else
+ {
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ const int total_regs = gdbarch_num_regs (gdbarch)
+ + gdbarch_num_pseudo_regs (gdbarch);
+ int i;
+
+ /* Implementation note: The code below causes a succession
+ of small memory reads, which we could optimize by making
+ one large memory read right from the start, and then fetching
+ the register from there. However, this complicates a bit
+ the implementation, so we'll stick to this simple approach
+ for now. The delay should not be noticeable. */
+ for (i = 0; i < total_regs; i++)
+ wtx_system_mode_store_one_register_in_memory (regcache, regs_addr,
+ fp_regs_addr, i);
+ }
+}
+
+/* Implement the "to_store" method in the target_ops vector. */
+
+static void
+wtx_prepare_to_store (struct regcache *regcache)
+{
+ /* Nothing to do. */
+}
+
+/* If there are any undefined symbols referenced by MODULE_INFO,
+ then print a list containing the name of all such symbols. */
+
+static void
+print_undefined_symbols (struct wtxapi_module_info *module_info)
+{
+ if (module_info->undef_list == NULL)
+ return;
+ if (! go_to_first_element_in_wtxapi_sym_list (module_info->undef_list))
+ return;
+
+ printf_filtered ("\n");
+ warning (_("the module contains some undefined symbols:"));
+ do
+ {
+ char *symbol_name = current_wtxapi_symbol_name (module_info->undef_list);
+ printf_filtered ("\t%s\n", symbol_name);
+ xfree (symbol_name);
+ }
+ while (go_to_next_element_in_wtxapi_sym_list (module_info->undef_list));
+}
+
+/* Kill the task with the given CONTEXT_ID.
+ Print a warning if not successful. */
+
+static void
+wtx_kill_one_task (WTX_CONTEXT_ID_T context_id)
+{
+ const int success = wtxapi_context_kill (WTX_CONTEXT_TASK, context_id);
+
+ /* Print a warning if we failed to kill the target task.
+ Do not throw an error as the failure is not serious enough
+ that we would abort the kill command, especially since one
+ possible reason for failing is if someone alreayd killed
+ the task using a different tool. We just warn the user
+ and proceed as if the killing was successful. The user
+ can then investigate more precisely why it failed and
+ try killing his task manually if necessary. */
+ if (!success)
+ warning (_("Failed to kill task 0x%lx: %s"), context_id,
+ wtxapi_err_msg_get ());
+}
+
+/* Kill the task identified by TASK. */
+
+static void
+wtx_kill_one_task_from_task_info (struct ada_task_info *task)
+{
+ wtx_kill_one_task (ptid_get_context_id (task->ptid));
+}
+
+/* Assuming we are currently debugging in multi-tasks mode,
+ kill all the tasks of inferior. */
+
+static void
+wtx_kill_all_tasks (void)
+{
+ iterate_over_live_ada_tasks (wtx_kill_one_task_from_task_info);
+}
+
+/* Wait for the CTX_EXIT event which is generated by the target server
+ when we kill the inferior. Discard any other events that may be
+ received while waiting for that event.
+
+ The user pressing ctrl-c while waiting for that event will cause
+ the wait to be aborted. Normally, we expect the exit event to be
+ received immediately after the kill, so the user should never really
+ have the time to do that before the event is received. But in case
+ the event is not received for any reason, this allows the user to get
+ out of what could be an infinite loop. */
+
+static void
+wtx_wait_for_exit_event (void)
+{
+ while (1)
+ {
+ struct wtxapi_event_desc *event_desc = wtx_event_poll ();
+
+ if (event_desc == NULL) /* The user pressed Ctrl-c. */
+ return;
+
+ if (event_desc->event_type == WTX_EVENT_CTX_EXIT
+ && event_desc->desc.ctx_exit.context_id == attached_context_id)
+ {
+ free_wtxapi_event_desc (event_desc);
+ return;
+ }
+
+ /* Not the event we were waiting for. Free it, and wait for
+ the next event. */
+ free_wtxapi_event_desc (event_desc);
+ }
+}
+
+/* Implement the "to_kill" method in task mode. */
+
+static void
+wtx_task_mode_kill (struct target_ops *ops)
+{
+ if (ptid_equal (inferior_ptid, null_ptid))
+ {
+ /* brobecker/2007-09-28: Can this really ever happen?
+ Add a warning to help us catch any situation where this
+ actually does happen, and only then return. Eventually,
+ if we find that this has never happened after a few years,
+ then we can remove this entire block. */
+ warning (_("cannot find id of task to kill"));
+ return;
+ }
+
+ if (multi_task_mode_is_on ())
+ wtx_kill_all_tasks ();
+ else
+ wtx_kill_one_task (ptid_get_context_id (inferior_ptid));
+ wtx_wait_for_exit_event ();
+
+ target_mourn_inferior ();
+}
+
+/* Implement the "to_kill" target_ops method when in "system" mode.
+ In pratice, one cannot kill the system task without rebooting it.
+ WindRiver chose to provide that command as a way of rebooting the
+ target, but it really is not that useful. There are enough ways
+ to reboot a system already, no need to provide one extra in GDB.
+ So just tell the user that he cannot "kill" the system. */
+
+static void
+wtx_system_mode_kill (struct target_ops *ops)
+{
+ error (_("The system cannot be killed."));
+}
+
+/* Find a given module using its name and unload it from the target.
+ The module lookup uses the module basename to do the matching. */
+
+static void
+unload_module_from_target (const char *module_name)
+{
+ const char *base_name = lbasename (module_name);
+ const module_id_t module_id = wtxapi_obj_module_in_system_find_id (base_name);
+ pd_id_t module_pd;
+
+ if (module_id == invalid_module_id)
+ {
+ warning (_("could not find %s on target: %s"),
+ base_name, wtxapi_err_msg_get ());
+ return;
+ }
+
+ module_pd = wtx_pd_get_module_pd (module_id);
+
+ /* unload the object module */
+ if (!wtxapi_obj_module_unload (module_pd, module_id))
+ warning (_("could not unload %s from target: %s"),
+ base_name, wtxapi_err_msg_get ());
+
+}
+
+/* Implement the "unload" target_ops method. */
+
+static void
+wtx_unload (char *args, int from_tty)
+{
+ char **argv = buildargv (args);
+
+ if (argv == NULL)
+ error_no_arg (_("module name to unload"));
+ make_cleanup_freeargv (argv);
+
+ unload_module_from_target (argv[0]);
+ unload_module_symbols (argv[0], from_tty);
+}
+
+/* Implement the "load" command for the WTX protocol.
+
+ The usage is as follow:
+
+ load <filename> [remote filename]
+
+ where <filename> is the name of the module as seen from GDB,
+ and [remote filename] should the full path of module given to
+ the target server. */
+
+static void
+wtx_load (char *args, int from_tty)
+{
+ char **argv = buildargv (args);
+ char *module_name = NULL;
+ /* The name/path of the module as seen from the debugger. */
+ char *gdb_path = NULL;
+ /* Same thing but for the target server. */
+ char *target_server_path = NULL;
+ int fd;
+ struct wtxapi_module_info *module_info;
+ struct objfile *objfile;
+
+ dont_repeat ();
+
+ if (argv == NULL)
+ error_no_arg (_("filename of module to load"));
+ make_cleanup_freeargv (argv);
+
+ /* Process the command line arguments, and issue a warning
+ if too many arguments were specified (we know at this point
+ that there is at least one argument so we cannot have too few
+ arguments). */
+
+ module_name = argv[0];
+ target_server_path = argv[1];
+ if (argv[1] != NULL && argv[2] != NULL)
+ warning
+ (_("too many arguments (\"%s ...\"), extra arguments will be ignored"),
+ argv[2]);
+
+ /* Resolve the module_name into a full path name. This allows us
+ to make sure we can open this file for reading (to load the
+ symbols), but more importantly, this allows us to pass an absolute
+ path to the target server if the remote filename was not provided
+ in the load command (TARGET_SERVER_PATH is NULL). */
+
+ fd = openp (source_path, OPF_TRY_CWD_FIRST, module_name,
+ O_RDONLY, &gdb_path);
+ if (fd < 0)
+ error (_("Unable to load %s: %s"), module_name, strerror (errno));
+ close (fd);
+
+ /* If the optional [remote filename] argument was not specified, then
+ use the full path of the module name. */
+
+ if (target_server_path == NULL)
+ target_server_path = gdb_path;
+
+ /* Scan the list of object files; if the module has already been
+ loaded, then unload it first. At reload, the symbols loaded
+ previously are discarded by the target server; so they should
+ also be discarded by the debugger. */
+
+ for (objfile = object_files; objfile; objfile = objfile->next)
+ {
+ if (strcmp (objfile->name, gdb_path) == 0)
+ {
+ wtx_unload (module_name, 0);
+ break;
+ }
+ }
+
+ /* Temporarily switch the timeout value to the load timeout, and
+ attempt to load the module on the board. Error out if we failed. */
+
+ printf_filtered (_("Downloading %s..."), target_server_path);
+ module_info = wtxapi_module_load (wtxapi_pd_current_get (),
+ target_server_path,
+ WTX_LOAD_GLOBAL_SYMBOLS,
+ wtx_opt_load_timeout () * 1000);
+ if (module_info == NULL)
+ error (_("Failed to load module %s: %s"), target_server_path,
+ wtxapi_err_msg_get ());
+
+ print_undefined_symbols (module_info);
+ printf_filtered (_("done.\n"));
+
+ /* Load the module symbols. */
+ read_module_symbols_1 (module_info, gdb_path);
+ ada_language_auto_set ();
+}
+
+/* Implement the "to_create_inferior" target method.
+
+ Implementation Note:
+ We must get the address of the entry point directly from the target,
+ as opposed to from doing a local symbol lookups. This is necessary
+ in order to avoid using an incorrect address if some of our objfiles
+ are not up to date. This can happen for instance in the following
+ scenario:
+ - from GDB: load [app], run [app]
+ - from Windsh: unld "[app]", ld < [app]
+ - from GDB: run [app]
+ What would happen is that GDB would use an obsolete objfile (the
+ one from the previous run) to compute the address of the entry
+ point, thus leading to catastrophic errors...
+
+ Note that it is technically possible to load 2 modules on
+ the target with the same entry point. In this case, it is not
+ clear what would happen. Right now, we just assume that this
+ not going to happen. Anybody trying to do that would just be
+ shooting themselves in the foot... */
+
+static void
+wtx_create_inferior (struct target_ops *ops, char *exec_file, char *args,
+ char **env, int from_tty)
+{
+ struct wtxapi_context_desc context_desc;
+ struct cleanup *old_chain = NULL;
+ char **argv;
+ char *entry_name;
+ int new_context_id;
+
+ memset (&context_desc, 0, sizeof (context_desc));
+
+ /* Parse the arguments. */
+
+ argv = buildargv (args);
+ if (argv == NULL)
+ nomem (0);
+ old_chain = make_cleanup_freeargv (argv);
+
+ /* Get the name of the entry point, which is the first argument. */
+ entry_name = argv[0];
+ if (entry_name == NULL || entry_name[0] == '\0')
+ error_no_arg (_("module entry point"));
+
+ /* FIXME: brobecker/2007-09-26: Add handling of the arguments...
+ For now, pretend there are none. */
+
+ TASK_CONTEXT_ARGC (context_desc) = 0;
+
+ /* Now, fill-in the rest of the CONTEXT_DESC structure. */
+ remote_wtxapi_get_symbol_address
+ (entry_name, &TASK_CONTEXT_ENTRY (context_desc));
+ if (TASK_CONTEXT_ENTRY (context_desc) == 0)
+ error (_("Failed to lookup entry point address"));
+ TASK_CONTEXT_NAME (context_desc) = "tDbgTask";
+ TASK_CONTEXT_CONTEXT_TYPE (context_desc) = WTX_CONTEXT_TASK;
+ TASK_CONTEXT_STACK_SIZE (context_desc) = wtx_opt_stack_size ();
+ TASK_CONTEXT_OPTIONS (context_desc) = wtx_opt_task_options ();
+ TASK_CONTEXT_PRIORITY (context_desc) = wtx_opt_task_priority ();
+ TASK_CONTEXT_PDID (context_desc) = wtxapi_pd_current_get ();
+
+ /* New create a new context for that task. */
+ new_context_id = wtxapi_context_create (&context_desc);
+ if (new_context_id == invalid_context_id)
+ error (_("failed to create task: %s"), wtxapi_err_msg_get ());
+
+ /* FIXME: brobecker/2007-09-26: Perform I/O redirection if necessary. */
+
+ inferior_ptid = context_id_to_ptid (new_context_id);
+ wtx_add_inferior (inferior_ptid, 0);
+ push_target (&wtx_task_mode_ops);
+ register_for_exit_event (new_context_id);
+ inferior_status = WTX_TASK_STATUS_CREATED;
+
+ if (multi_task_mode_is_on ())
+ start_multi_task_mode ();
+
+ /* Do our cleanup actions. */
+ do_cleanups (old_chain);
+}
+
+/* Return True if a module which name is equal to module_name is loaded
+ on the target. */
+
+static int
+module_is_loaded_on_target (const char *module_name)
+{
+ module_id_t module_id;
+
+ module_id = wtxapi_obj_module_in_system_find_id (module_name);
+ return (module_id != invalid_module_id);
+}
+
+/* Search the global object_files list for the objfile associated to
+ the module whose name is BASE_NAME, and free it. */
+
+static void
+free_module_objfile (const char *base_name)
+{
+ struct objfile *objfile;
+ struct objfile *temp;
+
+ /* search for matching objfile known to gdb, and free it */
+ ALL_OBJFILES_SAFE (objfile, temp)
+ {
+ if (!strcmp (lbasename (objfile->name), base_name))
+ {
+ const pd_id_t current_pd = wtxapi_pd_current_get ();
+
+ /* Print a message notifying the user that a module has been
+ unloaded. If the target also supports PDs, then print the
+ PD id and name from which the module has been unloaded. */
+ if (wtx_pd_system_has_pds ())
+ {
+ char *current_pd_name = wtx_pd_get_pd_name (current_pd);
+ printf_filtered (_("Unloaded file %s from PD 0x%lx (%s)\n"),
+ objfile->name, current_pd, current_pd_name);
+ xfree (current_pd_name);
+ }
+ else
+ printf_filtered (_("Unloaded file %s\n"), objfile->name);
+ free_objfile (objfile);
+ return;
+ }
+ }
+ warning (_("could not unload module %s from gdb"), base_name);
+}
+
+/* Given the name of a module that has just been unloaded from
+ the target, discard all the symbols associated to that module. */
+
+static void
+unload_module_symbols (const char *module_filename, int from_tty)
+{
+ const pd_id_t current_pd = wtxapi_pd_current_get ();
+ const char *module_name = lbasename (module_filename);
+ pd_id_t module_pd;
+ struct cleanup *old_chain = NULL;
+
+ /* Ignore this request if it comes from a WTX_EVENT_OBJ_UNLOADED event
+ and the object has been loaded back on the target. We can safely
+ ignore this event because either:
+ - it has been reloaded from this debugger, in which case we
+ already updated our objfile
+ - it has been reloaded from another client (eg windsh), in
+ which case there is necessarily a pending WTX_EVENT_OBJ_LOADED
+ event, which will cause us to update our objfile.
+
+ Not ignoring this event is causing the debugger to destroy the
+ associated objfile in scenarios like this:
+ 1/ from GDB: load <program>
+ (causes GDB to create an associated objfile)
+ 2/ from Windsh: unload <program>
+ 3/ from GDB: load <program>
+ (GDB correctly recreates the associated objfile)
+ 4/ from GDB: run <program>
+ (causes GDB to process the "unloaded" wtx event from step 2,
+ hence destroying our good objfile) */
+ if (!from_tty && module_is_loaded_on_target (module_name))
+ return;
+
+ module_pd = wtx_pd_get_unloaded_module_pd (module_name);
+ if (module_pd != current_pd)
+ {
+ wtx_pd_switch_to_pd (module_pd);
+ old_chain = wtx_pd_make_switch_to_pd_cleanup (current_pd);
+ }
+
+ free_module_objfile (module_name);
+
+ if (old_chain != NULL)
+ do_cleanups (old_chain);
+}
+
+/* Implements the to_xfer_partial method of the target vector for WTX. */
+
+static LONGEST
+wtx_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ int status;
+
+ /* The only object transfer that we support at this moment is memory
+ read/write. */
+ if (object != TARGET_OBJECT_MEMORY)
+ return -1;
+
+ if (readbuf != NULL)
+ status = wtxapi_mem_read (wtxapi_pd_current_get (), offset, readbuf, len);
+ else
+ status = wtxapi_mem_write (wtxapi_pd_current_get (),
+ (gdb_byte *) writebuf, offset, len);
+
+ if (status == WTX_ERROR)
+ return 0; /* No further transfer possible??? */
+
+ return status;
+}
+
+/* Handling of eventpoint IDs.
+
+ VxWorks provides eventpoints for breakpoints and watchpoints. For each
+ inserted breakpoint, the target server returns an evenpoint ID that we
+ need to record, because we will need this ID to remove this breakpoint.
+
+ We store that information as private data in the breakpoint location's
+ target_info. */
+
+struct eventpoint_info
+{
+ evtpt_id_t id;
+ WTX_CONTEXT_TYPE context_type;
+ WTX_CONTEXT_ID_T context_id;
+};
+
+/* Insert a breakpoint eventpoint using the information provided.
+ Return a non-zero errno code when the insertion failed. */
+
+static int
+wtx_insert_breakpoint_1 (struct bp_target_info *target_info,
+ WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id,
+ WTX_ACTION_TYPE action_type,
+ CORE_ADDR call_rtn)
+{
+ struct wtxapi_evtpt evtpt;
+ evtpt_id_t evtpt_id;
+ struct eventpoint_info *evtpt_info;
+
+ evtpt.context.context_type = context_type;
+ evtpt.context.context_id = context_id;
+ evtpt.action.action_type = action_type | WTX_ACTION_NOTIFY;
+ evtpt.event.event_type = WTX_EVENT_TEXT_ACCESS;
+ evtpt.event.num_args = 1;
+ evtpt.event.args = &target_info->placed_address;
+
+ if (call_rtn != 0)
+ evtpt.action.call_rtn = call_rtn;
+
+ evtpt_id = wtxapi_eventpoint_add (&evtpt);
+ wtx_opt_breakpoints_debug
+ ("wtxapi_eventpoint_add (context_type = 0x%x, context_id = 0x%lx, "
+ "address = 0x%s, target_info = 0x%s) -> 0x%x",
+ context_type, context_id,
+ paddress (target_gdbarch, target_info->placed_address),
+ host_address_to_string (target_info), evtpt_id);
+
+ if (evtpt_id == invalid_evtpt_id)
+ return ENOMEM;
+
+ /* Save the eventpoint info. We will use it later when removing
+ the breakpoint. */
+ evtpt_info = xmalloc (sizeof (struct eventpoint_info));
+ evtpt_info->id = evtpt_id;
+ evtpt_info->context_type = context_type;
+ evtpt_info->context_id = context_id;
+ target_info->target_private_data = evtpt_info;
+
+ return 0;
+}
+
+/* Insert a task-mode breakpoint at the location specified by
+ TARGET_INFO. TARGET_INFO is also used to determine what
+ scope & action to use for that breakpoint.
+
+ Return a non-zero errno code if the insertion failed. */
+
+static int
+wtx_insert_task_mode_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *target_info)
+{
+ const enum wtx_breakpoint_action action = get_break_command_action ();
+ WTX_CONTEXT_TYPE context_type;
+ WTX_CONTEXT_ID_T context_id;
+ WTX_ACTION_TYPE action_type;
+ CORE_ADDR call_rtn = 0;
+
+ context_type = WTX_CONTEXT_TASK;
+ context_id = ptid_get_context_id (inferior_ptid);
+
+ switch (action)
+ {
+ case action_task:
+ action_type = WTX_ACTION_STOP;
+ break;
+
+ case action_all:
+ action_type = WTX_ACTION_STOP_ALL;
+ break;
+
+ case action_call:
+ context_type = WTX_CONTEXT_ANY_TASK;
+ action_type = WTX_ACTION_STOP;
+ call_rtn = get_break_command_call_rtn ();
+ if (call_rtn != 0)
+ action_type |= WTX_ACTION_CALL;
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__, _("Unexpected action value: %d"),
+ action);
+ }
+
+ return wtx_insert_breakpoint_1 (target_info, context_type, context_id,
+ action_type, call_rtn);
+}
+
+/* On systems that support partitions, we need to record for each
+ breakpoint/watchpoint the associated partition ID, which is
+ the current partition at the time when the breakpoint was
+ created.
+
+ We achieve this by using a couple of observers, one that will
+ notify us of the creation of new breakpoints, and one that will
+ notify us of their destruction. They allow us to maintain a list
+ of (breakpoint, pd) elements that will later allow us to find
+ the PD associated to any breakpoint.
+
+ This is actually only useful when debugging in system mode, because
+ this is the only mode where we debug more than one partition.
+ However, we still need to maintain this information at all times,
+ because the user may be setting breakpoints while not in system
+ mode.
+
+ brobecker/2007-10-26: This is probably not going to be the final
+ implementation for this concept. For instance, it would probably
+ be nicer to have this information embedded directly inside the
+ breakpoint structure as a target-dependent data (like we do in
+ the gdbarch for instance), and avoid the need for the parallel
+ list that we're maintaining.
+
+ brobecker/2007-10-26: Another issue: When we insert breakpoints,
+ we are given a pointer to the target_info data. Not the bp_location.
+ Having the bp_location would allow us to directly find the associated
+ breakpoint, and from there its partition.
+
+ But all this needs to be discussed with the rest of the maintainers.
+ In the meantime, we're trying to make this change as contained as
+ possible. */
+
+struct bp_partition_info
+{
+ struct breakpoint *b;
+ WTX_CONTEXT_ID_T partition_id;
+
+ struct bp_partition_info *next;
+};
+
+static struct bp_partition_info *bp_partition_list = NULL;
+
+/* Add the breakpoint/context-id pair to BP_PARTITION_LIST. */
+
+static void
+add_bp_partition_info (struct breakpoint *b, WTX_CONTEXT_ID_T partition_id)
+{
+ struct bp_partition_info *info = xmalloc (sizeof (struct bp_partition_info));
+
+ info->b = b;
+ info->partition_id = partition_id;
+ info->next = bp_partition_list;
+
+ bp_partition_list = info;
+}
+
+/* Delete the element in BP_PARTITION_LIST corresponding to the given
+ breakpoint. */
+
+static void
+delete_bp_partition_info (struct breakpoint *b)
+{
+ struct bp_partition_info *prev = NULL;
+ struct bp_partition_info *this = bp_partition_list;
+
+ while (this != NULL && this->b != b)
+ {
+ prev = this;
+ this = this->next;
+ }
+
+ if (this == NULL)
+ return;
+
+ if (this == bp_partition_list)
+ bp_partition_list = bp_partition_list->next;
+ else
+ prev->next = this->next;
+
+ xfree (this);
+}
+
+/* Find the breakpoint associated to the given TARGET_INFO and
+ return its corresponding partition ID. */
+
+static WTX_CONTEXT_ID_T
+wtx_partition_id_from_target_info (struct bp_target_info *target_info)
+{
+ struct breakpoint *bp;
+
+ /* Shortcut: If the target system does not support more than one partition,
+ then we know which partition this breakpoint belongs to. */
+ if (!wtx_pd_system_has_pds ())
+ return wtxapi_pd_current_get ();
+
+ /* FIXME: We do not support partition switching just yet. So,
+ the breakpoint is necessarily in the current partition. */
+ return wtxapi_pd_current_get ();
+}
+
+/* Observer for the "breakpoint_created" event. */
+
+static void
+wtx_breakpoint_created_observer (int bpnum)
+{
+ /* FIXME: The observer only passes the bpnum, which forces us
+ to do a reverse search for the associated breakpoint structure.
+ It is silly to have to do so when the observer had in fact
+ the breakpoint structure and had to dereference it in order to
+ pass the bpnum. Propose that the observer be enhanced when
+ submitting this code to the FSF. */
+ struct breakpoint *b = get_breakpoint (bpnum);
+
+ gdb_assert (b != NULL);
+ add_bp_partition_info (b, wtxapi_pd_current_get ());
+}
+
+/* Observer for the "delete_breakpoint" event. */
+
+static void
+wtx_delete_breakpoint_observer (int bpnum)
+{
+ struct breakpoint *b = get_breakpoint (bpnum);
+
+ gdb_assert (b != NULL);
+ delete_bp_partition_info (b);
+}
+
+/* Insert a system-mode breakpoint at the location specified by TARGET_INFO.
+ Return a non-zero errno code if the insertion failed. */
+
+static int
+wtx_insert_system_mode_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *target_info)
+{
+ const WTX_CONTEXT_ID_T breakpoint_pd
+ = wtx_partition_id_from_target_info (target_info);
+
+ return wtx_insert_breakpoint_1 (target_info,
+ WTX_CONTEXT_SYSTEM, /* context_type */
+ breakpoint_pd, /* context_id */
+ WTX_ACTION_STOP, /* action_type */
+ 0); /* call_rtn */
+}
+
+/* Implement the "to_remove_breakpoint" method in the target_ops vector. */
+
+static int
+wtx_remove_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *target_info)
+{
+ struct eventpoint_info *evtpt_info = target_info->target_private_data;
+ evtpt_id_t evtpt_id;
+ WTX_CONTEXT_TYPE context_type;
+ WTX_CONTEXT_ID_T context_id;
+
+ if (evtpt_info == NULL)
+ {
+ /* This is really an internal error, but the user might be able
+ to continue debugging normally if the user is luckly (if his
+ code does not hit the breakpoint). So just report a warning
+ instead of an internal error. */
+ warning (_("Cannot find eventpoint info for breakpoint at 0x%lx"),
+ target_info->placed_address);
+ return EINVAL;
+ }
+
+ /* Free the target_info private data now, after having made a copy
+ of its contents for our use here. It's easier to do it that way,
+ rather than avoiding the copies, because of the multiple returns
+ used in this routine (we would have to be careful to xfree it before
+ each and every return). */
+ evtpt_id = evtpt_info->id;
+ context_type = evtpt_info->context_type;
+ context_id = evtpt_info->context_id;
+ xfree (target_info->target_private_data);
+ target_info->target_private_data = NULL;
+
+ if (evtpt_id == invalid_evtpt_id)
+ {
+ warning (_("Cannot find eventpoint ID for breakpoint at 0x%lx"),
+ target_info->placed_address);
+ return EINVAL;
+ }
+
+ wtx_opt_breakpoints_debug
+ ("wtx_remove_breakpoint (target_info = 0x%s) -> "
+ "evtpt_id = 0x%x, context_type = 0x%x, context_id = 0x%lx",
+ host_address_to_string (target_info), evtpt_id,
+ context_type, context_id);
+
+ if (!wtxapi_eventpoint_delete (evtpt_id))
+ {
+ /* If this eventpoint is a task-specific eventpoint, and if the
+ corresponding task is dead, then this eventpoint has already
+ been deleted. In this case, the error returned by
+ wtxapi_eventpoint_delete is expected. Ignore it. */
+ if (context_type == WTX_CONTEXT_TASK
+ && !wtx_context_alive (context_id))
+ return 0;
+
+ warning (_("Failed to delete eventpoint for breakpoint at 0x%lx"),
+ target_info->placed_address);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* Implement the target_can_use_hw_breakpoint target method for
+ the targets that support BoDA events. */
+
+static int
+can_use_BoDA_breakpoint (int bp_type, int count, int other_type)
+{
+ /* BoDA events provide all types of watchpoints, read, write, access,
+ so it's not necessary to check BP_TYPE. */
+
+ /* The system only provides one BoDA event. */
+ if (count < 2)
+ return 1;
+
+ /* All the conditions to use a hardware watchpoint are not met, so
+ we cannot use one. */
+ return 0;
+}
+
+/* Implement the target_can_use_hw_breakpoint target method for
+ the targets where we use data-access events to implement watchpoints. */
+
+static int
+can_use_data_access_breakpoint (int bp_type, int count, int other_type)
+{
+ /* Data-access events provide all types of watchpoints, read, write,
+ access, so it's not necessary to check BP_TYPE. */
+
+ /* The system only provides one data-access event. */
+ if (count < 2)
+ return 1;
+
+ /* All the conditions to use a hardware watchpoint are not met, so
+ we cannot use one. */
+ return 0;
+}
+
+/* Implement the target_can_use_hw_breakpoint target method for all WTX
+ targets. */
+
+static int
+wtx_can_use_hw_breakpoint (int bp_type, int count, int other_type)
+{
+ /* Tornado systems provide at most one hardware watchpoint,
+ so if another type of watchpoint is already being used,
+ then we have no hardware watchpoint left. Return -1 to
+ signal that the rejection is because of OTHER_TYPE. */
+ if (other_type)
+ return -1;
+
+ if (wtxapi_target_has_BoDA ())
+ return can_use_BoDA_breakpoint (bp_type, count, other_type);
+
+ /* On targets that do not support BoDA, we have access to
+ data-access events. Try using that as a fallback. */
+ if (can_use_data_access_breakpoint (bp_type, count, other_type))
+ return 1;
+
+ /* The system cannot provide hardware watchpoint support for this case. */
+ return 0;
+}
+
+/* Return non-zero if we just stopped after hitting a watchpoint. */
+
+static int
+wtx_stopped_by_watchpoint (void)
+{
+ int result = (watchpoint_data_address != 0);
+
+ wtx_opt_watchpoints_debug ("wtx_stopped_by_watchpoint() -> %d", result);
+
+ return result;
+}
+
+/* Return the address monitored by the watchpoint we just hit. */
+
+static int
+wtx_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
+{
+ const int success = wtx_stopped_by_watchpoint ();
+
+ if (wtx_stopped_by_watchpoint ())
+ {
+ *addr_p = watchpoint_data_address;
+ wtx_opt_watchpoints_debug ("wtx_stopped_data_address(0x%s) -> 1",
+ paddress (target_gdbarch, *addr_p));
+ return 1;
+ }
+
+ wtx_opt_watchpoints_debug ("wtx_stopped_data_address() -> 0");
+ return 0;
+}
+
+/* Implement the region_ok_for_hw_watchpoint target method for
+ targets that support BoDA. */
+
+static int
+region_ok_for_BoDA (CORE_ADDR addr, int len)
+{
+ /* BoDA events only allow us to watch a data that is 4 bytes
+ long. Apparently, there are no constraints in terms of
+ alignment, so we don't need to worry about it. */
+ return (len <= 4);
+}
+
+/* Return non-zero if a WTX_EVENT_DATA_ACCESS event can watch
+ a memory region of LEN bytes. */
+
+static int
+region_ok_for_data_access_event (CORE_ADDR addr, int len)
+{
+ /* brobecker/2006-09-27: As far as I know, DATA_ACCESS events
+ can only monitor 4-byte memory regions. */
+ return (len <= 4);
+}
+
+
+/* Implement the region_ok_for_hw_watchpoint for all tornado targets.
+ Basicallly determines the type of watchpoint the target supports and
+ dispatches to the appropriate function. */
+
+static int
+wtx_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+{
+ if (wtxapi_target_has_BoDA ())
+ return region_ok_for_BoDA (addr, len);
+
+ return (region_ok_for_data_access_event (addr, len));
+}
+
+/* Given the watchpoint type that needs to be inserted (that follows
+ GDB's encoding), return the equivalent value to be used in WTX
+ event insertion requests. */
+
+static wtxapi_tgt_addr_t
+gdb_watchpoint_type_to_wtx_breakpoint_type (int type)
+{
+ switch (type)
+ {
+ case hw_write: return 3; break;
+ case hw_read: return 2; break;
+ case hw_access: return 1; break;
+ case hw_execute: return 0; break;
+ }
+
+ /* We should never reach this code. If we do, print a warning and
+ pretend we were trying to use a read+write=access watchpoint type. */
+ warning (_("unexpected watchpoint type: %d"), type);
+ return gdb_watchpoint_type_to_wtx_breakpoint_type (hw_access);
+}
+
+/* Fill up EVENT with all the necessary information to insert
+ a WTX_EVENT_HW_BP eventpoint.
+
+ Even though this function does not setup EVENT to insert a BoDA,
+ this function assumes that the target does support BoDA. If not,
+ use set_data_access_event below. */
+
+static void
+set_hardware_watchpoint_event_no_value (struct wtxapi_event *event,
+ CORE_ADDR addr,
+ int type)
+{
+ event->event_type = WTX_EVENT_HW_BP;
+
+ /* WTX_EVENT_HW_BP eventpoints have 3 arguments:
+ . args[0] is the address of the data being watched.
+ . args[1] is the watchpoint count (the number of times
+ the watchpoint should be hit before generating
+ an event to the debugger). Unused by us, always
+ set to 0.
+ . args[2] is the type of watchpoint (read/write/access). */
+ event->num_args = 3;
+ event->args[0] = addr;
+ event->args[1] = 0;
+ event->args[2] = gdb_watchpoint_type_to_wtx_breakpoint_type (type);
+
+ wtx_opt_watchpoints_debug
+ ("Using WTX_EVENT_HW_BP event (addr = 0x%s, type = %d)",
+ paddress (target_gdbarch, event->args[0]), (int) event->args[2]);
+}
+
+/* Fill up EVENT with all the necessary information to insert
+ a WTX_EVENT_DATA_ACCESS eventpoint.
+
+ This function should be used with all systems that do NOT support BoDA. */
+
+static void
+set_data_access_event (struct wtxapi_event *event,
+ const CORE_ADDR addr,
+ const int type)
+{
+ event->event_type = WTX_EVENT_DATA_ACCESS;
+
+ /* WTX_EVENT_DATA_ACCESS eventpoints have 3 arguments:
+ . args[0] is the address of the data being watched.
+ . args[1] is the watchpoint count (the number of times
+ the watchpoint should be hit before generating
+ an event to the debugger). Unused by us, always
+ set to 0.
+ . args[2] is the type of watchpoint (read/write/access). */
+ event->num_args = 3;
+ event->args[0] = addr;
+ event->args[1] = 0;
+ event->args[2] = gdb_watchpoint_type_to_wtx_breakpoint_type (type);
+
+ wtx_opt_watchpoints_debug
+ ("Using DATA_ACCESS event (addr = 0x%s, type = %d)",
+ paddress (target_gdbarch, event->args[0]), (int) event->args[2]);
+}
+
+/* Implement the target_insert_watchpoint target method for all WTX
+ targets. */
+
+static int
+wtx_insert_watchpoint (WTX_CONTEXT_TYPE context_type,
+ WTX_CONTEXT_ID_T context_id,
+ WTX_ACTION_TYPE action_type,
+ CORE_ADDR addr, int len, int type)
+{
+ struct wtxapi_evtpt evtpt;
+ wtxapi_tgt_arg_t args[4];
+ evtpt_id_t watchpoint_id;
+
+ /* First, clear the evtpt struture. In other parts of this file,
+ we actually calloc this structure which makes the memset unecessary,
+ but doing it this way avoids the need to free the newly allocated
+ memory. */
+ memset (&evtpt, 0, sizeof (evtpt));
+
+ evtpt.context.context_type = context_type;
+ evtpt.context.context_id = context_id;
+ evtpt.action.action_type = action_type;
+ evtpt.event.args = args;
+
+ /* Tell the target server to notify us when the watchpoint is hit. */
+ evtpt.action.action_type |= WTX_ACTION_NOTIFY;
+
+ /* If the system provides data-matching support with its watchpoints,
+ and the watchpoint has a condition that can be tested using that
+ support, then use it. The data value in BoDA watchpoints will
+ be optional. */
+
+ if (wtxapi_target_has_BoDA ())
+ set_hardware_watchpoint_event_no_value (&evtpt.event, addr, type);
+ else
+ set_data_access_event (&evtpt.event, addr, type);
+
+ /* Insert the eventpoint. */
+ watchpoint_id = wtxapi_eventpoint_add (&evtpt);
+
+ if (watchpoint_id == invalid_evtpt_id)
+ error (_("Failed to insert watchpoint"));
+
+ /* Save the watchpoint_id in our list of eventpoints. Will be needed
+ later to map GDB watchpoints info back into WTX event ids. */
+ wtx_bp_register_watchpoint_id (watchpoint_id, addr, len, type);
+
+ return 0;
+}
+
+/* Implement the task-mode "to_insert_watchpoint" target_ops method. */
+
+static int
+wtx_insert_task_mode_watchpoint (CORE_ADDR addr, int len, int type,
+ struct expression *cond)
+{
+ const enum wtx_breakpoint_action action = get_break_command_action ();
+ WTX_CONTEXT_TYPE context_type;
+ WTX_CONTEXT_ID_T context_id;
+ WTX_ACTION_TYPE action_type;
+ CORE_ADDR call_rtn = 0;
+
+ /* FIXME: We do the exact same thing when inserting a task-mode
+ breakpoint. Put this code inside remote-wtx-bp and then reuse. */
+ context_type = WTX_CONTEXT_TASK;
+ context_id = ptid_get_context_id (inferior_ptid);
+
+ switch (action)
+ {
+ case action_task:
+ action_type = WTX_ACTION_STOP;
+ break;
+
+ case action_all:
+ action_type = WTX_ACTION_STOP_ALL;
+ break;
+
+ case action_call:
+ context_type = WTX_CONTEXT_ANY_TASK;
+ action_type = WTX_ACTION_STOP;
+ call_rtn = get_break_command_call_rtn ();
+ if (call_rtn != 0)
+ action_type |= WTX_ACTION_CALL;
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__, _("Unexpected action value: %d"),
+ action);
+ }
+
+ return wtx_insert_watchpoint (context_type, context_id,
+ action_type, addr, len, type);
+}
+
+/* Implement the system-mode "to_insert_watchpoint" target_ops method. */
+
+static int
+wtx_insert_system_mode_watchpoint (CORE_ADDR addr, int len, int type,
+ struct expression *cond)
+{
+ return wtx_insert_watchpoint (WTX_CONTEXT_SYSTEM, SYSTEM_CID,
+ WTX_ACTION_STOP, addr, len, type);
+}
+
+/* Implement the "to_remove_watchpoint" target_ops method. */
+
+static int
+wtx_remove_watchpoint (CORE_ADDR addr, int len, int type,
+ struct expression *cond)
+{
+ const evtpt_id_t watchpoint_id = wtx_bp_pop_watchpoint_id (addr, len, type);
+
+ if (watchpoint_id == invalid_evtpt_id)
+ {
+ warning (_("Unknown watchpoint at 0x%s (len = %d, type = %d)"),
+ paddress (target_gdbarch, addr), len, type);
+ return EINVAL;
+ }
+
+ if (!wtxapi_eventpoint_delete (watchpoint_id))
+ {
+ warning (_("Unable to remove watchpoint id 0x%x"), watchpoint_id);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* Implement the "to_mourn_inferior" method of the target_ops vector
+ for the task mode. */
+
+static void
+wtx_task_mode_mourn_inferior (struct target_ops *ops)
+{
+ remove_breakpoints ();
+ if (multi_task_mode_is_on ())
+ stop_multi_task_mode ();
+
+ unpush_target (&wtx_task_mode_ops);
+ generic_mourn_inferior ();
+}
+
+/* Return 1 if the thread whose id is CONTEXT_ID is still alive. */
+
+static int
+wtx_context_alive (int context_id)
+{
+ struct wtxapi_thread_info *thread_list = wtxapi_get_thread_list ();
+ struct wtxapi_thread_info *thread;
+ int is_alive = 0;
+
+ for (thread = thread_list; thread != NULL; thread = thread->next)
+ if (thread->id == context_id)
+ {
+ is_alive = 1;
+ break;
+ }
+ free_wtxapi_thread_info (thread_list);
+
+ return is_alive;
+}
+
+/* Implement the "to_thread_alive" method of the target_ops vector. */
+
+static int
+wtx_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+ const int context_id = ptid_get_context_id (ptid);
+ return wtx_context_alive (context_id);
+}
+
+/* Implement the "to_find_new_threads" method when in single-task mode. */
+
+static void
+wtx_single_task_mode_find_new_threads (void)
+{
+ /* In this mode, we only debug one thread. */
+ wtx_add_thread (inferior_ptid);
+}
+
+/* Add the thread associated to the given TASK to the thread list.
+ Does nothing if that thread was already added earlier. */
+
+static void
+wtx_multi_task_mode_add_thread_from_task (struct ada_task_info *task)
+{
+ wtx_add_thread (task->ptid);
+}
+
+/* Implement the "to_find_new_threads" method when in multi-tasks mode. */
+
+static void
+wtx_multi_task_mode_find_new_threads (void)
+{
+ iterate_over_live_ada_tasks (wtx_multi_task_mode_add_thread_from_task);
+}
+
+/* Implement the "to_find_new_threads" method of the task-mode target_ops
+ vector. Handles both single-task and multi-tasks modes. */
+
+static void
+wtx_task_mode_find_new_threads (struct target_ops *ops)
+{
+ if (multi_task_mode_is_on ())
+ wtx_multi_task_mode_find_new_threads ();
+ else
+ wtx_single_task_mode_find_new_threads ();
+}
+
+/* Implement the "to_find_new_threads" method of the system-mode
+ target_ops vector. */
+
+static void
+wtx_system_mode_find_new_threads (struct target_ops *ops)
+{
+ struct wtxapi_thread_info *thread_list = wtxapi_get_thread_list ();
+ struct wtxapi_thread_info *thread;
+
+ for (thread = thread_list; thread != NULL; thread = thread->next)
+ {
+ const ptid_t thread_ptid = context_id_to_ptid (thread->id);
+
+ wtx_add_thread (thread_ptid);
+ }
+
+ free_wtxapi_thread_info (thread_list);
+}
+
+/* Suspend the given CONTEXT_TYPE/CONTEXT_ID. */
+
+static void
+wtx_stop (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+ if (multi_task_mode_is_on ())
+ stop_all_ada_tasks (&inferior_status);
+ else
+ stop_inferior (context_type, context_id);
+}
+
+/* Implement the task-mode "to_stop" target_ops method. */
+
+static void
+wtx_task_mode_stop (ptid_t ptid)
+{
+ const WTX_CONTEXT_ID_T context_id = ptid_get_context_id (ptid);
+
+ wtx_stop (WTX_CONTEXT_TASK, context_id);
+}
+
+/* Implement the system-mode "to_stop" target_ops method. */
+
+static void
+wtx_system_mode_stop (ptid_t ptid)
+{
+ wtx_stop (WTX_CONTEXT_SYSTEM, SYSTEM_CID);
+ inferior_ptid =
+ context_id_to_ptid (wtxapi_system_mode_get_current_context_id ());
+}
+
+/* Return the name of a task from its CONTEXT_ID, or NULL if the task
+ could not be found.
+
+ The returned string must be deallocated after use. */
+
+static char *
+wtx_name_from_context_id (WTX_CONTEXT_ID_T context_id)
+{
+ struct wtxapi_thread_info *threads = wtxapi_get_thread_list ();
+ struct wtxapi_thread_info *this_thread = threads;
+ char *name = NULL;
+
+ while (this_thread != NULL)
+ {
+ if (this_thread->id == context_id)
+ {
+ name = xstrdup (this_thread->name);
+ break;
+ }
+ this_thread = this_thread->next;
+ }
+
+ free_wtxapi_thread_info (threads);
+ return name;
+}
+
+/* Implement the "to_pid_to_str" target_ops method. */
+
+static char *
+wtx_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+ static char *buf = NULL;
+ const WTX_CONTEXT_ID_T context_id = ptid_get_context_id (ptid);
+ char *task_name = wtx_name_from_context_id (context_id);
+
+ if (buf != NULL)
+ xfree (buf);
+ xasprintf (&buf, "task 0x%lx\t(%s)\t", context_id,
+ task_name ? task_name : "[no name]");
+ if (task_name != NULL)
+ xfree (task_name);
+
+ return buf;
+}
+
+/* Implement the to_get_ada_task_ptid function for the WTX targets. */
+
+static ptid_t
+wtx_get_ada_task_ptid (long lwp, long thread)
+{
+ return context_id_to_ptid (thread);
+}
+
+/* Always return zero. */
+
+static int
+wtx_return_zero (struct target_ops *target)
+{
+ return 0;
+}
+
+/* Always return one. */
+
+static int
+wtx_return_one (struct target_ops *target)
+{
+ return 1;
+}
+
+/* Initialize the WTX_OPS target_ops object. */
+
+static void
+init_wtx_ops (void)
+{
+ /* First, clear the structure. This is not strictly necessary provided
+ we make sure to always set all the fields of the our structure.
+ But in the event that we did not, this makes sure that the fields
+ we missed are initialized to something consistent. */
+ memset (&wtx_ops, 0, sizeof (wtx_ops));
+
+ wtx_ops.to_shortname = "wtx";
+ wtx_ops.to_longname = "WTX protocol";
+ wtx_ops.to_doc = "\
+Remote target connected via the WTX protocol.\n\
+Specify the name of the target server as the argument.";
+ wtx_ops.to_open = wtx_open;
+ wtx_ops.to_close = wtx_close;
+ wtx_ops.to_attach = wtx_attach;
+ /* No need to set to_detach, will never be called, because the detach
+ option from a higher stratum will be used instead. */
+ wtx_ops.to_load = wtx_load;
+ wtx_ops.to_unload = wtx_unload;
+ wtx_ops.to_create_inferior = wtx_create_inferior;
+ wtx_ops.to_pid_to_str = wtx_pid_to_str;
+ wtx_ops.to_stratum = file_stratum;
+ wtx_ops.to_has_all_memory = wtx_return_one;
+ wtx_ops.to_has_memory = wtx_return_one;
+ wtx_ops.to_has_stack = wtx_return_zero;
+ wtx_ops.to_has_registers = wtx_return_zero;
+ wtx_ops.to_has_execution = wtx_return_zero;
+ wtx_ops.to_xfer_partial = wtx_xfer_partial;
+ wtx_ops.to_get_ada_task_ptid = wtx_get_ada_task_ptid;
+ wtx_ops.to_magic = OPS_MAGIC;
+}
+
+static void
+init_wtx_task_mode_ops (void)
+{
+ /* First, clear the structure. This is not strictly necessary provided
+ we make sure to always set all the fields of the our structure.
+ But in the event that we did not, this makes sure that the fields
+ we missed are initialized to something consistent. */
+ memset (&wtx_task_mode_ops, 0, sizeof (wtx_task_mode_ops));
+
+ wtx_task_mode_ops.to_shortname = "wtx task mode";
+ wtx_task_mode_ops.to_longname =
+ "Task and Multi-tasks Mode support for the WTX protocol";
+ wtx_task_mode_ops.to_doc =
+ "Debugging a task or a set of tasks using the WTX protocol.";
+ wtx_task_mode_ops.to_open = NULL;
+ wtx_task_mode_ops.to_close = wtx_task_mode_close;
+ wtx_task_mode_ops.to_attach = wtx_attach;
+ wtx_task_mode_ops.to_detach = wtx_task_mode_detach;
+ wtx_task_mode_ops.to_resume = wtx_task_mode_resume;
+ wtx_task_mode_ops.to_wait = wtx_wait;
+ wtx_task_mode_ops.to_fetch_registers = wtx_task_mode_fetch_registers;
+ wtx_task_mode_ops.to_store_registers = wtx_task_mode_store_registers;
+ wtx_task_mode_ops.to_prepare_to_store = wtx_prepare_to_store;
+ wtx_task_mode_ops.to_insert_breakpoint = wtx_insert_task_mode_breakpoint;
+ wtx_task_mode_ops.to_remove_breakpoint = wtx_remove_breakpoint;
+ wtx_task_mode_ops.to_can_use_hw_breakpoint = wtx_can_use_hw_breakpoint;
+ wtx_task_mode_ops.to_remove_watchpoint = wtx_remove_watchpoint;
+ wtx_task_mode_ops.to_insert_watchpoint = wtx_insert_task_mode_watchpoint;
+ wtx_task_mode_ops.to_stopped_by_watchpoint = wtx_stopped_by_watchpoint;
+ wtx_task_mode_ops.to_stopped_data_address = wtx_stopped_data_address;
+ wtx_task_mode_ops.to_region_ok_for_hw_watchpoint =
+ wtx_region_ok_for_hw_watchpoint;
+
+ wtx_task_mode_ops.to_kill = wtx_task_mode_kill;
+ wtx_task_mode_ops.to_load = wtx_load;
+ wtx_task_mode_ops.to_unload = wtx_unload;
+ wtx_task_mode_ops.to_mourn_inferior = wtx_task_mode_mourn_inferior;
+ wtx_task_mode_ops.to_thread_alive = wtx_thread_alive;
+ wtx_task_mode_ops.to_find_new_threads = wtx_task_mode_find_new_threads;
+ wtx_task_mode_ops.to_stop = wtx_task_mode_stop;
+ wtx_task_mode_ops.to_stratum = process_stratum;
+ wtx_task_mode_ops.to_has_all_memory = wtx_return_one;
+ wtx_task_mode_ops.to_has_memory = wtx_return_one;
+ wtx_task_mode_ops.to_has_stack = wtx_return_one;
+ wtx_task_mode_ops.to_has_registers = wtx_return_one;
+ wtx_task_mode_ops.to_has_execution = wtx_return_one;
+ wtx_task_mode_ops.to_xfer_partial = wtx_xfer_partial;
+ wtx_task_mode_ops.to_magic = OPS_MAGIC;
+}
+
+static void
+init_wtx_system_mode_ops (void)
+{
+ /* First, clear the structure. This is not strictly necessary provided
+ we make sure to always set all the fields of the our structure.
+ But in the event that we did not, this makes sure that the fields
+ we missed are initialized to something consistent. */
+ memset (&wtx_system_mode_ops, 0, sizeof (wtx_system_mode_ops));
+
+ wtx_system_mode_ops.to_shortname = "wtx system mode";
+ wtx_system_mode_ops.to_longname =
+ "System Mode support for the WTX protocol";
+ wtx_system_mode_ops.to_doc =
+ "Debugging a target in system mode using the WTX protocol.";
+ wtx_system_mode_ops.to_open = NULL;
+ wtx_system_mode_ops.to_close = wtx_system_mode_close;
+ wtx_system_mode_ops.to_attach = wtx_attach;
+ wtx_system_mode_ops.to_detach = wtx_system_mode_detach;
+ wtx_system_mode_ops.to_resume = wtx_system_mode_resume;
+ wtx_system_mode_ops.to_wait = wtx_wait;
+ wtx_system_mode_ops.to_fetch_registers = wtx_system_mode_fetch_registers;
+ wtx_system_mode_ops.to_store_registers = wtx_system_mode_store_registers;
+ wtx_system_mode_ops.to_prepare_to_store = wtx_prepare_to_store;
+ wtx_system_mode_ops.to_insert_breakpoint = wtx_insert_system_mode_breakpoint;
+ wtx_system_mode_ops.to_remove_breakpoint = wtx_remove_breakpoint;
+ wtx_system_mode_ops.to_can_use_hw_breakpoint = wtx_can_use_hw_breakpoint;
+ wtx_system_mode_ops.to_remove_watchpoint = wtx_remove_watchpoint;
+ wtx_system_mode_ops.to_insert_watchpoint = wtx_insert_system_mode_watchpoint;
+ wtx_system_mode_ops.to_stopped_by_watchpoint = wtx_stopped_by_watchpoint;
+ wtx_system_mode_ops.to_stopped_data_address = wtx_stopped_data_address;
+ wtx_system_mode_ops.to_region_ok_for_hw_watchpoint =
+ wtx_region_ok_for_hw_watchpoint;
+
+ wtx_system_mode_ops.to_kill = wtx_system_mode_kill;
+ wtx_system_mode_ops.to_load = wtx_load;
+ wtx_system_mode_ops.to_unload = wtx_unload;
+ wtx_system_mode_ops.to_thread_alive = wtx_thread_alive;
+ wtx_system_mode_ops.to_find_new_threads = wtx_system_mode_find_new_threads;
+ wtx_system_mode_ops.to_stop = wtx_system_mode_stop;
+ wtx_system_mode_ops.to_stratum = process_stratum;
+ wtx_system_mode_ops.to_has_all_memory = wtx_return_one;
+ wtx_system_mode_ops.to_has_memory = wtx_return_one;
+ wtx_system_mode_ops.to_has_stack = wtx_return_one;
+ wtx_system_mode_ops.to_has_registers = wtx_return_one;
+ wtx_system_mode_ops.to_has_execution = wtx_return_one;
+ wtx_system_mode_ops.to_xfer_partial = wtx_xfer_partial;
+ wtx_system_mode_ops.to_magic = OPS_MAGIC;
+}
+
+static struct cmd_list_element *wtx_list = NULL;
+
+/* Implement the "wtx" command.
+ This is not actually a valid command, so inform the user of that fact,
+ and print the list of valid subcommands to help him. */
+
+static void
+wtx_command (char *args, int from_tty)
+{
+ printf_filtered (_("\"wtx\" must be followed by a subcommand.\n"));
+ help_list (wtx_list, "wtx ", -1, gdb_stdout);
+}
+
+/* Implement the "wtx add-symbol-file" command: same as add-symbol-file,
+ except that the load addresses are not taken from the command
+ line, but from the WTX module info. */
+
+static void
+wtx_add_symbol_file (char *args, int from_tty)
+{
+ char *module_name, *module_filename;
+ module_id_t module_id;
+ pd_id_t module_pd;
+ struct cleanup *old_chain = NULL;
+ struct wtxapi_module_info *module_info;
+
+ if (args == NULL)
+ error (_("wtx add-symbol-file takes a file name"));
+
+ module_filename = args;
+ module_name = (char *) lbasename (module_filename);
+ module_id = wtxapi_obj_module_in_system_find_id (module_name);
+
+ if (module_id == invalid_module_id)
+ {
+ warning (_("could not find %s on target: %s"),
+ module_name, wtxapi_err_msg_get ());
+ return;
+ }
+
+ module_pd = wtx_pd_get_module_pd (module_id);
+ module_info = wtxapi_obj_module_info_get (module_pd, module_id);
+ old_chain = make_cleanup (cleanup_wtxapi_module_info, module_info);
+
+ if (module_info == NULL)
+ error (_("Cannot get info for module 0x%x: %s"),
+ (unsigned int) module_id, wtxapi_err_msg_get ());
+
+ printf_filtered (_("Add symbols from file %s:\n"), module_filename);
+ if (read_module_symbols_1 (module_info, module_filename))
+ printf_filtered (_("ok\n"));
+ else
+ printf_filtered (_("failed\n"));
+
+ if (old_chain)
+ do_cleanups (old_chain);
+}
+
+void
+_initialize_remote_wtx (void)
+{
+ /* Initialize the WTX library. */
+ if (!wtxapi_initialize ())
+ {
+ warning (_("Failed to initialize WTX library:\n\t%s\n\
+(WTX target not activated)."),
+ wtxapi_err_msg_get ());
+
+ /* We cannot use the WTX protocol if for some reason we failed
+ to initialized the WTX API. So abort now, instead of pushing
+ the WTX target ops. */
+ return;
+ }
+
+ init_wtx_ops ();
+ init_wtx_task_mode_ops ();
+ init_wtx_system_mode_ops ();
+
+ add_target (&wtx_ops);
+
+ /* New commands. */
+
+ add_prefix_cmd ("wtx", no_class, wtx_command,
+ _("WTX protocol specific commands\n"),
+ &wtx_list, "wtx ",
+ 0 /* allow-unknown */, &cmdlist);
+
+ add_cmd ("add-symbol-file", no_class, wtx_add_symbol_file, _("\
+WTX specific version of add-symbol-file\n\
+Usage: add-symbol-file FILE\n\
+Load symbols from FILE, assuming that FILE has been dynamically loaded.\n\
+The section addresses for this module are fetched from the target server.\n\
+"), &wtx_list);
+
+ /* Observers. */
+
+ if (wtx_pd_system_has_pds ())
+ {
+ /* The following observers are only useful when the target system
+ supports partitions. */
+ observer_attach_breakpoint_created (wtx_breakpoint_created_observer);
+ observer_attach_breakpoint_deleted (wtx_delete_breakpoint_observer);
+ }
+}
diff --git a/gdb/remote-wtx.h b/gdb/remote-wtx.h
new file mode 100644
index 0000000..a81f55e
--- /dev/null
+++ b/gdb/remote-wtx.h
@@ -0,0 +1,25 @@
+/* WTX backend for GDB.
+
+ Copyright 2007, 2010, 2011 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef REMOTE_WTX_H
+#define REMOTE_WTX_H
+
+extern void wtx_open (char *args, int from_tty);
+
+#endif
--
1.7.0.4