diff --git a/gdb/ChangeLog b/gdb/ChangeLog index be75baf4fe..c9c2e62eec 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,33 @@ +2015-05-12 Don Breazeal + + * linux-nat.c (linux_nat_ptrace_options): New function. + (linux_init_ptrace, wait_lwp, linux_nat_filter_event): + Call linux_nat_ptrace_options and use different argument to + linux_enable_event_reporting. + (_initialize_linux_nat): Delete call to + linux_ptrace_set_additional_flags. + * nat/linux-ptrace.c (current_ptrace_options): Rename to + supported_ptrace_options. + (additional_flags): Delete variable. + (linux_check_ptrace_features): Use supported_ptrace_options. + (linux_test_for_tracesysgood, linux_test_for_tracefork): + Likewise, and remove additional_flags check. + (linux_enable_event_reporting): Change 'attached' argument to + 'options'. Use supported_ptrace_options. + (ptrace_supports_feature): Change comment. Use + supported_ptrace_options. + (linux_ptrace_set_additional_flags): Delete function. + * nat/linux-ptrace.h (linux_ptrace_set_additional_flags): + Delete function prototype. + * remote.c (remote_fork_event_p): New function. + (remote_detach_pid): New function. + (remote_detach_1): Call remote_detach_pid, don't mourn inferior + if doing detach-on-fork. + (remote_follow_fork): New function. + (remote_parse_stop_reply): Handle new "T" stop reason "fork". + (remote_pid_to_str): Print "process" strings for pid/0/0 ptids. + (init_extended_remote_ops): Initialize to_follow_fork. + 2015-05-12 Don Breazeal * nat/linux-ptrace.c (linux_check_ptrace_features): Change diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 73788ece87..0ef695c43a 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,30 @@ +2015-05-12 Don Breazeal + + * linux-low.c (handle_extended_wait): Implement return value, + rename argument 'event_child' to 'event_lwp', handle + PTRACE_EVENT_FORK, call internal_error for unrecognized event. + (linux_low_ptrace_options): New function. + (linux_low_filter_event): Call linux_low_ptrace_options, + use different argument fo linux_enable_event_reporting, + use return value from handle_extended_wait. + (extended_event_reported): New function. + (linux_wait_1): Call extended_event_reported and set + status to report fork events. + (linux_write_memory): Add pid to debug message. + (reset_lwp_ptrace_options_callback): New function. + (linux_handle_new_gdb_connection): New function. + (linux_target_ops): Initialize new structure member. + * linux-low.h (struct lwp_info) : New member. + * lynx-low.c: Initialize new structure member. + * remote-utils.c (prepare_resume_reply): Implement stop reason + "fork" for "T" stop message. + * server.c (handle_query): Call handle_new_gdb_connection. + * server.h (report_fork_events): Declare global flag. + * target.h (struct target_ops) : + New member. + (target_handle_new_gdb_connection): New macro. + * win32-low.c: Initialize new structure member. + 2015-05-12 Don Breazeal * mem-break.c (APPEND_TO_LIST): Define macro. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 1724b5491d..b280c174a9 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -20,6 +20,7 @@ #include "linux-low.h" #include "nat/linux-osdata.h" #include "agent.h" +#include "tdesc.h" #include "nat/linux-nat.h" #include "nat/linux-waitpid.h" @@ -414,22 +415,23 @@ linux_add_process (int pid, int attached) static CORE_ADDR get_pc (struct lwp_info *lwp); /* Handle a GNU/Linux extended wait response. If we see a clone - event, we need to add the new LWP to our list (and not report the - trap to higher layers). */ + event, we need to add the new LWP to our list (and return 0 so as + not to report the trap to higher layers). */ -static void -handle_extended_wait (struct lwp_info *event_child, int wstat) +static int +handle_extended_wait (struct lwp_info *event_lwp, int wstat) { int event = linux_ptrace_get_extended_event (wstat); - struct thread_info *event_thr = get_lwp_thread (event_child); + struct thread_info *event_thr = get_lwp_thread (event_lwp); struct lwp_info *new_lwp; - if (event == PTRACE_EVENT_CLONE) + if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE)) { ptid_t ptid; unsigned long new_pid; int ret, status; + /* Get the pid of the new lwp. */ ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0, &new_pid); @@ -449,6 +451,57 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) warning ("wait returned unexpected status 0x%x", status); } + if (event == PTRACE_EVENT_FORK) + { + struct process_info *parent_proc; + struct process_info *child_proc; + struct lwp_info *child_lwp; + struct target_desc *tdesc; + + ptid = ptid_build (new_pid, new_pid, 0); + + if (debug_threads) + { + debug_printf ("HEW: Got fork event from LWP %ld, " + "new child is %d\n", + ptid_get_lwp (ptid_of (event_thr)), + ptid_get_pid (ptid)); + } + + /* Add the new process to the tables and clone the breakpoint + lists of the parent. We need to do this even if the new process + will be detached, since we will need the process object and the + breakpoints to remove any breakpoints from memory when we + detach, and the client side will access registers. */ + child_proc = linux_add_process (new_pid, 0); + gdb_assert (child_proc != NULL); + child_lwp = add_lwp (ptid); + gdb_assert (child_lwp != NULL); + child_lwp->stopped = 1; + parent_proc = get_thread_process (event_thr); + child_proc->attached = parent_proc->attached; + clone_all_breakpoints (&child_proc->breakpoints, + &child_proc->raw_breakpoints, + parent_proc->breakpoints); + + tdesc = xmalloc (sizeof (struct target_desc)); + copy_target_description (tdesc, parent_proc->tdesc); + child_proc->tdesc = tdesc; + child_lwp->must_set_ptrace_flags = 1; + + /* Save fork info in the parent thread. */ + event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED; + event_lwp->waitstatus.value.related_pid = ptid; + /* The status_pending field contains bits denoting the + extended event, so when the pending event is handled, + the handler will look at lwp->waitstatus. */ + event_lwp->status_pending_p = 1; + event_lwp->status_pending = wstat; + + /* Report the event. */ + return 0; + } + if (debug_threads) debug_printf ("HEW: Got clone event " "from LWP %ld, new child is LWP %ld\n", @@ -477,7 +530,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) new_lwp->status_pending_p = 1; new_lwp->status_pending = status; } + + /* Don't report the event. */ + return 1; } + + internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event); } /* Return the PC as read from the regcache of LWP, without any @@ -1935,6 +1993,22 @@ check_stopped_by_watchpoint (struct lwp_info *child) return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT; } +/* Return the ptrace options that we want to try to enable. */ + +static int +linux_low_ptrace_options (int attached) +{ + int options = 0; + + if (!attached) + options |= PTRACE_O_EXITKILL; + + if (report_fork_events) + options |= PTRACE_O_TRACEFORK; + + return options; +} + /* Do low-level handling of the event, and check if we should go on and pass it to caller code. Return the affected lwp if we are, or NULL otherwise. */ @@ -2022,8 +2096,9 @@ linux_low_filter_event (int lwpid, int wstat) if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags) { struct process_info *proc = find_process_pid (pid_of (thread)); + int options = linux_low_ptrace_options (proc->attached); - linux_enable_event_reporting (lwpid, proc->attached); + linux_enable_event_reporting (lwpid, options); child->must_set_ptrace_flags = 0; } @@ -2033,8 +2108,12 @@ linux_low_filter_event (int lwpid, int wstat) && linux_is_extended_waitstatus (wstat)) { child->stop_pc = get_pc (child); - handle_extended_wait (child, wstat); - return NULL; + if (handle_extended_wait (child, wstat)) + { + /* The event has been handled, so just return without + reporting it. */ + return NULL; + } } /* Check first whether this was a SW/HW breakpoint before checking @@ -2622,6 +2701,18 @@ ignore_event (struct target_waitstatus *ourstatus) return null_ptid; } +/* Return non-zero if WAITSTATUS reflects an extended linux + event. Otherwise, return zero. */ + +static int +extended_event_reported (const struct target_waitstatus *waitstatus) +{ + if (waitstatus == NULL) + return 0; + + return (waitstatus->kind == TARGET_WAITKIND_FORKED); +} + /* Wait for process, returns status. */ static ptid_t @@ -2988,7 +3079,8 @@ linux_wait_1 (ptid_t ptid, && !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); @@ -3010,6 +3102,13 @@ linux_wait_1 (ptid_t ptid, paddress (event_child->stop_pc), paddress (event_child->step_range_start), paddress (event_child->step_range_end)); + if (extended_event_reported (&event_child->waitstatus)) + { + char *str = target_waitstatus_to_string (ourstatus); + debug_printf ("LWP %ld: extended event with waitstatus %s\n", + lwpid_of (get_lwp_thread (event_child)), str); + xfree (str); + } } /* We're not reporting this breakpoint to GDB, so apply the @@ -3119,7 +3218,17 @@ linux_wait_1 (ptid_t ptid, 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; + + /* Clear the event lwp's waitstatus since we handled it already. */ + event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE; + } + else + ourstatus->kind = TARGET_WAITKIND_STOPPED; /* Now that we've selected our final event LWP, un-adjust its PC if it was a software breakpoint, and the client doesn't know we can @@ -3152,7 +3261,7 @@ linux_wait_1 (ptid_t ptid, 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)); } @@ -4996,8 +5105,8 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) val = val & 0xffff; else if (len == 3) val = val & 0xffffff; - debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4), - val, (long)memaddr); + debug_printf ("Writing %0*x to 0x%08lx in process %d\n", + 2 * ((len < 4) ? len : 4), val, (long)memaddr, pid); } /* Fill start and end extra bytes of buffer with existing memory data. */ @@ -5454,6 +5563,48 @@ linux_supports_vfork_events (void) return linux_supports_tracefork (); } +/* Callback for 'find_inferior'. Set the (possibly changed) ptrace + options for the specified lwp. */ + +static int +reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry, + void *args) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct lwp_info *lwp = get_thread_lwp (thread); + + if (!lwp->stopped) + { + /* Stop the lwp so we can modify its ptrace options. */ + lwp->must_set_ptrace_flags = 1; + linux_stop_lwp (lwp); + } + else + { + /* Already stopped; go ahead and set the ptrace options. */ + struct process_info *proc = find_process_pid (pid_of (thread)); + int options = linux_low_ptrace_options (proc->attached); + + linux_enable_event_reporting (lwpid_of (thread), options); + lwp->must_set_ptrace_flags = 0; + } + + return 0; +} + +/* Target hook for 'handle_new_gdb_connection'. Causes a reset of the + ptrace flags for all inferiors. This is in case the new GDB connection + doesn't support the same set of events that the previous one did. */ + +static void +linux_handle_new_gdb_connection (void) +{ + pid_t pid; + + /* Request that all the lwps reset their ptrace options. */ + find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid); +} + static int linux_supports_disable_randomization (void) { @@ -6429,6 +6580,7 @@ static struct target_ops linux_target_ops = { linux_supports_multi_process, linux_supports_fork_events, linux_supports_vfork_events, + linux_handle_new_gdb_connection, #ifdef USE_THREAD_DB thread_db_handle_monitor_command, #else diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 1ae37017f5..41067d65ce 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -271,6 +271,10 @@ struct lwp_info /* When stopped is set, the last wait status recorded for this lwp. */ int last_status; + /* This is used to store extended ptrace event information until + it is reported to GDB. */ + struct target_waitstatus waitstatus; + /* When stopped is set, this is where the lwp last stopped, with decr_pc_after_break already accounted for. If the LWP is running, this is the address at which the lwp was resumed. */ diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c index f08741601d..ee7b28a3a0 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, /* handle_new_gdb_connection */ NULL, /* handle_monitor_command */ }; diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 1de86beecf..60783489b8 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -1114,12 +1114,24 @@ prepare_resume_reply (char *buf, ptid_t ptid, switch (status->kind) { case TARGET_WAITKIND_STOPPED: + case TARGET_WAITKIND_FORKED: { struct thread_info *saved_thread; const char **regp; struct regcache *regcache; - sprintf (buf, "T%02x", status->value.sig); + if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events) + { + enum gdb_signal signal = GDB_SIGNAL_TRAP; + + sprintf (buf, "T%02xfork:", signal); + buf += strlen (buf); + buf = write_ptid (buf, status->value.related_pid); + strcat (buf, ";"); + } + else + sprintf (buf, "T%02x", status->value.sig); + buf += strlen (buf); saved_thread = current_thread; diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 1544e99bdb..3969267213 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -2160,6 +2160,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (the_target->pid_to_exec_file != NULL) strcat (own_buf, ";qXfer:exec-file:read+"); + /* Reinitialize the target as needed for the new connection. */ + target_handle_new_gdb_connection (); + return; } diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 91d4080d55..696a24e8c4 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -84,6 +84,7 @@ extern int disable_packet_qfThreadInfo; extern int run_once; extern int multi_process; +extern int report_fork_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 697b223bcb..8d233834ce 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); + /* Allows target to re-initialize connection-specific settings. */ + void (*handle_new_gdb_connection) (void); + /* If not NULL, target-specific routine to process monitor command. Returns 1 if handled, or 0 to perform default processing. */ int (*handle_monitor_command) (char *); @@ -434,6 +437,10 @@ int kill_inferior (int); (the_target->supports_vfork_events ? \ (*the_target->supports_vfork_events) () : 0) +#define target_handle_new_gdb_connection() \ + (the_target->handle_new_gdb_connection ? \ + (*the_target->handle_new_gdb_connection) () : 0) + #define detach_inferior(pid) \ (*the_target->detach) (pid) diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index a58f963488..699115ca6b 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -1833,6 +1833,7 @@ static struct target_ops win32_target_ops = { NULL, /* supports_multi_process */ NULL, /* supports_fork_events */ NULL, /* supports_vfork_events */ + NULL, /* handle_new_gdb_connection */ NULL, /* handle_monitor_command */ NULL, /* core_of_thread */ NULL, /* read_loadmap */ diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index b04aa68c79..f95e76c601 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -368,6 +368,25 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp) return 0; } +/* Return the ptrace options that we want to try to enable. */ + +static int +linux_nat_ptrace_options (int attached) +{ + int options = 0; + + if (!attached) + options |= PTRACE_O_EXITKILL; + + options |= (PTRACE_O_TRACESYSGOOD + | PTRACE_O_TRACEVFORKDONE + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEFORK + | PTRACE_O_TRACEEXEC); + + return options; +} + /* Initialize ptrace warnings and check for supported ptrace features given PID. @@ -376,7 +395,9 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp) static void linux_init_ptrace (pid_t pid, int attached) { - linux_enable_event_reporting (pid, attached); + int options = linux_nat_ptrace_options (attached); + + linux_enable_event_reporting (pid, options); linux_ptrace_init_warnings (); } @@ -2301,8 +2322,9 @@ wait_lwp (struct lwp_info *lp) if (lp->must_set_ptrace_flags) { struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid)); + int options = linux_nat_ptrace_options (inf->attach_flag); - linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag); + linux_enable_event_reporting (ptid_get_lwp (lp->ptid), options); lp->must_set_ptrace_flags = 0; } @@ -3102,8 +3124,9 @@ linux_nat_filter_event (int lwpid, int status) if (WIFSTOPPED (status) && lp->must_set_ptrace_flags) { struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid)); + int options = linux_nat_ptrace_options (inf->attach_flag); - linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag); + linux_enable_event_reporting (ptid_get_lwp (lp->ptid), options); lp->must_set_ptrace_flags = 0; } @@ -5028,14 +5051,6 @@ Enables printf debugging output."), sigdelset (&suspend_mask, SIGCHLD); sigemptyset (&blocked_mask); - - /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to - support read-only process state. */ - linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD - | PTRACE_O_TRACEVFORKDONE - | PTRACE_O_TRACEVFORK - | PTRACE_O_TRACEFORK - | PTRACE_O_TRACEEXEC); } diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c index aba3da8579..fb12606000 100644 --- a/gdb/nat/linux-ptrace.c +++ b/gdb/nat/linux-ptrace.c @@ -25,14 +25,10 @@ #include -/* Stores the currently supported ptrace options. A value of - -1 means we did not check for features yet. A value of 0 means - there are no supported features. */ -static int current_ptrace_options = -1; - -/* Additional flags to test. */ - -static int additional_flags; +/* Stores the ptrace options supported by the running kernel. + A value of -1 means we did not check for features yet. A value + of 0 means there are no supported features. */ +static int supported_ptrace_options = -1; /* Find all possible reasons we could fail to attach PID and append these as strings to the already initialized BUFFER. '\0' @@ -343,7 +339,7 @@ linux_check_ptrace_features (void) int child_pid, ret, status; /* Initialize the options. */ - current_ptrace_options = 0; + supported_ptrace_options = 0; /* Fork a child so we can do some testing. The child will call linux_child_function and will get traced. The child will @@ -387,14 +383,11 @@ linux_test_for_tracesysgood (int child_pid) { int ret; - if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0) - return; - ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD); if (ret == 0) - current_ptrace_options |= PTRACE_O_TRACESYSGOOD; + supported_ptrace_options |= PTRACE_O_TRACESYSGOOD; } /* Determine if PTRACE_O_TRACEFORK can be used to follow fork @@ -414,15 +407,12 @@ linux_test_for_tracefork (int child_pid) if (ret != 0) return; - if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0) - { - /* Check if the target supports PTRACE_O_TRACEVFORKDONE. */ - ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0, - (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK - | PTRACE_O_TRACEVFORKDONE)); - if (ret == 0) - current_ptrace_options |= PTRACE_O_TRACEVFORKDONE; - } + /* Check if the target supports PTRACE_O_TRACEVFORKDONE. */ + ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0, + (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORKDONE)); + if (ret == 0) + supported_ptrace_options |= PTRACE_O_TRACEVFORKDONE; /* Setting PTRACE_O_TRACEFORK did not cause an error, however we don't know for sure that the feature is available; old @@ -458,10 +448,10 @@ linux_test_for_tracefork (int child_pid) /* We got the PID from the grandchild, which means fork tracing is supported. */ - current_ptrace_options |= PTRACE_O_TRACECLONE; - current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK - | PTRACE_O_TRACEVFORK - | PTRACE_O_TRACEEXEC)); + supported_ptrace_options |= PTRACE_O_TRACECLONE; + supported_ptrace_options |= (PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEEXEC); /* Do some cleanup and kill the grandchild. */ my_waitpid (second_pid, &second_status, 0); @@ -489,33 +479,31 @@ linux_test_for_exitkill (int child_pid) (PTRACE_TYPE_ARG4) PTRACE_O_EXITKILL); if (ret == 0) - current_ptrace_options |= PTRACE_O_EXITKILL; + supported_ptrace_options |= PTRACE_O_EXITKILL; } /* Enable reporting of all currently supported ptrace events. - ATTACHED should be nonzero if we have attached to the inferior. */ + OPTIONS is a bit mask of extended features we want enabled, + if supported by the kernel. PTRACE_O_TRACECLONE is always + enabled, if supported. */ void -linux_enable_event_reporting (pid_t pid, int attached) +linux_enable_event_reporting (pid_t pid, int options) { - int ptrace_options; - /* Check if we have initialized the ptrace features for this target. If not, do it now. */ - if (current_ptrace_options == -1) + if (supported_ptrace_options == -1) linux_check_ptrace_features (); - ptrace_options = current_ptrace_options; - if (attached) - { - /* When attached to our inferior, we do not want the inferior - to die with us if we terminate unexpectedly. */ - ptrace_options &= ~PTRACE_O_EXITKILL; - } + /* We always want clone events. */ + options |= PTRACE_O_TRACECLONE; + + /* Filter out unsupported options. */ + options &= supported_ptrace_options; /* Set the options. */ ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, - (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options); + (PTRACE_TYPE_ARG4) (uintptr_t) options); } /* Disable reporting of all currently supported ptrace events. */ @@ -528,16 +516,16 @@ linux_disable_event_reporting (pid_t pid) } /* Returns non-zero if PTRACE_OPTIONS is contained within - CURRENT_PTRACE_OPTIONS, therefore supported. Returns 0 + SUPPORTED_PTRACE_OPTIONS, therefore supported. Returns 0 otherwise. */ static int ptrace_supports_feature (int ptrace_options) { - if (current_ptrace_options == -1) + if (supported_ptrace_options == -1) linux_check_ptrace_features (); - return ((current_ptrace_options & ptrace_options) == ptrace_options); + return ((supported_ptrace_options & ptrace_options) == ptrace_options); } /* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace, @@ -595,17 +583,6 @@ linux_ptrace_init_warnings (void) linux_ptrace_test_ret_to_nx (); } -/* Set additional ptrace flags to use. Some such flags may be checked - by the implementation above. This function must be called before - any other function in this file; otherwise the flags may not take - effect appropriately. */ - -void -linux_ptrace_set_additional_flags (int flags) -{ - additional_flags = flags; -} - /* Extract extended ptrace event from wait status. */ int diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h index 03d98c92e5..1db0cde128 100644 --- a/gdb/nat/linux-ptrace.h +++ b/gdb/nat/linux-ptrace.h @@ -156,7 +156,6 @@ extern int linux_supports_tracefork (void); extern int linux_supports_traceclone (void); extern int linux_supports_tracevforkdone (void); extern int linux_supports_tracesysgood (void); -extern void linux_ptrace_set_additional_flags (int); extern int linux_ptrace_get_extended_event (int wstat); extern int linux_is_extended_waitstatus (int wstat); extern int linux_wstatus_maybe_breakpoint (int wstat); diff --git a/gdb/remote.c b/gdb/remote.c index 6dae9205c5..390d30db82 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -1462,6 +1462,14 @@ remote_multi_process_p (struct remote_state *rs) return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE; } +/* Returns true if fork events are supported. */ + +static int +remote_fork_event_p (struct remote_state *rs) +{ + return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE; +} + /* Tokens for use by the asynchronous signal handlers for SIGINT. */ static struct async_signal_handler *async_sigint_remote_twice_token; static struct async_signal_handler *async_sigint_remote_token; @@ -4430,16 +4438,42 @@ remote_open_1 (const char *name, int from_tty, wait_forever_enabled_p = 1; } -/* This takes a program previously attached to and detaches it. After - this is done, GDB can be used to debug some other program. We - better not have left any breakpoints in the target program or it'll - die when it hits one. */ +/* Detach the specified process. */ static void -remote_detach_1 (const char *args, int from_tty, int extended) +remote_detach_pid (int pid) +{ + struct remote_state *rs = get_remote_state (); + + if (remote_multi_process_p (rs)) + xsnprintf (rs->buf, get_remote_packet_size (), "D;%x", pid); + else + strcpy (rs->buf, "D"); + + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'O' && rs->buf[1] == 'K') + ; + else if (rs->buf[0] == '\0') + error (_("Remote doesn't know how to detach")); + else + error (_("Can't detach process.")); +} + +/* This detaches a program to which we previously attached, using + inferior_ptid to identify the process. After this is done, GDB + can be used to debug some other program. We better not have left + any breakpoints in the target program or it'll die when it hits + one. */ + +static void +remote_detach_1 (const char *args, int from_tty) { int pid = ptid_get_pid (inferior_ptid); struct remote_state *rs = get_remote_state (); + struct thread_info *tp = find_thread_ptid (inferior_ptid); + int is_fork_parent; if (args) error (_("Argument given to \"detach\" when remotely debugging.")); @@ -4458,37 +4492,74 @@ remote_detach_1 (const char *args, int from_tty, int extended) } /* Tell the remote target to detach. */ - if (remote_multi_process_p (rs)) - xsnprintf (rs->buf, get_remote_packet_size (), "D;%x", pid); - else - strcpy (rs->buf, "D"); + remote_detach_pid (pid); - putpkt (rs->buf); - getpkt (&rs->buf, &rs->buf_size, 0); - - if (rs->buf[0] == 'O' && rs->buf[1] == 'K') - ; - else if (rs->buf[0] == '\0') - error (_("Remote doesn't know how to detach")); - else - error (_("Can't detach process.")); - - if (from_tty && !extended) + if (from_tty && !rs->extended) puts_filtered (_("Ending remote debugging.\n")); - target_mourn_inferior (); + /* Check to see if we are detaching a fork parent. Note that if we + are detaching a fork child, tp == NULL. */ + is_fork_parent = (tp != NULL + && tp->pending_follow.kind == TARGET_WAITKIND_FORKED); + + /* If doing detach-on-fork, we don't mourn, because that will delete + breakpoints that should be available for the followed inferior. */ + if (!is_fork_parent) + target_mourn_inferior (); + else + { + inferior_ptid = null_ptid; + detach_inferior (pid); + } } static void remote_detach (struct target_ops *ops, const char *args, int from_tty) { - remote_detach_1 (args, from_tty, 0); + remote_detach_1 (args, from_tty); } static void extended_remote_detach (struct target_ops *ops, const char *args, int from_tty) { - remote_detach_1 (args, from_tty, 1); + remote_detach_1 (args, from_tty); +} + +/* Target follow-fork function for remote targets. On entry, and + at return, the current inferior is the fork parent. + + Note that although this is currently only used for extended-remote, + it is named remote_follow_fork in anticipation of using it for the + remote target as well. */ + +static int +remote_follow_fork (struct target_ops *ops, int follow_child, + int detach_fork) +{ + struct remote_state *rs = get_remote_state (); + + if (remote_fork_event_p (rs)) + { + /* When following the parent and detaching the child, we detach + the child here. For the case of following the child and + detaching the parent, the detach is done in the target- + independent follow fork code in infrun.c. We can't use + target_detach when detaching an unfollowed child because + the client side doesn't know anything about the child. */ + if (detach_fork && !follow_child) + { + /* Detach the fork child. */ + ptid_t child_ptid; + pid_t child_pid; + + child_ptid = inferior_thread ()->pending_follow.value.related_pid; + child_pid = ptid_get_pid (child_ptid); + + remote_detach_pid (child_pid); + detach_inferior (child_pid); + } + } + return 0; } /* Same as remote_detach, but don't send the "D" packet; just disconnect. */ @@ -5651,6 +5722,11 @@ Packet: '%s'\n"), p = unpack_varlen_hex (++p1, &c); event->core = c; } + else if (strncmp (p, "fork", p1 - p) == 0) + { + event->ws.value.related_pid = read_ptid (++p1, &p); + event->ws.kind = TARGET_WAITKIND_FORKED; + } else { ULONGEST pnum; @@ -9505,8 +9581,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid) if (ptid_equal (magic_null_ptid, ptid)) xsnprintf (buf, sizeof buf, "Thread
"); else if (rs->extended && remote_multi_process_p (rs)) - xsnprintf (buf, sizeof buf, "Thread %d.%ld", - ptid_get_pid (ptid), ptid_get_lwp (ptid)); + if (ptid_get_lwp (ptid) == 0) + return normal_pid_to_str (ptid); + else + xsnprintf (buf, sizeof buf, "Thread %d.%ld", + ptid_get_pid (ptid), ptid_get_lwp (ptid)); else xsnprintf (buf, sizeof buf, "Thread %ld", ptid_get_lwp (ptid)); @@ -11870,6 +11949,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya)."; extended_remote_ops.to_kill = extended_remote_kill; extended_remote_ops.to_supports_disable_randomization = extended_remote_supports_disable_randomization; + extended_remote_ops.to_follow_fork = remote_follow_fork; } static int