@@ -210,6 +210,7 @@ int using_threads = 1;
jump pads). */
static int stabilizing_threads;
+static void async_file_mark (void);
static void linux_resume_one_lwp (struct lwp_info *lwp,
int step, int signal, siginfo_t *info);
static void linux_resume (struct thread_resume *resume_info, size_t n);
@@ -218,6 +219,9 @@ static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
static int linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
int *wstat, int options);
static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
+static ptid_t linux_wait_1 (ptid_t ptid,
+ struct target_waitstatus *ourstatus,
+ int target_options);
static struct lwp_info *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
@@ -379,7 +383,8 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
struct thread_info *event_thr = get_lwp_thread (event_child);
struct lwp_info *new_lwp;
- if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_CLONE)
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
+ || event == PTRACE_EVENT_CLONE)
{
ptid_t ptid;
unsigned long new_pid;
@@ -404,7 +409,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
warning ("wait returned unexpected status 0x%x", status);
}
- if (event == PTRACE_EVENT_FORK)
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
{
struct process_info *parent_proc;
struct process_info *child_proc;
@@ -444,12 +449,18 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
if (the_low_target.new_fork != NULL)
the_low_target.new_fork (parent_proc, child_proc);
- /* Save fork info for target processing. */
- current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ /* Save fork info for target processing and reporting to GDB. */
+ if (event == PTRACE_EVENT_FORK)
+ {
+ current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ event_child->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ }
+ else if (event == PTRACE_EVENT_VFORK)
+ {
+ current_inferior->pending_follow.kind = TARGET_WAITKIND_VFORKED;
+ event_child->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+ }
current_inferior->pending_follow.value.related_pid = ptid;
-
- /* Save fork info for reporting to GDB. */
- event_child->waitstatus.kind = TARGET_WAITKIND_FORKED;
event_child->waitstatus.value.related_pid = ptid;
/* Report the event. */
@@ -509,6 +520,11 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
/* Don't report the event. */
return 1;
}
+ else if (event == PTRACE_EVENT_VFORK_DONE)
+ {
+ event_child->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+ return 0;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -1250,7 +1266,9 @@ linux_enable_extended_features (void)
/* There is no process yet, so include extended options in the
base options for subsequent ptrace configuration. */
linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE
- | PTRACE_O_TRACEFORK, 0);
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEVFORKDONE, 0);
}
else
{
@@ -1298,6 +1316,7 @@ linux_follow_fork (int follow_child, int detach_fork)
{
struct inferior_list_entry *parent_inf;
struct thread_info *parent_thread;
+ int has_vforked;
parent_inf = find_inferior (&all_threads, is_parent_callback, NULL);
@@ -1307,7 +1326,8 @@ linux_follow_fork (int follow_child, int detach_fork)
return 0;
parent_thread = (struct thread_info *)parent_inf;
- parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE;
+ has_vforked = (parent_thread->pending_follow.kind
+ == TARGET_WAITKIND_VFORKED);
if (!follow_child)
{
@@ -1350,7 +1370,7 @@ linux_follow_fork (int follow_child, int detach_fork)
signo = 0;
ptrace (PTRACE_DETACH, child_pid, 0, signo);
-
+
/* Deallocate all process-related storage. */
child_proc = find_process_pid (child_pid);
if (child_proc != NULL)
@@ -1359,8 +1379,63 @@ linux_follow_fork (int follow_child, int detach_fork)
current_inferior = NULL;
}
}
+
+ if (has_vforked)
+ {
+ struct lwp_info *parent_lp;
+
+ parent_lp = get_thread_lwp (parent_thread);
+ gdb_assert (linux_supports_tracefork () >= 0);
+
+ if (linux_supports_tracevforkdone ())
+ {
+ if (debug_threads)
+ {
+ debug_printf ("LFF: waiting for VFORK_DONE on %d\n",
+ ptid_get_pid (parent_thread->entry.id));
+ }
+ parent_lp->stopped = 1;
+
+ /* We'll handle the VFORK_DONE event like any other
+ event, in target_wait. */
+ }
+ else
+ {
+ /* We can't insert breakpoints until the child has
+ finished with the shared memory region. Without
+ VFORK_DONE events, the best we can do is to make sure it
+ runs for a little while. Hopefully it will be out of
+ range of any breakpoints we reinsert. Usually this
+ is only the single-step breakpoint at vfork's return
+ point.
+
+ See linux-nat.c:linux_child_follow_vfork for a more
+ detailed discussion of this. */
+
+ if (debug_threads)
+ debug_printf ("LFF: no VFORK_DONE support, sleeping a bit\n");
+
+ usleep (10000);
+
+ /* Pretend we've seen a PTRACE_EVENT_VFORK_DONE event,
+ and leave it pending. The next linux_resume_one_lwp call
+ will notice a pending event, and bypasses actually
+ resuming the inferior. */
+ parent_lp->status_pending_p = 1;
+ parent_lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+ parent_lp->stopped = 1;
+
+ /* If we're in async mode, need to tell the event loop
+ there's something here to process. */
+ if (target_is_async_p ())
+ async_file_mark ();
+ }
+ }
}
+ parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE;
+ parent_thread->pending_follow.value.related_pid = null_ptid;
+
return 0;
}
@@ -2560,9 +2635,6 @@ static void move_out_of_jump_pad_callback (struct inferior_list_entry *entry);
static int stuck_in_jump_pad_callback (struct inferior_list_entry *entry,
void *data);
static int lwp_running (struct inferior_list_entry *entry, void *data);
-static ptid_t linux_wait_1 (ptid_t ptid,
- struct target_waitstatus *ourstatus,
- int target_options);
/* Stabilize threads (move out of jump pads).
@@ -2675,7 +2747,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
if (waitstatus == NULL)
return 0;
- return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+ return (waitstatus->kind == TARGET_WAITKIND_FORKED
+ || waitstatus->kind == TARGET_WAITKIND_VFORKED
+ || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
}
/* Wait for process, returns status. */
@@ -6358,5 +6432,8 @@ initialize_low (void)
initialize_low_arch ();
- linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, PTRACE_O_TRACEFORK);
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE,
+ PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEVFORKDONE);
}
@@ -1112,15 +1112,18 @@ prepare_resume_reply (char *buf, ptid_t ptid,
{
case TARGET_WAITKIND_STOPPED:
case TARGET_WAITKIND_FORKED:
+ case TARGET_WAITKIND_VFORKED:
{
struct thread_info *saved_inferior;
const char **regp;
struct regcache *regcache;
- if (status->kind == TARGET_WAITKIND_FORKED && multi_process)
+ if ((status->kind == TARGET_WAITKIND_FORKED
+ || status->kind == TARGET_WAITKIND_VFORKED) && multi_process)
{
enum gdb_signal signal = GDB_SIGNAL_TRAP;
- const char *event = "fork";
+ const char *event = (status->kind == TARGET_WAITKIND_FORKED
+ ?"fork":"vfork");
sprintf (buf, "T%02x%s:p%x.%lx;", signal, event,
ptid_get_pid (status->value.related_pid),
@@ -1231,6 +1234,15 @@ prepare_resume_reply (char *buf, ptid_t ptid,
else
sprintf (buf, "X%02x", status->value.sig);
break;
+ case TARGET_WAITKIND_VFORK_DONE:
+ if (multi_process)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = "vforkdone";
+
+ sprintf (buf, "T%02x%s:;", signal, event);
+ }
+ break;
default:
error ("unhandled waitkind");
break;
@@ -5510,6 +5510,21 @@ Packet: '%s'\n"),
event->ws.value.related_pid = read_ptid (++p1, &p);
event->ws.kind = TARGET_WAITKIND_FORKED;
}
+ else if (strncmp (p, "vfork", p1 - p) == 0)
+ {
+ event->ws.value.related_pid = read_ptid (++p1, &p);
+ event->ws.kind = TARGET_WAITKIND_VFORKED;
+ }
+ else if (strncmp (p, "vforkdone", p1 - p) == 0)
+ {
+ p1++;
+ p_temp = p1;
+ while (*p_temp && *p_temp != ';')
+ p_temp++;
+
+ event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+ p = p_temp;
+ }
else
{
/* Silently skip unknown optional info. */