From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15086 invoked by alias); 25 Apr 2010 15:48:50 -0000 Received: (qmail 14719 invoked by uid 22791); 25 Apr 2010 15:48:33 -0000 X-SWARE-Spam-Status: No, hits=1.2 required=5.0 tests=BAYES_50,TW_BJ,TW_EG,TW_RG,TW_WT,TW_XZ,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from rock.gnat.com (HELO rock.gnat.com) (205.232.38.15) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 25 Apr 2010 15:47:58 +0000 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 1C8A02BAC9B for ; Sun, 25 Apr 2010 11:47:51 -0400 (EDT) Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id 5oxVzstpyDwh for ; Sun, 25 Apr 2010 11:47:50 -0400 (EDT) Received: from joel.gnat.com (localhost.localdomain [127.0.0.1]) by rock.gnat.com (Postfix) with ESMTP id AE15A2BAC98 for ; Sun, 25 Apr 2010 11:47:50 -0400 (EDT) Received: by joel.gnat.com (Postfix, from userid 1000) id 485C4F5896; Sun, 25 Apr 2010 11:47:50 -0400 (EDT) From: Joel Brobecker To: gdb-patches@sourceware.org Subject: [vxworks 10/14] Add new "wtx" target. Date: Sun, 25 Apr 2010 15:48:00 -0000 Message-Id: <1272210447-13895-11-git-send-email-brobecker@adacore.com> In-Reply-To: <1272210447-13895-1-git-send-email-brobecker@adacore.com> References: <1272210447-13895-1-git-send-email-brobecker@adacore.com> Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-04/txt/msg00844.txt.bz2 This adds the bulk of the support for VxWorks systems through a new target: wtx. 2010-04-24 Joel Brobecker * remote-wtx.h, remote-wtx.c: New files. --- gdb/remote-wtx.c | 3075 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/remote-wtx.h | 25 + 2 files changed, 3100 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..2fde258 --- /dev/null +++ b/gdb/remote-wtx.c @@ -0,0 +1,3075 @@ +/* Support for the WTX protocol. + + Copyright (C) 2007, 2008, 2009, 2010 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 . */ + +#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 + +/* 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 void unload_command (char *args, 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; +} + +/* 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); + } + + 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: + [ ":"] */ + + /* 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->step_range_end && (tp->step_range_end > tp->step_range_start)) + wtxapi_context_step (context_type, context_id, tp->step_range_start, + tp->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->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->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); +} + +/* 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)); + + 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.")); +} + +/* Implement the "load" command for the WTX protocol. + + The usage is as follow: + + load [remote filename] + + where 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) + { + unload_command (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); +} + +/* 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 ()); + +} + +/* 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 + (causes GDB to create an associated objfile) + 2/ from Windsh: unload + 3/ from GDB: load + (GDB correctly recreates the associated objfile) + 4/ from GDB: run + (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); +} + +/* Implement the "unload" command. */ + +static void +unload_command (char *args, int from_tty) +{ + char **argv = buildargv (args); + + dont_repeat (); + + 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); + + /* Clear the Ada symbol cache, because it might contain some symbols + from the module we just unloaded. */ + clear_ada_sym_cache (); +} + +/* 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; +} + +/* 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; + + 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); + + if (evtpt_id == invalid_evtpt_id) + return ENOMEM; + + wtx_bp_register_eventpoint_id (evtpt_id, target_info, context_type, + context_id); + + 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 (); + + error (_("Not implemented yet.")); +} + +/* 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) +{ + WTX_CONTEXT_TYPE context_type; + WTX_CONTEXT_ID_T context_id; + + const evtpt_id_t evtpt_id = wtx_bp_pop_eventpoint_id (target_info, + &context_type, + &context_id); + + if (evtpt_id == invalid_evtpt_id) + { + warning (_("Cannot find eventpoint ID for breakpoint at 0x%lx"), + target_info->placed_address); + return EINVAL; + } + + 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) +{ + 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) +{ + 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) +{ + 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_create_inferior = wtx_create_inferior; + wtx_ops.to_pid_to_str = wtx_pid_to_str; + wtx_ops.to_stratum = core_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_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_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; +} + +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_com ("unload", class_files, unload_command, + _("Unload the given module from the target")); + + /* 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..266369e --- /dev/null +++ b/gdb/remote-wtx.h @@ -0,0 +1,25 @@ +/* WTX backend for GDB. + + Copyright 2007, 2010 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 . */ + +#ifndef REMOTE_WTX_H +#define REMOTE_WTX_H + +extern void wtx_open (char *args, int from_tty); + +#endif -- 1.6.3.3