public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Pedro Alves <pedro@palves.net>
To: Simon Marchi <simark@simark.ca>, gdb-patches@sourceware.org
Subject: Re: [PATCH v2 07/29] Thread options & clone events (core + remote)
Date: Mon, 12 Dec 2022 20:15:50 +0000	[thread overview]
Message-ID: <d22bb485-e969-cbbb-9f56-c185399a5c55@palves.net> (raw)
In-Reply-To: <5d68cd36-e8d6-e8ad-5428-863e79742062@simark.ca>

On 2022-07-21 4:14 a.m., Simon Marchi wrote:
> 
> 
> On 2022-07-13 18:24, Pedro Alves wrote:
>> A previous patch taught GDB about a new TARGET_WAITKIND_THREAD_CLONED
>> event kind, and made the Linux target report clone events.
>>
>> A following patch will teach Linux GDBserver to do the same thing.
>>
>> However, for remote debugging, it wouldn't be ideal for GDBserver to
>> report every clone event to GDB, when GDB only cares about such events
>> in some specific situations.  Reporting clone events all the time
>> would be potentially chatty.  We don't enable thread create/exit
>> events all the time for the same reason.  Instead we have the
>> QThreadEvents packet.  QThreadEvents is target-wide, though.
>>
>> This patch makes GDB instead explicitly request that the target
>> reports clone events or not, on a per-thread basis.
>>
>> In order to be able to do that with GDBserver, we need a new remote
>> protocol feature.  Since a following patch will want to enable thread
>> exit events on per-thread basis too, the packet introduced here is
>> more generic than just for clone events.  It lets you enable/disable a
>> set of options at once, modelled on Linux ptrace's PTRACE_SETOPTIONS.
>>
>> IOW, this commit introduces a new QThreadOptions packet, that lets you
>> specify a set of per-thread event options you want to enable.  The
>> packet accepts a list of options/thread-id pairs, similarly to vCont,
>> processed left to right, with the options field being a number
>> interpreted as a bit mask of options.  The only option defined in this
>> commit is GDB_TO_CLONE (0x1), which ask the remote target to report
> 
> It took me a while to understand that "TO" means "Thread Options".
> Since this ends up in the documentation, I think it wouldn't hurt to be
> clear and use GDB_THREAD_OPTION_CLONE.

Done, in all affected patches, including commit logs / subjects.

> 
>> clone events.  Another patch later in the series will introduce
>> another option.
>>
>> For example, this packet sets option "1" (clone events) on thread
>> p1000.2345:
>>
>>   QThreadOptions;1:p1000.2345
>>
>> and this clears options for all threads of process 1000, and then sets
>> option "1" (clone events) on thread p1000.2345:
>>
>>   QThreadOptions;0:p1000.-1;1:p1000.2345
>>
>> This clears options of all threads of all processes:
>>
>>   QThreadOptions;0
>>
>> The target reports the set of supported options by including
>> "QThreadOptions=<supported options>" in its qSupported response.
>>
>> infrun is then tweaked to enable GDB_TO_CLONE when stepping over a
>> breakpoint.
>>
>> Unlike PTRACE_SETOPTIONS, fork/vfork/clone children do NOT inherit
>> their parent's thread options.  This is so that GDB can send e.g.,
>> "QThreadOptions;0;1:TID" without worrying about threads it doesn't
>> know about yet.
> 
> Just wondering (the behavior you chose is fine with me), couldn't we use
> the same strategy as we use for resumption, where each layer is
> responsible of "hiding" processes and threads it has not yet reported?
> This would mean that even is a wildcard selector is used, GDBserver
> would not apply the options to an unreported child thread.

If child threads inherit parent options, and then we apply that strategy, then we
have no way to clear the options of all threads, even those GDB doesn't know
about yet.  If threads don't inherit options, then their options start cleared,
and "QThreadOptions;0" thus results in all threads with options cleared, whether
we apply that strategy or not.

