Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <andrew.burgess@embecosm.com>
To: gdb-patches@sourceware.org
Subject: Re: [PATCH 3/3] gdb: make thread_info executing and resumed state more consistent
Date: Wed, 22 Sep 2021 12:21:14 +0100	[thread overview]
Message-ID: <20210922112114.GF4950@embecosm.com> (raw)
In-Reply-To: <68c1d1c6-940c-f38d-4f95-5cb863f75938@polymtl.ca>

* Simon Marchi <simon.marchi@polymtl.ca> [2021-09-01 11:09:31 -0400]:

> 
> 
> On 2021-08-30 4:03 p.m., Andrew Burgess wrote:
> > This commit was inspired by this comment from Simon:
> > 
> >   https://sourceware.org/pipermail/gdb-patches/2021-July/180694.html
> > 
> > The comment is about two thread_info flags m_executing and m_resumed.
> > The m_resumed flag indicates that at the infrun level GDB has set the
> > thread running, while the m_executing flag indicates that the thread
> > is actually running at the target level.
> > 
> > It is very common that a thread can be m_resumed, but not m_executing,
> > that is, core GDB thinks the thread is, or should be, running, but at
> > the target level the thread isn't currently running.
> > 
> > The comment Simon made was in reply to a patch I posted where I found
> > a case where a thread was marked m_executing, but not m_resumed, that
> > is, at the infrun level GDB thought the thread was stopped, but at the
> > target level the thread was actually running.  Simon suggests that
> > this feels like an invalid state, and after thinking about it, I
> > agree.
> > 
> > So, this commit adds some new asserts to GDB.  The core idea here is
> > that the resumed and executing flags should only change in the
> > following pattern, to begin with everything is set false:
> > 
> >   Resumed: false
> >   Executing: false
> > 
> > Then infrun marks the thread resumed:
> > 
> >   Resumed: true
> >   Executing: false
> > 
> > Then a target starts the thread executing:
> > 
> >   Resumed: true
> >   Executing: true
> > 
> > The thread stops, the target spots this and marks the thread no longer
> > executing:
> > 
> >   Resumed: true
> >   Executing: false
> > 
> > And finally, infrun acknowledges that the thread is now no longer
> > resumed:
> > 
> >   Resumed: false
> >   Executing: false
> > 
> > Notice that at no point is resumed false, while executing is true.
> > 
> > And so we can add some asserts:
> > 
> >  1. The resumed flag should only change state when the executing flag
> >  is false, and
> > 
> >  2. The executing flag should only change state when the resumed flag
> >  is true.
> > 
> > I added these asserts and ....
> > 
> > .... it turns out these rules are broken all over the place in GDB, we
> > have problems like:
> > 
> >  (a) When new threads appear they are marked executing, but not
> >  resumed, and
> > 
> >  (b) In some places we mark a single thread as resumed, but then
> >  actually set multiple threads executing.
> > 
> > For (a) it could be argued that this is a legitimate state - this is
> > actually the problem I addressed in the original patch that Simon was
> > replying too, however, we don't need to support this as a separate
> > state, so if we can make this case follow the expected set of state
> > transitions then it allows us to reduce the number of states that GDB
> > can be in, which I think is a good thing.
> > 
> > Case (b) seems to just be a bug to me.
> 
> Did you consider collapsing the two fields (resumed and executing) into
> a single one, with three states?  The names are silly, but as an
> example:
> 
> enum class thread_resumption_state
> {
>   NOT_RESUMED,
>   RESUMED_BUT_NOT_TARGET_RESUMED,
>   RESUMED_AND_TARGET_RESUMED,
> };
> 
> I think that would end up simpler than two booleans with rules.  But
> since it would probably be a pretty big change, I will accept "nope" as
> an answer.

I did think about this, but I guess I'm not quite sure how this ends
up being any simpler than the two booleans with rules.

My assumption is that we'd want to keep the API as
executing/set_executing and resumed/set_resumed as this represents the
questions we want to ask.

As such something like set_executing would become:

  void
  thread_info::set_executing (bool e)
  {
    if (m_state == RESUMED_AND_TARGET_RESUMED)
      return;

    gdb_assert (m_state == RESUMED_BUT_NOT_TARGET_RESUMED);

    m_state = RESUMED_AND_TARGET_RESUMED;
  }

Which doesn't feel any less complex to me.

Of course, we could change the API so the user sets the state
directly, like:

  void
  thread_info_::set_executing_resumed_state (enum state_type s)
  {
    /* ... */
  }

But then we'd _still_ end up having to assert the correct state
transitions.

Maybe I'm just not understanding what you're suggesting though...

> 
> > 
> > The interesting changes in this commit then are:
> > 
> >   * gdbthread.h (set_stop_pc): Add assert that the stop_pc can only be
> >   set when a thread is neither resumed, or executing.
> > 
> >   * infcmd.c (post_create_inferior): Ensure thread is non-resumed
> >   before clearing the stop_pc.
> 
> Should clear_stop_pc have the same assertions as set_stop_pc?  I
> guess you tried that and it didn't work?

I've added these in the new patch.

