gdb: fix vfork with multiple threads vfork-fixes-2021-10-28
authorSimon Marchi <simon.marchi@efficios.com>
Fri, 8 Oct 2021 17:28:55 +0000 (13:28 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Thu, 28 Oct 2021 20:43:57 +0000 (16:43 -0400)
There is a problem with how a vfork happening in a multi-threaded
program is handled by GDB today.

When a program vforks, the parent thread is suspended by the kernel
until the child process exits or execs.  Specifically, in a
multi-threaded program, only the thread that called vfork is suspended,
other threads keep running freely. This is documented in the vfork man
page.

Let's suppose GDB is handling a vfork and the user's desire is to detach
from the child. Before detaching the child, GDB must remove the software
breakpoints inserted in the shared parent/child address space, in case
there's a breakpoint in the path the child is going to take (unlikely,
but possible). Otherwise the child could hit a breakpoint instruction
while running outside the control of GDB, which would make it crash. GDB
must also avoid re-inserting breakpoints in the parent as long as it
didn't receive the "vfork done" event (that is, when the child has
exited or execed): since the address space is shared with the child,
that would re-insert breakpoints in the child's process also. So what GDB
does is:

  1. Receive "vfork" event for the parent
  2. Remove breakpoints from address space
  3. Detach from the child thread
  4. Resume the parent
  5. Wait for and receive "vfork done" event for the parent
  6. Re-insert breakpoints
  7. Resume the parent

Step 4 is necessary in order for the kernel to generate the "vfork done"
event.  The kernel won't generate ptrace events for threads that are
ptrace-stopped.  But the theory behind this is that between steps 4 and 5,
the parent won't actually do any progress, because the kernel keeps it
suspended, waiting for the child to exit or exec, so it doesn't matter
if breakpoints are not inserted.

Now, the problem is when the program is multi-threaded. In step 4, GDB
resumes all threads of the parent. The thread that did the vfork stays
suspended, so that's fine. But other threads are running freely while
breakpoints are removed, which is a problem because they could miss a
breakpoint that they should have hit.

The problem is present with all-stop and non-stop targets.  The only
difference is that with an all-stop targets, the other threads are
stopped when the target reports the vfork event, and are resumed when
resuming the parent.  With a non-stop target, the other threads are
simply never stopped.

With all-stop targets, GDB should therefore only resume the thread that
has called vfork.  This thread won't make progress but will be able to
receive the vfork-done event.  With non-stop targets, GDB should
actively stop the other threads before removing the breakpoints.  This
has similarities with how in-line steps are done.

FIXME: finish commit message

 - When a vfork happens and the child is detached (is out of control of
   GDB), only the thread that called vfork is resumed, waiting for
   vfork-done
 - When a vfork happens and the child is in control of GDB (an inferior
   was created for it), only resume it, waiting for it to exec or
   exit.

Change-Id: Iec4dc8dbebabcee8061dcaad53c3e72a4092ebe5

16 files changed:
gdb/debug.c
gdb/infcmd.c
gdb/inferior.h
gdb/infrun.c
gdb/infrun.h
gdb/linux-nat.c
gdb/remote.c
gdb/testsuite/gdb.threads/next-fork-other-thread.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/next-fork-other-thread.exp [new file with mode: 0644]
gdb/testsuite/gdb.threads/vfork-multi-thread.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/vfork-multi-thread.exp [new file with mode: 0644]
gdb/testsuite/lib/gdb.exp
gdb/testsuite/lib/gdbserver-support.exp
gdbserver/debug.cc
gdbserver/linux-low.cc
gdbserver/server.cc

index f7dfee1cfeb28556fb3d6727f242175467441983..1796b1afe968bd3aa721c719c801dcfe6b39ffe5 100644 (file)
@@ -30,5 +30,9 @@ int debug_print_depth = 0;
 void
 debug_vprintf (const char *fmt, va_list ap)
 {
-  vfprintf_unfiltered (gdb_stdlog, fmt, ap);
+  static FILE *out = nullptr;
+  if (!out)
+    out = fopen("/tmp/gdb.txt", "w");
+  vfprintf (out, fmt, ap);
+  fflush (out);
 }
index 8190ba36565eca56c1942a074af1c05ac30dba21..702c796cbf6c6e36399d2aaf2c5217eee3203e84 100644 (file)
@@ -2525,7 +2525,7 @@ attach_post_wait (int from_tty, enum attach_post_wait_mode mode)
        {
          struct thread_info *lowest = inferior_thread ();
 
-         stop_all_threads ();
+         stop_all_threads ("attaching");
 
          /* It's not defined which thread will report the attach
             stop.  For consistency, always select the thread with
index f61b5889e858f69e9c4f3f140f929e1a2ae7dfa1..ae5660813a04f4612c52f990ebedc3ecdf66068f 100644 (file)
@@ -508,10 +508,8 @@ public:
      exits or execs.  */
   bool pending_detach = false;
 
-  /* True if this inferior is a vfork parent waiting for a vfork child
-     not under our control to be done with the shared memory region,
-     either by exiting or execing.  */
-  bool waiting_for_vfork_done = false;
+  /* FIX COMMENT */
+  thread_info *thread_waiting_for_vfork_done = nullptr;
 
   /* True if we're in the process of detaching from this inferior.  */
   bool detaching = false;
index 9469b74af397dac9a3d47e362a49107e4e25cd67..afdca46432cafa08be07ffad748699af534ee7d6 100644 (file)
@@ -95,6 +95,12 @@ static void resume (gdb_signal sig);
 
 static void wait_for_inferior (inferior *inf);
 
+static void restart_threads (struct thread_info *event_thread, inferior *inf);
+
+static bool start_step_over (void);
+
+static bool step_over_info_valid_p (void);
+
 /* Asynchronous signal handler registered as event loop source for
    when we have pending events ready to be passed to the core.  */
 static struct async_event_handler *infrun_async_inferior_event_token;
@@ -541,7 +547,9 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
             insert breakpoints, so that we can debug it.  A
             subsequent child exec or exit is enough to know when does
             the child stops using the parent's address space.  */
-         parent_inf->waiting_for_vfork_done = detach_fork;
+         gdb_assert (parent_inf->thread_waiting_for_vfork_done == nullptr);
+         parent_inf->thread_waiting_for_vfork_done
+           = detach_fork ? inferior_thread () : nullptr;
          parent_inf->pspace->breakpoints_not_allowed = detach_fork;
        }
     }
