+2007-07-02 Daniel Jacobowitz <dan@codesourcery.com>
+
+ * inferiors.c (change_inferior_id): Add comment.
+ * linux-low.c (check_removed_breakpoint): Add an early
+ prototype. Improve debug output.
+ (linux_attach): Doc update.
+ (linux_detach_one_process, linux_detach): Clean up before releasing
+ each process.
+ (send_sigstop, wait_for_sigstop): Improve comments and debug output.
+ * linux-low.h (struct process_info): Doc improvement.
+ * mem-break.c (delete_all_breakpoints): New.
+ * mem-break.h (delete_all_breakpoints): New prototype.
+ * thread-db.c (find_first_thread): New.
+ (thread_db_create_event): Call it instead of
+ thread_db_find_new_threads. Clean up unused variables.
+ (maybe_attach_thread): Remove first thread handling.
+ (thread_db_find_new_threads): Use find_first_thread.
+ (thread_db_get_tls_address): Likewise.
+
2007-06-27 Daniel Jacobowitz <dan@codesourcery.com>
* thread-db.c (thread_db_find_new_threads): Add prototype.
}
}
+/* 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)
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);
struct pending_signals
{
linux_attach_lwp (pid, pid);
- /* Don't ignore the initial SIGSTOP if we just attached to this process. */
+ /* Don't ignore the initial SIGSTOP if we just attached to this process.
+ It will be collected by wait shortly. */
process = (struct process_info *) find_inferior_id (&all_processes, pid);
process->stop_expected = 0;
struct thread_info *thread = (struct thread_info *) entry;
struct process_info *process = get_thread_process (thread);
+ /* Make sure the process isn't stopped at a breakpoint that's
+ no longer there. */
+ check_removed_breakpoint (process);
+
+ /* If this process is stopped but is expecting a SIGSTOP, then make
+ sure we take care of that now. This isn't absolutely guaranteed
+ to collect the SIGSTOP, but is fairly likely to. */
+ if (process->stop_expected)
+ {
+ /* Clear stop_expected, so that the SIGSTOP will be reported. */
+ process->stop_expected = 0;
+ if (process->stopped)
+ linux_resume_one_process (&process->head, 0, 0, NULL);
+ linux_wait_for_event (thread);
+ }
+
+ /* Flush any pending changes to the process's registers. */
+ regcache_invalidate_one ((struct inferior_list_entry *)
+ get_process_thread (process));
+
+ /* Finally, let it resume. */
ptrace (PTRACE_DETACH, pid_of (process), 0, 0);
}
static int
linux_detach (void)
{
+ delete_all_breakpoints ();
for_each_inferior (&all_threads, linux_detach_one_process);
+ clear_inferiors ();
return 0;
}
return 0;
if (debug_threads)
- fprintf (stderr, "Checking for breakpoint.\n");
+ fprintf (stderr, "Checking for breakpoint in process %ld.\n",
+ event_child->lwpid);
saved_inferior = current_inferior;
current_inferior = get_process_thread (event_child);
if (stop_pc != event_child->pending_stop_pc)
{
if (debug_threads)
- fprintf (stderr, "Ignoring, PC was changed.\n");
+ fprintf (stderr, "Ignoring, PC was changed. Old PC was 0x%08llx\n",
+ event_child->pending_stop_pc);
event_child->pending_is_breakpoint = 0;
current_inferior = saved_inferior;
send another. */
if (process->stop_expected)
{
+ if (debug_threads)
+ fprintf (stderr, "Have pending sigstop for process %ld\n",
+ process->lwpid);
+
+ /* We clear the stop_expected flag so that wait_for_sigstop
+ will receive the SIGSTOP event (instead of silently resuming and
+ waiting again). It'll be reset below. */
process->stop_expected = 0;
return;
}
&& WSTOPSIG (wstat) != SIGSTOP)
{
if (debug_threads)
- fprintf (stderr, "Stopped with non-sigstop signal\n");
+ fprintf (stderr, "Process %ld (thread %ld) "
+ "stopped with non-sigstop status %06x\n",
+ process->lwpid, process->tid, wstat);
process->status_pending_p = 1;
process->status_pending = wstat;
process->stop_expected = 1;
unsigned long lwpid;
unsigned long tid;
- /* If this flag is set, the next SIGSTOP will be ignored (the process will
- be immediately resumed). */
+ /* If this flag is set, the next SIGSTOP will be ignored (the
+ process will be immediately resumed). This means that either we
+ sent the SIGSTOP to it ourselves and got some other pending event
+ (so the SIGSTOP is still pending), or that we stopped the
+ inferior implicitly via PTRACE_ATTACH and have not waited for it
+ yet. */
int stop_expected;
/* If this flag is set, the process is known to be stopped right now (stop
memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len);
}
}
+
+/* Delete all breakpoints. */
+
+void
+delete_all_breakpoints (void)
+{
+ while (breakpoints)
+ delete_breakpoint (breakpoints);
+}
void set_breakpoint_data (const unsigned char *bp_data, int bp_len);
+/* Delete all breakpoints. */
+
+void delete_all_breakpoints (void);
+
#endif /* MEM_BREAK_H */
/* Connection to the libthread_db library. */
static td_thragent_t *thread_agent;
-static void thread_db_find_new_threads (void);
+static int find_first_thread (void);
static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
static char *
{
td_event_msg_t msg;
td_err_e err;
- struct inferior_linux_data *tdata;
- struct thread_info *inferior;
struct process_info *process;
if (debug_threads)
fprintf (stderr, "Thread creation event.\n");
- tdata = inferior_target_data (current_inferior);
-
/* FIXME: This assumes we don't get another event.
In the LinuxThreads implementation, this is safe,
because all events come from the manager thread
/* If we do not know about the main thread yet, this would be a good time to
find it. We need to do this to pick up the main thread before any newly
created threads. */
- inferior = (struct thread_info *) all_threads.head;
- process = get_thread_process (inferior);
+ process = get_thread_process (current_inferior);
if (process->thread_known == 0)
- thread_db_find_new_threads ();
+ find_first_thread ();
/* msg.event == TD_EVENT_CREATE */
return 1;
}
-static void
-maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
+static int
+find_first_thread (void)
{
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
td_err_e err;
struct thread_info *inferior;
struct process_info *process;
- /* If we are attaching to our first thread, things are a little
- different. */
- if (all_threads.head == all_threads.tail)
+ inferior = (struct thread_info *) all_threads.head;
+ process = get_thread_process (inferior);
+ if (process->thread_known)
+ return 1;
+
+ /* Get information about the one thread we know we have. */
+ 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));
+
+ err = td_thr_get_info (&th, &ti);
+ if (err != TD_OK)
+ error ("Cannot get first thread info: %s", thread_db_err_str (err));
+
+ if (debug_threads)
+ fprintf (stderr, "Found first 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);
+
+ /* 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)
{
- inferior = (struct thread_info *) all_threads.head;
- process = get_thread_process (inferior);
-
- if (process->thread_known == 0)
- {
- /* If the new thread ID is zero, a final thread ID will be
- available later. Do not enable thread debugging yet. */
- if (ti_p->ti_tid == 0)
- {
- 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));
- return;
- }
-
- if (process->lwpid != ti_p->ti_lid)
- fatal ("PID mismatch! Expected %ld, got %ld",
- (long) process->lwpid, (long) ti_p->ti_lid);
-
- /* Switch to indexing the threads list by TID. */
- change_inferior_id (&all_threads, ti_p->ti_tid);
- goto found;
- }
+ 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);
+
+ new_thread_notify (ti.ti_tid);
+
+ process->tid = ti.ti_tid;
+ process->lwpid = ti.ti_lid;
+ process->thread_known = 1;
+ 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;
+}
+
+static void
+maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
+{
+ td_err_e err;
+ struct thread_info *inferior;
+ struct process_info *process;
+
inferior = (struct thread_info *) find_inferior_id (&all_threads,
ti_p->ti_tid);
if (inferior != NULL)
process = inferior_target_data (inferior);
-found:
new_thread_notify (ti_p->ti_tid);
process->tid = ti_p->ti_tid;
{
td_err_e err;
+ /* 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)
+ return;
+
/* Iterate over all user-space threads to discover new threads. */
err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL,
TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
process = get_thread_process (thread);
if (!process->thread_known)
- thread_db_find_new_threads ();
+ find_first_thread ();
if (!process->thread_known)
return TD_NOTHR;