> 
> > @@ -5616,6 +5668,9 @@ handle_inferior_event (struct execution_control_state *ecs)
> >  	 execd thread for that case (this is a nop otherwise).  */
> >        ecs->event_thread = inferior_thread ();
> >  
> > +      /* If we did select a new thread, make sure its non-resumed.  */
> > +      ecs->event_thread->set_resumed (false);
> > +
> 
> This one is in the TARGET_WAITKIND_EXECD handling... I'm wondering if it
> should be target_ops::follow_exec's contract that any new thread of the
> new inferior should be not resumed.  Because follow_exec is always
> called in a context where the event thread on entry is not resumed.  So
> it's not like new threads that are added in target_ops::wait, for
> example.  For those, it makes sense to be added as resumed.
> 
> I think it would just mean adding a set_resumed (false) call in
> process_stratum_target::follow_exec.  And possibly some assertions in
> the follow_exec free function.  For example, I would add to the existing
> assertions:
> 
>   /* If a new inferior was created, the target must have added at least
>      one thread.  If the same inferior was used, we have left one
>      thread.  */
>   gdb_assert (!inf->thread_list.empty ());
> 
>   /* Any added thread must have been added in the not resumed state.  */
>   for (thread_info *thread : inf->threads ())
>     gdb_assert (!thread->resumed ());
> 
> PS: if taking into consideration my comment about the observer seeing
> the correct resumed state, in add_thread_silent (below), then perhaps we
> would need add_thread_silent to take a "resumed" parameter, so it can
> set the right resumed state before calling the observer.

I think I've addressed all of this in the new patch below.  The thread
coming out of ::follow_exec should now be non-resumed.

> 
> > diff --git a/gdb/thread.c b/gdb/thread.c
> > index 10c3dcd6991..aceb233be80 100644
> > --- a/gdb/thread.c
> > +++ b/gdb/thread.c
> > @@ -262,6 +262,13 @@ add_thread_silent (process_stratum_target *targ, ptid_t ptid)
> >    tp = new_thread (inf, ptid);
> >    gdb::observers::new_thread.notify (tp);
> >  
> > +  /* All threads are created in a resumed state, that is as soon as GDB
> > +     sees a new thread it is expected to be running as far as the core of
> > +     GDB is concerned.  At a target level the thread is probably stopped
> > +     right now, hence the executing flag is left initialized to false.  */
> > +  tp->set_resumed (true);
> > +  gdb_assert (!tp->executing ());
> 
> I don't know if it matters, but should resumed be set before calling the
> new_thread observer?  So that observers see the thread in the state we
> intend it to be in?

I checked, and as far as I can tell it doesn't currently matter, but I
agree we should try to do the right thing here, so I've changed this
in the new patch version.

Below is V2 of this patch.  There's a lot of change since V1.

 - The add_thread* calls now take a flag, and threads can be started
   as resumed or non-resumed.

 - Some of the state changes, like in ::follow_exec, are now pushed
   back to where the thread is created, as a result, some places where
   in V1 I was changing the state, I'm now asserting the state.

 - Added some asserts to clear_stop_pc

One big consequence of this patch is that we now expect that threads
are created in the "correct" resumed/non-resumed state.  A place where
this is definitely now wrong is during the initial creation of a
process to debug - threads are created in the default resumed state,
which is not correct.

I've fixed this for the native Linux and remote targets, but for all
other targets this will likely be incorrect.

As I'm not able to test these targets I made the choice to, instead of
asserting that GDB has the correct state, fix the state.  This can be
seen in gdb_startup_inferior (fork-child.c) and one in run_command_1
(infcmd.c), where I've added comments about this.

I don't really see a way around this.  If I make the default thread
state be non-resumed then the inferior creation code would work, but
any target specific code to handle creating new threads would
potentially be wrong (as threads would be created non-resumed when
they should be resumed).

I could potentially just add the asserts and let all the other targets
break; then offer help as/when these issues are reported.

Thoughts on this welcome.

Thanks,
Andrew

----