@@ -603,7 +611,7 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
            child_inf->pending_detach = 0;
            parent_inf->vfork_child = child_inf;
            parent_inf->pending_detach = detach_fork;
-           parent_inf->waiting_for_vfork_done = 0;
+           parent_inf->thread_waiting_for_vfork_done = nullptr;
          }
        else if (detach_fork)
          {
@@ -769,6 +777,10 @@ follow_fork ()
        parent = inferior_ptid;
        child = tp->pending_follow.value.related_pid;
 
+       if (tp->pending_follow.kind == TARGET_WAITKIND_VFORKED
+           && target_is_non_stop_p ())
+         stop_all_threads ("handling vfork", tp->inf);
+
        process_stratum_target *parent_targ = tp->inf->process_target ();
        /* Set up inferior(s) as specified by the caller, and tell the
           target to do whatever is necessary to follow either parent
@@ -1045,6 +1057,28 @@ handle_vfork_child_exec_or_exit (int exec)
     }
 }
 
+static void
+handle_vfork_done (thread_info *event_thread)
+{
+  if (event_thread->inf->thread_waiting_for_vfork_done == nullptr)
+    return;
+
+  gdb_assert (event_thread->inf->thread_waiting_for_vfork_done == event_thread);
+  event_thread->inf->thread_waiting_for_vfork_done = nullptr;
+  current_inferior ()->pspace->breakpoints_not_allowed = 0;
+
+  INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
+  if (target_is_non_stop_p ())
+    {
+      scoped_restore_current_thread restore_thread;
+
+      insert_breakpoints ();
+      restart_threads (event_thread, event_thread->inf);
+      start_step_over ();
+    }
+}
+
 /* Enum strings for "set|show follow-exec-mode".  */
 
 static const char follow_exec_mode_new[] = "new";
@@ -1373,7 +1407,6 @@ step_over_info_valid_p (void)
          || stepping_past_nonsteppable_watchpoint ());
 }
 
-\f
 /* Displaced stepping.  */
 
 /* In non-stop debugging mode, we must take special care to manage
@@ -1897,6 +1930,12 @@ start_step_over (void)
          continue;
        }
 
+      if (tp->inf->thread_waiting_for_vfork_done)
+       {
+         /* FIXME COMMENT */
+         continue;
+       }
+
       /* Remove thread from the THREADS_TO_STEP chain.  If anything goes wrong
         while we try to prepare the displaced step, we don't add it back to
         the global step over chain.  This is to avoid a thread staying in the
@@ -2133,8 +2172,46 @@ internal_resume_ptid (int user_step)
      return a wildcard ptid.  */
   if (target_is_non_stop_p ())
     return inferior_ptid;
-  else
-    return user_visible_resume_ptid (user_step);
+
+  /* The rest of the function assumes non-stop==off and
+     target-non-stop==off.  */
+
+  /* If a thread in the resumption set is waiting for a vfork-done event (the
+     vfork child is not under GDB's control), resume just that thread.
+
+     If the target_resume interface was more flexible, we could be smarter
+     here when schedule-multiple is on .  For example, imagine 3 inferiors with
+     2 threads each (1.1, 1.2, 2.1, 2.2, 3.1 and 3.2).  Threads 2.1 and 3.2 are
+     both waiting for a vfork-done event.  Then we could ask the target(s) to
+     resume:
+
+      - All threads of inferior 1
+      - Thread 2.1
+      - Thread 3.2
+
+     Since we don't have that flexibility, just resume the first thread waiting
+     for a vfork-done event we find (e.g. thread 2.1).  */
+  if (sched_multi)
+    {
+      for (inferior *inf : all_non_exited_inferiors ())
+       if (inf->thread_waiting_for_vfork_done != nullptr)
+         return inf->thread_waiting_for_vfork_done->ptid;
+    }
+  else if (current_inferior ()->thread_waiting_for_vfork_done != nullptr)
+    return current_inferior ()->thread_waiting_for_vfork_done->ptid;
+
+  /* If an inferior (so, under GDB's control) is a vfork child of another
+     continue just that inferior.  */
+  if (sched_multi)
+    {
+      for (inferior *inf : all_non_exited_inferiors ())
+       if (inf->vfork_parent != nullptr)
+         return ptid_t (inf->pid);
+    }
+  else if (current_inferior ()->vfork_parent != nullptr)
+    return ptid_t (current_inferior ()->pid);
+
+  return user_visible_resume_ptid (user_step);
 }
 
 /* Wrapper for target_resume, that handles infrun-specific
@@ -2179,6 +2256,9 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig)
   else
     target_pass_signals (signal_pass);
 
+  infrun_debug_printf ("resume_ptid=%s, step=%d, sig=%s",
+                      resume_ptid.to_string ().c_str (),
+                      step, gdb_signal_to_symbol_string (sig));
   target_resume (resume_ptid, step, sig);
 
   if (target_can_async_p ())
@@ -2196,7 +2276,6 @@ resume_1 (enum gdb_signal sig)
   struct gdbarch *gdbarch = regcache->arch ();
   struct thread_info *tp = inferior_thread ();
   const address_space *aspace = regcache->aspace ();
-  ptid_t resume_ptid;
   /* This represents the user's step vs continue request.  When
      deciding whether "set scheduler-locking step" applies, it's the
      user's intention that counts.  */
@@ -2209,6 +2288,8 @@ resume_1 (enum gdb_signal sig)
 
   gdb_assert (!tp->stop_requested);
   gdb_assert (!thread_is_in_step_over_chain (tp));
