btrace, gdbserver: check btrace target pointers

Message ID 1519727985-17914-1-git-send-email-markus.t.metzger@intel.com
State New, archived
Headers

Commit Message

Metzger, Markus T Feb. 27, 2018, 10:39 a.m. UTC
  By removing the supports_btrace gdbserver target method we relied on GDB trying
to enable branch tracing and failing on the attempt.

For targets that do not provide the btrace methods, however, an initial request
from GDB for the branch trace configuration to detect whether gdbserver is
already recording resulted in a protocol error.

Have the btrace target methods throw a "Target does not suppor branch tracing"
error and be prepared to handle exceptions in all functions that call btrace
target methods.  We therefore turn the target_* macros into static inline
functions.

Also remove the additional btrace target method checks that resulted in the
above protocol error.

Thanks to Maciej W. Rozycki <macro@mips.com> for reporting this.

2018-02-27  Markus Metzger  <markus.t.metzger@intel.com>

gdbserver/
	* target.h (target_enable_btrace, target_disable_btrace,
	target_read_btrace, target_read_btrace_conf): Turn macro into inline
	function.  Throw error if target method not defined.
	* server.c (handle_qxfer_btrace, handle_qxfer_btrace_conf): Remove
	check for btrace target method.  Be prepared to handle exceptions
	from btrace target methods.
---
 gdb/gdbserver/server.c | 38 ++++++++++++++++++++++++++++----------
 gdb/gdbserver/target.h | 42 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 62 insertions(+), 18 deletions(-)
  

Comments

Yao Qi Feb. 28, 2018, 4:25 p.m. UTC | #1
Markus Metzger <markus.t.metzger@intel.com> writes:

> -#define target_enable_btrace(ptid, conf) \
> -  (*the_target->enable_btrace) (ptid, conf)
> +static inline struct btrace_target_info *
> +target_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
> +{
> +  if (the_target->enable_btrace == nullptr)
> +    error ("Target does not support branch tracing.");
> +
> +  return (*the_target->enable_btrace) (ptid, conf);
> +}

It is reasonable to me that (*the_target->enable_btrace) may throw
various exceptions due to different reasons, but I am not convinced that
we should error on (the_target->enable_btrace == nullptr).  I don't like
replacing control flow logic with exception.  This is my personal
flavor.  Instead, can we check
(the_target->enable_btrace == nullptr) before using
target_enable_btrace, and error in handle_btrace_general_set if
thread->btrace is NULL.  What do you think?
  
Metzger, Markus T Feb. 28, 2018, 5:10 p.m. UTC | #2
Hello Yao,

Thanks for your review.


> > -#define target_enable_btrace(ptid, conf) \

> > -  (*the_target->enable_btrace) (ptid, conf)

> > +static inline struct btrace_target_info *

> > +target_enable_btrace (ptid_t ptid, const struct btrace_config *conf)

> > +{

> > +  if (the_target->enable_btrace == nullptr)

> > +    error ("Target does not support branch tracing.");

> > +

> > +  return (*the_target->enable_btrace) (ptid, conf);

> > +}

> 

> It is reasonable to me that (*the_target->enable_btrace) may throw

> various exceptions due to different reasons, but I am not convinced that

> we should error on (the_target->enable_btrace == nullptr).  I don't like

> replacing control flow logic with exception.  This is my personal

> flavor.  Instead, can we check

> (the_target->enable_btrace == nullptr) before using

> target_enable_btrace, and error in handle_btrace_general_set if

> thread->btrace is NULL.  What do you think?


I moved two of those checks into the target_* method since I thought
it would be a good idea to be able to handle exceptions and since I had
to add the TRY/CATCH/CATCH_END, anyway, I thought it made sense
to handle the nullptr case also via exceptions.

If we're not doing it that way, the caller would need to check the pointer
(either directly or via a *_p () function like we do with gdbarch) and still
be prepared to handle exceptions from the actual call.

My motivation was to simplify the caller but if you think the other way
is clearer I can move the check back into the callers.  Is that your preference?

Regards,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Yao Qi March 1, 2018, 9:14 a.m. UTC | #3
On Wed, Feb 28, 2018 at 5:10 PM, Metzger, Markus T
<markus.t.metzger@intel.com> wrote:
>
> I moved two of those checks into the target_* method since I thought
> it would be a good idea to be able to handle exceptions and since I had
> to add the TRY/CATCH/CATCH_END, anyway, I thought it made sense
> to handle the nullptr case also via exceptions.
>
> If we're not doing it that way, the caller would need to check the pointer
> (either directly or via a *_p () function like we do with gdbarch) and still
> be prepared to handle exceptions from the actual call.
>
> My motivation was to simplify the caller but if you think the other way
> is clearer I can move the check back into the callers.  Is that your preference?
>

I don't have a strong opinion on this.  It is more about personal flavour of
writing code.  You are the major btrace contributor, so it is better to keep
the code in a way you prefer.  Patch is good to me, please push.
  