commit 32b05d0b4f46070df5583bc8e411034da8dc4d5a
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Mon Aug 16 12:09:45 2021 +0100

    gdb: make thread_info executing and resumed state more consistent
    
    This commit was inspired by this comment from Simon:
    
      https://sourceware.org/pipermail/gdb-patches/2021-July/180694.html
    
    The comment is about two thread_info flags m_executing and m_resumed.
    The m_resumed flag indicates that at the infrun level GDB has set the
    thread running, while the m_executing flag indicates that the thread
    is actually running at the target level.
    
    It is very common that a thread can be m_resumed, but not m_executing,
    that is, core GDB thinks the thread is, or should be, running, but at
    the target level the thread isn't currently running.
    
    The comment Simon made was in reply to a patch I posted where I found
    a case where a thread was marked m_executing, but not m_resumed, that
    is, at the infrun level GDB thought the thread was stopped, but at the
    target level the thread was actually running.  Simon suggests that
    this feels like an invalid state, and after thinking about it, I
    agree.
    
    So, the goal of this commit is to add some asserts to GDB.  The core
    idea here is that the resumed and executing flags should only change
    in the following pattern, to begin with everything is set false:
    
      Resumed: false
      Executing: false
    
    Then infrun marks the thread resumed:
    
      Resumed: true
      Executing: false
    
    Then a target starts the thread executing:
    
      Resumed: true
      Executing: true
    
    The thread stops, the target spots this and marks the thread no longer
    executing:
    
      Resumed: true
      Executing: false
    
    And finally, infrun acknowledges that the thread is now no longer
    resumed:
    
      Resumed: false
      Executing: false
    
    Notice that at no point is resumed false, while executing is true.
    
    And so we can add these asserts:
    
     1. The resumed flag should only change state when the executing flag
     is false, and
    
     2. The executing flag should only change state when the resumed flag
     is true.
    
    I added these asserts and ....
    
    .... it turns out these rules are broken all over the place in GDB, we
    have problems like:
    
     (a) When new threads appear they are marked executing, but not
     resumed, and
    
     (b) In some places we mark a single thread as resumed, but then
     actually set multiple threads executing.
    
    For (a) it could be argued that this is a legitimate state - this is
    actually the problem I addressed in the original patch that Simon was
    replying too, however, we don't need to support this as a separate
    state, so if we can make this case follow the expected set of state
    transitions then it allows us to reduce the number of states that GDB
    can be in, which I think is a good thing.
    
    Case (b) seems to just be a bug to me.
    
    My initial approach was to retain the idea that threads are always
    created in the non-executing, non-resumed state, and then find all the
    places where new threads are created (which should be in the resumed
    state), and mark the thread resumed in that case.
    
    However, after looking at the changes for a while, it felt like it
    would be simpler to create threads as resumed by default and instead
    force the threads back to non-resumed in the few cases where this was
    appropriate.  The appropriate case being (as far as I can tell), just
    the initial threads created as part of starting up a new inferior.
    
    So, that's what this patch does.  The thread creation routines now
    take a flag to indicate if the new thread should be created resumed or
    not.  Threads created as part of the ::create_inferior process should
    be created non-resumed, while all other threads are created resumed.
    
    The only problem is that this change requires updates to all targets
    that supply ::create_inferior, and I'm mostly only able to test
    GNU/Linux targets.
    
    And so, I have two changes, one in gdb_startup_inferior (fork-child.c)
    and one in run_command_1 (infcmd.c) where, instead of asserting that
    a thread is in a particular resumed/non-resumed state, I am setting
    the thread back to non-resumed.  I believe that this should fix up
    GDB's state for any target that is not (yet) doing the correct thing
    when creating its initial threads.
    
    Here is a description of all the individual changes:
    
      * corelow.c (add_to_thread_list): When adding threads as part of
      setting up a core target, the threads should not be resumed.
    
      (core_target_open): When adding threads as part of setting up a core
      target, the threads should not be resumed.  For sanity, assert that
      the threads of a core target are not resumed once the initial target
      has been setup.
    
      * fork-child.c (gdb_startup_inferior): Upon entry to this function
      no threads in the inferior should be resumed, however, I suspect not
      all targets comply with this assertion, so for now I don't actually
      assert anything.  As we're about to resume the inferior though, mark
      all threads as resumed.
    
      * gdbthread.h (set_stop_pc): Add assert that the stop_pc can only be
      set when a thread is neither resumed, or executing.
    
      (clear_stop_pc): The stop_pc is cleared as part of setting the
      thread executing.  And, as a thread is only set executing after it
      is marked resumed, we can add some asserts to this function to
      validate GDB's state.
    
      (add_thread): Update header comment, add extra 'resumed_p' parameter.
    
      (add_thread_silent): Add extra 'resumed_p' parameter.
    
      (add_thread_with_info): Add extra 'resumed_p' parameter.
    
      * inf-ptrace.c (inf_ptrace_target::create_inferior): The initial
      thread is created non-resumed.
    
      * infcmd.c (post_create_inferior): Only attempt to set the stop_pc
      once per-stop.  I've updated the comment, and re-indented the code
      under the new if condition.
    
      (run_command_1): After creating the inferior all threads should be
      non-resumed, however, I suspect that not all targets comply with
      this assertion, so, instead of asserting this, I set all threads
      back to non-resumed to save any badly behaved targets.
    
      * infrun.c (follow_fork_inferior): After a fork all threads in the
      child should already have been marked as non-resumed.
    
      (follow_exec): Upon entry the thread performing the exec should
      already be marked as non-resumed.  After the exec the selected
      thread should also be non-resumed.
    
      (struct scoped_mark_thread_resumed): This new class is used to
      ensure that all the required threads are marked resumed when
      required, this addresses issue (b) above.  I make use of this new
      class in...
    
      (do_target_resume):  Use scoped_mark_thread_resumed to mark all
      threads resumed prior to actually calling into the target to resume
      the threads.  Placing this call here allows me to remove some calls
      to thread_info::set_resumed() in other places...
    
      (resume_1): Remove a call to thread_info::set_resumed() from here.
    
      (handle_inferior_event): Assert that the thread is not resumed when
      handling an exec event.
    
      (finish_step_over): When we need to place a thread back into the
      resumed state so that we can later find its pending event, we must
      mark the thread resumed after setting the stop_pc, see the asserts
      added to set_stop_pc for why.
    
      (keep_going_stepped_thread): Remove a call to set_resumed thanks to
      our changes in do_target_resume.
    
      * process-stratum-target.c (process_stratum_target::follow_exec):
      If creating a new thread to handle the exec in, then it should be
      created non-resumed.
    
      (process_stratum_target::follow_fork): If creating a new thread to
      follow the child of a fork, the new thread should be non-resumed.
    
      * remote.c (remote_target::add_current_inferior_and_thread): Add
      resumed_p parameter.  Use this parameter to decide if the new thread
      should be started resumed or not.
    
      (remote_target::remote_add_thread): Mark the thread resumed before
      marking it executed.
    
      (remote_target::process_initial_stop_replies): Mark the thread as
      non-resumed.
    
      (remote_target::start_remote): Create the initial thread in a
      resumed state.
    
      (extended_remote_target::create_inferior): Create the initial thread
      in a non-resumed state.
    
      * thread.c (add_thread_silent): Take a new resumed_p parameter, use
      this to set if the new thread is resumed or not.  All new threads
      are created non-executing, so assert this.
    
      (add_thread_with_info): Pass through the new resumed_p parameter.
    
      (add_thread): Pass through the new resumed_p parameter.
    
      (thread_info::set_executing): Add an early exit patch like we
      already have in thread_info::set_executing().  Also add the
      assertion that is one of the two core asserts for this patch.  Mark
      the thread executing after clearing the stop_pc, this allows the
      assertion we added to thread_info::clear_stop_pc above to work.
    
      (thread_info::set_resumed): Add the other core assert for this
      patch.
    
    This series has been tested on X86-64 GNU/Linux with the unix,
    native-gdbserver, and native-extended-gdbserver boards.

