From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 22340 invoked by alias); 9 May 2014 21:17:25 -0000 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 Received: (qmail 22324 invoked by uid 89); 9 May 2014 21:17:24 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.7 required=5.0 tests=AWL,BAYES_00 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; Fri, 09 May 2014 21:17:21 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1WisAP-0006vV-Kj from donb@codesourcery.com for gdb-patches@sourceware.org; Fri, 09 May 2014 14:17:17 -0700 Received: from [127.0.0.1] ([172.30.1.203]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Fri, 9 May 2014 14:17:16 -0700 Message-ID: <536D45DB.1050202@codesourcery.com> Date: Fri, 09 May 2014 21:17:00 -0000 From: "Breazeal, Don" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Thunderbird/24.5.0 MIME-Version: 1.0 To: lgustavo@codesourcery.com, gdb-patches@sourceware.org Subject: Re: [PATCH 2/4][REPOST] Remote Linux ptrace exec events References: <1398885482-8449-1-git-send-email-donb@codesourcery.com> <1398885482-8449-3-git-send-email-donb@codesourcery.com> <536A9100.4080502@codesourcery.com> In-Reply-To: <536A9100.4080502@codesourcery.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2014-05/txt/msg00121.txt.bz2 Hi Luis, Thanks for the review. On 5/7/2014 1:01 PM, Luis Machado wrote: > Hi, > > On 04/30/2014 04:18 PM, Don Breazeal wrote: >> From: Don Breazeal >> >> [Reposting to eliminate unexpected attachment type.] >> >> This patch implements support for exec events in gdbserver on linux, in >> multiprocess mode (target extended-remote). Follow-exec-mode and rerun >> behave as expected. Catchpoints for exec are not yet implemented >> since it >> will be easier to implement catchpoints for fork, vfork, and exec all at >> the same time. >> >> TESTING >> --------- >> The patch was tested on GNU/Linux x86_64 with --target_board set to >> native-gdbserver and native-extended-gdbserver, as well as testing native >> GDB. The test results for native-gdbserver were unchanged. Thirteen >> tests >> that used to fail for native-extended-gdbserver on Linux pass with this >> patch, and the non-ldr-exc-*.exp tests all pass in non-stop mode and >> extended-remote. There are several failures in the new non-ldr-exc-*.exp >> tests in non-stop mode with native GDB. >> >> One caveat: when an exec is detected, gdbserver emits a couple of >> warnings: >> gdbserver: unexpected r_debug version 0 >> gdbserver: unexpected r_debug version 0 >> However, debugging of shared libraries that are loaded by the exec'd >> program works just fine. These messages may be caused by gdbserver >> making >> an attempt to initialize the solib hook before the r_debug structure has >> been initialized. I intend to follow up in a subsequent patch. >> >> 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. >> 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 that required a couple of significant changes to existing code. >> In a >> nutshell, the changes are to: >> 4) Use the PTRACE_EVENT_EXIT extended event to handle thread exit, >> while not exposing any change in exit handling to the user. The >> rationale for this is discussed in the "patch 0" email of this >> series. >> 5) Recognize when the exec'ing thread has vanished (become the thread >> group leader) in send_sigstop. Native GDB does this differently. >> >> Regarding items 4 & 5: Recall that when a non-leader thread exec's, >> all the >> other threads are terminated and the exec'ing thread changes its >> thread id >> to that of the old leader (the process id) as part of the exec. There is >> no event reported for the "exit" of the exec'ing thread; it appears to >> have >> vanished. The original thread group leader can't be reaped until all the >> other threads have been reaped, and some way of determining that it has >> exited is required in order to update the lwp list (#4 above). Also, some >> mechanism for deleting the lwp entry corresponding to the exec'ing thread >> is needed (#5 above). >> >> The rationale for #4 is that in my testing I ran into a race condition in >> the mechanism that's intended to detect when a thread group leader has >> exited, check_zombie_leaders. The race occurred when there were only two >> threads in the program. In this case the leader thread passes through a >> very brief zombie state before being replaced by the exec'ing thread as >> the thread group leader. This state transition is asynchronous, with no >> dependency on anything gdbserver does. Using PTRACE_EVENT_EXIT ensures >> that the leader exit will be detected. I can provide a more detailed >> explanation or the race, but I didn't want to be too long-winded here. >> >> Regarding item #5, determining that the exec'ing thread has >> "vanished", In >> native GDB this is done by calling waitpid(PID), and if it returns ECHILD >> it 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. An >> alternative is to send a signal to each thread and look for an ESRCH (No >> such process) error. In all-stop mode this can be done in the normal >> course of events, since when gdbserver reports an exec event it stops all >> the other threads with a SIGSTOP. In non-stop mode, when an exec >> event has >> been detected, we can call stop_all_lwps/unstop_all_lwps to accomplish >> the >> same thing. >> >> gdb/ >> 2014-04-02 Don Breazeal >> >> * common/linux-ptrace.c (linux_test_for_tracefork) >> [GDBSERVER]: Add exec tracing to ptrace options if OS supports >> it. >> * remote.c (remote_parse_stop_reply): Support new RSP stop >> reply reason 'exec'. >> >> gdbserver/ >> 2014-04-02 Don Breazeal >> >> * gdb/gdbserver/linux-low.c (linux_child_pid_to_exec_file): New >> function. >> (handle_extended_wait): Add support for PTRACE_EVENT_EXEC. >> (check_zombie_leaders): Update comment. >> (linux_low_filter_event): Add support for PTRACE_EVENT_EXEC. >> (linux_wait_for_event_filtered): Update comment. >> (extended_event_reported): New function. >> (send_sigstop): Delete lwp on 'No such process' error and >> reset current_inferior. >> * gdb/gdbserver/linux-low.h (struct lwp_info): New member >> 'waitstatus'. >> * gdb/gdbserver/remote-utils.c (prepare_resume_reply): Support >> new RSP stop reply reason 'exec'. >> >> --- >> gdb/common/linux-ptrace.c | 7 +- >> gdb/gdbserver/linux-low.c | 175 >> ++++++++++++++++++++++++++++++++++++++---- >> gdb/gdbserver/linux-low.h | 5 + >> gdb/gdbserver/remote-utils.c | 28 +++++++- >> gdb/remote.c | 27 ++++++- >> 5 files changed, 222 insertions(+), 20 deletions(-) >> >> diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c >> index e3fc705..b137df9 100644 >> --- a/gdb/common/linux-ptrace.c >> +++ b/gdb/common/linux-ptrace.c >> @@ -491,8 +491,11 @@ linux_test_for_tracefork (int child_pid) >> if (ret == child_pid && WIFSTOPPED (status) >> && status >> 16 == PTRACE_EVENT_EXIT) >> { >> - /* PTRACE_O_TRACEEXIT is supported. */ >> - current_ptrace_options |= PTRACE_O_TRACEEXIT; >> + /* PTRACE_O_TRACEEXIT is supported. We use exit events to >> + implement support for exec events. Since fork events are >> + supported we know exec events are supported, so we enable >> + exec events here. */ >> + current_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC; >> } >> #endif >> } >> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c >> index 90e7b15..5f94490 100644 >> --- a/gdb/gdbserver/linux-low.c >> +++ b/gdb/gdbserver/linux-low.c >> @@ -281,6 +281,32 @@ static int linux_event_pipe[2] = { -1, -1 }; >> static void send_sigstop (struct lwp_info *lwp); >> static void wait_for_sigstop (void); >> >> +/* Accepts an integer PID. Returns a string containing the >> + name of the executable file for the child process. >> + Space for the result is malloc'd, caller must free. */ >> + >> +static char * >> +linux_child_pid_to_exec_file (int pid) >> +{ >> + char *name1, *name2; >> + >> + name1 = xmalloc (PATH_MAX); >> + name2 = xmalloc (PATH_MAX); >> + memset (name2, 0, PATH_MAX); >> + >> + sprintf (name1, "/proc/%d/exe", pid); >> + if (readlink (name1, name2, PATH_MAX) > 0) >> + { >> + free (name1); >> + return name2; >> + } >> + else >> + { >> + free (name2); >> + return name1; >> + } >> +} >> + > > At one point in time this function existed in gdbserver/linux-low.c but > got deleted by the following commit: > > commit be07f1a20c962deb662b93209b4ca91bc8e5cbd8 > Author: Pedro Alves > Date: Fri Jan 27 19:23:43 2012 +0000 > > 2012-01-27 Pedro Alves > > * linux-low.c (linux_child_pid_to_exec_file): Delete. > (elf_64_file_p): Make static. > (linux_pid_exe_is_elf_64_file): New. > * linux-low.h (linux_child_pid_to_exec_file, elf_64_file_p): > Delete declarations. > (linux_pid_exe_is_elf_64_file): Declare. > * linux-x86-low.c (x86_arch_setup): Use > linux_pid_exe_is_elf_64_file. > > It still exists in linux-nat.c though. I remember i tried to unify those > and move the resulting function to common/, but there may be differences > preventing such move. > > If it can be done, it would be best. I am working on doing this. It seems like it should be possible. One question: the version of the function in linux-nat.c takes a "struct target_ops *" argument, I assume because it is used in the target_ops function vector. Is it acceptable to include "target.h" in a common file? It will pick up different files for GDB and GDBserver. I'm not sure if that would violate some aspect of the design. common/btrace.c includes it, but it's the only common file that does. > >> /* Return non-zero if HEADER is a 64-bit ELF file. */ >> >> static int >> @@ -514,6 +540,19 @@ handle_extended_wait (struct lwp_info >> *event_child, int *wstatp) >> (PTRACE_TYPE_ARG4) 0); >> return ret; >> } >> + else if (event == PTRACE_EVENT_EXEC) >> + { >> + if (debug_threads) >> + debug_printf ("LHEW: Got exec event from LWP %ld\n", >> + lwpid_of (event_thr)); >> + >> + event_child->waitstatus.kind = TARGET_WAITKIND_EXECD; >> + event_child->waitstatus.value.execd_pathname >> + = linux_child_pid_to_exec_file (lwpid_of (event_thr)); >> + >> + /* Report the event. */ >> + return 0; >> + } >> internal_error (__FILE__, __LINE__, >> _("unknown ptrace event %d"), event); >> } >> @@ -1376,18 +1415,19 @@ check_zombie_leaders (void) >> program). In the latter case, we can't waitpid the >> leader's exit status until all other threads are gone. >> >> - - There are 3 or more threads in the group, and a thread >> + - There are multiple threads in the group, and a thread >> other than the leader exec'd. On an exec, the Linux >> kernel destroys all other threads (except the execing >> one) in the thread group, and resets the execing thread's >> tid to the tgid. No exit notification is sent for the >> execing thread -- from the ptracer's perspective, it >> appears as though the execing thread just vanishes. >> - Until we reap all other threads except the leader and the >> - execing thread, the leader will be zombie, and the >> - execing thread will be in `D (disc sleep)'. As soon as >> - all other threads are reaped, the execing thread changes >> - it's tid to the tgid, and the previous (zombie) leader >> + Until we reap all other threads (if any) except the >> + leader and the execing thread, the leader will be zombie, >> + and the execing thread will be in `D (disc sleep)'. As >> + soon as all other threads are reaped, or have reported >> + PTRACE_EVENT_EXIT events, the execing thread changes its >> + tid to the tgid, and the previous (zombie) leader >> vanishes, giving place to the "new" leader. We could try >> distinguishing the exit and exec cases, by waiting once >> more, and seeing if something comes out, but it doesn't >> @@ -1395,7 +1435,11 @@ check_zombie_leaders (void) >> we'll re-add the new one once we see the exec event >> (which is just the same as what would happen if the >> previous leader did exit voluntarily before some other >> - thread execs). */ >> + thread execs). >> + >> + Note that when PTRACE_EVENT_EXEC is supported, we use >> + that mechanism to detect thread exit, including the >> + exit of zombie leaders. */ >> >> if (debug_threads) >> fprintf (stderr, >> @@ -1791,6 +1835,57 @@ linux_low_filter_event (ptid_t filter_ptid, int >> lwpid, int *wstatp) >> >> 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. On an exec, >> + the Linux kernel destroys all other threads (except the execing >> + one) in the thread group, and resets the execing thread's tid >> + to the tgid. No exit notification is sent for the execing >> + thread -- from the ptracer's perspective, it appears as though >> + the execing thread just vanishes. When they are available, we >> + use exit events (PTRACE_EVENT_EXIT) to detect thread exit >> + reliably. As soon as all other threads (if any) are reaped or >> + have reported their PTRACE_EVENT_EXIT events, the execing >> + thread changes it's tid to the tgid, and the previous (zombie) >> + leader vanishes, giving place to the "new" leader. The lwp >> + entry for the previous leader is deleted when we handle its >> + exit event, and we re-add the new one here. */ >> + >> + if (WIFSTOPPED (wstat) && child == NULL >> + && (WSTOPSIG (wstat) == SIGTRAP && wstat >> 16 == >> PTRACE_EVENT_EXEC)) >> + { >> + ptid_t child_ptid; >> + > > It would be nice to replace the shift operation with a predicate to test > for PTRACE_EVENT_EXEC, just like you did for PTRACE_EVENT_EXIT. Will do. Actually, given your previous comment, I intend to move all of these predicates into common/linux-ptrace.[ch]. > >> + /* A multi-thread exec after we had seen the leader exiting. */ >> + if (debug_threads) >> + debug_printf ("LLW: Re-adding thread group leader LWP %d.\n", >> + lwpid); >> + >> + child_ptid = ptid_build (lwpid, lwpid, 0); >> + child = add_lwp (child_ptid); >> + child->stopped = 1; >> + current_inferior = child->thread; >> + >> + if (non_stop && stopping_threads == NOT_STOPPING_THREADS) >> + { >> + /* Make sure we delete the lwp entry for the 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 before reporting the event. */ >> + stop_all_lwps (0, child); >> + unstop_all_lwps (0, child); >> + } >> + } >> + >> /* 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 >> @@ -2122,8 +2217,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); >> >> @@ -2520,6 +2614,21 @@ linux_stabilize_threads (void) >> } >> } >> >> +/* Return non-zero if WAITSTATUS reflects an extended linux >> + event. Otherwise, return 0. Note that extended EXIT >> + events are fixed up and handled like normal events, so >> + they are not considered here. */ >> + >> +static int >> +extended_event_reported (const struct target_waitstatus *waitstatus) >> +{ >> + >> + if (waitstatus == NULL) >> + return 0; >> + >> + return waitstatus->kind == TARGET_WAITKIND_EXECD; >> +} >> + > > Someone mentioned putting ()'s around the condition for better > readability. Though not required, i also agree that it improves things. > > Not a requirement, just a suggestion. I'll do it. > >> /* Wait for process, returns status. */ >> >> static ptid_t >> @@ -2883,7 +2992,8 @@ retry: >> && !bp_explains_trap && !trace_event) >> || (gdb_breakpoint_here (event_child->stop_pc) >> && gdb_condition_true_at_breakpoint >> (event_child->stop_pc) >> - && gdb_no_commands_at_breakpoint >> (event_child->stop_pc))); >> + && gdb_no_commands_at_breakpoint (event_child->stop_pc)) >> + || extended_event_reported (&event_child->waitstatus)); >> >> run_breakpoint_commands (event_child->stop_pc); >> >> @@ -2905,6 +3015,15 @@ retry: >> paddress (event_child->stop_pc), >> paddress (event_child->step_range_start), >> paddress (event_child->step_range_end)); >> + if (debug_threads >> + && extended_event_reported (&event_child->waitstatus)) >> + { >> + char *str = target_waitstatus_to_string (ourstatus); >> + debug_printf ("LWP %ld has forked, cloned, vforked or execd" >> + " with waitstatus %s\n", >> + lwpid_of (get_lwp_thread (event_child)), str); >> + xfree (str); >> + } >> } >> > > Maybe we could provide more precise information about the event here > instead of something generic? It may help debugging in the future or if > we ever have a mix of these events happening very close to each other. It turns out the string from target_waitstatus_to_string contains the precise information. I'll clean this up. > >> /* We're not reporting this breakpoint to GDB, so apply the >> @@ -3003,7 +3122,19 @@ retry: >> unstop_all_lwps (1, event_child); >> } >> >> - ourstatus->kind = TARGET_WAITKIND_STOPPED; >> + if (extended_event_reported (&event_child->waitstatus)) >> + { >> + /* If the reported event is a fork, vfork or exec, let GDB >> + know. */ >> + ourstatus->kind = event_child->waitstatus.kind; >> + ourstatus->value = event_child->waitstatus.value; >> + >> + /* Reset the event child's waitstatus since we handled it >> + already. */ >> + event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE; >> + } >> + else >> + ourstatus->kind = TARGET_WAITKIND_STOPPED; >> > > This chunk of code setting waitstatus.kind to TARGET_WAITKIND_IGNORE may > be a bit confusing/hackish, but we really don't want gdbserver's event > loop to handle the extended events as TARGET_WAITKIND_STOPPED. > > gdbserver's event loop could probably use a cleanup . For the time > being, this looks good to me though. > >> if (current_inferior->last_resume_kind == resume_stop >> && WSTOPSIG (w) == SIGSTOP) >> @@ -3014,13 +3145,14 @@ retry: >> ourstatus->value.sig = GDB_SIGNAL_0; >> } >> else if (current_inferior->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. */ >> ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w)); >> } >> - else >> + else if (ourstatus->kind == TARGET_WAITKIND_STOPPED) >> { >> ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w)); >> } >> @@ -3126,6 +3258,7 @@ static void >> send_sigstop (struct lwp_info *lwp) >> { >> int pid; >> + int ret; >> >> pid = lwpid_of (get_lwp_thread (lwp)); >> >> @@ -3143,7 +3276,21 @@ 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 due to a thread other than the thread group leader >> + calling exec. See comments in linux_low_filter_event regarding >> + PTRACE_EVENT_EXEC. */ >> + delete_lwp (lwp); >> + set_desired_inferior (0); >> + >> + if (debug_threads) >> + debug_printf ("send_sigstop: lwp %d has vanished\n", pid); >> + } >> } >> >> static int >> diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h >> index 7459710..7759f01 100644 >> --- a/gdb/gdbserver/linux-low.h >> +++ b/gdb/gdbserver/linux-low.h >> @@ -265,6 +265,11 @@ struct lwp_info >> /* When stopped is set, the last wait status recorded for this >> lwp. */ >> int last_status; >> >> + /* If WAITSTATUS->KIND != TARGET_WAITKIND_IGNORE, the waitstatus for >> + this LWP's last event. This may correspond to LAST_STATUS above, >> + or to the current status during event processing. */ >> + struct target_waitstatus waitstatus; >> + >> /* When stopped is set, this is where the lwp stopped, with >> decr_pc_after_break already accounted for. */ >> CORE_ADDR stop_pc; >> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c >> index 4fcafa0..9ce25dc 100644 >> --- a/gdb/gdbserver/remote-utils.c >> +++ b/gdb/gdbserver/remote-utils.c >> @@ -1111,14 +1111,40 @@ prepare_resume_reply (char *buf, ptid_t ptid, >> switch (status->kind) >> { >> case TARGET_WAITKIND_STOPPED: >> + case TARGET_WAITKIND_EXECD: >> { >> struct thread_info *saved_inferior; >> const char **regp; >> struct regcache *regcache; >> + enum gdb_signal signal; >> + >> + if (status->kind == TARGET_WAITKIND_EXECD) >> + signal = GDB_SIGNAL_TRAP; >> + else >> + signal = status->value.sig; >> + >> + sprintf (buf, "T%02x", signal); >> >> - sprintf (buf, "T%02x", status->value.sig); >> buf += strlen (buf); >> >> + if (status->kind == TARGET_WAITKIND_EXECD && multi_process) >> + { >> + const char *event = "exec"; >> + char hexified_pathname[PATH_MAX]; >> + >> + sprintf (buf, "%s:", 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); >> + } >> + >> saved_inferior = current_inferior; >> >> current_inferior = find_thread_ptid (ptid); >> diff --git a/gdb/remote.c b/gdb/remote.c >> index be8c423..f4412d8 100644 >> --- a/gdb/remote.c >> +++ b/gdb/remote.c >> @@ -5542,11 +5542,13 @@ remote_parse_stop_reply (char *buf, struct >> stop_reply *event) >> pnum and set p1 to point to the character following it. >> Otherwise p1 points to p. */ >> >> - /* If this packet is an awatch packet, don't parse the 'a' >> - as a register number. */ >> + /* If this packet has a stop reason string that starts >> + with a character that could be a hex digit, don't parse >> + it as a register number. */ >> >> if (strncmp (p, "awatch", strlen("awatch")) != 0 >> - && strncmp (p, "core", strlen ("core") != 0)) >> + && strncmp (p, "core", strlen ("core") != 0) >> + && strncmp (p, "exec", strlen ("exec") != 0)) >> { >> /* Read the ``P'' register number. */ >> pnum = strtol (p, &p_temp, 16); >> @@ -5598,6 +5600,25 @@ Packet: '%s'\n"), >> p = unpack_varlen_hex (++p1, &c); >> event->core = c; >> } >> + 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 = pathname; >> + } >> else >> { >> /* Silently skip unknown optional info. */ >> > > >