mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-19 00:59:15 +08:00
gdb/
* gdbthread.h (struct thread_info): New `pending_follow' field. * thread.c (new_thread): New function. (add_thread_silent): Use it. * breakpoint.c (internal_breakpoint_number): New global, moved from inside... (create_internal_breakpoint): ... this. (clone_momentary_breakpoint): New. * breakpoint.h (clone_momentary_breakpoint): Declare. * infrun.c (nullify_last_target_wait_ptid): Move declaration higher. (pending_follow): Delete. (follow_fork): Handle pending follow fork event here. Moved the preserving of thread stepping state here. (resume): Don't handle pending follow fork events here. Only install the inferior's terminal modes if we're about to resume it. (proceed): Handle possible pending follow fork events here. (init_wait_for_inferior): No need to clear pending_follow anymore, it's gone. (handle_inferior_event): Adjust to per-thread `pending_follow'. Call `follow_fork' to handle following the fork. If the follow-fork is cancelled, stop stepping. * linux-nat.c (linux_child_follow_fork): Adjust to per-thread `pending_follow' events. Remove code that handled preserving the thread stepping state. * inf-ptrace.c (inf_ptrace_follow_fork): Ditto. * inf-ttrace.c (inf_ttrace_follow_fork): Ditto. gdb/testsuite/ * gdb.threads/fork-thread-pending.c: New. * gdb.threads/fork-thread-pending.exp: New.
This commit is contained in:
@ -1,3 +1,32 @@
|
|||||||
|
2009-05-24 Pedro Alves <pedro@codesourcery.com>
|
||||||
|
|
||||||
|
* gdbthread.h (struct thread_info): New `pending_follow' field.
|
||||||
|
* thread.c (new_thread): New function.
|
||||||
|
(add_thread_silent): Use it.
|
||||||
|
* breakpoint.c (internal_breakpoint_number): New global, moved
|
||||||
|
from inside...
|
||||||
|
(create_internal_breakpoint): ... this.
|
||||||
|
(clone_momentary_breakpoint): New.
|
||||||
|
* breakpoint.h (clone_momentary_breakpoint): Declare.
|
||||||
|
* infrun.c (nullify_last_target_wait_ptid): Move declaration
|
||||||
|
higher.
|
||||||
|
(pending_follow): Delete.
|
||||||
|
(follow_fork): Handle pending follow fork event here. Moved the
|
||||||
|
preserving of thread stepping state here.
|
||||||
|
(resume): Don't handle pending follow fork events here. Only
|
||||||
|
install the inferior's terminal modes if we're about to resume it.
|
||||||
|
(proceed): Handle possible pending follow fork events here.
|
||||||
|
(init_wait_for_inferior): No need to clear pending_follow anymore,
|
||||||
|
it's gone.
|
||||||
|
(handle_inferior_event): Adjust to per-thread `pending_follow'.
|
||||||
|
Call `follow_fork' to handle following the fork. If the
|
||||||
|
follow-fork is cancelled, stop stepping.
|
||||||
|
* linux-nat.c (linux_child_follow_fork): Adjust to per-thread
|
||||||
|
`pending_follow' events. Remove code that handled preserving the
|
||||||
|
thread stepping state.
|
||||||
|
* inf-ptrace.c (inf_ptrace_follow_fork): Ditto.
|
||||||
|
* inf-ttrace.c (inf_ttrace_follow_fork): Ditto.
|
||||||
|
|
||||||
2009-05-24 Pierre Muller <muller@ics.u-strasbg.fr>
|
2009-05-24 Pierre Muller <muller@ics.u-strasbg.fr>
|
||||||
|
|
||||||
* symfile.c (add_shared_symbol_files_command): Remove
|
* symfile.c (add_shared_symbol_files_command): Remove
|
||||||
|
@ -1456,10 +1456,11 @@ reattach_breakpoints (int pid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int internal_breakpoint_number = -1;
|
||||||
|
|
||||||
static struct breakpoint *
|
static struct breakpoint *
|
||||||
create_internal_breakpoint (CORE_ADDR address, enum bptype type)
|
create_internal_breakpoint (CORE_ADDR address, enum bptype type)
|
||||||
{
|
{
|
||||||
static int internal_breakpoint_number = -1;
|
|
||||||
struct symtab_and_line sal;
|
struct symtab_and_line sal;
|
||||||
struct breakpoint *b;
|
struct breakpoint *b;
|
||||||
|
|
||||||
@ -5007,6 +5008,43 @@ set_momentary_breakpoint (struct symtab_and_line sal, struct frame_id frame_id,
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make a deep copy of momentary breakpoint ORIG. Returns NULL if
|
||||||
|
ORIG is NULL. */
|
||||||
|
|
||||||
|
struct breakpoint *
|
||||||
|
clone_momentary_breakpoint (struct breakpoint *orig)
|
||||||
|
{
|
||||||
|
struct breakpoint *copy;
|
||||||
|
|
||||||
|
/* If there's nothing to clone, then return nothing. */
|
||||||
|
if (orig == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
copy = set_raw_breakpoint_without_location (orig->type);
|
||||||
|
copy->loc = allocate_bp_location (copy);
|
||||||
|
set_breakpoint_location_function (copy->loc);
|
||||||
|
|
||||||
|
copy->loc->requested_address = orig->loc->requested_address;
|
||||||
|
copy->loc->address = orig->loc->address;
|
||||||
|
copy->loc->section = orig->loc->section;
|
||||||
|
|
||||||
|
if (orig->source_file == NULL)
|
||||||
|
copy->source_file = NULL;
|
||||||
|
else
|
||||||
|
copy->source_file = xstrdup (orig->source_file);
|
||||||
|
|
||||||
|
copy->line_number = orig->line_number;
|
||||||
|
copy->frame_id = orig->frame_id;
|
||||||
|
copy->thread = orig->thread;
|
||||||
|
|
||||||
|
copy->enable_state = bp_enabled;
|
||||||
|
copy->disposition = disp_donttouch;
|
||||||
|
copy->number = internal_breakpoint_number--;
|
||||||
|
|
||||||
|
update_global_location_list_nothrow (0);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
struct breakpoint *
|
struct breakpoint *
|
||||||
set_momentary_breakpoint_at_pc (CORE_ADDR pc, enum bptype type)
|
set_momentary_breakpoint_at_pc (CORE_ADDR pc, enum bptype type)
|
||||||
{
|
{
|
||||||
|
@ -696,6 +696,8 @@ extern struct breakpoint *set_momentary_breakpoint
|
|||||||
extern struct breakpoint *set_momentary_breakpoint_at_pc
|
extern struct breakpoint *set_momentary_breakpoint_at_pc
|
||||||
(CORE_ADDR pc, enum bptype type);
|
(CORE_ADDR pc, enum bptype type);
|
||||||
|
|
||||||
|
extern struct breakpoint *clone_momentary_breakpoint (struct breakpoint *bpkt);
|
||||||
|
|
||||||
extern void set_ignore_count (int, int, int);
|
extern void set_ignore_count (int, int, int);
|
||||||
|
|
||||||
extern void set_default_breakpoint (int, CORE_ADDR, struct symtab *, int);
|
extern void set_default_breakpoint (int, CORE_ADDR, struct symtab *, int);
|
||||||
|
@ -165,6 +165,11 @@ struct thread_info
|
|||||||
next time inferior stops if it stops due to stepping. */
|
next time inferior stops if it stops due to stepping. */
|
||||||
int step_multi;
|
int step_multi;
|
||||||
|
|
||||||
|
/* This is used to remember when a fork or vfork event was caught by
|
||||||
|
a catchpoint, and thus the event is to be followed at the next
|
||||||
|
resume of the thread, and not immediately. */
|
||||||
|
struct target_waitstatus pending_follow;
|
||||||
|
|
||||||
/* Last signal that the inferior received (why it stopped). */
|
/* Last signal that the inferior received (why it stopped). */
|
||||||
enum target_signal stop_signal;
|
enum target_signal stop_signal;
|
||||||
|
|
||||||
|
@ -46,20 +46,8 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
{
|
{
|
||||||
pid_t pid, fpid;
|
pid_t pid, fpid;
|
||||||
ptrace_state_t pe;
|
ptrace_state_t pe;
|
||||||
struct thread_info *last_tp = NULL;
|
|
||||||
|
|
||||||
/* FIXME: kettenis/20050720: This stuff should really be passed as
|
pid = ptid_get_pid (inferior_ptid);
|
||||||
an argument by our caller. */
|
|
||||||
{
|
|
||||||
ptid_t ptid;
|
|
||||||
struct target_waitstatus status;
|
|
||||||
|
|
||||||
get_last_target_status (&ptid, &status);
|
|
||||||
gdb_assert (status.kind == TARGET_WAITKIND_FORKED);
|
|
||||||
|
|
||||||
pid = ptid_get_pid (ptid);
|
|
||||||
last_tp = find_thread_pid (ptid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ptrace (PT_GET_PROCESS_STATE, pid,
|
if (ptrace (PT_GET_PROCESS_STATE, pid,
|
||||||
(PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
|
(PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
|
||||||
@ -70,18 +58,9 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
|
|
||||||
if (follow_child)
|
if (follow_child)
|
||||||
{
|
{
|
||||||
/* Copy user stepping state to the new inferior thread. */
|
|
||||||
struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
|
|
||||||
CORE_ADDR step_range_start = last_tp->step_range_start;
|
|
||||||
CORE_ADDR step_range_end = last_tp->step_range_end;
|
|
||||||
struct frame_id step_frame_id = last_tp->step_frame_id;
|
|
||||||
struct inferior *parent_inf, *child_inf;
|
struct inferior *parent_inf, *child_inf;
|
||||||
struct thread_info *tp;
|
struct thread_info *tp;
|
||||||
|
|
||||||
/* Otherwise, deleting the parent would get rid of this
|
|
||||||
breakpoint. */
|
|
||||||
last_tp->step_resume_breakpoint = NULL;
|
|
||||||
|
|
||||||
parent_inf = find_inferior_pid (pid);
|
parent_inf = find_inferior_pid (pid);
|
||||||
|
|
||||||
/* Add the child. */
|
/* Add the child. */
|
||||||
@ -102,26 +81,15 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
/* Delete the parent. */
|
/* Delete the parent. */
|
||||||
detach_inferior (pid);
|
detach_inferior (pid);
|
||||||
|
|
||||||
tp = add_thread_silent (inferior_ptid);
|
add_thread_silent (inferior_ptid);
|
||||||
|
|
||||||
tp->step_resume_breakpoint = step_resume_breakpoint;
|
|
||||||
tp->step_range_start = step_range_start;
|
|
||||||
tp->step_range_end = step_range_end;
|
|
||||||
tp->step_frame_id = step_frame_id;
|
|
||||||
|
|
||||||
/* Reset breakpoints in the child as appropriate. */
|
|
||||||
follow_inferior_reset_breakpoints ();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inferior_ptid = pid_to_ptid (pid);
|
|
||||||
|
|
||||||
/* Breakpoints have already been detached from the child by
|
/* Breakpoints have already been detached from the child by
|
||||||
infrun.c. */
|
infrun.c. */
|
||||||
|
|
||||||
if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
|
if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
|
||||||
perror_with_name (("ptrace"));
|
perror_with_name (("ptrace"));
|
||||||
detach_inferior (pid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -412,25 +412,13 @@ inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
pid_t pid, fpid;
|
pid_t pid, fpid;
|
||||||
lwpid_t lwpid, flwpid;
|
lwpid_t lwpid, flwpid;
|
||||||
ttstate_t tts;
|
ttstate_t tts;
|
||||||
struct thread_info *last_tp = NULL;
|
struct thread_info *tp = inferior_thread ();
|
||||||
struct breakpoint *step_resume_breakpoint = NULL;
|
|
||||||
CORE_ADDR step_range_start = 0, step_range_end = 0;
|
|
||||||
struct frame_id step_frame_id = null_frame_id;
|
|
||||||
|
|
||||||
/* FIXME: kettenis/20050720: This stuff should really be passed as
|
gdb_assert (tp->pending_follow.kind == TARGET_WAITKIND_FORKED
|
||||||
an argument by our caller. */
|
|| tp->pending_follow.kind == TARGET_WAITKIND_VFORKED);
|
||||||
{
|
|
||||||
ptid_t ptid;
|
|
||||||
struct target_waitstatus status;
|
|
||||||
|
|
||||||
get_last_target_status (&ptid, &status);
|
pid = ptid_get_pid (inferior_ptid);
|
||||||
gdb_assert (status.kind == TARGET_WAITKIND_FORKED
|
lwpid = ptid_get_lwp (inferior_ptid);
|
||||||
|| status.kind == TARGET_WAITKIND_VFORKED);
|
|
||||||
|
|
||||||
pid = ptid_get_pid (ptid);
|
|
||||||
lwpid = ptid_get_lwp (ptid);
|
|
||||||
last_tp = find_thread_pid (ptid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get all important details that core GDB doesn't (and shouldn't)
|
/* Get all important details that core GDB doesn't (and shouldn't)
|
||||||
know about. */
|
know about. */
|
||||||
@ -462,16 +450,6 @@ inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
|
|
||||||
parent_inf = find_inferior_pid (pid);
|
parent_inf = find_inferior_pid (pid);
|
||||||
|
|
||||||
/* Copy user stepping state to the new inferior thread. */
|
|
||||||
step_resume_breakpoint = last_tp->step_resume_breakpoint;
|
|
||||||
step_range_start = last_tp->step_range_start;
|
|
||||||
step_range_end = last_tp->step_range_end;
|
|
||||||
step_frame_id = last_tp->step_frame_id;
|
|
||||||
|
|
||||||
/* Otherwise, deleting the parent would get rid of this
|
|
||||||
breakpoint. */
|
|
||||||
last_tp->step_resume_breakpoint = NULL;
|
|
||||||
|
|
||||||
inferior_ptid = ptid_build (fpid, flwpid, 0);
|
inferior_ptid = ptid_build (fpid, flwpid, 0);
|
||||||
inf = add_inferior (fpid);
|
inf = add_inferior (fpid);
|
||||||
inf->attach_flag = parent_inf->attach_flag;
|
inf->attach_flag = parent_inf->attach_flag;
|
||||||
@ -553,14 +531,6 @@ Detaching after fork from child process %ld.\n"), (long)fpid);
|
|||||||
xmalloc (sizeof (struct inf_ttrace_private_thread_info));
|
xmalloc (sizeof (struct inf_ttrace_private_thread_info));
|
||||||
memset (ti->private, 0,
|
memset (ti->private, 0,
|
||||||
sizeof (struct inf_ttrace_private_thread_info));
|
sizeof (struct inf_ttrace_private_thread_info));
|
||||||
|
|
||||||
ti->step_resume_breakpoint = step_resume_breakpoint;
|
|
||||||
ti->step_range_start = step_range_start;
|
|
||||||
ti->step_range_end = step_range_end;
|
|
||||||
ti->step_frame_id = step_frame_id;
|
|
||||||
|
|
||||||
/* Reset breakpoints in the child as appropriate. */
|
|
||||||
follow_inferior_reset_breakpoints ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
241
gdb/infrun.c
241
gdb/infrun.c
@ -83,6 +83,8 @@ static int prepare_to_proceed (int);
|
|||||||
|
|
||||||
void _initialize_infrun (void);
|
void _initialize_infrun (void);
|
||||||
|
|
||||||
|
void nullify_last_target_wait_ptid (void);
|
||||||
|
|
||||||
/* When set, stop the 'step' command if we enter a function which has
|
/* When set, stop the 'step' command if we enter a function which has
|
||||||
no line number information. The normal behavior is that we step
|
no line number information. The normal behavior is that we step
|
||||||
over such function. */
|
over such function. */
|
||||||
@ -255,21 +257,6 @@ void init_thread_stepping_state (struct thread_info *tss);
|
|||||||
|
|
||||||
void init_infwait_state (void);
|
void init_infwait_state (void);
|
||||||
|
|
||||||
/* This is used to remember when a fork or vfork event was caught by a
|
|
||||||
catchpoint, and thus the event is to be followed at the next resume
|
|
||||||
of the inferior, and not immediately. */
|
|
||||||
static struct
|
|
||||||
{
|
|
||||||
enum target_waitkind kind;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
ptid_t parent_pid;
|
|
||||||
ptid_t child_pid;
|
|
||||||
}
|
|
||||||
fork_event;
|
|
||||||
}
|
|
||||||
pending_follow;
|
|
||||||
|
|
||||||
static const char follow_fork_mode_child[] = "child";
|
static const char follow_fork_mode_child[] = "child";
|
||||||
static const char follow_fork_mode_parent[] = "parent";
|
static const char follow_fork_mode_parent[] = "parent";
|
||||||
|
|
||||||
@ -290,12 +277,157 @@ Debugger response to a program call of fork or vfork is \"%s\".\n"),
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tell the target to follow the fork we're stopped at. Returns true
|
||||||
|
if the inferior should be resumed; false, if the target for some
|
||||||
|
reason decided it's best not to resume. */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
follow_fork (void)
|
follow_fork (void)
|
||||||
{
|
{
|
||||||
int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
|
int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
|
||||||
|
int should_resume = 1;
|
||||||
|
struct thread_info *tp;
|
||||||
|
|
||||||
return target_follow_fork (follow_child);
|
/* Copy user stepping state to the new inferior thread. FIXME: the
|
||||||
|
followed fork child thread should have a copy of most of the
|
||||||
|
parent thread structure's run control related fields, not just
|
||||||
|
these. */
|
||||||
|
struct breakpoint *step_resume_breakpoint;
|
||||||
|
CORE_ADDR step_range_start;
|
||||||
|
CORE_ADDR step_range_end;
|
||||||
|
struct frame_id step_frame_id;
|
||||||
|
|
||||||
|
if (!non_stop)
|
||||||
|
{
|
||||||
|
ptid_t wait_ptid;
|
||||||
|
struct target_waitstatus wait_status;
|
||||||
|
|
||||||
|
/* Get the last target status returned by target_wait(). */
|
||||||
|
get_last_target_status (&wait_ptid, &wait_status);
|
||||||
|
|
||||||
|
/* If not stopped at a fork event, then there's nothing else to
|
||||||
|
do. */
|
||||||
|
if (wait_status.kind != TARGET_WAITKIND_FORKED
|
||||||
|
&& wait_status.kind != TARGET_WAITKIND_VFORKED)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Check if we switched over from WAIT_PTID, since the event was
|
||||||
|
reported. */
|
||||||
|
if (!ptid_equal (wait_ptid, minus_one_ptid)
|
||||||
|
&& !ptid_equal (inferior_ptid, wait_ptid))
|
||||||
|
{
|
||||||
|
/* We did. Switch back to WAIT_PTID thread, to tell the
|
||||||
|
target to follow it (in either direction). We'll
|
||||||
|
afterwards refuse to resume, and inform the user what
|
||||||
|
happened. */
|
||||||
|
switch_to_thread (wait_ptid);
|
||||||
|
should_resume = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tp = inferior_thread ();
|
||||||
|
|
||||||
|
/* If there were any forks/vforks that were caught and are now to be
|
||||||
|
followed, then do so now. */
|
||||||
|
switch (tp->pending_follow.kind)
|
||||||
|
{
|
||||||
|
case TARGET_WAITKIND_FORKED:
|
||||||
|
case TARGET_WAITKIND_VFORKED:
|
||||||
|
{
|
||||||
|
ptid_t parent, child;
|
||||||
|
|
||||||
|
/* If the user did a next/step, etc, over a fork call,
|
||||||
|
preserve the stepping state in the fork child. */
|
||||||
|
if (follow_child && should_resume)
|
||||||
|
{
|
||||||
|
step_resume_breakpoint
|
||||||
|
= clone_momentary_breakpoint (tp->step_resume_breakpoint);
|
||||||
|
step_range_start = tp->step_range_start;
|
||||||
|
step_range_end = tp->step_range_end;
|
||||||
|
step_frame_id = tp->step_frame_id;
|
||||||
|
|
||||||
|
/* For now, delete the parent's sr breakpoint, otherwise,
|
||||||
|
parent/child sr breakpoints are considered duplicates,
|
||||||
|
and the child version will not be installed. Remove
|
||||||
|
this when the breakpoints module becomes aware of
|
||||||
|
inferiors and address spaces. */
|
||||||
|
delete_step_resume_breakpoint (tp);
|
||||||
|
tp->step_range_start = 0;
|
||||||
|
tp->step_range_end = 0;
|
||||||
|
tp->step_frame_id = null_frame_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = inferior_ptid;
|
||||||
|
child = tp->pending_follow.value.related_pid;
|
||||||
|
|
||||||
|
/* Tell the target to do whatever is necessary to follow
|
||||||
|
either parent or child. */
|
||||||
|
if (target_follow_fork (follow_child))
|
||||||
|
{
|
||||||
|
/* Target refused to follow, or there's some other reason
|
||||||
|
we shouldn't resume. */
|
||||||
|
should_resume = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This pending follow fork event is now handled, one way
|
||||||
|
or another. The previous selected thread may be gone
|
||||||
|
from the lists by now, but if it is still around, need
|
||||||
|
to clear the pending follow request. */
|
||||||
|
tp = find_thread_pid (parent);
|
||||||
|
if (tp)
|
||||||
|
tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
|
||||||
|
|
||||||
|
/* This makes sure we don't try to apply the "Switched
|
||||||
|
over from WAIT_PID" logic above. */
|
||||||
|
nullify_last_target_wait_ptid ();
|
||||||
|
|
||||||
|
/* If we followed the child, switch to it... */
|
||||||
|
if (follow_child)
|
||||||
|
{
|
||||||
|
switch_to_thread (child);
|
||||||
|
|
||||||
|
/* ... and preserve the stepping state, in case the
|
||||||
|
user was stepping over the fork call. */
|
||||||
|
if (should_resume)
|
||||||
|
{
|
||||||
|
tp = inferior_thread ();
|
||||||
|
tp->step_resume_breakpoint = step_resume_breakpoint;
|
||||||
|
tp->step_range_start = step_range_start;
|
||||||
|
tp->step_range_end = step_range_end;
|
||||||
|
tp->step_frame_id = step_frame_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If we get here, it was because we're trying to
|
||||||
|
resume from a fork catchpoint, but, the user
|
||||||
|
has switched threads away from the thread that
|
||||||
|
forked. In that case, the resume command
|
||||||
|
issued is most likely not applicable to the
|
||||||
|
child, so just warn, and refuse to resume. */
|
||||||
|
warning (_("\
|
||||||
|
Not resuming: switched threads before following fork child.\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset breakpoints in the child as appropriate. */
|
||||||
|
follow_inferior_reset_breakpoints ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
switch_to_thread (parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TARGET_WAITKIND_SPURIOUS:
|
||||||
|
/* Nothing to follow. */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
internal_error (__FILE__, __LINE__,
|
||||||
|
"Unexpected pending_follow.kind %d\n",
|
||||||
|
tp->pending_follow.kind);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return should_resume;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -987,8 +1119,6 @@ resume (int step, enum target_signal sig)
|
|||||||
{
|
{
|
||||||
int should_resume = 1;
|
int should_resume = 1;
|
||||||
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
|
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
|
||||||
|
|
||||||
/* Note that these must be reset if we follow a fork below. */
|
|
||||||
struct regcache *regcache = get_current_regcache ();
|
struct regcache *regcache = get_current_regcache ();
|
||||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||||
struct thread_info *tp = inferior_thread ();
|
struct thread_info *tp = inferior_thread ();
|
||||||
@ -1058,31 +1188,6 @@ a command like `return' or `jump' to continue execution."));
|
|||||||
if (step)
|
if (step)
|
||||||
step = maybe_software_singlestep (gdbarch, pc);
|
step = maybe_software_singlestep (gdbarch, pc);
|
||||||
|
|
||||||
/* If there were any forks/vforks/execs that were caught and are
|
|
||||||
now to be followed, then do so. */
|
|
||||||
switch (pending_follow.kind)
|
|
||||||
{
|
|
||||||
case TARGET_WAITKIND_FORKED:
|
|
||||||
case TARGET_WAITKIND_VFORKED:
|
|
||||||
pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
|
|
||||||
if (follow_fork ())
|
|
||||||
should_resume = 0;
|
|
||||||
|
|
||||||
/* Following a child fork will change our notion of current
|
|
||||||
thread. */
|
|
||||||
tp = inferior_thread ();
|
|
||||||
regcache = get_current_regcache ();
|
|
||||||
gdbarch = get_regcache_arch (regcache);
|
|
||||||
pc = regcache_read_pc (regcache);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Install inferior's terminal modes. */
|
|
||||||
target_terminal_inferior ();
|
|
||||||
|
|
||||||
if (should_resume)
|
if (should_resume)
|
||||||
{
|
{
|
||||||
ptid_t resume_ptid;
|
ptid_t resume_ptid;
|
||||||
@ -1164,6 +1269,9 @@ a command like `return' or `jump' to continue execution."));
|
|||||||
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
|
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Install inferior's terminal modes. */
|
||||||
|
target_terminal_inferior ();
|
||||||
|
|
||||||
/* Avoid confusing the next resume, if the next stop/resume
|
/* Avoid confusing the next resume, if the next stop/resume
|
||||||
happens to apply to another thread. */
|
happens to apply to another thread. */
|
||||||
tp->stop_signal = TARGET_SIGNAL_0;
|
tp->stop_signal = TARGET_SIGNAL_0;
|
||||||
@ -1305,12 +1413,26 @@ prepare_to_proceed (int step)
|
|||||||
void
|
void
|
||||||
proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
||||||
{
|
{
|
||||||
struct regcache *regcache = get_current_regcache ();
|
struct regcache *regcache;
|
||||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
struct gdbarch *gdbarch;
|
||||||
struct thread_info *tp;
|
struct thread_info *tp;
|
||||||
CORE_ADDR pc = regcache_read_pc (regcache);
|
CORE_ADDR pc;
|
||||||
int oneproc = 0;
|
int oneproc = 0;
|
||||||
|
|
||||||
|
/* If we're stopped at a fork/vfork, follow the branch set by the
|
||||||
|
"set follow-fork-mode" command; otherwise, we'll just proceed
|
||||||
|
resuming the current thread. */
|
||||||
|
if (!follow_fork ())
|
||||||
|
{
|
||||||
|
/* The target for some reason decided not to resume. */
|
||||||
|
normal_stop ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regcache = get_current_regcache ();
|
||||||
|
gdbarch = get_regcache_arch (regcache);
|
||||||
|
pc = regcache_read_pc (regcache);
|
||||||
|
|
||||||
if (step > 0)
|
if (step > 0)
|
||||||
step_start_function = find_pc_function (pc);
|
step_start_function = find_pc_function (pc);
|
||||||
if (step < 0)
|
if (step < 0)
|
||||||
@ -1517,9 +1639,6 @@ init_wait_for_inferior (void)
|
|||||||
|
|
||||||
breakpoint_init_inferior (inf_starting);
|
breakpoint_init_inferior (inf_starting);
|
||||||
|
|
||||||
/* The first resume is not following a fork/vfork/exec. */
|
|
||||||
pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */
|
|
||||||
|
|
||||||
clear_proceed_status ();
|
clear_proceed_status ();
|
||||||
|
|
||||||
stepping_past_singlestep_breakpoint = 0;
|
stepping_past_singlestep_breakpoint = 0;
|
||||||
@ -1698,8 +1817,6 @@ infrun_thread_stop_requested (ptid_t ptid)
|
|||||||
iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
|
iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nullify_last_target_wait_ptid (void);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
infrun_thread_thread_exit (struct thread_info *tp, int silent)
|
infrun_thread_thread_exit (struct thread_info *tp, int silent)
|
||||||
{
|
{
|
||||||
@ -2407,10 +2524,6 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
case TARGET_WAITKIND_VFORKED:
|
case TARGET_WAITKIND_VFORKED:
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_FORKED\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_FORKED\n");
|
||||||
pending_follow.kind = ecs->ws.kind;
|
|
||||||
|
|
||||||
pending_follow.fork_event.parent_pid = ecs->ptid;
|
|
||||||
pending_follow.fork_event.child_pid = ecs->ws.value.related_pid;
|
|
||||||
|
|
||||||
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
||||||
{
|
{
|
||||||
@ -2439,6 +2552,11 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
detach_breakpoints (child_pid);
|
detach_breakpoints (child_pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* In case the event is caught by a catchpoint, remember that
|
||||||
|
the event is to be followed at the next resume of the thread,
|
||||||
|
and not immediately. */
|
||||||
|
ecs->event_thread->pending_follow = ecs->ws;
|
||||||
|
|
||||||
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
|
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
|
||||||
|
|
||||||
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
||||||
@ -2448,8 +2566,19 @@ handle_inferior_event (struct execution_control_state *ecs)
|
|||||||
/* If no catchpoint triggered for this, then keep going. */
|
/* If no catchpoint triggered for this, then keep going. */
|
||||||
if (ecs->random_signal)
|
if (ecs->random_signal)
|
||||||
{
|
{
|
||||||
|
int should_resume;
|
||||||
|
|
||||||
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
|
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
|
||||||
keep_going (ecs);
|
|
||||||
|
should_resume = follow_fork ();
|
||||||
|
|
||||||
|
ecs->event_thread = inferior_thread ();
|
||||||
|
ecs->ptid = inferior_ptid;
|
||||||
|
|
||||||
|
if (should_resume)
|
||||||
|
keep_going (ecs);
|
||||||
|
else
|
||||||
|
stop_stepping (ecs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
|
ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
|
||||||
|
@ -575,19 +575,17 @@ static int
|
|||||||
linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||||
{
|
{
|
||||||
sigset_t prev_mask;
|
sigset_t prev_mask;
|
||||||
ptid_t last_ptid;
|
|
||||||
struct target_waitstatus last_status;
|
|
||||||
int has_vforked;
|
int has_vforked;
|
||||||
int parent_pid, child_pid;
|
int parent_pid, child_pid;
|
||||||
|
|
||||||
block_child_signals (&prev_mask);
|
block_child_signals (&prev_mask);
|
||||||
|
|
||||||
get_last_target_status (&last_ptid, &last_status);
|
has_vforked = (inferior_thread ()->pending_follow.kind
|
||||||
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
|
== TARGET_WAITKIND_VFORKED);
|
||||||
parent_pid = ptid_get_lwp (last_ptid);
|
parent_pid = ptid_get_lwp (inferior_ptid);
|
||||||
if (parent_pid == 0)
|
if (parent_pid == 0)
|
||||||
parent_pid = ptid_get_pid (last_ptid);
|
parent_pid = ptid_get_pid (inferior_ptid);
|
||||||
child_pid = PIDGET (last_status.value.related_pid);
|
child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
|
||||||
|
|
||||||
if (! follow_child)
|
if (! follow_child)
|
||||||
{
|
{
|
||||||
@ -625,7 +623,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
/* Add process to GDB's tables. */
|
/* Add process to GDB's tables. */
|
||||||
child_inf = add_inferior (child_pid);
|
child_inf = add_inferior (child_pid);
|
||||||
|
|
||||||
parent_inf = find_inferior_pid (GET_PID (last_ptid));
|
parent_inf = current_inferior ();
|
||||||
child_inf->attach_flag = parent_inf->attach_flag;
|
child_inf->attach_flag = parent_inf->attach_flag;
|
||||||
copy_terminal_info (child_inf, parent_inf);
|
copy_terminal_info (child_inf, parent_inf);
|
||||||
|
|
||||||
@ -692,21 +690,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct thread_info *last_tp = find_thread_pid (last_ptid);
|
|
||||||
struct thread_info *tp;
|
struct thread_info *tp;
|
||||||
char child_pid_spelling[40];
|
|
||||||
struct inferior *parent_inf, *child_inf;
|
struct inferior *parent_inf, *child_inf;
|
||||||
|
|
||||||
/* Copy user stepping state to the new inferior thread. */
|
|
||||||
struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
|
|
||||||
CORE_ADDR step_range_start = last_tp->step_range_start;
|
|
||||||
CORE_ADDR step_range_end = last_tp->step_range_end;
|
|
||||||
struct frame_id step_frame_id = last_tp->step_frame_id;
|
|
||||||
|
|
||||||
/* Otherwise, deleting the parent would get rid of this
|
|
||||||
breakpoint. */
|
|
||||||
last_tp->step_resume_breakpoint = NULL;
|
|
||||||
|
|
||||||
/* Before detaching from the parent, remove all breakpoints from it. */
|
/* Before detaching from the parent, remove all breakpoints from it. */
|
||||||
remove_breakpoints ();
|
remove_breakpoints ();
|
||||||
|
|
||||||
@ -723,7 +709,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
|
|
||||||
child_inf = add_inferior (child_pid);
|
child_inf = add_inferior (child_pid);
|
||||||
|
|
||||||
parent_inf = find_inferior_pid (GET_PID (last_ptid));
|
parent_inf = current_inferior ();
|
||||||
child_inf->attach_flag = parent_inf->attach_flag;
|
child_inf->attach_flag = parent_inf->attach_flag;
|
||||||
copy_terminal_info (child_inf, parent_inf);
|
copy_terminal_info (child_inf, parent_inf);
|
||||||
|
|
||||||
@ -772,15 +758,6 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
|||||||
|
|
||||||
linux_nat_switch_fork (inferior_ptid);
|
linux_nat_switch_fork (inferior_ptid);
|
||||||
check_for_thread_db ();
|
check_for_thread_db ();
|
||||||
|
|
||||||
tp = inferior_thread ();
|
|
||||||
tp->step_resume_breakpoint = step_resume_breakpoint;
|
|
||||||
tp->step_range_start = step_range_start;
|
|
||||||
tp->step_range_end = step_range_end;
|
|
||||||
tp->step_frame_id = step_frame_id;
|
|
||||||
|
|
||||||
/* Reset breakpoints in the child as appropriate. */
|
|
||||||
follow_inferior_reset_breakpoints ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_child_signals_mask (&prev_mask);
|
restore_child_signals_mask (&prev_mask);
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
2009-05-24 Pedro Alves <pedro@codesourcery.com>
|
||||||
|
|
||||||
|
* gdb.threads/fork-thread-pending.c: New.
|
||||||
|
* gdb.threads/fork-thread-pending.exp: New.
|
||||||
|
|
||||||
2009-05-21 Jan Kratochvil <jan.kratochvil@redhat.com>
|
2009-05-21 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
* gdb.dwarf2/dw2-strp.exp (p a_string2, ptype a_string2): New.
|
* gdb.dwarf2/dw2-strp.exp (p a_string2, ptype a_string2): New.
|
||||||
|
109
gdb/testsuite/gdb.threads/fork-thread-pending.c
Normal file
109
gdb/testsuite/gdb.threads/fork-thread-pending.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#define NUMTHREADS 10
|
||||||
|
|
||||||
|
volatile int done = 0;
|
||||||
|
|
||||||
|
static void *
|
||||||
|
start (void *arg)
|
||||||
|
{
|
||||||
|
while (!done)
|
||||||
|
usleep (100);
|
||||||
|
assert (0);
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
thread_function (void *arg)
|
||||||
|
{
|
||||||
|
int x = * (int *) arg;
|
||||||
|
|
||||||
|
printf ("Thread <%d> executing\n", x);
|
||||||
|
|
||||||
|
while (!done)
|
||||||
|
usleep (100);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
thread_forker (void *arg)
|
||||||
|
{
|
||||||
|
int x = * (int *) arg;
|
||||||
|
pid_t pid;
|
||||||
|
int rv;
|
||||||
|
int i;
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
printf ("Thread forker <%d> executing\n", x);
|
||||||
|
|
||||||
|
switch ((pid = fork ()))
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
assert (0);
|
||||||
|
default:
|
||||||
|
wait (&rv);
|
||||||
|
done = 1;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
i = pthread_create (&thread, NULL, start, NULL);
|
||||||
|
assert (i == 0);
|
||||||
|
i = pthread_join (thread, NULL);
|
||||||
|
assert (i == 0);
|
||||||
|
|
||||||
|
assert (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
pthread_t threads[NUMTHREADS];
|
||||||
|
int args[NUMTHREADS];
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
/* Create a few threads that do mostly nothing, and then one that
|
||||||
|
forks. */
|
||||||
|
for (j = 0; j < NUMTHREADS - 1; ++j)
|
||||||
|
{
|
||||||
|
args[j] = j;
|
||||||
|
pthread_create (&threads[j], NULL, thread_function, &args[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
args[j] = j;
|
||||||
|
pthread_create (&threads[j], NULL, thread_forker, &args[j]);
|
||||||
|
|
||||||
|
for (j = 0; j < NUMTHREADS; ++j)
|
||||||
|
{
|
||||||
|
pthread_join (threads[j], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
128
gdb/testsuite/gdb.threads/fork-thread-pending.exp
Normal file
128
gdb/testsuite/gdb.threads/fork-thread-pending.exp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# Copyright (C) 2009 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# There's no support for `set follow-fork-mode' in the remote
|
||||||
|
# protocol.
|
||||||
|
if { [is_remote target] } {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only GNU/Linux is known to support `set follow-fork-mode child'.
|
||||||
|
#
|
||||||
|
if { ! [istarget "*-*-linux*"] } {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
set testfile fork-thread-pending
|
||||||
|
set srcfile ${testfile}.c
|
||||||
|
set binfile ${objdir}/${subdir}/${testfile}
|
||||||
|
|
||||||
|
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_exit
|
||||||
|
gdb_start
|
||||||
|
gdb_reinitialize_dir $srcdir/$subdir
|
||||||
|
|
||||||
|
gdb_load ${binfile}
|
||||||
|
if ![runto_main] then {
|
||||||
|
fail "Can't run to main"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test "set follow-fork-mode child" "" "1, set follow-fork-mode child"
|
||||||
|
gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "1, insert fork catchpoint"
|
||||||
|
gdb_breakpoint "start" "" "1, set breakpoint at start"
|
||||||
|
|
||||||
|
gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
|
||||||
|
|
||||||
|
gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
|
||||||
|
|
||||||
|
gdb_test "thread 2" "" "1, switched away from event thread"
|
||||||
|
|
||||||
|
gdb_test "continue" "Not resuming.*" "1, refused to resume"
|
||||||
|
|
||||||
|
set test "1, followed to the child, found one thread"
|
||||||
|
gdb_test_multiple "info threads" "metest" {
|
||||||
|
-re " Thread .* Thread .*$gdb_prompt $" {
|
||||||
|
fail "$test"
|
||||||
|
}
|
||||||
|
-re " Thread .*$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
fail "$test (unknown output)"
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
fail "$test (timeout)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test "continue" "Breakpoint 3, start.*" "1, get to the spawned thread in fork child"
|
||||||
|
|
||||||
|
set test "1, followed to the child, found two threads"
|
||||||
|
gdb_test_multiple "info threads" "$test" {
|
||||||
|
-re " Thread .* Thread .* Thread .*$gdb_prompt $" {
|
||||||
|
fail "$test"
|
||||||
|
}
|
||||||
|
-re " Thread .* Thread .*$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
fail "$test (unknown output)"
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
fail "$test (timeout)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start over, but this time, don't switch away from the fork event thread.
|
||||||
|
|
||||||
|
gdb_exit
|
||||||
|
gdb_start
|
||||||
|
gdb_reinitialize_dir $srcdir/$subdir
|
||||||
|
|
||||||
|
gdb_load ${binfile}
|
||||||
|
if ![runto_main] then {
|
||||||
|
fail "Can't run to main"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test "set follow-fork-mode child" "" "2, set follow-fork-mode child"
|
||||||
|
gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "2, insert fork catchpoint"
|
||||||
|
gdb_breakpoint "start"
|
||||||
|
|
||||||
|
gdb_test "continue" "Catchpoint.*" "2, get to the fork event"
|
||||||
|
|
||||||
|
gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "2, multiple threads found"
|
||||||
|
|
||||||
|
gdb_test "continue" "Breakpoint 3, start.*" "2, get to the spawned thread in fork child"
|
||||||
|
|
||||||
|
set test "2, followed to the child, found two threads"
|
||||||
|
gdb_test_multiple "info threads" "$test" {
|
||||||
|
-re " Thread .* Thread .* Thread .*$gdb_prompt $" {
|
||||||
|
fail "$test"
|
||||||
|
}
|
||||||
|
-re " Thread .* Thread .*$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
fail "$test (unknown output)"
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
fail "$test (timeout)"
|
||||||
|
}
|
||||||
|
}
|
37
gdb/thread.c
37
gdb/thread.c
@ -141,6 +141,28 @@ init_thread_list (void)
|
|||||||
thread_list = NULL;
|
thread_list = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Allocate a new thread with target id PTID and add it to the thread
|
||||||
|
list. */
|
||||||
|
|
||||||
|
static struct thread_info *
|
||||||
|
new_thread (ptid_t ptid)
|
||||||
|
{
|
||||||
|
struct thread_info *tp;
|
||||||
|
|
||||||
|
tp = xcalloc (1, sizeof (*tp));
|
||||||
|
|
||||||
|
tp->ptid = ptid;
|
||||||
|
tp->num = ++highest_thread_num;
|
||||||
|
tp->next = thread_list;
|
||||||
|
thread_list = tp;
|
||||||
|
|
||||||
|
/* Nothing to follow yet. */
|
||||||
|
tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
|
||||||
|
tp->state_ = THREAD_STOPPED;
|
||||||
|
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
|
||||||
struct thread_info *
|
struct thread_info *
|
||||||
add_thread_silent (ptid_t ptid)
|
add_thread_silent (ptid_t ptid)
|
||||||
{
|
{
|
||||||
@ -162,12 +184,7 @@ add_thread_silent (ptid_t ptid)
|
|||||||
|
|
||||||
if (ptid_equal (inferior_ptid, ptid))
|
if (ptid_equal (inferior_ptid, ptid))
|
||||||
{
|
{
|
||||||
tp = xmalloc (sizeof (*tp));
|
tp = new_thread (ptid);
|
||||||
memset (tp, 0, sizeof (*tp));
|
|
||||||
tp->ptid = minus_one_ptid;
|
|
||||||
tp->num = ++highest_thread_num;
|
|
||||||
tp->next = thread_list;
|
|
||||||
thread_list = tp;
|
|
||||||
|
|
||||||
/* Make switch_to_thread not read from the thread. */
|
/* Make switch_to_thread not read from the thread. */
|
||||||
tp->state_ = THREAD_EXITED;
|
tp->state_ = THREAD_EXITED;
|
||||||
@ -191,13 +208,7 @@ add_thread_silent (ptid_t ptid)
|
|||||||
delete_thread (ptid);
|
delete_thread (ptid);
|
||||||
}
|
}
|
||||||
|
|
||||||
tp = (struct thread_info *) xmalloc (sizeof (*tp));
|
tp = new_thread (ptid);
|
||||||
memset (tp, 0, sizeof (*tp));
|
|
||||||
tp->ptid = ptid;
|
|
||||||
tp->num = ++highest_thread_num;
|
|
||||||
tp->next = thread_list;
|
|
||||||
thread_list = tp;
|
|
||||||
|
|
||||||
observer_notify_new_thread (tp);
|
observer_notify_new_thread (tp);
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
|
Reference in New Issue
Block a user