diff --git a/gdb/corelow.c b/gdb/corelow.c
index 711e86c4cd4..d6846b8f609 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -347,7 +347,8 @@ add_to_thread_list (asection *asect, asection *reg_sect)
 
   ptid_t ptid (pid, lwpid);
 
-  thread_info *thr = add_thread (inf->process_target (), ptid);
+  /* Pass false to indicate the thread should be added non-resumed.  */
+  thread_info *thr = add_thread (inf->process_target (), ptid, false);
 
 /* Warning, Will Robinson, looking at BFD private data! */
 
@@ -505,7 +506,7 @@ core_target_open (const char *arg, int from_tty)
       if (thread == NULL)
 	{
 	  inferior_appeared (current_inferior (), CORELOW_PID);
-	  thread = add_thread_silent (target, ptid_t (CORELOW_PID));
+	  thread = add_thread_silent (target, ptid_t (CORELOW_PID), false);
 	}
 
       switch_to_thread (thread);
@@ -514,6 +515,10 @@ core_target_open (const char *arg, int from_tty)
   if (current_program_space->exec_bfd () == nullptr)
     locate_exec_from_corefile_build_id (core_bfd, from_tty);
 
+  /* Threads in a core file are not resumed.  */
+  for (thread_info *thread : current_inferior ()->threads ())
+    gdb_assert (!thread->resumed ());
+
   post_create_inferior (from_tty);
 
   /* Now go through the target stack looking for threads since there
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 3ce7d64b855..8890ea41e05 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -126,10 +126,24 @@ gdb_startup_inferior (pid_t pid, int num_traps)
   inferior *inf = current_inferior ();
   process_stratum_target *proc_target = inf->process_target ();
 
+  for (thread_info *thread : inf->threads ())
+    {
+      /* Ideally, all targets would created their initial threads in the
+	 non-resumed state, and only when we get here would we mark the
+	 threads as resumed.  However, this is not currently the case, some
+	 targets create their initial threads in the resumed state (for no
+	 particular reason other than historical), and so we can't assert
+	 anything about the state of the inferior threads at this point.
+
+	 What we'd like to say is:  gdb_assert (!thread->resumed ());   */
+      thread->set_resumed (true);
+    }
+
   ptid_t ptid = startup_inferior (proc_target, pid, num_traps, NULL, NULL);
 
   /* Mark all threads non-executing.  */
   set_executing (proc_target, ptid, false);
+  set_resumed (proc_target, ptid, false);
 
   return ptid;
 }
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4b271943d50..9ffe9233c77 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -345,6 +345,8 @@ class thread_info : public refcounted_object,
 
   void set_stop_pc (CORE_ADDR stop_pc)
   {
+    gdb_assert (!m_resumed);
+    gdb_assert (!m_executing);
     m_suspend.stop_pc = stop_pc;
   }
 
@@ -352,6 +354,8 @@ class thread_info : public refcounted_object,
 
   void clear_stop_pc ()
   {
+    gdb_assert (m_resumed);
+    gdb_assert (!m_executing);
     m_suspend.stop_pc.reset ();
   }
 
@@ -547,22 +551,25 @@ using inferior_ref
 /* Create an empty thread list, or empty the existing one.  */
 extern void init_thread_list (void);
 