+  gdb_assert (tp->inf->thread_waiting_for_vfork_done == nullptr
+             || tp->inf->thread_waiting_for_vfork_done == tp);
 
   if (tp->suspend.waitstatus_pending_p)
     {
@@ -2248,7 +2329,7 @@ resume_1 (enum gdb_signal sig)
   /* Depends on stepped_breakpoint.  */
   step = currently_stepping (tp);
 
-  if (current_inferior ()->waiting_for_vfork_done)
+  if (current_inferior ()->thread_waiting_for_vfork_done != nullptr)
     {
       /* Don't try to single-step a vfork parent that is waiting for
         the child to get out of the shared memory region (by exec'ing
@@ -2340,7 +2421,7 @@ resume_1 (enum gdb_signal sig)
              insert_single_step_breakpoint (gdbarch, aspace, pc);
              insert_breakpoints ();
 
-             resume_ptid = internal_resume_ptid (user_step);
+             ptid_t resume_ptid = internal_resume_ptid (user_step);
              do_target_resume (resume_ptid, false, GDB_SIGNAL_0);
              tp->resumed = true;
              return;
@@ -2368,7 +2449,7 @@ resume_1 (enum gdb_signal sig)
       && use_displaced_stepping (tp)
       && !step_over_info_valid_p ()
       && sig == GDB_SIGNAL_0
-      && !current_inferior ()->waiting_for_vfork_done)
+      && current_inferior ()->thread_waiting_for_vfork_done == nullptr)
     {
       displaced_step_prepare_status prepare_status
        = displaced_step_prepare (tp);
@@ -2385,7 +2466,7 @@ resume_1 (enum gdb_signal sig)
          /* Fallback to stepping over the breakpoint in-line.  */
 
          if (target_is_non_stop_p ())
-           stop_all_threads ();
+           stop_all_threads ("displaced stepping falling back on inline stepping");
 
          set_step_over_info (regcache->aspace (),
                              regcache_read_pc (regcache), 0, tp->global_num);
@@ -2464,6 +2545,7 @@ resume_1 (enum gdb_signal sig)
   gdb_assert (!(thread_has_single_step_breakpoints_set (tp) && step));
 
   /* Decide the set of threads to ask the target to resume.  */
+  ptid_t resume_ptid;
   if (tp->control.trap_expected)
     {
       /* We're allowing a thread to run past a breakpoint it has
@@ -3057,7 +3139,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
   CORE_ADDR pc;
   struct execution_control_state ecss;
   struct execution_control_state *ecs = &ecss;
-  bool started;
 
   /* If we're stopped at a fork/vfork, follow the branch set by the
      "set follow-fork-mode" command; otherwise, we'll just proceed
@@ -3206,7 +3287,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
   {
     scoped_disable_commit_resumed disable_commit_resumed ("proceeding");
 
-    started = start_step_over ();
+    bool displaced_step_started = start_step_over ();
 
     if (step_over_info_valid_p ())
       {
@@ -3214,7 +3295,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
           other thread was already doing one.  In either case, don't
           resume anything else until the step-over is finished.  */
       }
-    else if (started && !target_is_non_stop_p ())
+    else if (displaced_step_started && !target_is_non_stop_p ())
       {
        /* A new displaced stepping sequence was started.  In all-stop,
           we can't talk to the target anymore until it next stops.  */
@@ -3253,6 +3334,21 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
                continue;
              }
 
+           if (tp->inf->thread_waiting_for_vfork_done != nullptr
+               && tp != tp->inf->thread_waiting_for_vfork_done)
+             {
+               infrun_debug_printf ("[%s] a thread of this inferior is waiting for vfork-done",
+                                    tp->ptid.to_string ().c_str ());
+               continue;
+             }
+
+           //if (tp->inf->pending_detach)
+           //  {
+               //infrun_debug_printf ("[%s] inferior pending detach",
+               //                   tp->ptid.to_string ().c_str ());
+               //continue;
+            // }
+
            infrun_debug_printf ("resuming %s",
                                 target_pid_to_str (tp->ptid).c_str ());
 
@@ -3263,7 +3359,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
              error (_("Command aborted."));
          }
       }
-    else if (!cur_thr->resumed && !thread_is_in_step_over_chain (cur_thr))
+    else if (!cur_thr->resumed
+            && !thread_is_in_step_over_chain (cur_thr)
+            && !(non_stop && cur_thr->inf->thread_waiting_for_vfork_done))
       {
        /* The thread wasn't started, and isn't queued, run it now.  */
        reset_ecs (ecs, cur_thr);
@@ -3763,7 +3861,6 @@ struct wait_one_event
 };
 
 static bool handle_one (const wait_one_event &event);
-static void restart_threads (struct thread_info *event_thread);
 
 /* Prepare and stabilize the inferior for detaching it.  E.g.,
    detaching while a thread is displaced stepping is a recipe for
@@ -3815,7 +3912,7 @@ prepare_for_detach (void)
                 previously-stepping thread, since that one is still
                 running).  */
              if (!step_over_info_valid_p ())
-               restart_threads (thr);
+               restart_threads (thr, nullptr);
            }
        }
     }
@@ -4956,7 +5053,7 @@ handle_one (const wait_one_event &event)
 /* See infrun.h.  */
 
 void
