* inferiors.c (change_inferior_id): Delete.
(add_pid_to_list, pull_pid_from_list): New. * linux-low.c (PTRACE_SETOPTIONS, PTRACE_GETEVENTMSG) (PTRACE_O_TRACESYSGOOD, PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) (PTRACE_O_TRACECLONE, PTRACE_O_TRACEEXEC, PTRACE_O_TRACEVFORKDONE) (PTRACE_O_TRACEEXIT, PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK) (PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC, PTRACE_EVENT_VFORK_DONE) (PTRACE_EVENT_EXIT, __WALL): Provide default definitions. (stopped_pids, thread_db_active, must_set_ptrace_flags): New variables. (using_threads): Always set to 1. (handle_extended_wait): New. (add_process): Do not set TID. (linux_create_inferior): Set must_set_ptrace_flags. (linux_attach_lwp): Remove TID argument. Do not check using_threads. Use PTRACE_SETOPTIONS. Call new_thread_notify. Update all callers. (linux_thread_alive): Rename TID argument to LWPID. (linux_wait_for_process): Handle unknown processes. Do not use TID. (linux_wait_for_event): Do not use TID or check using_threads. Update call to dead_thread_notify. Call handle_extended_wait. (linux_create_inferior): Use PTRACE_SETOPTIONS. (send_sigstop): Delete sigstop_sent. (wait_for_sigstop): Avoid TID. (linux_supports_tracefork_flag, linux_tracefork_child, my_waitpid) (linux_test_for_tracefork): New. (linux_lookup_signals): Use thread_db_active and linux_supports_tracefork_flag. (initialize_low): Use thread_db_active and linux_test_for_tracefork. * linux-low.h (get_process_thread): Avoid TID. (struct process_ifo): Move thread_known and tid to the end. Remove sigstop_sent. (linux_attach_lwp, thread_db_init): Update prototypes. * server.h (change_inferior_id): Delete prototype. (add_pid_to_list, pull_pid_from_list): New prototypes. * thread-db.c (thread_db_use_events): New. (find_first_thread): Rename to... (find_one_thread): ...this. Update callers and messages. Do not call fatal. Check thread_db_use_events. Do not call change_inferior_id or new_thread_notify. (maybe_attach_thread): Update. Do not call new_thread_notify. (thread_db_init): Set thread_db_use_events. Check use_events. * utils.c (fatal, warning): Correct message prefix.
This commit is contained in:
parent
3d5f6d122e
commit
24a09b5f37
7 changed files with 392 additions and 106 deletions
|
@ -1,3 +1,47 @@
|
|||
2007-10-23 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* inferiors.c (change_inferior_id): Delete.
|
||||
(add_pid_to_list, pull_pid_from_list): New.
|
||||
* linux-low.c (PTRACE_SETOPTIONS, PTRACE_GETEVENTMSG)
|
||||
(PTRACE_O_TRACESYSGOOD, PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK)
|
||||
(PTRACE_O_TRACECLONE, PTRACE_O_TRACEEXEC, PTRACE_O_TRACEVFORKDONE)
|
||||
(PTRACE_O_TRACEEXIT, PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK)
|
||||
(PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC, PTRACE_EVENT_VFORK_DONE)
|
||||
(PTRACE_EVENT_EXIT, __WALL): Provide default definitions.
|
||||
(stopped_pids, thread_db_active, must_set_ptrace_flags): New variables.
|
||||
(using_threads): Always set to 1.
|
||||
(handle_extended_wait): New.
|
||||
(add_process): Do not set TID.
|
||||
(linux_create_inferior): Set must_set_ptrace_flags.
|
||||
(linux_attach_lwp): Remove TID argument. Do not check using_threads.
|
||||
Use PTRACE_SETOPTIONS. Call new_thread_notify. Update all callers.
|
||||
(linux_thread_alive): Rename TID argument to LWPID.
|
||||
(linux_wait_for_process): Handle unknown processes. Do not use TID.
|
||||
(linux_wait_for_event): Do not use TID or check using_threads. Update
|
||||
call to dead_thread_notify. Call handle_extended_wait.
|
||||
(linux_create_inferior): Use PTRACE_SETOPTIONS.
|
||||
(send_sigstop): Delete sigstop_sent.
|
||||
(wait_for_sigstop): Avoid TID.
|
||||
(linux_supports_tracefork_flag, linux_tracefork_child, my_waitpid)
|
||||
(linux_test_for_tracefork): New.
|
||||
(linux_lookup_signals): Use thread_db_active and
|
||||
linux_supports_tracefork_flag.
|
||||
(initialize_low): Use thread_db_active and linux_test_for_tracefork.
|
||||
* linux-low.h (get_process_thread): Avoid TID.
|
||||
(struct process_ifo): Move thread_known and tid to the end. Remove
|
||||
sigstop_sent.
|
||||
(linux_attach_lwp, thread_db_init): Update prototypes.
|
||||
* server.h (change_inferior_id): Delete prototype.
|
||||
(add_pid_to_list, pull_pid_from_list): New prototypes.
|
||||
* thread-db.c (thread_db_use_events): New.
|
||||
(find_first_thread): Rename to...
|
||||
(find_one_thread): ...this. Update callers and messages. Do not
|
||||
call fatal. Check thread_db_use_events. Do not call
|
||||
change_inferior_id or new_thread_notify.
|
||||
(maybe_attach_thread): Update. Do not call new_thread_notify.
|
||||
(thread_db_init): Set thread_db_use_events. Check use_events.
|
||||
* utils.c (fatal, warning): Correct message prefix.
|
||||
|
||||
2007-10-15 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* Makefile.in (clean): Remove new files.
|
||||
|
|
|
@ -65,21 +65,6 @@ for_each_inferior (struct inferior_list *list,
|
|||
}
|
||||
}
|
||||
|
||||
/* When debugging a single-threaded program, the threads list (such as
|
||||
it is) is indexed by PID. When debugging a multi-threaded program,
|
||||
we index by TID. This ugly routine replaces the
|
||||
first-debugged-thread's PID with its TID. */
|
||||
|
||||
void
|
||||
change_inferior_id (struct inferior_list *list,
|
||||
unsigned long new_id)
|
||||
{
|
||||
if (list->head != list->tail)
|
||||
error ("tried to change thread ID after multiple threads are created");
|
||||
|
||||
list->head->id = new_id;
|
||||
}
|
||||
|
||||
void
|
||||
remove_inferior (struct inferior_list *list,
|
||||
struct inferior_list_entry *entry)
|
||||
|
@ -318,3 +303,32 @@ clear_inferiors (void)
|
|||
clear_list (&all_threads);
|
||||
clear_list (&all_dlls);
|
||||
}
|
||||
|
||||
/* Two utility functions for a truly degenerate inferior_list: a simple
|
||||
PID listing. */
|
||||
|
||||
void
|
||||
add_pid_to_list (struct inferior_list *list, unsigned long pid)
|
||||
{
|
||||
struct inferior_list_entry *new_entry;
|
||||
|
||||
new_entry = malloc (sizeof (struct inferior_list_entry));
|
||||
new_entry->id = pid;
|
||||
add_inferior_to_list (list, new_entry);
|
||||
}
|
||||
|
||||
int
|
||||
pull_pid_from_list (struct inferior_list *list, unsigned long pid)
|
||||
{
|
||||
struct inferior_list_entry *new_entry;
|
||||
|
||||
new_entry = find_inferior_id (list, pid);
|
||||
if (new_entry == NULL)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
remove_inferior (list, new_entry);
|
||||
free (new_entry);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,26 +44,67 @@
|
|||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
/* If the system headers did not provide the constants, hard-code the normal
|
||||
values. */
|
||||
#ifndef PTRACE_EVENT_FORK
|
||||
|
||||
#define PTRACE_SETOPTIONS 0x4200
|
||||
#define PTRACE_GETEVENTMSG 0x4201
|
||||
|
||||
/* options set using PTRACE_SETOPTIONS */
|
||||
#define PTRACE_O_TRACESYSGOOD 0x00000001
|
||||
#define PTRACE_O_TRACEFORK 0x00000002
|
||||
#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_VFORK_DONE 5
|
||||
#define PTRACE_EVENT_EXIT 6
|
||||
|
||||
#endif /* PTRACE_EVENT_FORK */
|
||||
|
||||
/* We can't always assume that this flag is available, but all systems
|
||||
with the ptrace event handlers also have __WALL, so it's safe to use
|
||||
in some contexts. */
|
||||
#ifndef __WALL
|
||||
#define __WALL 0x40000000 /* Wait for any child. */
|
||||
#endif
|
||||
|
||||
#ifdef __UCLIBC__
|
||||
#if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__))
|
||||
#define HAS_NOMMU
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead,
|
||||
however. This requires changing the ID in place when we go from !using_threads
|
||||
to using_threads, immediately.
|
||||
/* ``all_threads'' is keyed by the LWP ID, which we use as the GDB protocol
|
||||
representation of the thread ID.
|
||||
|
||||
``all_processes'' is keyed by the process ID - which on Linux is (presently)
|
||||
the same as the LWP ID. */
|
||||
|
||||
struct inferior_list all_processes;
|
||||
|
||||
/* A list of all unknown processes which receive stop signals. Some other
|
||||
process will presumably claim each of these as forked children
|
||||
momentarily. */
|
||||
|
||||
struct inferior_list stopped_pids;
|
||||
|
||||
/* FIXME this is a bit of a hack, and could be removed. */
|
||||
int stopping_threads;
|
||||
|
||||
/* FIXME make into a target method? */
|
||||
int using_threads;
|
||||
int using_threads = 1;
|
||||
static int thread_db_active;
|
||||
|
||||
static int must_set_ptrace_flags;
|
||||
|
||||
static void linux_resume_one_process (struct inferior_list_entry *entry,
|
||||
int step, int signal, siginfo_t *info);
|
||||
|
@ -71,6 +112,7 @@ static void linux_resume (struct thread_resume *resume_info);
|
|||
static void stop_all_processes (void);
|
||||
static int linux_wait_for_event (struct thread_info *child);
|
||||
static int check_removed_breakpoint (struct process_info *event_child);
|
||||
static void *add_process (unsigned long pid);
|
||||
|
||||
struct pending_signals
|
||||
{
|
||||
|
@ -91,6 +133,56 @@ static int use_regsets_p = 1;
|
|||
/* FIXME: Delete eventually. */
|
||||
#define inferior_pid (pid_of (get_thread_process (current_inferior)))
|
||||
|
||||
static void
|
||||
handle_extended_wait (struct process_info *event_child, int wstat)
|
||||
{
|
||||
int event = wstat >> 16;
|
||||
struct process_info *new_process;
|
||||
|
||||
if (event == PTRACE_EVENT_CLONE)
|
||||
{
|
||||
unsigned long new_pid;
|
||||
int ret, status;
|
||||
|
||||
ptrace (PTRACE_GETEVENTMSG, inferior_pid, 0, &new_pid);
|
||||
|
||||
/* If we haven't already seen the new PID stop, wait for it now. */
|
||||
if (! pull_pid_from_list (&stopped_pids, new_pid))
|
||||
{
|
||||
/* The new child has a pending SIGSTOP. We can't affect it until it
|
||||
hits the SIGSTOP, but we're already attached. */
|
||||
|
||||
do {
|
||||
ret = waitpid (new_pid, &status, __WALL);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
|
||||
if (ret == -1)
|
||||
perror_with_name ("waiting for new child");
|
||||
else if (ret != new_pid)
|
||||
warning ("wait returned unexpected PID %d", ret);
|
||||
else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP)
|
||||
warning ("wait returned unexpected status 0x%x", status);
|
||||
}
|
||||
|
||||
ptrace (PTRACE_SETOPTIONS, new_pid, 0, PTRACE_O_TRACECLONE);
|
||||
|
||||
new_process = (struct process_info *) add_process (new_pid);
|
||||
add_thread (new_pid, new_process, new_pid);
|
||||
new_thread_notify (thread_id_to_gdb_id (new_process->lwpid));
|
||||
|
||||
if (stopping_threads)
|
||||
new_process->stopped = 1;
|
||||
else
|
||||
ptrace (PTRACE_CONT, new_pid, 0, 0);
|
||||
|
||||
/* Always resume the current thread. If we are stopping
|
||||
threads, it will have a pending SIGSTOP; we may as well
|
||||
collect it now. */
|
||||
linux_resume_one_process (&event_child->head,
|
||||
event_child->stepping, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function should only be called if the process got a SIGTRAP.
|
||||
The SIGTRAP could mean several things.
|
||||
|
||||
|
@ -133,9 +225,6 @@ add_process (unsigned long pid)
|
|||
memset (process, 0, sizeof (*process));
|
||||
|
||||
process->head.id = pid;
|
||||
|
||||
/* Default to tid == lwpid == pid. */
|
||||
process->tid = pid;
|
||||
process->lwpid = pid;
|
||||
|
||||
add_inferior_to_list (&all_processes, &process->head);
|
||||
|
@ -180,6 +269,7 @@ linux_create_inferior (char *program, char **allargs)
|
|||
|
||||
new_process = add_process (pid);
|
||||
add_thread (pid, new_process, pid);
|
||||
must_set_ptrace_flags = 1;
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
@ -187,7 +277,7 @@ linux_create_inferior (char *program, char **allargs)
|
|||
/* Attach to an inferior process. */
|
||||
|
||||
void
|
||||
linux_attach_lwp (unsigned long pid, unsigned long tid)
|
||||
linux_attach_lwp (unsigned long pid)
|
||||
{
|
||||
struct process_info *new_process;
|
||||
|
||||
|
@ -198,13 +288,16 @@ linux_attach_lwp (unsigned long pid, unsigned long tid)
|
|||
fflush (stderr);
|
||||
|
||||
/* If we fail to attach to an LWP, just return. */
|
||||
if (!using_threads)
|
||||
if (all_threads.head == NULL)
|
||||
_exit (0177);
|
||||
return;
|
||||
}
|
||||
|
||||
ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE);
|
||||
|
||||
new_process = (struct process_info *) add_process (pid);
|
||||
add_thread (tid, new_process, pid);
|
||||
add_thread (pid, new_process, pid);
|
||||
new_thread_notify (thread_id_to_gdb_id (new_process->lwpid));
|
||||
|
||||
/* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
|
||||
brings it to a halt. We should ignore that SIGSTOP and resume the process
|
||||
|
@ -225,7 +318,7 @@ linux_attach (unsigned long pid)
|
|||
{
|
||||
struct process_info *process;
|
||||
|
||||
linux_attach_lwp (pid, pid);
|
||||
linux_attach_lwp (pid);
|
||||
|
||||
/* Don't ignore the initial SIGSTOP if we just attached to this process.
|
||||
It will be collected by wait shortly. */
|
||||
|
@ -338,9 +431,9 @@ linux_join (void)
|
|||
|
||||
/* Return nonzero if the given thread is still alive. */
|
||||
static int
|
||||
linux_thread_alive (unsigned long tid)
|
||||
linux_thread_alive (unsigned long lwpid)
|
||||
{
|
||||
if (find_inferior_id (&all_threads, tid) != NULL)
|
||||
if (find_inferior_id (&all_threads, lwpid) != NULL)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
@ -440,6 +533,7 @@ linux_wait_for_process (struct process_info **childp, int *wstatp)
|
|||
if (*childp != NULL)
|
||||
to_wait_for = (*childp)->lwpid;
|
||||
|
||||
retry:
|
||||
while (1)
|
||||
{
|
||||
ret = waitpid (to_wait_for, wstatp, WNOHANG);
|
||||
|
@ -474,6 +568,18 @@ linux_wait_for_process (struct process_info **childp, int *wstatp)
|
|||
if (to_wait_for == -1)
|
||||
*childp = (struct process_info *) find_inferior_id (&all_processes, ret);
|
||||
|
||||
/* If we didn't find a process, one of two things presumably happened:
|
||||
- A process we started and then detached from has exited. Ignore it.
|
||||
- A process we are controlling has forked and the new child's stop
|
||||
was reported to us by the kernel. Save its PID. */
|
||||
if (*childp == NULL && WIFSTOPPED (*wstatp))
|
||||
{
|
||||
add_pid_to_list (&stopped_pids, ret);
|
||||
goto retry;
|
||||
}
|
||||
else if (*childp == NULL)
|
||||
goto retry;
|
||||
|
||||
(*childp)->stopped = 1;
|
||||
(*childp)->pending_is_breakpoint = 0;
|
||||
|
||||
|
@ -483,7 +589,7 @@ linux_wait_for_process (struct process_info **childp, int *wstatp)
|
|||
&& WIFSTOPPED (*wstatp))
|
||||
{
|
||||
current_inferior = (struct thread_info *)
|
||||
find_inferior_id (&all_threads, (*childp)->tid);
|
||||
find_inferior_id (&all_threads, (*childp)->lwpid);
|
||||
/* For testing only; i386_stop_pc prints out a diagnostic. */
|
||||
if (the_low_target.get_pc != NULL)
|
||||
get_stop_pc ();
|
||||
|
@ -548,20 +654,19 @@ linux_wait_for_event (struct thread_info *child)
|
|||
error ("event from unknown child");
|
||||
|
||||
current_inferior = (struct thread_info *)
|
||||
find_inferior_id (&all_threads, event_child->tid);
|
||||
find_inferior_id (&all_threads, event_child->lwpid);
|
||||
|
||||
/* Check for thread exit. */
|
||||
if (using_threads && ! WIFSTOPPED (wstat))
|
||||
if (! WIFSTOPPED (wstat))
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "Thread %ld (LWP %ld) exiting\n",
|
||||
event_child->tid, event_child->head.id);
|
||||
fprintf (stderr, "LWP %ld exiting\n", event_child->head.id);
|
||||
|
||||
/* If the last thread is exiting, just return. */
|
||||
if (all_threads.head == all_threads.tail)
|
||||
return wstat;
|
||||
|
||||
dead_thread_notify (event_child->tid);
|
||||
dead_thread_notify (thread_id_to_gdb_id (event_child->lwpid));
|
||||
|
||||
remove_inferior (&all_processes, &event_child->head);
|
||||
free (event_child);
|
||||
|
@ -577,8 +682,7 @@ linux_wait_for_event (struct thread_info *child)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (using_threads
|
||||
&& WIFSTOPPED (wstat)
|
||||
if (WIFSTOPPED (wstat)
|
||||
&& WSTOPSIG (wstat) == SIGSTOP
|
||||
&& event_child->stop_expected)
|
||||
{
|
||||
|
@ -590,6 +694,13 @@ linux_wait_for_event (struct thread_info *child)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
|
||||
&& wstat >> 16 != 0)
|
||||
{
|
||||
handle_extended_wait (event_child, wstat);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If GDB is not interested in this signal, don't stop other
|
||||
threads, and don't report it to GDB. Just resume the
|
||||
inferior right away. We do this for threading-related
|
||||
|
@ -601,18 +712,20 @@ linux_wait_for_event (struct thread_info *child)
|
|||
thread library? */
|
||||
if (WIFSTOPPED (wstat)
|
||||
&& !event_child->stepping
|
||||
&& ((using_threads && (WSTOPSIG (wstat) == __SIGRTMIN
|
||||
|| WSTOPSIG (wstat) == __SIGRTMIN + 1))
|
||||
|| (pass_signals[target_signal_from_host (WSTOPSIG (wstat))]
|
||||
&& (WSTOPSIG (wstat) != SIGSTOP
|
||||
|| !event_child->sigstop_sent))))
|
||||
&& (
|
||||
#ifdef USE_THREAD_DB
|
||||
(thread_db_active && (WSTOPSIG (wstat) == __SIGRTMIN
|
||||
|| WSTOPSIG (wstat) == __SIGRTMIN + 1))
|
||||
||
|
||||
#endif
|
||||
(pass_signals[target_signal_from_host (WSTOPSIG (wstat))]
|
||||
&& (WSTOPSIG (wstat) != SIGSTOP || !stopping_threads))))
|
||||
{
|
||||
siginfo_t info, *info_p;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "Ignored signal %d for %ld (LWP %ld).\n",
|
||||
WSTOPSIG (wstat), event_child->tid,
|
||||
event_child->head.id);
|
||||
fprintf (stderr, "Ignored signal %d for LWP %ld.\n",
|
||||
WSTOPSIG (wstat), event_child->head.id);
|
||||
|
||||
if (ptrace (PTRACE_GETSIGINFO, event_child->lwpid, 0, &info) == 0)
|
||||
info_p = &info;
|
||||
|
@ -769,6 +882,12 @@ retry:
|
|||
stop_all_processes ();
|
||||
disable_async_io ();
|
||||
|
||||
if (must_set_ptrace_flags)
|
||||
{
|
||||
ptrace (PTRACE_SETOPTIONS, inferior_pid, 0, PTRACE_O_TRACECLONE);
|
||||
must_set_ptrace_flags = 0;
|
||||
}
|
||||
|
||||
/* If we are waiting for a particular child, and it exited,
|
||||
linux_wait_for_event will return its exit status. Similarly if
|
||||
the last child exited. If this is not the last child, however,
|
||||
|
@ -863,7 +982,6 @@ send_sigstop (struct inferior_list_entry *entry)
|
|||
fprintf (stderr, "Sending sigstop to process %ld\n", process->head.id);
|
||||
|
||||
kill_lwp (process->head.id, SIGSTOP);
|
||||
process->sigstop_sent = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -880,7 +998,7 @@ wait_for_sigstop (struct inferior_list_entry *entry)
|
|||
saved_inferior = current_inferior;
|
||||
saved_tid = ((struct inferior_list_entry *) saved_inferior)->id;
|
||||
thread = (struct thread_info *) find_inferior_id (&all_threads,
|
||||
process->tid);
|
||||
process->lwpid);
|
||||
wstat = linux_wait_for_event (thread);
|
||||
|
||||
/* If we stopped with a non-SIGSTOP signal, save it for later
|
||||
|
@ -890,9 +1008,8 @@ wait_for_sigstop (struct inferior_list_entry *entry)
|
|||
&& WSTOPSIG (wstat) != SIGSTOP)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "Process %ld (thread %ld) "
|
||||
"stopped with non-sigstop status %06x\n",
|
||||
process->lwpid, process->tid, wstat);
|
||||
fprintf (stderr, "LWP %ld stopped with non-sigstop status %06x\n",
|
||||
process->lwpid, wstat);
|
||||
process->status_pending_p = 1;
|
||||
process->status_pending = wstat;
|
||||
process->stop_expected = 1;
|
||||
|
@ -1593,14 +1710,127 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int linux_supports_tracefork_flag;
|
||||
|
||||
/* A helper function for linux_test_for_tracefork, called after fork (). */
|
||||
|
||||
static void
|
||||
linux_tracefork_child (void)
|
||||
{
|
||||
ptrace (PTRACE_TRACEME, 0, 0, 0);
|
||||
kill (getpid (), SIGSTOP);
|
||||
fork ();
|
||||
_exit (0);
|
||||
}
|
||||
|
||||
/* Wrapper function for waitpid which handles EINTR. */
|
||||
|
||||
static int
|
||||
my_waitpid (int pid, int *status, int flags)
|
||||
{
|
||||
int ret;
|
||||
do
|
||||
{
|
||||
ret = waitpid (pid, status, flags);
|
||||
}
|
||||
while (ret == -1 && errno == EINTR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. Make
|
||||
sure that we can enable the option, and that it had the desired
|
||||
effect. */
|
||||
|
||||
static void
|
||||
linux_test_for_tracefork (void)
|
||||
{
|
||||
int child_pid, ret, status;
|
||||
long second_pid;
|
||||
|
||||
linux_supports_tracefork_flag = 0;
|
||||
|
||||
child_pid = fork ();
|
||||
if (child_pid == -1)
|
||||
perror_with_name ("fork");
|
||||
|
||||
if (child_pid == 0)
|
||||
linux_tracefork_child ();
|
||||
|
||||
ret = my_waitpid (child_pid, &status, 0);
|
||||
if (ret == -1)
|
||||
perror_with_name ("waitpid");
|
||||
else if (ret != child_pid)
|
||||
error ("linux_test_for_tracefork: waitpid: unexpected result %d.", ret);
|
||||
if (! WIFSTOPPED (status))
|
||||
error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status);
|
||||
|
||||
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
|
||||
if (ret != 0)
|
||||
{
|
||||
ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
warning ("linux_test_for_tracefork: failed to kill child");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = my_waitpid (child_pid, &status, 0);
|
||||
if (ret != child_pid)
|
||||
warning ("linux_test_for_tracefork: failed to wait for killed child");
|
||||
else if (!WIFSIGNALED (status))
|
||||
warning ("linux_test_for_tracefork: unexpected wait status 0x%x from "
|
||||
"killed child", status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
|
||||
if (ret != 0)
|
||||
warning ("linux_test_for_tracefork: failed to resume child");
|
||||
|
||||
ret = my_waitpid (child_pid, &status, 0);
|
||||
|
||||
if (ret == child_pid && WIFSTOPPED (status)
|
||||
&& status >> 16 == PTRACE_EVENT_FORK)
|
||||
{
|
||||
second_pid = 0;
|
||||
ret = ptrace (PTRACE_GETEVENTMSG, child_pid, 0, &second_pid);
|
||||
if (ret == 0 && second_pid != 0)
|
||||
{
|
||||
int second_status;
|
||||
|
||||
linux_supports_tracefork_flag = 1;
|
||||
my_waitpid (second_pid, &second_status, 0);
|
||||
ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
|
||||
if (ret != 0)
|
||||
warning ("linux_test_for_tracefork: failed to kill second child");
|
||||
my_waitpid (second_pid, &status, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
warning ("linux_test_for_tracefork: unexpected result from waitpid "
|
||||
"(%d, status 0x%x)", ret, status);
|
||||
|
||||
do
|
||||
{
|
||||
ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
|
||||
if (ret != 0)
|
||||
warning ("linux_test_for_tracefork: failed to kill child");
|
||||
my_waitpid (child_pid, &status, 0);
|
||||
}
|
||||
while (WIFSTOPPED (status));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
linux_look_up_symbols (void)
|
||||
{
|
||||
#ifdef USE_THREAD_DB
|
||||
if (using_threads)
|
||||
if (thread_db_active)
|
||||
return;
|
||||
|
||||
using_threads = thread_db_init ();
|
||||
thread_db_active = thread_db_init (!linux_supports_tracefork_flag);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1782,10 +2012,11 @@ linux_init_signals ()
|
|||
void
|
||||
initialize_low (void)
|
||||
{
|
||||
using_threads = 0;
|
||||
thread_db_active = 0;
|
||||
set_target_ops (&linux_target_ops);
|
||||
set_breakpoint_data (the_low_target.breakpoint,
|
||||
the_low_target.breakpoint_len);
|
||||
init_registers ();
|
||||
linux_init_signals ();
|
||||
linux_test_for_tracefork ();
|
||||
}
|
||||
|
|
|
@ -81,14 +81,12 @@ extern struct linux_target_ops the_low_target;
|
|||
#define get_thread_process(thr) (get_process (inferior_target_data (thr)))
|
||||
#define get_process_thread(proc) ((struct thread_info *) \
|
||||
find_inferior_id (&all_threads, \
|
||||
get_process (proc)->tid))
|
||||
get_process (proc)->lwpid))
|
||||
|
||||
struct process_info
|
||||
{
|
||||
struct inferior_list_entry head;
|
||||
int thread_known;
|
||||
unsigned long lwpid;
|
||||
unsigned long tid;
|
||||
|
||||
/* If this flag is set, the next SIGSTOP will be ignored (the
|
||||
process will be immediately resumed). This means that either we
|
||||
|
@ -105,10 +103,6 @@ struct process_info
|
|||
/* When stopped is set, the last wait status recorded for this process. */
|
||||
int last_status;
|
||||
|
||||
/* If this flag is set, we have sent a SIGSTOP to this process and are
|
||||
waiting for it to stop. */
|
||||
int sigstop_sent;
|
||||
|
||||
/* If this flag is set, STATUS_PENDING is a waitstatus that has not yet
|
||||
been reported. */
|
||||
int status_pending_p;
|
||||
|
@ -135,16 +129,19 @@ struct process_info
|
|||
|
||||
struct thread_resume *resume;
|
||||
|
||||
int thread_known;
|
||||
unsigned long tid;
|
||||
#ifdef HAVE_THREAD_DB_H
|
||||
/* The thread handle, used for e.g. TLS access. */
|
||||
/* The thread handle, used for e.g. TLS access. Only valid if
|
||||
THREAD_KNOWN is set. */
|
||||
td_thrhandle_t th;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct inferior_list all_processes;
|
||||
|
||||
void linux_attach_lwp (unsigned long pid, unsigned long tid);
|
||||
void linux_attach_lwp (unsigned long pid);
|
||||
|
||||
int thread_db_init (void);
|
||||
int thread_db_init (int use_events);
|
||||
int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
|
||||
CORE_ADDR load_module, CORE_ADDR *address);
|
||||
|
|
|
@ -137,8 +137,8 @@ void *inferior_target_data (struct thread_info *);
|
|||
void set_inferior_target_data (struct thread_info *, void *);
|
||||
void *inferior_regcache_data (struct thread_info *);
|
||||
void set_inferior_regcache_data (struct thread_info *, void *);
|
||||
void change_inferior_id (struct inferior_list *list,
|
||||
unsigned long new_id);
|
||||
void add_pid_to_list (struct inferior_list *list, unsigned long pid);
|
||||
int pull_pid_from_list (struct inferior_list *list, unsigned long pid);
|
||||
|
||||
void loaded_dll (const char *name, CORE_ADDR base_addr);
|
||||
void unloaded_dll (const char *name, CORE_ADDR base_addr);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
extern int debug_threads;
|
||||
|
||||
static int thread_db_use_events;
|
||||
|
||||
#ifdef HAVE_THREAD_DB_H
|
||||
#include <thread_db.h>
|
||||
#endif
|
||||
|
@ -39,7 +41,7 @@ static struct ps_prochandle proc_handle;
|
|||
/* Connection to the libthread_db library. */
|
||||
static td_thragent_t *thread_agent;
|
||||
|
||||
static int find_first_thread (void);
|
||||
static int find_one_thread (int);
|
||||
static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
|
||||
|
||||
static char *
|
||||
|
@ -152,7 +154,7 @@ thread_db_create_event (CORE_ADDR where)
|
|||
created threads. */
|
||||
process = get_thread_process (current_inferior);
|
||||
if (process->thread_known == 0)
|
||||
find_first_thread ();
|
||||
find_one_thread (process->lwpid);
|
||||
|
||||
/* msg.event == TD_EVENT_CREATE */
|
||||
|
||||
|
@ -224,7 +226,7 @@ thread_db_enable_reporting ()
|
|||
}
|
||||
|
||||
static int
|
||||
find_first_thread (void)
|
||||
find_one_thread (int lwpid)
|
||||
{
|
||||
td_thrhandle_t th;
|
||||
td_thrinfo_t ti;
|
||||
|
@ -232,54 +234,50 @@ find_first_thread (void)
|
|||
struct thread_info *inferior;
|
||||
struct process_info *process;
|
||||
|
||||
inferior = (struct thread_info *) all_threads.head;
|
||||
inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid);
|
||||
process = get_thread_process (inferior);
|
||||
if (process->thread_known)
|
||||
return 1;
|
||||
|
||||
/* Get information about the one thread we know we have. */
|
||||
/* Get information about this thread. */
|
||||
err = td_ta_map_lwp2thr (thread_agent, process->lwpid, &th);
|
||||
if (err != TD_OK)
|
||||
error ("Cannot get first thread handle: %s", thread_db_err_str (err));
|
||||
error ("Cannot get thread handle for LWP %d: %s",
|
||||
lwpid, thread_db_err_str (err));
|
||||
|
||||
err = td_thr_get_info (&th, &ti);
|
||||
if (err != TD_OK)
|
||||
error ("Cannot get first thread info: %s", thread_db_err_str (err));
|
||||
error ("Cannot get thread info for LWP %d: %s",
|
||||
lwpid, thread_db_err_str (err));
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "Found first thread %ld (LWP %d)\n",
|
||||
fprintf (stderr, "Found thread %ld (LWP %d)\n",
|
||||
ti.ti_tid, ti.ti_lid);
|
||||
|
||||
if (process->lwpid != ti.ti_lid)
|
||||
fatal ("PID mismatch! Expected %ld, got %ld",
|
||||
(long) process->lwpid, (long) ti.ti_lid);
|
||||
{
|
||||
warning ("PID mismatch! Expected %ld, got %ld",
|
||||
(long) process->lwpid, (long) ti.ti_lid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the new thread ID is zero, a final thread ID will be available
|
||||
later. Do not enable thread debugging yet. */
|
||||
if (ti.ti_tid == 0)
|
||||
if (thread_db_use_events)
|
||||
{
|
||||
err = td_thr_event_enable (&th, 1);
|
||||
if (err != TD_OK)
|
||||
error ("Cannot enable thread event reporting for %d: %s",
|
||||
ti.ti_lid, thread_db_err_str (err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Switch to indexing the threads list by TID. */
|
||||
change_inferior_id (&all_threads, ti.ti_tid);
|
||||
/* If the new thread ID is zero, a final thread ID will be available
|
||||
later. Do not enable thread debugging yet. */
|
||||
if (ti.ti_tid == 0)
|
||||
return 0;
|
||||
|
||||
new_thread_notify (ti.ti_tid);
|
||||
|
||||
process->tid = ti.ti_tid;
|
||||
process->lwpid = ti.ti_lid;
|
||||
process->thread_known = 1;
|
||||
process->tid = ti.ti_tid;
|
||||
process->th = th;
|
||||
|
||||
err = td_thr_event_enable (&th, 1);
|
||||
if (err != TD_OK)
|
||||
error ("Cannot enable thread event reporting for %d: %s",
|
||||
ti.ti_lid, thread_db_err_str (err));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -291,16 +289,16 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
|
|||
struct process_info *process;
|
||||
|
||||
inferior = (struct thread_info *) find_inferior_id (&all_threads,
|
||||
ti_p->ti_tid);
|
||||
ti_p->ti_lid);
|
||||
if (inferior != NULL)
|
||||
return;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "Attaching to thread %ld (LWP %d)\n",
|
||||
ti_p->ti_tid, ti_p->ti_lid);
|
||||
linux_attach_lwp (ti_p->ti_lid, ti_p->ti_tid);
|
||||
linux_attach_lwp (ti_p->ti_lid);
|
||||
inferior = (struct thread_info *) find_inferior_id (&all_threads,
|
||||
ti_p->ti_tid);
|
||||
ti_p->ti_lid);
|
||||
if (inferior == NULL)
|
||||
{
|
||||
warning ("Could not attach to thread %ld (LWP %d)\n",
|
||||
|
@ -310,17 +308,17 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
|
|||
|
||||
process = inferior_target_data (inferior);
|
||||
|
||||
new_thread_notify (ti_p->ti_tid);
|
||||
|
||||
process->tid = ti_p->ti_tid;
|
||||
process->lwpid = ti_p->ti_lid;
|
||||
|
||||
process->thread_known = 1;
|
||||
process->th = *th_p;
|
||||
err = td_thr_event_enable (th_p, 1);
|
||||
if (err != TD_OK)
|
||||
error ("Cannot enable thread event reporting for %d: %s",
|
||||
ti_p->ti_lid, thread_db_err_str (err));
|
||||
|
||||
if (thread_db_use_events)
|
||||
{
|
||||
err = td_thr_event_enable (th_p, 1);
|
||||
if (err != TD_OK)
|
||||
error ("Cannot enable thread event reporting for %d: %s",
|
||||
ti_p->ti_lid, thread_db_err_str (err));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -350,7 +348,7 @@ thread_db_find_new_threads (void)
|
|||
/* This function is only called when we first initialize thread_db.
|
||||
First locate the initial thread. If it is not ready for
|
||||
debugging yet, then stop. */
|
||||
if (find_first_thread () == 0)
|
||||
if (find_one_thread (all_threads.head->id) == 0)
|
||||
return;
|
||||
|
||||
/* Iterate over all user-space threads to discover new threads. */
|
||||
|
@ -387,7 +385,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
|
|||
|
||||
process = get_thread_process (thread);
|
||||
if (!process->thread_known)
|
||||
find_first_thread ();
|
||||
find_one_thread (process->lwpid);
|
||||
if (!process->thread_known)
|
||||
return TD_NOTHR;
|
||||
|
||||
|
@ -409,7 +407,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
|
|||
}
|
||||
|
||||
int
|
||||
thread_db_init ()
|
||||
thread_db_init (int use_events)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
@ -428,6 +426,8 @@ thread_db_init ()
|
|||
/* Allow new symbol lookups. */
|
||||
all_symbols_looked_up = 0;
|
||||
|
||||
thread_db_use_events = use_events;
|
||||
|
||||
err = td_ta_new (&proc_handle, &thread_agent);
|
||||
switch (err)
|
||||
{
|
||||
|
@ -438,7 +438,7 @@ thread_db_init ()
|
|||
case TD_OK:
|
||||
/* The thread library was detected. */
|
||||
|
||||
if (thread_db_enable_reporting () == 0)
|
||||
if (use_events && thread_db_enable_reporting () == 0)
|
||||
return 0;
|
||||
thread_db_find_new_threads ();
|
||||
thread_db_look_up_symbols ();
|
||||
|
|
|
@ -78,7 +78,7 @@ fatal (const char *string,...)
|
|||
{
|
||||
va_list args;
|
||||
va_start (args, string);
|
||||
fprintf (stderr, "gdb: ");
|
||||
fprintf (stderr, "gdbserver: ");
|
||||
vfprintf (stderr, string, args);
|
||||
fprintf (stderr, "\n");
|
||||
va_end (args);
|
||||
|
@ -91,7 +91,7 @@ warning (const char *string,...)
|
|||
{
|
||||
va_list args;
|
||||
va_start (args, string);
|
||||
fprintf (stderr, "gdb: ");
|
||||
fprintf (stderr, "gdbserver: ");
|
||||
vfprintf (stderr, string, args);
|
||||
fprintf (stderr, "\n");
|
||||
va_end (args);
|
||||
|
|
Loading…
Add table
Reference in a new issue