-/* Add a thread to the thread list, print a message
-   that a new thread is found, and return the pointer to
-   the new thread.  Caller my use this pointer to 
-   initialize the private thread data.  */
+/* Add a thread to the thread list, print a message that a new thread is
+   found, and return the pointer to the new thread.  Caller my use this
+   pointer to initialize the private thread data.  When RESUMED_P is true
+   the thread is created in a resumed state, otherwise, the thread is
+   created in a non-resumed state.  */
 extern struct thread_info *add_thread (process_stratum_target *targ,
-				       ptid_t ptid);
+				       ptid_t ptid, bool resumed_p = true);
 
 /* Same as add_thread, but does not print a message about new
    thread.  */
 extern struct thread_info *add_thread_silent (process_stratum_target *targ,
-					      ptid_t ptid);
+					      ptid_t ptid,
+					      bool resumed_p = true);
 
 /* Same as add_thread, and sets the private info.  */
 extern struct thread_info *add_thread_with_info (process_stratum_target *targ,
 						 ptid_t ptid,
-						 private_thread_info *);
+						 private_thread_info *,
+						 bool resumed_p = true);
 
 /* Delete thread THREAD and notify of thread exit.  If the thread is
    currently not deletable, don't actually delete it but still tag it
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index afa38de6ef7..0366bb940e0 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -94,7 +94,7 @@ inf_ptrace_target::create_inferior (const char *exec_file,
   /* We have something that executes now.  We'll be running through
      the shell at this point (if startup-with-shell is true), but the
      pid shouldn't change.  */
-  thread_info *thr = add_thread_silent (this, ptid);
+  thread_info *thr = add_thread_silent (this, ptid, false);
   switch_to_thread (thr);
 
   unpusher.release ();
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index d948f4bafc5..d31ba5af144 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -244,22 +244,29 @@ post_create_inferior (int from_tty)
      don't need to.  */
   target_find_description ();
 
-  /* Now that we know the register layout, retrieve current PC.  But
-     if the PC is unavailable (e.g., we're opening a core file with
-     missing registers info), ignore it.  */
+  /* Now that we know the register layout, update the stop_pc if it is not
+     already set.  If GDB attached to a running process then the stop_pc
+     will have been set while processing the stop events triggered during
+     the attach.  If this is a core file, or we're just starting a new
+     process, then the stop_pc will not currently be set.
+
+     But, if the PC is unavailable (e.g., we're opening a core file with
+     missing registers info), ignore it.  Obviously, if we're trying to
+     debug a running process and we can't read the PC then this is bad and
+     shouldn't be ignored, but we'll soon hit errors trying to read the PC
+     elsewhere in GDB, so ignoring this here is fine.  */
   thread_info *thr = inferior_thread ();