-stop_all_threads (void)
+stop_all_threads (const char *reason, inferior *inf)
 {
   /* We may need multiple passes to discover all threads.  */
   int pass;
@@ -4964,13 +5061,16 @@ stop_all_threads (void)
 
   gdb_assert (exists_non_stop_target ());
 
-  infrun_debug_printf ("starting");
+  INFRUN_SCOPED_DEBUG_START_END ("reason=%s, inf=%d", reason, inf != nullptr ? inf->num : -1);
 
   scoped_restore_current_thread restore_thread;
 
   /* Enable thread events of all targets.  */
   for (auto *target : all_non_exited_process_targets ())
     {
+      if (inf != nullptr && inf->process_target () != target)
+       continue;
+
       switch_to_target_no_thread (target);
       target_thread_events (true);
     }
@@ -4980,6 +5080,9 @@ stop_all_threads (void)
       /* Disable thread events of all targets.  */
       for (auto *target : all_non_exited_process_targets ())
        {
+         if (inf != nullptr && inf->process_target () != target)
+           continue;
+
          switch_to_target_no_thread (target);
          target_thread_events (false);
        }
@@ -5004,6 +5107,9 @@ stop_all_threads (void)
 
          for (auto *target : all_non_exited_process_targets ())
            {
+             if (inf != nullptr && inf->process_target () != target)
+               continue;
+
              switch_to_target_no_thread (target);
              update_thread_list ();
            }
@@ -5012,6 +5118,9 @@ stop_all_threads (void)
             to tell the target to stop.  */
          for (thread_info *t : all_non_exited_threads ())
            {
+             if (inf != nullptr && t->inf != inf)
+               continue;
+
              /* For a single-target setting with an all-stop target,
                 we would not even arrive here.  For a multi-target
                 setting, until GDB is able to handle a mixture of
@@ -5625,8 +5734,8 @@ handle_inferior_event (struct execution_control_state *ecs)
            child->set_running (true);
 
          /* In non-stop mode, also resume the other branch.  */
-         if (!detach_fork && (non_stop
-                              || (sched_multi && target_is_non_stop_p ())))
+         if (!detach_fork
+             && (non_stop || (sched_multi && target_is_non_stop_p ())))
            {
              if (follow_child)
                switch_to_thread (parent);
@@ -5635,7 +5744,8 @@ handle_inferior_event (struct execution_control_state *ecs)
 
              ecs->event_thread = inferior_thread ();
              ecs->ptid = inferior_ptid;
-             keep_going (ecs);
+             if (current_inferior ()->vfork_child == nullptr)
+               keep_going (ecs);
            }
 
          if (follow_child)
@@ -5647,7 +5757,19 @@ handle_inferior_event (struct execution_control_state *ecs)
          ecs->ptid = inferior_ptid;
 
          if (should_resume)
-           keep_going (ecs);
+           {
+             if (ecs->ws.kind == TARGET_WAITKIND_VFORKED)
+               {
+                 if (current_inferior ()->vfork_child != nullptr
+                     && target_is_non_stop_p ())
+                   prepare_to_wait (ecs);
+                 else
+                   keep_going (ecs);
+               }
+             else
+                 if (!switch_back_to_stepped_thread (ecs))
+                   keep_going (ecs);
+           }
          else
            stop_waiting (ecs);
          return;
@@ -5661,15 +5783,20 @@ handle_inferior_event (struct execution_control_state *ecs)
 
       context_switch (ecs);
 
-      current_inferior ()->waiting_for_vfork_done = 0;
-      current_inferior ()->pspace->breakpoints_not_allowed = 0;
+      handle_vfork_done (ecs->event_thread);
+
+      gdb_assert (inferior_thread () == ecs->event_thread);
 
       if (handle_stop_requested (ecs))
        return;
 
       /* This also takes care of reinserting breakpoints in the
         previously locked inferior.  */
-      keep_going (ecs);
+      if (!switch_back_to_stepped_thread (ecs))
+       {
+         gdb_assert (inferior_thread () == ecs->event_thread);
+         keep_going (ecs);
+       }
       return;
 
     case TARGET_WAITKIND_EXECD:
@@ -5765,13 +5892,22 @@ handle_inferior_event (struct execution_control_state *ecs)
    ignored.  */
 
 static void
-restart_threads (struct thread_info *event_thread)
+restart_threads (struct thread_info *event_thread, inferior *inf)
 {
+  INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
+  gdb_assert (target_is_non_stop_p ());
+
+  scoped_restore_current_thread restore_thread;
+
   /* In case the instruction just stepped spawned a new thread.  */
   update_thread_list ();
 
   for (thread_info *tp : all_non_exited_threads ())
     {
+      if (inf != nullptr && tp->inf != inf)
+       continue;
+
       if (tp->inf->detaching)
        {
          infrun_debug_printf ("restart threads: [%s] inferior detaching",
@@ -5914,7 +6050,7 @@ finish_step_over (struct execution_control_state *ecs)
       context_switch (ecs);
       insert_breakpoints ();
 
-      restart_threads (ecs->event_thread);
+      restart_threads (ecs->event_thread, nullptr);
 
       /* If we have events pending, go through handle_inferior_event
         again, picking up a pending event at random.  This avoids
@@ -8016,7 +8152,7 @@ stop_waiting (struct execution_control_state *ecs)
   /* If all-stop, but there exists a non-stop target, stop all
      threads now that we're presenting the stop to the user.  */
   if (!non_stop && exists_non_stop_target ())
-    stop_all_threads ();
+    stop_all_threads ("presenting stop to user in all-stop");
 }
 
 /* Like keep_going, but passes the signal to the inferior, even if the
@@ -8118,7 +8254,7 @@ keep_going_pass_signal (struct execution_control_state *ecs)
         we're about to step over, otherwise other threads could miss
         it.  */
       if (step_over_info_valid_p () && target_is_non_stop_p ())
-       stop_all_threads ();
+       stop_all_threads ("starting in-line step-over");
 
       /* Stop stepping if inserting breakpoints fails.  */
       try
index 7ebb9fc9f4e646a587e4370b409116d61c7c580f..2d742849c5499cb77819b4ba6a98640edf06d6fe 100644 (file)
@@ -38,8 +38,8 @@ extern bool debug_infrun;
 
 /* Print "infrun" start/end debug statements.  */
 
-#define INFRUN_SCOPED_DEBUG_START_END(msg) \
-  scoped_debug_start_end (debug_infrun, "infrun", msg)
+#define INFRUN_SCOPED_DEBUG_START_END(msg, ...) \
+  scoped_debug_start_end (debug_infrun, "infrun", msg, ##__VA_ARGS__)
 
 /* Print "infrun" enter/exit debug statements.  */
 
@@ -138,7 +138,7 @@ extern void set_last_target_status (process_stratum_target *target, ptid_t ptid,
 extern void nullify_last_target_wait_ptid ();
 
 /* Stop all threads.  Only returns after everything is halted.  */
-extern void stop_all_threads (void);
+extern void stop_all_threads (const char *reason, struct inferior *inf = nullptr);
 
 extern void prepare_for_detach (void);
 
index 10fa8f57d4a28b5df028c4128fe41f99335e9506..3432256c2c37fe954caac297555fdf839f93e4b5 100644 (file)
@@ -1700,8 +1700,22 @@ resume_clear_callback (struct lwp_info *lp)
 static int
 resume_set_callback (struct lwp_info *lp)
 {
+  inferior *inf = find_inferior_ptid (linux_target, lp->ptid);
+  gdb_assert (inf != nullptr);
+
+  ///* When we are waiting for a detached vfork child to exec or exit,
+  //   breakpoints are removed, so only let the  */
+
+  //if (inf->thread_waiting_for_vfork_done != nullptr
+  //    && inf->thread_waiting_for_vfork_done->ptid != lp->ptid)
+  //  return 0;
+
+  //if (inf->pending_detach)
+  //  return 0;
+
   lp->resumed = 1;
   lp->last_resume_kind = resume_continue;
+
   return 0;
 }
 
@@ -2095,20 +2109,10 @@ linux_handle_extended_wait (struct lwp_info *lp, int status)
 
   if (event == PTRACE_EVENT_VFORK_DONE)
     {
-      if (current_inferior ()->waiting_for_vfork_done)
-       {
-         linux_nat_debug_printf
-           ("Got expected PTRACE_EVENT_VFORK_DONE from LWP %ld: stopping",
-            lp->ptid.lwp ());
-
-         ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
-         return 0;
-       }
-
       linux_nat_debug_printf
-       ("Got PTRACE_EVENT_VFORK_DONE from LWP %ld: ignoring", lp->ptid.lwp ());
-
-      return 1;
+       ("Got PTRACE_EVENT_VFORK_DONE from LWP %ld: stopping", lp->ptid.lwp ());
+      ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
+      return 0;
     }
 
   internal_error (__FILE__, __LINE__,
index c09d714087596fcfdcdc07f0c136120d9d98b687..d8c4adc1edd70e876d5112cc75426a388fe5140b 100644 (file)
@@ -957,6 +957,8 @@ public: /* Remote specific methods.  */
 
   void packet_command (const char *args, int from_tty);
 
+  void commit_resumed (ptid_t filter);
+
 private: /* data fields */
 
   /* The remote state.  Don't reference this directly.  Use the
@@ -2494,9 +2496,11 @@ remote_target::remote_add_inferior (bool fake_pid_p, int pid, int attached,
        }
       if (inf == nullptr)
        {
+         gdb_assert(0);
          /* Since all inferiors were already bound to a process, add
             a new inferior.  */
          inf = add_inferior_with_spaces ();
+
        }
       switch_to_inferior_no_thread (inf);
       inf->push_target (this);
@@ -4591,7 +4595,7 @@ remote_target::process_initial_stop_replies (int from_tty)
      the inferiors.  */
   if (!non_stop)
     {
-      stop_all_threads ();
+      stop_all_threads ("remote connect in all-stop");
 
       /* If all threads of an inferior were already stopped, we
         haven't setup the inferior yet.  */
@@ -6665,6 +6669,12 @@ vcont_builder::push_action (ptid_t ptid, bool step, gdb_signal siggnal)
 
 void
 remote_target::commit_resumed ()
+{
+  this->commit_resumed (minus_one_ptid);
+}
+
+void
+remote_target::commit_resumed (ptid_t filter)
 {
   /* If connected in all-stop mode, we'd send the remote resume
      request directly from remote_resume.  Likewise if
@@ -6722,14 +6732,14 @@ remote_target::commit_resumed ()
      (vCont;c).  We can still send process-wide wildcards though.  */
 
   /* Start by assuming a global wildcard (vCont;c) is possible.  */
-  bool may_global_wildcard_vcont = true;
+  bool may_global_wildcard_vcont = filter == minus_one_ptid;
 
   /* And assume every process is individually wildcard-able too.  */
   for (inferior *inf : all_non_exited_inferiors (this))
     {
       remote_inferior *priv = get_remote_inferior (inf);
 
-      priv->may_wildcard_vcont = true;
+      priv->may_wildcard_vcont = filter == minus_one_ptid || (filter.is_pid () && inf->pid == filter.pid ());
     }
 
   /* Check for any pending events (not reported or processed yet) and
@@ -6738,7 +6748,7 @@ remote_target::commit_resumed ()
 
   bool any_pending_vcont_resume = false;
 
-  for (thread_info *tp : all_non_exited_threads (this))
+  for (thread_info *tp : all_non_exited_threads (this, filter))
     {
       remote_thread_info *priv = get_remote_thread_info (tp);
 
@@ -6777,7 +6787,7 @@ remote_target::commit_resumed ()
   struct vcont_builder vcont_builder (this);
 
   /* Threads first.  */
-  for (thread_info *tp : all_non_exited_threads (this))
+  for (thread_info *tp : all_non_exited_threads (this, filter))
     {
       remote_thread_info *remote_thr = get_remote_thread_info (tp);
 
@@ -6907,7 +6917,7 @@ remote_target::remote_stop_ns (ptid_t ptid)
     }
 
   if (needs_commit)
-    commit_resumed ();
+    commit_resumed (ptid);
   else
     for (thread_info *tp : all_non_exited_threads (this, ptid))
       {
@@ -9990,6 +10000,9 @@ remote_target::getpkt_or_notif_sane_1 (gdb::char_vector *buf,
          /* We have tried hard enough, and just can't receive the
             packet/notification.  Give up.  */
          printf_unfiltered (_("Ignoring packet error, continuing...\n"));
+         gdb_assert(0);
+
+
 
          /* Skip the ack char if we're in no-ack mode.  */
          if (!rs->noack_mode)
@@ -10223,10 +10236,16 @@ remote_target::mourn_inferior ()
   discard_pending_stop_replies (current_inferior ());
 
   /* In 'target remote' mode with one inferior, we close the connection.  */
-  if (!rs->extended && number_of_live_inferiors (this) <= 1)
+  if (!rs->extended)
     {
-      remote_unpush_target (this);
-      return;
+      // needed to make the test not hang at exit with native-gdbserver, should be investigated
+      if (number_of_live_inferiors (this) <= 1)
+       {
+         remote_unpush_target (this);
+         return;
+       }
+      else
+       pop_all_targets_at_and_above (process_stratum);
     }
 
   /* In case we got here due to an error, but we're going to stay
@@ -12277,6 +12296,9 @@ remote_target::remote_hostio_open (inferior *inf, const char *filename,
   char *p = rs->buf.data ();
   int left = get_remote_packet_size () - 1;
 
+  REMOTE_SCOPED_DEBUG_ENTER_EXIT;
+  remote_debug_printf ("inf=%d, filename=%s", inf != nullptr ? inf->num : -1, filename);
+
   if (warn_if_slow)
     {
       static int warning_issued = 0;
diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.c b/gdb/testsuite/gdb.threads/next-fork-other-thread.c
new file mode 100644 (file)
index 0000000..fbd1691
--- /dev/null
@@ -0,0 +1,65 @@
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <limits.h>
+
+#define N_FORKERS 4
+
+static void *
+forker (void *arg)
+{
+  for (;;)
+    {
+      pid_t pid = FORK_FUNC ();
+
+      if (pid == 0)
+       _exit(0);
+
+      int ret;
+
+      do {
+        ret = waitpid (pid, NULL, 0);
+      } while (ret == EINTR);
+
+      assert (ret == pid);
+
+      usleep (1000 * 40);
+    }
+
+  return NULL;
+}
+
+static void
+sleep_a_bit (void)
+{
+  usleep (1000 * 50);
+}
+
+int
+main (void)
+{
+  int dummy;
+
+  alarm (60);
+
+  pthread_t thread[N_FORKERS];
+  for (int i = 0; i < N_FORKERS; ++i)
+    {
+      int ret = pthread_create (&thread[i], NULL, forker, NULL);
+      assert (ret == 0);
+    }
+
+  for (int i = 0; i < INT_MAX; ++i) /* for loop */
+    {
+      sleep_a_bit ();  /* break here */
+      sleep_a_bit ();  /* other line */
+    }
+
+  for (int i = 0; i < N_FORKERS; ++i)
+    pthread_join (thread[i], NULL);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp
new file mode 100644 (file)
index 0000000..3a53301
--- /dev/null
@@ -0,0 +1,77 @@
+# Copyright 2020-2021 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/>.
+
+standard_testfile
+
+set break_here_line [gdb_get_line_number "break here"]
+
+set timeout 99999
+
+foreach_with_prefix fork_func {fork vfork} {
+    set opts [list debug pthreads additional_flags=-DFORK_FUNC=${fork_func}]
+    if { [build_executable "failed to prepare" \
+           ${testfile}-${fork_func} ${srcfile} $opts] } {
+       return
+    }
+}
+
+proc do_test { fork_func target-non-stop non-stop displaced-stepping } {
+    save_vars { ::GDBFLAGS } {
+       append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\" -ex \"set non-stop ${non-stop}\""
+       clean_restart ${::binfile}-${fork_func}
+    }
+    #gdb_test "set debug infrun"
+    #gdb_test "set debug remote-packet-max-chars 4096"
+    #gdb_test "set debug remote 1"
+    #gdb_test "set remotetimeout 300"
+
+    gdb_test_no_output "set displaced-stepping ${displaced-stepping}"
+
+    if { ![runto_main] } {
+       return
+    }
+
+    # The "Detached after (v)fork" messages get in the way in non-stop, disable them.
+    gdb_test_no_output "set print inferior-events off"
+
+    gdb_test "break $::break_here_line" "Breakpoint $::decimal at $::hex.*"
+    gdb_test "continue" "hit Breakpoint $::decimal, main.*"
+
+    for { set i 0 } { $i < 20 } { incr i } {
+       with_test_prefix "i=$i" {
+           if { [gdb_test "next" "other line.*" "next to other line"] != 0 } {
+               return
+           }
+
+           if { [gdb_test "next" "for loop.*" "next to for loop"] != 0 } {
+               return
+           }
+
+           if { [gdb_test "next" "break here.*" "next to break here"] != 0} {
+               return
+           }
+       }
+    }
+}
+
+foreach_with_prefix fork_func {fork vfork} {
+    foreach_with_prefix target-non-stop {auto on off} {
+       foreach_with_prefix non-stop {off on} {
+           foreach_with_prefix displaced-stepping {auto on off} {
+               do_test ${fork_func} ${target-non-stop} ${non-stop} ${displaced-stepping}
+           }
+       }
+    }
+}
diff --git a/gdb/testsuite/gdb.threads/vfork-multi-thread.c b/gdb/testsuite/gdb.threads/vfork-multi-thread.c
new file mode 100644 (file)
index 0000000..093be14
--- /dev/null
@@ -0,0 +1,66 @@
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+// Start with:
+//
+//   ./gdb -nx -q --data-directory=data-directory repro -ex "tb break_here_first" -ex r -ex "b should_break_here"
+//
+// Then do "continue".
+//
+// The main thread will likely cross should_break_here while we are handling
+// the vfork and breakpoints are removed, therefore missing the breakpoint.
+
+static volatile int release_vfork = 0;
+static volatile int release_main = 0;
+
+static void *vforker(void *arg)
+{
+  while (!release_vfork);
+
+  if (!vfork()) {
+    /* A vfork child is not supposed to mess with the state of the program,
+       but it is helpful for the purpose of this test.  */
+    release_main = 1;
+    _exit(0);
+  }
+
+  return NULL;
+}
+
+static void should_break_here(void) {}
+
+int main()
+{
+
+  pthread_t thread;
+  int ret = pthread_create(&thread, NULL, vforker, NULL);
+  assert(ret == 0);
+
+  /* We break here first, while the thread is stuck on `!release_fork`.  */
+  release_vfork = 1;
+
+  /* We set a breakpoint on should_break_here.
+
+     We then set "release_fork" from the debugger and continue.  The main
+     thread hangs on `!release_main` while the non-main thread vforks.  During
+     the window of time where the two processes have a shared address space
+     (after vfork, before _exit), GDB removes the breakpoints from the address
+     space.  During that window, only the vfork-ing thread (the non-main
+     thread) is frozen by the kernel.  The main thread is free to execute.  The
+     child process sets `release_main`, releasing the main thread. A buggy GDB
+     would let the main thread execute during that window, leading to the
+     breakpoint on should_break_here being missed.  A fixed GDB does not resume
+     the threads of the vforking process other than the vforking thread.  When
+     the vfork child exits, the fixed GDB resumes the main thread, after
+     breakpoints are reinserted, so the breakpoint is not missed.  */
+
+  while (!release_main);
+
+  should_break_here();
+
+  pthread_join (thread, NULL);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp
new file mode 100644 (file)
index 0000000..ce5e1b4
--- /dev/null
@@ -0,0 +1,209 @@
+# Copyright 2020-2021 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/>.
+
+# Test that a multi-threaded program doing a vfork doesn't miss breakpoints.
+#
+# When a program vforks, its address space is shared with the parent.  When we
+# detach a vfork child, we must therefore keep breakpoints out of that address
+# space until the child either exits or execs.  For this reason, threads from
+# the parent must be held stopped, otherwise they could miss breakpoints.
+#
+# The thread that did the vfork is suspended by the kernel, so it's not a
+# concern, the other threads need to be manually stopped by GDB, and restarted
+# once the vfork critical region is done.
+#
+# This test spawns one thread that calls vfork.  Meanwhile, the main thread
+# crosses a breakpoint.  A buggy GDB would let the main thread run while
+# breakpoints are removed, so the main thread would miss the breakpoint and run
+# until exit.
+
+standard_testfile
+
+if { [build_executable "failed to prepare" ${testfile} ${srcfile} {debug pthreads}] } {
+    return
+}
+
+set any "\[^\r\n\]*"
+
+# A bunch of util procedures to continue an inferior to an expected point.
+
+proc continue_to_parent_breakpoint {} {
+    gdb_test "continue" \
+       "hit Breakpoint .* should_break_here .*" \
+       "continue parent to breakpoint"
+}
+
+proc continue_to_parent_end {} {
+    gdb_test "continue" "Inferior 1.*exited normally.*" \
+       "continue parent to end"
+}
+
+proc continue_to_child_end {} {
+    gdb_test "continue" "Inferior 2.*exited normally.*" \
+       "continue child to end"
+}
+
+proc continue_to_child_end_ns {} {
+    # Since we're in non-stop, the vfork child is always resumed and executes
+    # until exit, while the parent is blocked.  When the child exits, the
+    # prompt is shown, then the parent resumes and hits its breakpoint, which
+    # prints additional text to the console.  Consume up to the prompt.
+    gdb_test_multiple "continue" "continue to end of inferior 2" {
+       -re "\\\[Inferior 2${::any}exited normally\\\]\r\n${::gdb_prompt} " {
+           pass $gdb_test_name
+       }
+    }
+}
+
+proc consume_parent_breakpoint_hit_ns {} {
+    # After the child exits, the parent gets resumed and hits its breakpoint.
+    # Consume part of this output.
+    gdb_test_multiple "" "stop at should_break_here" {
+       -re "Thread 1.1 ${::any} hit Breakpoint ${::decimal}, should_break_here " {
+           pass $gdb_test_name
+       }
+    }
+}
+
+# Switch to the parent inferior.
+
+proc switch_to_parent { {msg "switch to parent"} } {
+    gdb_test "inferior 1" "Switching to inferior 1 .*" \
+       $msg
+}
+
+# Test with detach-on-fork on/off.
+#
+# Other axes values can be checked using global variables defined by the
+# iteration below (e.g. ${::non-stop}).
+
+proc test_dof_on {} {
+    if { ${::follow-fork-mode} == "parent" } {
+       continue_to_parent_breakpoint
+       continue_to_parent_end
+    } else {
+       continue_to_child_end
+    }
+}
+
+proc test_dof_off {} {
+    if { ${::non-stop} == "on" } {
+       set seen_inferior_1_stop 0
+       set seen_inferior_2_exit 0
+
+       gdb_test_multiple "continue" "" {
+           -re -wrap "Inferior 2${::any}exited normally.*" {
+               incr seen_inferior_2_exit
+           }
+
+           -re -wrap "Thread 1.1${::any}hit Breakpoint.*" {
+               incr seen_inferior_1_stop
+           }
+       }
+
+       gdb_test_multiple "" "consume second event" {
+           -re "Inferior 2${::any}exited normally" {
+               incr seen_inferior_2_exit
+           }
+
+           -re "Thread 1.1${::any}hit Breakpoint" {
+               incr seen_inferior_1_stop
+           }
+       }
+
+       gdb_assert { $seen_inferior_1_stop == 1 }
+       gdb_assert { $seen_inferior_2_exit == 1 }
+
+       switch_to_parent
+       continue_to_parent_end
+    } elseif { ${::follow-fork-mode} == "parent" && ${::schedule-multiple} == "off" } {
+       gdb_test "continue" \
+         "Can not resume the parent process over vfork .*" \
+         "continue 1"
+         gdb_test "continue" \
+         "Can not resume the parent process over vfork .*" \
+         "continue 2"
+
+         gdb_test_no_output "set detach-on-fork on"
+         test_dof_on
+    } else {
+       continue_to_child_end
+       switch_to_parent
+       continue_to_parent_breakpoint
+       continue_to_parent_end
+#      set seen_inferior_1_stop 0
+#      set seen_inferior_2_exit 0
+#
+#      gdb_test_multiple "continue" "continue 1" {
+#          -re -wrap "Inferior 2${::any}exited normally.*" {
+#              incr seen_inferior_2_exit
+#          }
+#
+#          -re -wrap "Thread 1.1${::any}hit Breakpoint.*" {
+#              incr seen_inferior_1_stop
+#          }
+#      }
+#
+#      switch_to_parent "switch to parent 1"
+#
+#      gdb_test_multiple "continue" "continue 2" {
+#          -re -wrap "Inferior 2${::any}exited normally.*" {
+#              incr seen_inferior_2_exit
+#          }
+#
+#          -re -wrap "Thread 1.1${::any}hit Breakpoint.*" {
+#              incr seen_inferior_1_stop
+#          }
+#      }
+#
+#      gdb_assert { $seen_inferior_1_stop == 1 }
+#      gdb_assert { $seen_inferior_2_exit == 1 }
+#
+#      switch_to_parent "switch to parent 2"
+#      continue_to_parent_end
+    }
+}
+
+proc do_test { target-non-stop non-stop follow-fork-mode detach-on-fork schedule-multiple } {
+    save_vars { ::GDBFLAGS } {
+       append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\" -ex \"set non-stop ${non-stop}\""
+       clean_restart ${::binfile}
+    }
+
+    gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}"
+    gdb_test_no_output "set detach-on-fork ${detach-on-fork}"
+    gdb_test_no_output "set schedule-multiple ${schedule-multiple}"
+
+    if { ![runto_main] } {
+       return
+    }
+
+    # The main thread is expected to hit this breakpoint.
+    gdb_test "break should_break_here" "Breakpoint $::decimal at .*"
+
+    test_dof_${detach-on-fork}
+}
+
+foreach_with_prefix target-non-stop {auto on off} {
+    foreach_with_prefix non-stop {off on} {
+       foreach_with_prefix follow-fork-mode {parent child} {
+           foreach_with_prefix detach-on-fork {on off} {
+               foreach_with_prefix schedule-multiple {off on} {
+                   do_test ${target-non-stop} ${non-stop} ${follow-fork-mode} ${detach-on-fork} ${schedule-multiple}
+               }
+           }
+       }
+    }
+}
index 236917e6776cc939325a18024d416bccbb68d2ee..beda5fd6bcee66ae081edaae02d9ca97a9567c25 100644 (file)
@@ -1053,6 +1053,8 @@ proc gdb_test_multiple { command message args } {
        }
     }
 
+    drain_gdbserver_output
+
     set code $early_processed_code
     append code {
        -re ".*A problem internal to GDB has been detected" {
@@ -8054,3 +8056,21 @@ gdb_caching_proc have_avx {
 
 # Always load compatibility stuff.
 load_lib future.exp
+
+proc drain_gdbserver_output { } {
+    if { [info exists ::server_spawn_id] } {
+       #puts "gonna expect"
+       gdb_expect {
+           -i "$::server_spawn_id"
+           -timeout 0
+
+           -re ".+" {
+               exp_continue
+               #puts "consumed: $expect_out(buffer)"
+           }
+
+           
+       }
+       #puts "expected"
+    }
+}
index 08866349e2cb9b9c0075d8ed087fd2fab3f8a651..6a8301c4f5a810e60d6e4be6d0d64eb4a0d52627 100644 (file)
@@ -53,6 +53,7 @@ proc gdb_target_cmd_ext { targetname serialport {additional_text ""} } {
     set serialport_re [string_to_regexp $serialport]
     for {set i 1} {$i <= 3} {incr i} {
        send_gdb "target $targetname $serialport\n"
+       drain_gdbserver_output
        gdb_expect 60 {
            -re "A program is being debugged already.*ill it.*y or n. $" {
                send_gdb "y\n"
@@ -77,6 +78,7 @@ proc gdb_target_cmd_ext { targetname serialport {additional_text ""} } {
            }
            -re "Remote debugging using .*$serialport_re.*$additional_text.*$gdb_prompt $" {
                verbose "Set target to $targetname"
+               drain_gdbserver_output
                return 0
            }
            -re "Remote debugging using stdio.*$additional_text.*$gdb_prompt $" {
@@ -241,6 +243,7 @@ proc gdbserver_default_get_comm_port { port } {
 # Returns the target protocol and socket to connect to.
 
 proc gdbserver_start { options arguments } {
+    #return [list "remote" localhost:1234]
     global portnum
     global GDB_TEST_SOCKETHOST
 
@@ -309,6 +312,10 @@ proc gdbserver_start { options arguments } {
            append gdbserver_command " --once"
        }
 
+       #append gdbserver_command " --event-loop-debug"
+       #append gdbserver_command " --remote-debug"
+       #append gdbserver_command " --debug"
+
        # Enable debug if set.
        if [gdbserver_debug_enabled] {
            global gdbserverdebug
@@ -383,6 +390,8 @@ proc gdbserver_start { options arguments } {
        break
     }
 
+    drain_gdbserver_output
+
     return [list $protocol [$get_remote_address $debughost $portnum]]
 }
 
@@ -526,6 +535,8 @@ proc gdbserver_run { child_args } {
     global gdbserver_protocol
     global gdbserver_gdbport
 
+    drain_gdbserver_output
+
     # Kill anything running before we try to start gdbserver, in case
     # we are sharing a serial connection.
     global gdb_prompt
index 12f6db0316f3f071048c06b38a2cac86318bfeed..69dc223a60078e6dd19540790c3c41ff2bdf5c88 100644 (file)
@@ -76,6 +76,10 @@ int debug_print_depth = 0;
 void
 debug_vprintf (const char *format, va_list ap)
 {
+  static FILE *foo = nullptr;
+  if (!foo)
+    foo = fopen("/tmp/gdbserver.txt", "w");
+
 #if !defined (IN_PROCESS_AGENT)
   /* N.B. Not thread safe, and can't be used, as is, with IPA.  */
   static int new_line = 1;
@@ -92,7 +96,10 @@ debug_vprintf (const char *format, va_list ap)
     }
 #endif
 
-  vfprintf (debug_file, format, ap);
+
+
+  vfprintf (foo, format, ap);
+  fflush(foo);
 
 #if !defined (IN_PROCESS_AGENT)
   if (*format)
index dc51b8906c8dd251d3ea2218828739f114a1e207..a68290da73aeeda43443a9fbaed2fb9d50ac0dd7 100644 (file)
@@ -493,13 +493,11 @@ linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
 
          ptid = ptid_t (new_pid, new_pid, 0);
 
-         if (debug_threads)
-           {
-             debug_printf ("HEW: Got fork event from LWP %ld, "
+         debug_printf("HEW: Got fork event from LWP %ld, "
                            "new child is %d\n",
                            ptid_of (event_thr).lwp (),
                            ptid.pid ());
-           }
+
 
          /* Add the new process to the tables and clone the breakpoint
             lists of the parent.  We need to do this even if the new process
@@ -3598,12 +3596,15 @@ linux_process_target::wait_1 (ptid_t ptid, target_waitstatus *ourstatus,
       if (event_child->waitstatus.kind == TARGET_WAITKIND_FORKED
          || event_child->waitstatus.kind == TARGET_WAITKIND_VFORKED)
        {
+             debug_printf("wait returning vfork %s -> %s\n", event_child->thread->id.to_string().c_str(),
+                          event_child->thread->fork_child->id.to_string().c_str());
          gdb_assert (event_child->thread->fork_child != nullptr);
          gdb_assert (event_child->thread->fork_child->fork_parent != nullptr);
          event_child->thread->fork_child->fork_parent = nullptr;
          event_child->thread->fork_child = nullptr;
        }
 
+
       *ourstatus = event_child->waitstatus;
       /* Clear the event lwp's waitstatus since we handled it already.  */
       event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
@@ -3641,17 +3642,8 @@ linux_process_target::wait_1 (ptid_t ptid, target_waitstatus *ourstatus,
         SIGSTOP is an implementation detail.  */
       ourstatus->value.sig = GDB_SIGNAL_0;
     }
-  else if (current_thread->last_resume_kind == resume_stop
-          && WSTOPSIG (w) != SIGSTOP)
-    {
-      /* A thread that has been requested to stop by GDB with vCont;t,
-        but, it stopped for other reasons.  */
-      ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
-    }
   else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
-    {
-      ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
-    }
+    ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
 
   gdb_assert (step_over_bkpt == null_ptid);
 
@@ -3723,6 +3715,8 @@ linux_process_target::wait (ptid_t ptid,
       && event_ptid != null_ptid)
     async_file_mark ();
 
+  debug_printf(":;wait return %s, %s\n", event_ptid.to_string().c_str(), target_waitstatus_to_string(ourstatus).c_str());
+
   return event_ptid;
 }
 
index 50465c39c2aa025845b99ced3124913cec42d471..6c299898c840f321529bb7296989bd7e83fe116e 100644 (file)
@@ -1690,7 +1690,10 @@ handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
      know about this process, and must not know about it until it gets the
      corresponding (v)fork event.  Exclude this thread from the list.  */
   if (thread->fork_parent != nullptr)
+    {
+      debug_printf("Hiding thread %s\n", thread->id.to_string().c_str());
     return;
+    }
 
   write_ptid (ptid_s, ptid);
 
This page took 0.047126 seconds and 4 git commands to generate.