* linux-nat.c (PTRACE_O_TRACEVFORKDONE, PTRACE_O_TRACEEXIT): Define.
(PTRACE_EVENT_VFORKDONE, PTRACE_EVENT_EXIT): Define. (linux_parent_pid, linux_supports_tracevforkdone_flag): New variable. (linux_test_for_tracefork): Set linux_supports_tracevforkdone_flag. (linux_supports_tracevforkdone): New function. (linux_enable_event_reporting): Enable TRACEVFORK, TRACEEXEC, and TRACEVFORKDONE. (child_follow_fork): Handle vfork. (linux_handle_extended_wait): Likewise. Also handle exec. (child_insert_vfork_catchpoint, child_insert_exec_catchpoint): Enable. * NEWS: Mention fork tracing.
This commit is contained in:
parent
c538c11c48
commit
9016a515a3
|
@ -1,3 +1,17 @@
|
|||
2003-08-17 Daniel Jacobowitz <drow@mvista.com>
|
||||
|
||||
* linux-nat.c (PTRACE_O_TRACEVFORKDONE, PTRACE_O_TRACEEXIT): Define.
|
||||
(PTRACE_EVENT_VFORKDONE, PTRACE_EVENT_EXIT): Define.
|
||||
(linux_parent_pid, linux_supports_tracevforkdone_flag): New variable.
|
||||
(linux_test_for_tracefork): Set linux_supports_tracevforkdone_flag.
|
||||
(linux_supports_tracevforkdone): New function.
|
||||
(linux_enable_event_reporting): Enable TRACEVFORK, TRACEEXEC, and
|
||||
TRACEVFORKDONE.
|
||||
(child_follow_fork): Handle vfork.
|
||||
(linux_handle_extended_wait): Likewise. Also handle exec.
|
||||
(child_insert_vfork_catchpoint, child_insert_exec_catchpoint): Enable.
|
||||
* NEWS: Mention fork tracing.
|
||||
|
||||
2003-08-17 Daniel Jacobowitz <drow@mvista.com>
|
||||
|
||||
* lin-lwp.c (child_wait): Call linux_record_stopped_pid.
|
||||
|
|
148
gdb/linux-nat.c
148
gdb/linux-nat.c
|
@ -40,12 +40,16 @@
|
|||
#define PTRACE_O_TRACEVFORK 0x00000004
|
||||
#define PTRACE_O_TRACECLONE 0x00000008
|
||||
#define PTRACE_O_TRACEEXEC 0x00000010
|
||||
#define PTRACE_O_TRACEVFORKDONE 0x00000020
|
||||
#define PTRACE_O_TRACEEXIT 0x00000040
|
||||
|
||||
/* Wait extended result codes for the above trace options. */
|
||||
#define PTRACE_EVENT_FORK 1
|
||||
#define PTRACE_EVENT_VFORK 2
|
||||
#define PTRACE_EVENT_CLONE 3
|
||||
#define PTRACE_EVENT_EXEC 4
|
||||
#define PTRACE_EVENT_VFORKDONE 5
|
||||
#define PTRACE_EVENT_EXIT 6
|
||||
|
||||
#endif /* PTRACE_EVENT_FORK */
|
||||
|
||||
|
@ -58,6 +62,8 @@
|
|||
|
||||
extern struct target_ops child_ops;
|
||||
|
||||
static int linux_parent_pid;
|
||||
|
||||
struct simple_pid_list
|
||||
{
|
||||
int pid;
|
||||
|
@ -70,6 +76,11 @@ struct simple_pid_list *stopped_pids;
|
|||
|
||||
static int linux_supports_tracefork_flag = -1;
|
||||
|
||||
/* If we have PTRACE_O_TRACEFORK, this flag indicates whether we also have
|
||||
PTRACE_O_TRACEVFORKDONE. */
|
||||
|
||||
static int linux_supports_tracevforkdone_flag = -1;
|
||||
|
||||
|
||||
/* Trivial list manipulation functions to keep track of a list of
|
||||
new stopped processes. */
|
||||
|
@ -155,6 +166,11 @@ linux_test_for_tracefork (void)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Check whether PTRACE_O_TRACEVFORKDONE is available. */
|
||||
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0,
|
||||
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
|
||||
linux_supports_tracevforkdone_flag = (ret == 0);
|
||||
|
||||
ptrace (PTRACE_CONT, child_pid, 0, 0);
|
||||
ret = waitpid (child_pid, &status, 0);
|
||||
if (ret == child_pid && WIFSTOPPED (status)
|
||||
|
@ -190,6 +206,14 @@ linux_supports_tracefork (void)
|
|||
return linux_supports_tracefork_flag;
|
||||
}
|
||||
|
||||
static int
|
||||
linux_supports_tracevforkdone (void)
|
||||
{
|
||||
if (linux_supports_tracefork_flag == -1)
|
||||
linux_test_for_tracefork ();
|
||||
return linux_supports_tracevforkdone_flag;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
linux_enable_event_reporting (ptid_t ptid)
|
||||
|
@ -200,7 +224,12 @@ linux_enable_event_reporting (ptid_t ptid)
|
|||
if (! linux_supports_tracefork ())
|
||||
return;
|
||||
|
||||
options = PTRACE_O_TRACEFORK;
|
||||
options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC;
|
||||
if (linux_supports_tracevforkdone ())
|
||||
options |= PTRACE_O_TRACEVFORKDONE;
|
||||
|
||||
/* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
|
||||
read-only process state. */
|
||||
|
||||
ptrace (PTRACE_SETOPTIONS, pid, 0, options);
|
||||
}
|
||||
|
@ -230,9 +259,11 @@ child_follow_fork (int follow_child)
|
|||
{
|
||||
ptid_t last_ptid;
|
||||
struct target_waitstatus last_status;
|
||||
int has_vforked;
|
||||
int parent_pid, child_pid;
|
||||
|
||||
get_last_target_status (&last_ptid, &last_status);
|
||||
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
|
||||
parent_pid = ptid_get_pid (last_ptid);
|
||||
child_pid = last_status.value.related_pid;
|
||||
|
||||
|
@ -243,6 +274,8 @@ child_follow_fork (int follow_child)
|
|||
/* Before detaching from the child, remove all breakpoints from
|
||||
it. (This won't actually modify the breakpoint list, but will
|
||||
physically remove the breakpoints from the child.) */
|
||||
/* If we vforked this will remove the breakpoints from the parent
|
||||
also, but they'll be reinserted below. */
|
||||
detach_breakpoints (child_pid);
|
||||
|
||||
fprintf_filtered (gdb_stdout,
|
||||
|
@ -250,13 +283,67 @@ child_follow_fork (int follow_child)
|
|||
child_pid);
|
||||
|
||||
ptrace (PTRACE_DETACH, child_pid, 0, 0);
|
||||
|
||||
if (has_vforked)
|
||||
{
|
||||
if (linux_supports_tracevforkdone ())
|
||||
{
|
||||
int status;
|
||||
|
||||
ptrace (PTRACE_CONT, parent_pid, 0, 0);
|
||||
waitpid (parent_pid, &status, __WALL);
|
||||
if ((status >> 16) != PTRACE_EVENT_VFORKDONE)
|
||||
warning ("Unexpected waitpid result %06x when waiting for "
|
||||
"vfork-done", status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We can't insert breakpoints until the child has
|
||||
finished with the shared memory region. We need to
|
||||
wait until that happens. Ideal would be to just
|
||||
call:
|
||||
- ptrace (PTRACE_SYSCALL, parent_pid, 0, 0);
|
||||
- waitpid (parent_pid, &status, __WALL);
|
||||
However, most architectures can't handle a syscall
|
||||
being traced on the way out if it wasn't traced on
|
||||
the way in.
|
||||
|
||||
We might also think to loop, continuing the child
|
||||
until it exits or gets a SIGTRAP. One problem is
|
||||
that the child might call ptrace with PTRACE_TRACEME.
|
||||
|
||||
There's no simple and reliable way to figure out when
|
||||
the vforked child will be done with its copy of the
|
||||
shared memory. We could step it out of the syscall,
|
||||
two instructions, let it go, and then single-step the
|
||||
parent once. When we have hardware single-step, this
|
||||
would work; with software single-step it could still
|
||||
be made to work but we'd have to be able to insert
|
||||
single-step breakpoints in the child, and we'd have
|
||||
to insert -just- the single-step breakpoint in the
|
||||
parent. Very awkward.
|
||||
|
||||
In the end, 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. */
|
||||
|
||||
usleep (10000);
|
||||
}
|
||||
|
||||
/* Since we vforked, breakpoints were removed in the parent
|
||||
too. Put them back. */
|
||||
reattach_breakpoints (parent_pid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char child_pid_spelling[40];
|
||||
|
||||
/* Needed to keep the breakpoint lists in sync. */
|
||||
detach_breakpoints (child_pid);
|
||||
if (! has_vforked)
|
||||
detach_breakpoints (child_pid);
|
||||
|
||||
/* Before detaching from the parent, remove all breakpoints from it. */
|
||||
remove_breakpoints ();
|
||||
|
@ -265,7 +352,28 @@ child_follow_fork (int follow_child)
|
|||
"Attaching after fork to child process %d.\n",
|
||||
child_pid);
|
||||
|
||||
target_detach (NULL, 0);
|
||||
/* If we're vforking, we may want to hold on to the parent until
|
||||
the child exits or execs. At exec time we can remove the old
|
||||
breakpoints from the parent and detach it; at exit time we
|
||||
could do the same (or even, sneakily, resume debugging it - the
|
||||
child's exec has failed, or something similar).
|
||||
|
||||
This doesn't clean up "properly", because we can't call
|
||||
target_detach, but that's OK; if the current target is "child",
|
||||
then it doesn't need any further cleanups, and lin_lwp will
|
||||
generally not encounter vfork (vfork is defined to fork
|
||||
in libpthread.so).
|
||||
|
||||
The holding part is very easy if we have VFORKDONE events;
|
||||
but keeping track of both processes is beyond GDB at the
|
||||
moment. So we don't expose the parent to the rest of GDB.
|
||||
Instead we quietly hold onto it until such time as we can
|
||||
safely resume it. */
|
||||
|
||||
if (has_vforked)
|
||||
linux_parent_pid = parent_pid;
|
||||
else
|
||||
target_detach (NULL, 0);
|
||||
|
||||
inferior_ptid = pid_to_ptid (child_pid);
|
||||
push_target (&child_ops);
|
||||
|
@ -287,7 +395,7 @@ linux_handle_extended_wait (int pid, int status,
|
|||
internal_error (__FILE__, __LINE__,
|
||||
"unexpected clone event");
|
||||
|
||||
if (event == PTRACE_EVENT_FORK)
|
||||
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
|
||||
{
|
||||
unsigned long new_pid;
|
||||
int ret;
|
||||
|
@ -315,11 +423,29 @@ linux_handle_extended_wait (int pid, int status,
|
|||
"wait returned unexpected status 0x%x", status);
|
||||
}
|
||||
|
||||
ourstatus->kind = TARGET_WAITKIND_FORKED;
|
||||
ourstatus->kind = (event == PTRACE_EVENT_FORK)
|
||||
? TARGET_WAITKIND_FORKED : TARGET_WAITKIND_VFORKED;
|
||||
ourstatus->value.related_pid = new_pid;
|
||||
return inferior_ptid;
|
||||
}
|
||||
|
||||
if (event == PTRACE_EVENT_EXEC)
|
||||
{
|
||||
ourstatus->kind = TARGET_WAITKIND_EXECD;
|
||||
ourstatus->value.execd_pathname
|
||||
= xstrdup (child_pid_to_exec_file (pid));
|
||||
|
||||
if (linux_parent_pid)
|
||||
{
|
||||
detach_breakpoints (linux_parent_pid);
|
||||
ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0);
|
||||
|
||||
linux_parent_pid = 0;
|
||||
}
|
||||
|
||||
return inferior_ptid;
|
||||
}
|
||||
|
||||
internal_error (__FILE__, __LINE__,
|
||||
"unknown ptrace event %d", event);
|
||||
}
|
||||
|
@ -337,19 +463,19 @@ child_insert_fork_catchpoint (int pid)
|
|||
int
|
||||
child_insert_vfork_catchpoint (int pid)
|
||||
{
|
||||
if (linux_supports_tracefork ())
|
||||
error ("Vfork catchpoints have not been implemented yet.");
|
||||
else
|
||||
if (!linux_supports_tracefork ())
|
||||
error ("Your system does not support vfork catchpoints.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
child_insert_exec_catchpoint (int pid)
|
||||
{
|
||||
if (linux_supports_tracefork ())
|
||||
error ("Exec catchpoints have not been implemented yet.");
|
||||
else
|
||||
if (!linux_supports_tracefork ())
|
||||
error ("Your system does not support exec catchpoints.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in New Issue