-
-  thr->clear_stop_pc ();
-  try
-    {
-      regcache *rc = get_thread_regcache (thr);
-      thr->set_stop_pc (regcache_read_pc (rc));
-    }
-  catch (const gdb_exception_error &ex)
-    {
-      if (ex.error != NOT_AVAILABLE_ERROR)
-	throw;
-    }
+  if (!thr->stop_pc_p ())
+    try
+      {
+	regcache *rc = get_thread_regcache (thr);
+	thr->set_stop_pc (regcache_read_pc (rc));
+      }
+    catch (const gdb_exception_error &ex)
+      {
+	if (ex.error != NOT_AVAILABLE_ERROR)
+	  throw;
+      }
 
   if (current_program_space->exec_bfd ())
     {
@@ -453,6 +460,19 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
      shouldn't refer to run_target again.  */
   run_target = NULL;
 
+  /* After creating the inferior its threads are not resumed.  */
+  for (thread_info *thread : current_inferior ()->threads ())
+    {
+      /* Ideally, all targets would leave their threads non-resumed after
+	 the create_inferior call above.  Unfortunately, this is probably
+	 not the case.  The 'probably' here is simply due to an inability
+	 to test all targets.  So, rather than asserting that all threads
+	 are non-resumed here, set the threads to be non-resumed.
+
+	 We'd like to say: gdb_assert (!thread->resumed ());  */
+      thread->set_resumed (false);
+    }
+
   /* We're starting off a new process.  When we get out of here, in
      non-stop mode, finish the state of all threads of that process,
      but leave other threads alone, as they may be stopped in internal
diff --git a/gdb/infrun.c b/gdb/infrun.c
index d1ac9b4cbbb..ca4568ba272 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -580,7 +580,13 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
   /* If there is a child inferior, target_follow_fork must have created a thread
      for it.  */
   if (child_inf != nullptr)
-    gdb_assert (!child_inf->thread_list.empty ());
+    {
+      gdb_assert (!child_inf->thread_list.empty ());
+
+      /* Any added thread should have been marked non-resumed already.  */
+      for (thread_info *thread : child_inf->threads ())
+	gdb_assert (!thread->resumed ());
+    }
 
   /* Detach the parent if needed.  */
   if (follow_child)
@@ -1091,6 +1097,7 @@ follow_exec (ptid_t ptid, const char *exec_file_target)
      breakpoint or similar, it's gone now.  We cannot truly
      step-to-next statement through an exec().  */
   thread_info *th = inferior_thread ();
+  gdb_assert (!th->resumed ());
   th->control.step_resume_breakpoint = NULL;
   th->control.exception_resume_breakpoint = NULL;
   th->control.single_step_breakpoints = NULL;
@@ -1172,6 +1179,11 @@ follow_exec (ptid_t ptid, const char *exec_file_target)
   gdb_assert (current_inferior () == inf);
   gdb_assert (current_program_space == inf->pspace);
 
+  /* The inferior (and so thread) might have changed depending on which
+     path we took through the above code.  However, the currently selected
+     thread should not be in a resumed state.  */
+  gdb_assert (!inferior_thread ()->resumed ());
+
   /* Attempt to open the exec file.  SYMFILE_DEFER_BP_RESET is used
      because the proper displacement for a PIE (Position Independent
      Executable) main symbol file will only be computed by
@@ -2100,6 +2112,52 @@ internal_resume_ptid (int user_step)
     return user_visible_resume_ptid (user_step);
 }
 
+/* Before calling into target code to set inferior threads executing we
+   must mark all threads as resumed.  If an exception is thrown while
+   trying to set the threads executing then we should mark the threads as
+   non-resumed.
+
+   Create an instance of this struct before */
+struct scoped_mark_thread_resumed
+{
+  /* Constructor.  All threads matching PTID will be marked as resumed.  */
+  scoped_mark_thread_resumed (process_stratum_target *targ, ptid_t ptid)
+    : m_target (targ), m_ptid (ptid)
+  {
+    gdb_assert (m_target != nullptr);
+    set_resumed (m_target, m_ptid, true);
+  }
+
+  /* Destructor.  If M_TARGET is not nullptr then mark all threads matching
+     M_PTID as no longer being resumed.  The expectation is that on the
+     exception path this will be called with M_TARGET still set to a valid
+     target.  If however, the threads were successfully set executing then
+     this->commit() will have been called, and M_TARGET will now be
+     nullptr.  */
+  ~scoped_mark_thread_resumed ()
+  {
+    if (m_target != nullptr)
+      set_resumed (m_target, m_ptid, false);
+  }
+
+  /* Called once all of the threads have successfully be set executing (by
+     calling into the target code).  Clears M_TARGET as an indication that,
+     when this object is destructed, we should leave all matching threads
+     as being marked resumed.  */
+  void commit ()
+  {
+    m_target = nullptr;
+  }
+
+private:
+
+  /* The target used for marking threads as resumed or non-resumed.  */
+  process_stratum_target *m_target;
+
+  /* The thread (or threads) to mark as resumed.  */
+  ptid_t m_ptid;
+};
+
 /* Wrapper for target_resume, that handles infrun-specific
    bookkeeping.  */
 
@@ -2108,6 +2166,11 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig)
 {
   struct thread_info *tp = inferior_thread ();
 
+  /* Create a scoped_mark_thread_resumed to mark all threads matching
+     RESUME_PTID as resumed.  */
+  process_stratum_target *curr_target = current_inferior ()->process_target ();
+  scoped_mark_thread_resumed scoped_resume (curr_target, resume_ptid);
+
   gdb_assert (!tp->stop_requested);
 
   /* Install inferior's terminal modes.  */
@@ -2146,6 +2209,9 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig)
 
   if (target_can_async_p ())
     target_async (1);
+
+  /* Call commit so SCOPED_RESUME leaves threads marked as resumed.  */
+  scoped_resume.commit ();
 }
 
 /* Resume the inferior.  SIG is the signal to give the inferior
@@ -2305,7 +2371,6 @@ resume_1 (enum gdb_signal sig)
 
 	      resume_ptid = internal_resume_ptid (user_step);
 	      do_target_resume (resume_ptid, false, GDB_SIGNAL_0);
-	      tp->set_resumed (true);
 	      return;
 	    }
 	}
@@ -2514,7 +2579,6 @@ resume_1 (enum gdb_signal sig)
     }
 
   do_target_resume (resume_ptid, step, sig);
-  tp->set_resumed (true);
 }
 
 /* Resume the inferior.  SIG is the signal to give the inferior
@@ -5616,6 +5680,10 @@ handle_inferior_event (struct execution_control_state *ecs)
 	 execd thread for that case (this is a nop otherwise).  */
       ecs->event_thread = inferior_thread ();
 
+      /* If we did switch threads above, then the new thread should still
+	 be in the non-resumed state.  */
+      gdb_assert (!ecs->event_thread->resumed ());
+
       ecs->event_thread->set_stop_pc
 	(regcache_read_pc (get_thread_regcache (ecs->event_thread)));
 
@@ -5865,12 +5933,6 @@ finish_step_over (struct execution_control_state *ecs)
 
 	  /* Record the event thread's event for later.  */
 	  save_waitstatus (tp, &ecs->ws);
-	  /* This was cleared early, by handle_inferior_event.  Set it
-	     so this pending event is considered by
-	     do_target_wait.  */
-	  tp->set_resumed (true);
-
-	  gdb_assert (!tp->executing ());
 
 	  regcache = get_thread_regcache (tp);
 	  tp->set_stop_pc (regcache_read_pc (regcache));
@@ -5881,6 +5943,10 @@ finish_step_over (struct execution_control_state *ecs)
 			       target_pid_to_str (tp->ptid).c_str (),
 			       currently_stepping (tp));
 
