From patchwork Wed Jul 15 21:49:35 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 7707 Received: (qmail 18673 invoked by alias); 15 Jul 2015 21:50:06 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 18660 invoked by uid 89); 15 Jul 2015 21:50:05 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.9 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 15 Jul 2015 21:50:02 +0000 Received: from svr-orw-fem-06.mgc.mentorg.com ([147.34.97.120]) by relay1.mentorg.com with esmtp id 1ZFUYx-0000T8-Is from Don_Breazeal@mentor.com ; Wed, 15 Jul 2015 14:49:59 -0700 Received: from build4-lucid-cs (147.34.91.1) by SVR-ORW-FEM-06.mgc.mentorg.com (147.34.97.120) with Microsoft SMTP Server id 14.3.224.2; Wed, 15 Jul 2015 14:49:59 -0700 Received: by build4-lucid-cs (Postfix, from userid 1905) id CBD4D40F0A; Wed, 15 Jul 2015 14:49:59 -0700 (PDT) From: Don Breazeal To: , Subject: [PATCH 1/5] Extended-remote exec events Date: Wed, 15 Jul 2015 14:49:35 -0700 Message-ID: <1436996979-32350-2-git-send-email-donb@codesourcery.com> In-Reply-To: <1436996979-32350-1-git-send-email-donb@codesourcery.com> References: <1436996979-32350-1-git-send-email-donb@codesourcery.com> MIME-Version: 1.0 X-IsSubscribed: yes This patch is the latest version implementing support for exec events on extended-remote Linux targets. Follow-exec-mode and rerun behave as expected. Catchpoints and test updates are implemented in subsequent patches. This patch was most recently derived from a patch posted last October: https://sourceware.org/ml/gdb-patches/2014-10/msg00877.html. It was originally based on some work done by Luis Machado in 2013. IMPLEMENTATION ---------------- Support for exec events in single-threaded programs was a fairly straightforward replication of the implementation in native GDB: 1) Enable exec events via ptrace options. 2) Add support for handling the exec events to the handle_extended_wait and linux_wait_for_event_filtered. Detect the exec event, then find and save the pathname of the executable file being exec'd and set event status flags. 3) Implement an additional "stop reason", "exec", in the RSP stop reply packet "T". Existing GDB code takes care of handling the exec event on the host side without modification. Support for exec events in multi-threaded programs required some additional work. When exec is called, the Linux kernel destroys all of the threads except the execing one. If the execing thread was not the thread group leader, the kernel resets the execing thread's tid to the tgid, and no exit notification is sent for the execing thread -- from the ptracer's perspective, it appears as though the execing thread just vanishes. The non-leader exec leaves gdbserver with one or more of several potential scenarios which require it to bring its thread lists (e.g. struct thread_info, struct lwp_info) in sync with reality. - The leader thread exited before the exec event was reported, and the execing thread cannot re-use its data structures. In this case gdbserver must recognize that an exec event occurred and there are no thread structures for the leader thread, so it must add new structures to the lists for the 'new' leader thread. if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP) && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)) { ptid_t child_ptid; /* A multi-thread exec after we had seen the leader exiting. */ if (debug_threads) { debug_printf ("LLW: Re-adding thread group leader LWP %d" "after exec.\n", lwpid); } child_ptid = ptid_build (lwpid, lwpid, 0); child = add_lwp (child_ptid); child->stopped = 1; current_thread = child->thread; } - The execing thread can re-use the previous leader thread's data structures, and the old data structures used for the execing thread prior to the exec are left with a running status and no actual thread associated with it. When a non-leader execing thread re-uses the previous leader's thread_info structure, it inherits the old thread's register cache. If this is left as-is it will eventually be flushed to the target, clobbering the valid register values with those from the old thread. So when an EXEC event occurs we always invalidate the register cache. Note that we can't call regcache_invalidate, since that flushes the cache to the target. else if (event == PTRACE_EVENT_EXEC && report_exec_events) { struct regcache *regcache; ---snip snip--- regcache = (struct regcache *) inferior_regcache_data (event_thr); free_register_cache (regcache); set_inferior_regcache_data (event_thr, NULL); event_lwp->stop_pc = get_pc (event_lwp); In this case (stale thread data structures for execing thread) when in all-stop mode, gdbserver must clean up any stale thread/lwp structures before it tries to stop all the threads and hangs in sigsuspend, waiting for an event from a non-existent thread. We do this by checking the return value from kill_lwp in send_sigstop, eventually called after calling stop_all_lwps, which is used to stop the threads in all-stop mode. If kill_lwp returns an error and errno is ESRCH, we know that the lwp with that pid is gone, and we delete the associated data structures. - kill_lwp (pid, SIGSTOP); + errno = 0; + ret = kill_lwp (pid, SIGSTOP); + if (ret == -1 && errno == ESRCH) + { + /* If the kill fails with "No such process", on GNU/Linux we know + that the LWP has vanished - it is not a zombie, it is gone. + This is because a thread that was not the thread group leader + called exec and took over the leader's lwp. */ + delete_lwp (lwp); + set_desired_thread (0); In the same case in non-stop mode, we don't need to stop all the lwps, but in order to utilize the same mechanism used in all-stop mode, we call stop_all_lwps/unstop_all_lwps in succession, just to check for ESRCH errors and to delete any stale thread structures. if (non_stop && stopping_threads == NOT_STOPPING_THREADS) { /* In non-stop mode, make sure we delete the lwp entry for a non-leader exec'ing thread, which will have vanished. We do this by sending a signal to all the other threads in the lwp list, deleting any that are not found. Note that in all-stop mode this will happen when we stop all the threads. */ stop_all_lwps (0, event_lwp); unstop_all_lwps (0, event_lwp); } Note that the native implementation uses a different mechanism for identifying the stale data structure scenario. It determines that the execing thread has "vanished" by calling waitpid(PID) and checking for a return value of ECHILD, which means that the thread is gone. We don't want to use waitpid(PID) in gdbserver, based on the discussion in: https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html so we use the send_sigstop method described above instead. TESTING -------- x86_64 GNU/Linux for native, native-gdbserver, and native-extended-gdbserver targets. Most of the exec-related tests fail due to the lack of catchpoints and extended-remote support in the tests. Thanks --Don gdb/gdbserver/ 2015-07-15 Don Breazeal Luis Machado * linux-low.c (handle_extended_wait): Handle exec events. (check_zombie_leaders): Do not check stopped threads. (linux_low_ptrace_options): Add PTRACE_O_TRACEEXEC. (linux_low_filter_event): Add thread structures for exec'ing non-leader thread after leader thread had been deleted. (linux_wait_for_event_filtered): Fix comment saying exec events are not supported in remote. (extended_event_reported): Add TARGET_WAITKIND_EXECD. (linux_wait_1): Prevent clobbering extended event status. (send_sigstop): Check return from kill_lwp, and if ESRCH then call delete_lwp. (linux_supports_exec_events): New function. * lynx-low.c (lynx_target_ops): Initialize new structure member 'supports_exec_events'. * remote-utils.c (prepare_resume_reply): New stop reason 'exec'. * server.c (report_exec_events): New global variable. (handle_query): Handle qSupported query for exec-events feature. (captured_main): Initialize report_exec_events. * server.h (report_exec_events): Declare new global variable. * target.h (struct target_ops) : New member. (target_supports_exec_events): New macro. * win32-low.c (win32_target_ops): Initialize new structure member 'supports_exec_events'. gdb/ 2015-07-15 Don Breazeal Luis Machado * nat/linux-ptrace.c (linux_supports_tracefork): Delete out-of-date comment verbiage. (linux_supports_traceexec): New function. * nat/linux-ptrace.h (linux_supports_traceexec): Declare. * remote.c (anonymous enum) New enumeration constant. (remote_protocol_features): Add entry for exec-events feature. (remote_query_supported): Add client side of qSupported query for exec-events feature. (_initialize_remote): Call add_packet_config_cmd for remote exec-events feature. --- gdb/gdbserver/linux-low.c | 117 ++++++++++++++++++++++++++++++++++++++++--- gdb/gdbserver/lynx-low.c | 1 + gdb/gdbserver/remote-utils.c | 20 ++++++++ gdb/gdbserver/server.c | 11 ++++ gdb/gdbserver/server.h | 1 + gdb/gdbserver/target.h | 7 +++ gdb/gdbserver/win32-low.c | 1 + gdb/nat/linux-ptrace.c | 13 +++-- gdb/nat/linux-ptrace.h | 1 + gdb/remote.c | 30 +++++++++++ 10 files changed, 193 insertions(+), 9 deletions(-) diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 2dafb03..47d8bc3 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -557,6 +557,52 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat) /* Report the event. */ return 0; } + else if (event == PTRACE_EVENT_EXEC && report_exec_events) + { + struct regcache *regcache; + + if (debug_threads) + { + debug_printf ("HEW: Got exec event from LWP %ld\n", + lwpid_of (event_thr)); + } + + /* If the exec was not called by the thread group leader, then + the lwp_info and thread_info structures are out-of-date, + containing information about the original leader thread and + not the new exec'ing leader thread. Invalidate the register + cache without flushing it to the target, and reset the stop + pc value in the lwp. */ + regcache = (struct regcache *) inferior_regcache_data (event_thr); + free_register_cache (regcache); + set_inferior_regcache_data (event_thr, NULL); + event_lwp->stop_pc = get_pc (event_lwp); + + event_lwp->waitstatus.kind = TARGET_WAITKIND_EXECD; + event_lwp->waitstatus.value.execd_pathname + = xstrdup (linux_proc_pid_to_exec_file (lwpid_of (event_thr))); + + /* Mark the exec status as pending. */ + event_lwp->stopped = 1; + event_lwp->status_pending_p = 1; + event_lwp->status_pending = wstat; + event_thr->last_resume_kind = resume_stop; + event_thr->last_status.kind = TARGET_WAITKIND_IGNORE; + + if (non_stop && stopping_threads == NOT_STOPPING_THREADS) + { + /* In non-stop mode, make sure we delete the lwp entry for a + non-leader exec'ing thread, which will have vanished. We + do this by sending a signal to all the other threads in the + lwp list, deleting any that are not found. Note that in + all-stop mode this will happen when we stop all the threads. */ + stop_all_lwps (0, event_lwp); + unstop_all_lwps (0, event_lwp); + } + + /* Report the event. */ + return 0; + } internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event); } @@ -1609,7 +1655,7 @@ check_zombie_leaders (void) leader_pid, leader_lp!= NULL, num_lwps (leader_pid), linux_proc_pid_is_zombie (leader_pid)); - if (leader_lp != NULL + if (leader_lp != NULL && !leader_lp->stopped /* Check if there are other threads in the group, as we may have raced with the inferior simply exiting. */ && !last_thread_of_process_p (leader_pid) @@ -2035,6 +2081,9 @@ linux_low_ptrace_options (int attached) if (report_vfork_events) options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE); + if (report_exec_events) + options |= PTRACE_O_TRACEEXEC; + return options; } @@ -2051,6 +2100,38 @@ linux_low_filter_event (int lwpid, int wstat) child = find_lwp_pid (pid_to_ptid (lwpid)); + /* Check for stop events reported by a process we didn't already + know about - anything not already in our LWP list. + + If we're expecting to receive stopped processes after + fork, vfork, and clone events, then we'll just add the + new one to our list and go back to waiting for the event + to be reported - the stopped process might be returned + from waitpid before or after the event is. + + But note the case of a non-leader thread exec'ing after the + leader having exited, and gone from our lists (because + check_zombie_leaders deleted it). The non-leader thread + changes its tid to the tgid. */ + + if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP) + && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)) + { + ptid_t child_ptid; + + /* A multi-thread exec after we had seen the leader exiting. */ + if (debug_threads) + { + debug_printf ("LLW: Re-adding thread group leader LWP %d" + "after exec.\n", lwpid); + } + + child_ptid = ptid_build (lwpid, lwpid, 0); + child = add_lwp (child_ptid); + child->stopped = 1; + current_thread = child->thread; + } + /* If we didn't find a process, one of two things presumably happened: - A process we started and then detached from has exited. Ignore it. - A process we are controlling has forked and the new child's stop @@ -2342,8 +2423,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid, - When a non-leader thread execs, that thread just vanishes without reporting an exit (so we'd hang if we waited for it explicitly in that case). The exec event is reported to - the TGID pid (although we don't currently enable exec - events). */ + the TGID pid. */ errno = 0; ret = my_waitpid (-1, wstatp, options | WNOHANG); @@ -2741,7 +2821,8 @@ extended_event_reported (const struct target_waitstatus *waitstatus) return (waitstatus->kind == TARGET_WAITKIND_FORKED || waitstatus->kind == TARGET_WAITKIND_VFORKED - || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE); + || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE + || waitstatus->kind == TARGET_WAITKIND_EXECD); } /* Wait for process, returns status. */ @@ -3286,7 +3367,8 @@ linux_wait_1 (ptid_t ptid, ourstatus->value.sig = GDB_SIGNAL_0; } else if (current_thread->last_resume_kind == resume_stop - && WSTOPSIG (w) != SIGSTOP) + && WSTOPSIG (w) != SIGSTOP + && !extended_event_reported (ourstatus)) { /* A thread that has been requested to stop by GDB with vCont;t, but, it stopped for other reasons. */ @@ -3404,6 +3486,7 @@ static void send_sigstop (struct lwp_info *lwp) { int pid; + int ret; pid = lwpid_of (get_lwp_thread (lwp)); @@ -3421,7 +3504,20 @@ send_sigstop (struct lwp_info *lwp) debug_printf ("Sending sigstop to lwp %d\n", pid); lwp->stop_expected = 1; - kill_lwp (pid, SIGSTOP); + errno = 0; + ret = kill_lwp (pid, SIGSTOP); + if (ret == -1 && errno == ESRCH) + { + /* If the kill fails with "No such process", on GNU/Linux we know + that the LWP has vanished - it is not a zombie, it is gone. + This is because a thread that was not the thread group leader + called exec and took over the leader's lwp. */ + delete_lwp (lwp); + set_desired_thread (0); + + if (debug_threads) + debug_printf ("send_sigstop: lwp %d has vanished\n", pid); + } } static int @@ -5594,6 +5690,14 @@ linux_supports_vfork_events (void) return linux_supports_tracefork (); } +/* Check if exec events are supported. */ + +static int +linux_supports_exec_events (void) +{ + return linux_supports_traceexec (); +} + /* Callback for 'find_inferior'. Set the (possibly changed) ptrace options for the specified lwp. */ @@ -6681,6 +6785,7 @@ static struct target_ops linux_target_ops = { linux_supports_multi_process, linux_supports_fork_events, linux_supports_vfork_events, + linux_supports_exec_events, linux_handle_new_gdb_connection, #ifdef USE_THREAD_DB thread_db_handle_monitor_command, diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c index ee7b28a..fd89869 100644 --- a/gdb/gdbserver/lynx-low.c +++ b/gdb/gdbserver/lynx-low.c @@ -764,6 +764,7 @@ static struct target_ops lynx_target_ops = { NULL, /* supports_multi_process */ NULL, /* supports_fork_events */ NULL, /* supports_vfork_events */ + NULL, /* supports_exec_events */ NULL, /* handle_new_gdb_connection */ NULL, /* handle_monitor_command */ }; diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index bb31456..79d5ee8 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -1116,6 +1116,7 @@ prepare_resume_reply (char *buf, ptid_t ptid, case TARGET_WAITKIND_STOPPED: case TARGET_WAITKIND_FORKED: case TARGET_WAITKIND_VFORKED: + case TARGET_WAITKIND_EXECD: { struct thread_info *saved_thread; const char **regp; @@ -1133,6 +1134,25 @@ prepare_resume_reply (char *buf, ptid_t ptid, buf = write_ptid (buf, status->value.related_pid); strcat (buf, ";"); } + else if ((status->kind == TARGET_WAITKIND_EXECD) && multi_process) + { + enum gdb_signal signal = GDB_SIGNAL_TRAP; + const char *event = "exec"; + char hexified_pathname[PATH_MAX]; + + sprintf (buf, "T%02x%s:", signal, event); + buf += strlen (buf); + + /* Encode pathname to hexified format. */ + bin2hex ((const gdb_byte *) status->value.execd_pathname, + hexified_pathname, + strlen (status->value.execd_pathname)); + + sprintf (buf, "%s;", hexified_pathname); + xfree (status->value.execd_pathname); + status->value.execd_pathname = NULL; + buf += strlen (buf); + } else sprintf (buf, "T%02x", status->value.sig); diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 7e388dd..2b98d7c 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -59,6 +59,7 @@ int run_once; int multi_process; int report_fork_events; int report_vfork_events; +int report_exec_events; int non_stop; int swbreak_feature; int hwbreak_feature; @@ -2107,6 +2108,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (target_supports_vfork_events ()) report_vfork_events = 1; } + if (strcmp (p, "exec-events+") == 0) + { + /* GDB supports and wants exec events if possible. */ + if (target_supports_exec_events ()) + report_exec_events = 1; + } else target_process_qsupported (p); @@ -2163,6 +2170,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (target_supports_vfork_events ()) strcat (own_buf, ";vfork-events+"); + if (target_supports_exec_events ()) + strcat (own_buf, ";exec-events+"); + if (target_supports_non_stop ()) strcat (own_buf, ";QNonStop+"); @@ -3545,6 +3555,7 @@ captured_main (int argc, char *argv[]) multi_process = 0; report_fork_events = 0; report_vfork_events = 0; + report_exec_events = 0; /* Be sure we're out of tfind mode. */ current_traceframe = -1; cont_thread = null_ptid; diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 09a5624..258909d 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -86,6 +86,7 @@ extern int run_once; extern int multi_process; extern int report_fork_events; extern int report_vfork_events; +extern int report_exec_events; extern int non_stop; /* True if the "swbreak+" feature is active. In that case, GDB wants diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 9a40867..dd5eb3e 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -287,6 +287,9 @@ struct target_ops /* Returns true if vfork events are supported. */ int (*supports_vfork_events) (void); + /* Returns true if exec events are supported. */ + int (*supports_exec_events) (void); + /* Allows target to re-initialize connection-specific settings. */ void (*handle_new_gdb_connection) (void); @@ -458,6 +461,10 @@ int kill_inferior (int); (the_target->supports_vfork_events ? \ (*the_target->supports_vfork_events) () : 0) +#define target_supports_exec_events() \ + (the_target->supports_exec_events ? \ + (*the_target->supports_exec_events) () : 0) + #define target_handle_new_gdb_connection() \ do \ { \ diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index 64caf24..f7a3c0b 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -1831,6 +1831,7 @@ static struct target_ops win32_target_ops = { NULL, /* supports_multi_process */ NULL, /* supports_fork_events */ NULL, /* supports_vfork_events */ + NULL, /* supports_exec_events */ NULL, /* handle_new_gdb_connection */ NULL, /* handle_monitor_command */ NULL, /* core_of_thread */ diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c index 1a926f9..db3c4ce 100644 --- a/gdb/nat/linux-ptrace.c +++ b/gdb/nat/linux-ptrace.c @@ -527,9 +527,7 @@ ptrace_supports_feature (int ptrace_options) } /* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace, - 0 otherwise. Note that if PTRACE_EVENT_FORK is supported so is - PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK, - since they were all added to the kernel at the same time. */ + 0 otherwise. */ int linux_supports_tracefork (void) @@ -537,6 +535,15 @@ linux_supports_tracefork (void) return ptrace_supports_feature (PTRACE_O_TRACEFORK); } +/* Returns non-zero if PTRACE_EVENT_EXEC is supported by ptrace, + 0 otherwise. */ + +int +linux_supports_traceexec (void) +{ + return ptrace_supports_feature (PTRACE_O_TRACEEXEC); +} + /* Returns non-zero if PTRACE_EVENT_CLONE is supported by ptrace, 0 otherwise. Note that if PTRACE_EVENT_CLONE is supported so is PTRACE_EVENT_FORK, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK, diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h index be6c395..6d534ee 100644 --- a/gdb/nat/linux-ptrace.h +++ b/gdb/nat/linux-ptrace.h @@ -161,6 +161,7 @@ extern void linux_check_ptrace_features (void); extern void linux_enable_event_reporting (pid_t pid, int attached); extern void linux_disable_event_reporting (pid_t pid); extern int linux_supports_tracefork (void); +extern int linux_supports_traceexec (void); extern int linux_supports_traceclone (void); extern int linux_supports_tracevforkdone (void); extern int linux_supports_tracesysgood (void); diff --git a/gdb/remote.c b/gdb/remote.c index 9d97f6b..c0b3423 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -1366,6 +1366,9 @@ enum { /* Support for the Qbtrace-conf:pt:size packet. */ PACKET_Qbtrace_conf_pt_size, + /* Support for exec events. */ + PACKET_exec_event_feature, + PACKET_MAX }; @@ -4162,6 +4165,8 @@ static const struct protocol_feature remote_protocol_features[] = { PACKET_fork_event_feature }, { "vfork-events", PACKET_DISABLE, remote_supported_packet, PACKET_vfork_event_feature }, + { "exec-events", PACKET_DISABLE, remote_supported_packet, + PACKET_exec_event_feature }, { "Qbtrace-conf:pt:size", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_conf_pt_size } }; @@ -4250,6 +4255,9 @@ remote_query_supported (void) if (packet_set_cmd_state (PACKET_vfork_event_feature) != AUTO_BOOLEAN_FALSE) q = remote_query_supported_append (q, "vfork-events+"); + if (packet_set_cmd_state (PACKET_exec_event_feature) + != AUTO_BOOLEAN_FALSE) + q = remote_query_supported_append (q, "exec-events+"); } q = reconcat (q, "qSupported:", q, (char *) NULL); @@ -5909,6 +5917,25 @@ Packet: '%s'\n"), event->ws.kind = TARGET_WAITKIND_VFORK_DONE; p = skip_to_semicolon (p1 + 1); } + else if (strncmp (p, "exec", p1 - p) == 0) + { + ULONGEST pid; + char pathname[PATH_MAX]; + + p = unpack_varlen_hex (++p1, &pid); + + /* Save the pathname for event reporting and for + the next run command. */ + hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2); + /* Add the null terminator. */ + pathname[(p - p1)/2] = '\0'; + /* This is freed during event handling. */ + event->ws.value.execd_pathname = xstrdup (pathname); + event->ws.kind = TARGET_WAITKIND_EXECD; + /* Save the pathname for the next run command. */ + xfree (remote_exec_file); + remote_exec_file = xstrdup (pathname); + } else { ULONGEST pnum; @@ -12960,6 +12987,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_pt_size], "Qbtrace-conf:pt:size", "btrace-conf-pt-size", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_exec_event_feature], + "exec-event-feature", "exec-event-feature", 0); + /* Assert that we've registered "set remote foo-packet" commands for all packet configs. */ {