Pedro Alves March 1, 2018, 11:09 a.m. UTC | #4
On 03/01/2018 09:14 AM, Yao Qi wrote:
> On Wed, Feb 28, 2018 at 5:10 PM, Metzger, Markus T
> <markus.t.metzger@intel.com> wrote:
>>
>> I moved two of those checks into the target_* method since I thought
>> it would be a good idea to be able to handle exceptions and since I had
>> to add the TRY/CATCH/CATCH_END, anyway, I thought it made sense
>> to handle the nullptr case also via exceptions.
>>
>> If we're not doing it that way, the caller would need to check the pointer
>> (either directly or via a *_p () function like we do with gdbarch) and still
>> be prepared to handle exceptions from the actual call.
>>
>> My motivation was to simplify the caller but if you think the other way
>> is clearer I can move the check back into the callers.  Is that your preference?
>>
> 
> I don't have a strong opinion on this.  It is more about personal flavour of
> writing code.  You are the major btrace contributor, so it is better to keep
> the code in a way you prefer.  Patch is good to me, please push.

To me too.

A comment based on my experience hacking on the multi-target branch:

I like imagining how the code would look like if it were C++-ified already.

Here, if gdbserver's target_ops function pointers are converted to C++ methods,
then it'll no longer be possible to check for null pointer.  [1]

So, whatever we do, I think checking for null pointers in function
pointer tables is the option that just kicks the can down the road.

The only options here then will be error out or check a *_p() function upfront.
The latter is another way to say, go back to having a supports_btrace method,
I guess.

[*] - I've run into some cases like these in my gdb target_ops C++-ification
in my multi-target branch.  Like e.g., I had to add
target_ops::can_create_inferior to replace target_ops->to_create_inferior
null pointer checks.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index cb02b58..4fd274f 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1818,7 +1818,7 @@  handle_qxfer_btrace (const char *annex,
   enum btrace_read_type type;
   int result;
 
-  if (the_target->read_btrace == NULL || writebuf != NULL)
+  if (writebuf != NULL)
     return -2;
 
   if (ptid_equal (general_thread, null_ptid)
@@ -1857,12 +1857,21 @@  handle_qxfer_btrace (const char *annex,
     {
       buffer_free (&cache);
 
-      result = target_read_btrace (thread->btrace, &cache, type);
-      if (result != 0)
+      TRY
 	{
-	  memcpy (own_buf, cache.buffer, cache.used_size);
-	  return -3;
+	  result = target_read_btrace (thread->btrace, &cache, type);
+	  if (result != 0)
+	    memcpy (own_buf, cache.buffer, cache.used_size);
 	}
+      CATCH (exception, RETURN_MASK_ERROR)
+	{
+	  sprintf (own_buf, "E.%s", exception.message);
+	  result = -1;
+	}
+      END_CATCH
+
+      if (result != 0)
+	return -3;
     }
   else if (offset > cache.used_size)
     {
@@ -1889,7 +1898,7 @@  handle_qxfer_btrace_conf (const char *annex,
   struct thread_info *thread;
   int result;
 
-  if (the_target->read_btrace_conf == NULL || writebuf != NULL)
+  if (writebuf != NULL)
     return -2;
 
   if (annex[0] != '\0')
@@ -1919,12 +1928,21 @@  handle_qxfer_btrace_conf (const char *annex,
     {
       buffer_free (&cache);
 
-      result = target_read_btrace_conf (thread->btrace, &cache);
-      if (result != 0)
+      TRY
 	{
-	  memcpy (own_buf, cache.buffer, cache.used_size);
-	  return -3;
+	  result = target_read_btrace_conf (thread->btrace, &cache);
+	  if (result != 0)
+	    memcpy (own_buf, cache.buffer, cache.used_size);
 	}
+      CATCH (exception, RETURN_MASK_ERROR)
+	{
+	  sprintf (own_buf, "E.%s", exception.message);
+	  result = -1;
+	}
+      END_CATCH
+
+      if (result != 0)
+	return -3;
     }
   else if (offset > cache.used_size)
     {
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index dcefe1a..ff38c70 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -620,17 +620,43 @@  int kill_inferior (int);
   (the_target->supports_agent ? \
    (*the_target->supports_agent) () : 0)
 
-#define target_enable_btrace(ptid, conf) \
-  (*the_target->enable_btrace) (ptid, conf)
+static inline struct btrace_target_info *
+target_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
+{
+  if (the_target->enable_btrace == nullptr)
+    error ("Target does not support branch tracing.");
+
+  return (*the_target->enable_btrace) (ptid, conf);
+}
+
+static inline int
+target_disable_btrace (struct btrace_target_info *tinfo)
+{
+  if (the_target->disable_btrace == nullptr)
+    error ("Target does not support branch tracing.");
 
-#define target_disable_btrace(tinfo) \
-  (*the_target->disable_btrace) (tinfo)
+  return (*the_target->disable_btrace) (tinfo);
+}
 
-#define target_read_btrace(tinfo, buffer, type)	\
-  (*the_target->read_btrace) (tinfo, buffer, type)
+static inline int
+target_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
+		    enum btrace_read_type type)
+{
+  if (the_target->read_btrace == nullptr)
+    error ("Target does not support branch tracing.");
+
+  return (*the_target->read_btrace) (tinfo, buffer, type);
+}
+
+static inline int
+target_read_btrace_conf (struct btrace_target_info *tinfo,
+			 struct buffer *buffer)
+{
+  if (the_target->read_btrace_conf == nullptr)
+    error ("Target does not support branch tracing.");
 
-#define target_read_btrace_conf(tinfo, buffer)	\
-  (*the_target->read_btrace_conf) (tinfo, buffer)
+  return (*the_target->read_btrace_conf) (tinfo, buffer);
+}
 
 #define target_supports_range_stepping() \
   (the_target->supports_range_stepping ? \