+	  /* This was cleared early, by handle_inferior_event.  Set it so
+	     this pending event is considered by do_target_wait.  */
+	  tp->set_resumed (true);
+
 	  /* This in-line step-over finished; clear this so we won't
 	     start a new one.  This is what handle_signal_stop would
 	     do, if we returned false.  */
@@ -7530,7 +7596,6 @@ keep_going_stepped_thread (struct thread_info *tp)
 				     get_frame_address_space (frame),
 				     tp->stop_pc ());
 
-      tp->set_resumed (true);
       resume_ptid = internal_resume_ptid (tp->control.stepping_command);
       do_target_resume (resume_ptid, false, GDB_SIGNAL_0);
     }
diff --git a/gdb/process-stratum-target.c b/gdb/process-stratum-target.c
index 4c726419b99..b6871403317 100644
--- a/gdb/process-stratum-target.c
+++ b/gdb/process-stratum-target.c
@@ -100,7 +100,7 @@ process_stratum_target::follow_exec (inferior *follow_inf, ptid_t ptid,
 	 may decide to unpush itself from the original inferior's target stack
 	 after that, at its discretion.  */
       follow_inf->push_target (orig_inf->process_target ());
-      thread_info *t = add_thread (follow_inf->process_target (), ptid);
+      thread_info *t = add_thread (follow_inf->process_target (), ptid, false);
 
       /* Leave the new inferior / thread as the current inferior / thread.  */
       switch_to_thread (t);
@@ -118,7 +118,7 @@ process_stratum_target::follow_fork (inferior *child_inf, ptid_t child_ptid,
   if (child_inf != nullptr)
     {
       child_inf->push_target (this);
-      add_thread_silent (this, child_ptid);
+      add_thread_silent (this, child_ptid, false);
     }
 }
 
diff --git a/gdb/remote.c b/gdb/remote.c
index b6da6b086a2..bb96149f70f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -751,7 +751,8 @@ class remote_target : public process_stratum_target
   int remote_resume_with_vcont (ptid_t ptid, int step,
 				gdb_signal siggnal);
 
-  thread_info *add_current_inferior_and_thread (const char *wait_status);
+  thread_info *add_current_inferior_and_thread (const char *wait_status,
+						bool resumed_p);
 
   ptid_t wait_ns (ptid_t ptid, struct target_waitstatus *status,
 		  target_wait_flags options);
@@ -2546,8 +2547,9 @@ remote_target::remote_add_thread (ptid_t ptid, bool running, bool executing)
      when we process a matching stop reply.  */
   get_remote_thread_info (thread)->set_resumed ();
 
-  set_executing (this, ptid, executing);
   set_running (this, ptid, running);
+  set_resumed (this, ptid, running);
+  set_executing (this, ptid, executing);
 
   return thread;
 }
@@ -4440,7 +4442,8 @@ remote_target::get_current_thread (const char *wait_status)
    The function returns pointer to the main thread of the inferior. */
 
 thread_info *
-remote_target::add_current_inferior_and_thread (const char *wait_status)
+remote_target::add_current_inferior_and_thread (const char *wait_status,
+						bool resumed_p)
 {
   struct remote_state *rs = get_remote_state ();
   bool fake_pid_p = false;
@@ -4471,7 +4474,7 @@ remote_target::add_current_inferior_and_thread (const char *wait_status)
   /* Add the main thread and switch to it.  Don't try reading
      registers yet, since we haven't fetched the target description
      yet.  */
-  thread_info *tp = add_thread_silent (this, curr_ptid);
+  thread_info *tp = add_thread_silent (this, curr_ptid, resumed_p);
   switch_to_thread_no_regs (tp);
 
   return tp;
@@ -4586,6 +4589,7 @@ remote_target::process_initial_stop_replies (int from_tty)
 	evthread->set_pending_waitstatus (ws);
 
       set_executing (this, event_ptid, false);
+      set_resumed (this, event_ptid, false);
       set_running (this, event_ptid, false);
       get_remote_thread_info (evthread)->set_not_resumed ();
     }
@@ -4841,7 +4845,8 @@ remote_target::start_remote (int from_tty, int extended_p)
 	  /* Target has no concept of threads at all.  GDB treats
 	     non-threaded target as single-threaded; add a main
 	     thread.  */
-	  thread_info *tp = add_current_inferior_and_thread (wait_status);
+	  thread_info *tp
+	    = add_current_inferior_and_thread (wait_status, true);
 	  get_remote_thread_info (tp)->set_resumed ();
 	}
       else
@@ -10488,7 +10493,7 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
 
   /* vRun's success return is a stop reply.  */
   stop_reply = run_worked ? rs->buf.data () : NULL;
-  add_current_inferior_and_thread (stop_reply);
+  add_current_inferior_and_thread (stop_reply, false);
 
   /* Get updated offsets, if the stub uses qOffsets.  */
   get_offsets ();
diff --git a/gdb/thread.c b/gdb/thread.c
index 10c3dcd6991..a74ba94bbe8 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -245,7 +245,7 @@ new_thread (struct inferior *inf, ptid_t ptid)
 }
 
 struct thread_info *
