* inferiors.c (change_inferior_id): Add comment.
authorDaniel Jacobowitz <drow@false.org>
Mon, 2 Jul 2007 15:35:36 +0000 (15:35 +0000)
committerDaniel Jacobowitz <drow@false.org>
Mon, 2 Jul 2007 15:35:36 +0000 (15:35 +0000)
* 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.

gdb/gdbserver/ChangeLog
gdb/gdbserver/inferiors.c
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/mem-break.c
gdb/gdbserver/mem-break.h
gdb/gdbserver/thread-db.c

index 0f25df7..7b78653 100644 (file)
@@ -1,3 +1,22 @@
+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.
index 63587e5..6262d7e 100644 (file)
@@ -64,6 +64,11 @@ 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)
index 067a632..f37e407 100644 (file)
@@ -68,6 +68,7 @@ static void linux_resume_one_process (struct inferior_list_entry *entry,
 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
 {
@@ -224,7 +225,8 @@ linux_attach (unsigned long pid)
 
   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;
 
@@ -286,13 +288,36 @@ linux_detach_one_process (struct inferior_list_entry *entry)
   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;
 }
 
@@ -332,7 +357,8 @@ check_removed_breakpoint (struct process_info *event_child)
     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);
@@ -345,7 +371,8 @@ check_removed_breakpoint (struct process_info *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;
@@ -817,6 +844,13 @@ send_sigstop (struct inferior_list_entry *entry)
      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;
     }
@@ -852,7 +886,9 @@ wait_for_sigstop (struct inferior_list_entry *entry)
       && 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;
index aa3bfe1..d2e6c14 100644 (file)
@@ -92,8 +92,12 @@ struct process_info
   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
index 19233e0..20281af 100644 (file)
@@ -283,3 +283,12 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
        memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len);
     }
 }
+
+/* Delete all breakpoints.  */
+
+void
+delete_all_breakpoints (void)
+{
+  while (breakpoints)
+    delete_breakpoint (breakpoints);
+}
index 9c8dc28..7353807 100644 (file)
@@ -72,4 +72,8 @@ void check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len);
 
 void set_breakpoint_data (const unsigned char *bp_data, int bp_len);
 
+/* Delete all breakpoints.  */
+
+void delete_all_breakpoints (void);
+
 #endif /* MEM_BREAK_H */
index f3b8561..5566e0e 100644 (file)
@@ -41,7 +41,7 @@ static struct ps_prochandle proc_handle;
 /* 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 *
@@ -135,15 +135,11 @@ thread_db_create_event (CORE_ADDR where)
 {
   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
@@ -156,10 +152,9 @@ thread_db_create_event (CORE_ADDR where)
   /* 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 */
 
@@ -230,43 +225,73 @@ thread_db_enable_reporting ()
   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)
@@ -287,7 +312,6 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
 
   process = inferior_target_data (inferior);
 
-found:
   new_thread_notify (ti_p->ti_tid);
 
   process->tid = ti_p->ti_tid;
@@ -325,6 +349,12 @@ thread_db_find_new_threads (void)
 {
   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,
@@ -359,7 +389,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
 
   process = get_thread_process (thread);
   if (!process->thread_known)
-    thread_db_find_new_threads ();
+    find_first_thread ();
   if (!process->thread_known)
     return TD_NOTHR;