> 
>> @@ -892,6 +904,114 @@ handle_general_set (char *own_buf)
>>        return;
>>      }
>>  
>> +  if (startswith (own_buf, "QThreadOptions;"))
>> +    {
>> +      const char *p = own_buf + strlen ("QThreadOptions");
>> +
>> +      gdb_thread_options supported_options;
>> +      if (!target_supports_set_thread_options (&supported_options))
>> +	{
>> +	  /* Something went wrong -- we don't support options, but GDB
>> +	     sent the packet anyway.  */
>> +	  write_enn (own_buf);
>> +	  return;
>> +	}
>> +
>> +      /* We could store the options directly in thread->thread_options
>> +	 without this map, but that would mean that a QThreadOptions
>> +	 packet with a wildcard like "QThreadOptions;0;3:TID" would
>> +	 result in the debug logs showing:
>> +
>> +	   [options for TID are now 0x0]
>> +	   [options for TID are now 0x3]
>> +
>> +	 It's nicer if we only print the final options for each TID,
>> +	 and if we only print about it if the options changed compared
>> +	 to the options that were previously set on the thread.  */
>> +      std::unordered_map<thread_info *, gdb_thread_options> set_options;
>> +
>> +      while (*p != '\0')
>> +	{
>> +	  if (p[0] != ';')
>> +	    {
>> +	      write_enn (own_buf);
>> +	      return;
>> +	    }
>> +	  p++;
>> +
>> +	  /* Read the options.  */
>> +
>> +	  gdb_thread_options options = parse_gdb_thread_options (&p);
>> +
>> +	  if ((options & ~supported_options) != 0)
>> +	    {
>> +	      /* GDB asked for an unknown or unsupported option, so
>> +		 error out.  */
>> +	      std::string err
>> +		= string_printf ("E.Unknown option requested: %s\n",
>> +				 hex_string (options));
>> +	      strcpy (own_buf, err.c_str ());
>> +	      return;
>> +	    }
>> +
>> +	  ptid_t ptid;
>> +
>> +	  if (p[0] == ';' || p[0] == '\0')
>> +	    ptid = minus_one_ptid;
>> +	  else if (p[0] == ':')
>> +	    {
>> +	      const char *q;
>> +
>> +	      ptid = read_ptid (p + 1, &q);
>> +
>> +	      if (p == q)
>> +		{
>> +		  write_enn (own_buf);
>> +		  return;
>> +		}
>> +	      p = q;
>> +	      if (p[0] != ';' && p[0] != '\0')
>> +		{
>> +		  write_enn (own_buf);
>> +		  return;
>> +		}
>> +	    }
>> +	  else
>> +	    {
>> +	      write_enn (own_buf);
>> +	      return;
>> +	    }
>> +
>> +	  /* Convert PID.-1 => PID.0 for ptid.matches.  */
>> +	  if (ptid != minus_one_ptid && ptid.lwp () == -1)
> 
> The "ptid != minus_one_ptid" part seems unnecessary.  If the second part
> of the condition is going to match, then for sure ptid is not going to
> be equal to (-1,0,0).

True.  Tweaked.

> 
>> +	    ptid = ptid_t (ptid.pid ());
>> +
>> +	  for_each_thread ([&] (thread_info *thread)
>> +	    {
>> +	      if (ptid_of (thread).matches (ptid))
>> +		set_options[thread] = options;
>> +	    });
>> +	}
>> +
>> +      for (const auto &iter : set_options)
>> +	{
>> +	  thread_info *thread = iter.first;
>> +	  gdb_thread_options options = iter.second;
>> +
>> +	  if (debug_threads && thread->thread_options != options)
>> +	    {
>> +	      debug_printf ("[options for %s are now 0x%x]\n",
>> +			    target_pid_to_str (ptid_of (thread)).c_str (),
>> +			    (unsigned) options);
> 
> This should probably use threads_debug_printf.

Done.

> 
> IWBN to have a small function that formats `options` as an "or" of the
> enabled bits, it would make logs easier to read.  For example if the two
> options are enabled, it would show up as "GDB_TO_CLONE | GDB_TO_EXIT".

Done, in a separate patch.

> 
>> diff --git a/gdbserver/target.h b/gdbserver/target.h
>> index 6c536a30778..33142363a02 100644
>> --- a/gdbserver/target.h
>> +++ b/gdbserver/target.h
>> @@ -277,6 +277,12 @@ class process_stratum_target
>>    /* Returns true if vfork events are supported.  */
>>    virtual bool supports_vfork_events ();
>>  
>> +  /* Returns true if the target supports setting thread options.  If
>> +     options are supported, write into SUPPORTED_OPTIONS the set of
>> +     supported options.  */
>> +  virtual bool supports_set_thread_options
>> +    (gdb_thread_options *supported_options);
> 
> I know I'm the one who used to dislike non-const references, but I have
> accepted it now.  Therefore, this should probably be a reference, if we
> don't intend to be able to pass nullptr.  Or, have the target return an
> optional<gdb_thread_options>?

I got rid of the boolean 'supports/doesn't support' distinction instead,
it's not really useful.  There's no good use for gdbserver supporting the packet
but then supporting no option.  Instead, we'll have this:

+  /* Returns the set of supported thread options.  */
+  virtual gdb_thread_options supported_thread_options ();

and the target can just return 0 to indicate no support.  The callers
end up changing like this:

diff --git c/gdbserver/server.cc w/gdbserver/server.cc
index 307290ed7de..871cfccd74e 100644
--- c/gdbserver/server.cc
+++ w/gdbserver/server.cc
@@ -908,11 +908,11 @@ handle_general_set (char *own_buf)
     {
       const char *p = own_buf + strlen ("QThreadOptions");
 
-      gdb_thread_options supported_options;
-      if (!target_supports_set_thread_options (&supported_options))
+      gdb_thread_options supported_options = target_supported_thread_options ();
+      if (supported_options == 0)
        {
-         /* Something went wrong -- we don't support options, but GDB
-            sent the packet anyway.  */
+         /* Something went wrong -- we don't support any option, but
+            GDB sent the packet anyway.  */
          write_enn (own_buf);
          return;
        }
@@ -2611,8 +2611,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       strcat (own_buf, ";vContSupported+");
 
-      gdb_thread_options supported_options;
-      if (target_supports_set_thread_options (&supported_options))
+      gdb_thread_options supported_options = target_supported_thread_options ();
+      if (supported_options != 0)
        {
          char *end_buf = own_buf + strlen (own_buf);
          sprintf (end_buf, ";QThreadOptions=%s",

  parent reply	other threads:[~2022-12-12 20:15 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-13 22:24 [PATCH v2 00/29] Step over thread clone and thread exit Pedro Alves
2022-07-13 22:24 ` [PATCH v2 01/29] displaced step: pass down target_waitstatus instead of gdb_signal Pedro Alves
2022-07-20 20:21   ` Simon Marchi
2022-07-13 22:24 ` [PATCH v2 02/29] linux-nat: introduce pending_status_str Pedro Alves
2022-07-20 20:38   ` Simon Marchi
2022-07-13 22:24 ` [PATCH v2 03/29] gdb/linux: Delete all other LWPs immediately on ptrace exec event Pedro Alves
2022-07-21  0:45   ` Simon Marchi
2022-07-13 22:24 ` [PATCH v2 04/29] Step over clone syscall w/ breakpoint, TARGET_WAITKIND_THREAD_CLONED Pedro Alves
2022-07-21  1:35   ` Simon Marchi
2022-10-17 18:54     ` Pedro Alves
2022-10-18 12:40     ` [PATCH] Don't explicitly set clone child ptrace options (was: Re: [PATCH v2 04/29] Step over clone syscall w/ breakpoint, TARGET_WAITKIND_THREAD_CLONED) Pedro Alves
2022-10-28 14:50       ` Simon Marchi
2022-12-12 20:13     ` [PATCH v2 04/29] Step over clone syscall w/ breakpoint, TARGET_WAITKIND_THREAD_CLONED Pedro Alves
2022-07-13 22:24 ` [PATCH v2 05/29] Support clone events in the remote protocol Pedro Alves
2022-07-21  2:25   ` Simon Marchi
2022-12-12 20:14     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 06/29] Avoid duplicate QThreadEvents packets Pedro Alves
2022-07-21  2:30   ` Simon Marchi
2022-12-12 20:14     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 07/29] Thread options & clone events (core + remote) Pedro Alves
2022-07-21  3:14   ` Simon Marchi
2022-10-27 19:55     ` [PATCH] enum_flags to_string (was: Re: [PATCH v2 07/29] Thread options & clone events (core + remote)) Pedro Alves
2022-10-28 10:26       ` [PATCH v2] " Pedro Alves
2022-10-28 11:08         ` [PATCH v3] " Pedro Alves
2022-10-28 15:59           ` Simon Marchi
2022-10-28 18:23             ` Pedro Alves
2022-10-31 12:47               ` Simon Marchi
2022-11-07 17:26                 ` [PATCH v5] " Pedro Alves
2022-11-07 18:29                   ` Simon Marchi
2022-11-08 14:56                     ` Pedro Alves
2022-12-12 20:15     ` Pedro Alves [this message]
2022-07-13 22:24 ` [PATCH v2 08/29] Thread options & clone events (native Linux) Pedro Alves
2022-07-21 12:38   ` Simon Marchi
2022-12-12 20:16     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 09/29] Thread options & clone events (Linux GDBserver) Pedro Alves
2022-07-21 13:11   ` Simon Marchi
2022-12-12 20:16     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 10/29] gdbserver: Hide and don't detach pending clone children Pedro Alves
2022-07-13 22:24 ` [PATCH v2 11/29] Remove gdb/19675 kfails (displaced stepping + clone) Pedro Alves
2022-07-13 22:24 ` [PATCH v2 12/29] Add test for stepping over clone syscall Pedro Alves
2022-07-13 22:24 ` [PATCH v2 13/29] all-stop/synchronous RSP support thread-exit events Pedro Alves
2022-07-13 22:24 ` [PATCH v2 14/29] gdbserver/linux-low.cc: Ignore event_ptid if TARGET_WAITKIND_IGNORE Pedro Alves
2022-07-13 22:24 ` [PATCH v2 15/29] Introduce GDB_TO_EXIT thread option, fix step-over-thread-exit Pedro Alves
2022-07-13 22:24 ` [PATCH v2 16/29] Implement GDB_TO_EXIT support for Linux GDBserver Pedro Alves
2022-07-13 22:24 ` [PATCH v2 17/29] Implement GDB_TO_EXIT support for native Linux Pedro Alves
2022-07-21 15:26   ` Simon Marchi
2022-12-12 20:17     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 18/29] gdb: clear step over information on thread exit (PR gdb/27338) Pedro Alves
2022-07-21 18:12   ` Simon Marchi
2022-12-12 20:18     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 19/29] stop_all_threads: (re-)enable async before waiting for stops Pedro Alves
2022-07-21 18:21   ` Simon Marchi
2022-12-12 20:18     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 20/29] gdbserver: Queue no-resumed event after thread exit Pedro Alves
2022-07-13 22:24 ` [PATCH v2 21/29] Don't resume new threads if scheduler-locking is in effect Pedro Alves
2022-07-14  5:28   ` Eli Zaretskii
2022-07-21 18:49   ` Simon Marchi
2022-12-12 20:19     ` Pedro Alves
2022-07-13 22:24 ` [PATCH v2 22/29] Report thread exit event for leader if reporting thread exit events Pedro Alves
2022-07-13 22:24 ` [PATCH v2 23/29] Ignore failure to read PC when resuming Pedro Alves
2022-07-13 22:24 ` [PATCH v2 24/29] gdb/testsuite/lib/my-syscalls.S: Refactor new SYSCALL macro Pedro Alves
2022-07-13 22:24 ` [PATCH v2 25/29] Testcases for stepping over thread exit syscall (PR gdb/27338) Pedro Alves
2022-07-13 22:24 ` [PATCH v2 26/29] Document remote clone events, and QThreadOptions packet Pedro Alves
2022-07-14  5:27   ` Eli Zaretskii
2022-07-13 22:24 ` [PATCH v2 27/29] inferior::clear_thread_list always silent Pedro Alves
2022-07-13 22:24 ` [PATCH v2 28/29] Centralize "[Thread ...exited]" notifications Pedro Alves
2022-07-13 22:24 ` [PATCH v2 29/29] Cancel execution command on thread exit, when stepping, nexting, etc Pedro Alves
2022-07-21 19:28 ` [PATCH v2 00/29] Step over thread clone and thread exit Simon Marchi
2022-10-03 13:46 ` Tom de Vries
2022-10-03 18:37   ` Tom de Vries
2022-12-12 20:20     ` Pedro Alves
2022-12-12 20:19   ` Pedro Alves

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=d22bb485-e969-cbbb-9f56-c185399a5c55@palves.net \
    --to=pedro@palves.net \
    --cc=gdb-patches@sourceware.org \
    --cc=simark@simark.ca \
    /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;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).