-add_thread_silent (process_stratum_target *targ, ptid_t ptid)
+add_thread_silent (process_stratum_target *targ, ptid_t ptid, bool resumed_p)
 {
   gdb_assert (targ != nullptr);
 
@@ -260,6 +260,14 @@ add_thread_silent (process_stratum_target *targ, ptid_t ptid)
     delete_thread (tp);
 
   tp = new_thread (inf, ptid);
+
+  /* Upon creation, all threads are non-executing, and non-resumed.  Before
+     notifying observers of the new thread, we set the resumed flag to the
+     desired value.  */
+  gdb_assert (!tp->executing ());
+  tp->set_resumed (resumed_p);
+
+  /* Announce the new thread to all observers.  */
   gdb::observers::new_thread.notify (tp);
 
   return tp;
@@ -267,9 +275,9 @@ add_thread_silent (process_stratum_target *targ, ptid_t ptid)
 
 struct thread_info *
 add_thread_with_info (process_stratum_target *targ, ptid_t ptid,
-		      private_thread_info *priv)
+		      private_thread_info *priv, bool resumed_p)
 {
-  thread_info *result = add_thread_silent (targ, ptid);
+  thread_info *result = add_thread_silent (targ, ptid, resumed_p);
 
   result->priv.reset (priv);
 
@@ -281,9 +289,9 @@ add_thread_with_info (process_stratum_target *targ, ptid_t ptid,
 }
 
 struct thread_info *
-add_thread (process_stratum_target *targ, ptid_t ptid)
+add_thread (process_stratum_target *targ, ptid_t ptid, bool resumed_p)
 {
-  return add_thread_with_info (targ, ptid, NULL);
+  return add_thread_with_info (targ, ptid, NULL, resumed_p);
 }
 
 private_thread_info::~private_thread_info () = default;
@@ -322,9 +330,15 @@ thread_info::deletable () const
 void
 thread_info::set_executing (bool executing)
 {
-  m_executing = executing;
+  if (executing == m_executing)
+    return;
+
+  gdb_assert (m_resumed);
+
   if (executing)
     this->clear_stop_pc ();
+
+  m_executing = executing;
 }
 
 /* See gdbthread.h.  */
@@ -335,6 +349,8 @@ thread_info::set_resumed (bool resumed)
   if (resumed == m_resumed)
     return;
 
+  gdb_assert (!m_executing);
+
   process_stratum_target *proc_target = this->inf->process_target ();
 
   /* If we transition from resumed to not resumed, we might need to remove

  reply	other threads:[~2021-09-22 11:21 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-30 20:03 [PATCH 0/3] Changes to thread state tracking Andrew Burgess
2021-08-30 20:03 ` [PATCH 1/3] gdb: make thread_info::executing private Andrew Burgess
2021-09-01 13:53   ` Simon Marchi via Gdb-patches
2021-09-07 11:46     ` Andrew Burgess
2021-08-30 20:03 ` [PATCH 2/3] gdb: make thread_suspend_state::stop_pc optional Andrew Burgess
2021-09-01 14:23   ` Simon Marchi via Gdb-patches
2021-09-07 13:21     ` Andrew Burgess
2021-09-07 14:10       ` Simon Marchi via Gdb-patches
2021-09-08  9:50         ` Andrew Burgess
2021-08-30 20:03 ` [PATCH 3/3] gdb: make thread_info executing and resumed state more consistent Andrew Burgess
2021-09-01 15:09   ` Simon Marchi via Gdb-patches
2021-09-22 11:21     ` Andrew Burgess [this message]
2021-09-23 17:14       ` Simon Marchi via Gdb-patches
2021-09-29  8:09         ` Andrew Burgess
2021-10-08 19:33           ` Simon Marchi via Gdb-patches
2022-01-13 18:34   ` [PATCHv3] " Andrew Burgess via Gdb-patches
2022-01-14 17:10     ` Simon Marchi via Gdb-patches
2022-02-24 15:52       ` Andrew Burgess via Gdb-patches
2022-03-03 19:42         ` Simon Marchi via Gdb-patches
2022-03-07  7:39           ` Aktemur, Tankut Baris via Gdb-patches
2022-03-30  9:19         ` Andrew Burgess via Gdb-patches
2022-04-21 16:45     ` [PATCHv4 0/2] Make " Andrew Burgess via Gdb-patches
2022-04-21 16:45       ` [PATCHv4 1/2] gdb: add some additional thread status debug output Andrew Burgess via Gdb-patches
2022-04-21 20:35         ` Lancelot SIX via Gdb-patches
2022-04-22 17:50           ` Andrew Burgess via Gdb-patches
2022-05-03 14:04             ` Andrew Burgess via Gdb-patches
2022-04-21 16:45       ` [PATCHv4 2/2] gdb: make thread_info executing and resumed state more consistent Andrew Burgess via Gdb-patches
2022-04-26 13:28       ` Nidal Faour via Gdb-patches
2022-08-08 11:04       ` [PATCHv4 0/2] Make " Craig Blackmore
2022-08-08 12:01         ` Andrew Burgess via Gdb-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210922112114.GF4950@embecosm.com \
    --to=andrew.burgess@embecosm.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox