[v2,2/4] Use the existing instruction to determine the RISC-V breakpoint kind.

Message ID 20180924205151.22217-3-jhb@FreeBSD.org
State New, archived
Headers

Commit Message

John Baldwin Sept. 24, 2018, 8:51 p.m. UTC
  RISC-V supports instructions of varying lengths.  Standard existing
instructions in the base ISA are 4 bytes in length, but the 'C'
extension adds support for compressed, 2 byte instructions.  RISC-V
supports two different breakpoint instructions: EBREAK is a 4 byte
instruction in the base ISA, and C.EBREAK is a 2 byte instruction only
available on processors implementing the 'C' extension.  Using EBREAK
to set breakpoints on compressed instructions causes problems as the
second half of EBREAK will overwrite the first 2 bytes of the
following instruction breaking other threads in the process if their
PC is the following instruction.  Thus, breakpoints on compressed
instructions need to use C.EBREAK instead of EBREAK.

Previously, the riscv architecture checked the MISA register to
determine if the 'C' extension was available.  If so, it used C.EBREAK
for all breakpoints.  However, the MISA register is not necessarily
available to supervisor mode operating systems.  While native targets
could provide a fake MISA register value, this patch instead examines
the existing instruction at a breakpoint target to determine which
breakpoint instruction to use.  If the existing instruction is a
compressed instruction, C.EBREAK is used, otherwise EBREAK is used.

gdb/ChangeLog:

	* disasm-selftests.c (print_one_insn_test): Add bfd_arch_riscv to
	case with explicit breakpoint kind.
	* riscv-tdep.c (show_use_compressed_breakpoints): Remove
	'additional_info' and related logic.
	(riscv_debug_breakpoints): New variable.
	(riscv_breakpoint_kind_from_pc): Use the length of the existing
	instruction to determine the breakpoint kind.
	(_initialize_riscv_tdep): Add 'set/show debug riscv breakpoints'
	flag.  Update description of 'set/show riscv
	use-compressed-breakpoints' flag.
---
 gdb/ChangeLog          | 13 +++++++++++
 gdb/disasm-selftests.c |  7 +++---
 gdb/riscv-tdep.c       | 50 ++++++++++++++++++++++++++++--------------
 3 files changed, 51 insertions(+), 19 deletions(-)
  

Comments

Simon Marchi Sept. 27, 2018, 7:47 p.m. UTC | #1
On 2018-09-24 04:51 PM, John Baldwin wrote:
> @@ -417,7 +411,21 @@ riscv_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)

>  {

>    if (use_compressed_breakpoints == AUTO_BOOLEAN_AUTO)

>      {

> -      if (riscv_has_feature (gdbarch, 'C'))

> +      gdb_byte buf[1];

> +      int status;

> +

> +      /* Read the opcode byte to determine the instruction length.  */

> +      status = target_read_code (*pcptr, buf, 1);

> +      if (status)

> +	memory_error (TARGET_XFER_E_IO, *pcptr);


Here you could use read_code if you want, which takes care of throwing an exception
on failure.

Based on the discussion you guys had about v1, this looks good to me, but the RISC-V
experts should take a look.

Simon
  
Andrew Burgess Sept. 28, 2018, 10:23 a.m. UTC | #2
* John Baldwin <jhb@FreeBSD.org> [2018-09-24 13:51:49 -0700]:

> RISC-V supports instructions of varying lengths.  Standard existing
> instructions in the base ISA are 4 bytes in length, but the 'C'
> extension adds support for compressed, 2 byte instructions.  RISC-V
> supports two different breakpoint instructions: EBREAK is a 4 byte
> instruction in the base ISA, and C.EBREAK is a 2 byte instruction only
> available on processors implementing the 'C' extension.  Using EBREAK
> to set breakpoints on compressed instructions causes problems as the
> second half of EBREAK will overwrite the first 2 bytes of the
> following instruction breaking other threads in the process if their
> PC is the following instruction.  Thus, breakpoints on compressed
> instructions need to use C.EBREAK instead of EBREAK.
> 
> Previously, the riscv architecture checked the MISA register to
> determine if the 'C' extension was available.  If so, it used C.EBREAK
> for all breakpoints.  However, the MISA register is not necessarily
> available to supervisor mode operating systems.  While native targets
> could provide a fake MISA register value, this patch instead examines
> the existing instruction at a breakpoint target to determine which
> breakpoint instruction to use.  If the existing instruction is a
> compressed instruction, C.EBREAK is used, otherwise EBREAK is used.
> 
> gdb/ChangeLog:
> 
> 	* disasm-selftests.c (print_one_insn_test): Add bfd_arch_riscv to
> 	case with explicit breakpoint kind.
> 	* riscv-tdep.c (show_use_compressed_breakpoints): Remove
> 	'additional_info' and related logic.
> 	(riscv_debug_breakpoints): New variable.
> 	(riscv_breakpoint_kind_from_pc): Use the length of the existing
> 	instruction to determine the breakpoint kind.
> 	(_initialize_riscv_tdep): Add 'set/show debug riscv breakpoints'
> 	flag.  Update description of 'set/show riscv
> 	use-compressed-breakpoints' flag.
> ---
>  gdb/ChangeLog          | 13 +++++++++++
>  gdb/disasm-selftests.c |  7 +++---
>  gdb/riscv-tdep.c       | 50 ++++++++++++++++++++++++++++--------------
>  3 files changed, 51 insertions(+), 19 deletions(-)
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 2624975146..f56b0487cc 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,16 @@
> +2018-09-24  John Baldwin  <jhb@FreeBSD.org>
> +
> +	* disasm-selftests.c (print_one_insn_test): Add bfd_arch_riscv to
> +	case with explicit breakpoint kind.
> +	* riscv-tdep.c (show_use_compressed_breakpoints): Remove
> +	'additional_info' and related logic.
> +	(riscv_debug_breakpoints): New variable.
> +	(riscv_breakpoint_kind_from_pc): Use the length of the existing
> +	instruction to determine the breakpoint kind.
> +	(_initialize_riscv_tdep): Add 'set/show debug riscv breakpoints'
> +	flag.  Update description of 'set/show riscv
> +	use-compressed-breakpoints' flag.
> +
>  2018-09-24  John Baldwin  <jhb@FreeBSD.org>
>  
>  	* trad-frame.c (trad_frame_set_regmap, trad_frame_set_reg_regmap):
> diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
> index f7d0ecca0e..8cc267631e 100644
> --- a/gdb/disasm-selftests.c
> +++ b/gdb/disasm-selftests.c
> @@ -77,9 +77,10 @@ print_one_insn_test (struct gdbarch *gdbarch)
>        /* fall through */
>      case bfd_arch_nios2:
>      case bfd_arch_score:
> -      /* nios2 and score need to know the current instruction to select
> -	 breakpoint instruction.  Give the breakpoint instruction kind
> -	 explicitly.  */
> +    case bfd_arch_riscv:
> +      /* nios2, riscv, and score need to know the current instruction
> +	 to select breakpoint instruction.  Give the breakpoint
> +	 instruction kind explicitly.  */
>        int bplen;
>        insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 4, &bplen);
>        len = bplen;
> diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
> index 254914c9c7..7faa7d01f0 100644
> --- a/gdb/riscv-tdep.c
> +++ b/gdb/riscv-tdep.c
> @@ -210,20 +210,9 @@ show_use_compressed_breakpoints (struct ui_file *file, int from_tty,
>  				 struct cmd_list_element *c,
>  				 const char *value)
>  {
> -  const char *additional_info;
> -  struct gdbarch *gdbarch = target_gdbarch ();
> -
> -  if (use_compressed_breakpoints == AUTO_BOOLEAN_AUTO)
> -    if (riscv_has_feature (gdbarch, 'C'))
> -      additional_info = _(" (currently on)");
> -    else
> -      additional_info = _(" (currently off)");
> -  else
> -    additional_info = "";
> -
>    fprintf_filtered (file,
>  		    _("Debugger's use of compressed breakpoints is set "
> -		      "to %s%s.\n"), value, additional_info);
> +		      "to %s.\n"), value);
>  }
>  
>  /* The set and show lists for 'set riscv' and 'show riscv' prefixes.  */
> @@ -284,6 +273,11 @@ show_riscv_debug_variable (struct ui_file *file, int from_tty,
>  		    c->name, value);
>  }
>  
> +/* When this is set to non-zero debugging information about breakpoint
> +   kinds will be printed.  */
> +
> +static unsigned int riscv_debug_breakpoints = 0;
> +
>  /* When this is set to non-zero debugging information about inferior calls
>     will be printed.  */
>  
> @@ -417,7 +411,21 @@ riscv_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)
>  {
>    if (use_compressed_breakpoints == AUTO_BOOLEAN_AUTO)
>      {
> -      if (riscv_has_feature (gdbarch, 'C'))
> +      gdb_byte buf[1];
> +      int status;
> +
> +      /* Read the opcode byte to determine the instruction length.  */
> +      status = target_read_code (*pcptr, buf, 1);
> +      if (status)
> +	memory_error (TARGET_XFER_E_IO, *pcptr);
> +
> +      if (riscv_debug_breakpoints)
> +	fprintf_unfiltered
> +	  (gdb_stdlog,
> +	   "Using %s for breakpoint at %s (instruction length %d)\n",
> +	   riscv_insn_length (buf[0]) == 2 ? "C.EBREAK" : "EBREAK",
> +	   paddress (gdbarch, *pcptr), riscv_insn_length (buf[0]));
> +      if (riscv_insn_length (buf[0]) == 2)
>  	return 2;
>        else
>  	return 4;
> @@ -2936,6 +2944,16 @@ _initialize_riscv_tdep (void)
>  		  &showdebugriscvcmdlist, "show debug riscv ", 0,
>  		  &showdebuglist);
>  
> +  add_setshow_zuinteger_cmd ("breakpoints", class_maintenance,
> +			     &riscv_debug_breakpoints,  _("\
> +Set riscv breakpoint debugging."), _("\
> +Show riscv breakpoint debugging."), _("\
> +When non-zero, print debugging information for the riscv specific parts\n\
> +of the breakpoint mechanism."),
> +			     NULL,
> +			     show_riscv_debug_variable,
> +			     &setdebugriscvcmdlist, &showdebugriscvcmdlist);
> +
>    add_setshow_zuinteger_cmd ("infcall", class_maintenance,
>  			     &riscv_debug_infcall,  _("\
>  Set riscv inferior call debugging."), _("\
> @@ -2973,9 +2991,9 @@ of the stack unwinding mechanism."),
>  Set debugger's use of compressed breakpoints."), _("	\
>  Show debugger's use of compressed breakpoints."), _("\
>  Debugging compressed code requires compressed breakpoints to be used. If\n \
> -left to 'auto' then gdb will use them if $misa indicates the C extension\n \
> -is supported. If that doesn't give the correct behavior, then this option\n\
> -can be used."),
> +left to 'auto' then gdb will use them if the existing instruction is a\n \
> +compressed instruction. If that doesn't give the correct behavior, then\n \
> +this option can be used."),

While you're editing these lines could you remove the trailing
whitespace please, so '\n\' instead of '\n \', the latter doesn't seem
to be the norm in GDB doc strings.

Feel free to switch 'read_code' as Simon suggested.

Otherwise this is fine to commit.

Thanks,
Andrew

>  				NULL,
>  				show_use_compressed_breakpoints,
>  				&setriscvcmdlist,
> -- 
> 2.18.0
>
  
John Baldwin Sept. 28, 2018, 9:16 p.m. UTC | #3
On 9/28/18 3:23 AM, Andrew Burgess wrote:
> While you're editing these lines could you remove the trailing
> whitespace please, so '\n\' instead of '\n \', the latter doesn't seem
> to be the norm in GDB doc strings.
> 
> Feel free to switch 'read_code' as Simon suggested.
> 
> Otherwise this is fine to commit.

Ok, I've pushed it with both of these fixes applied.
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 2624975146..f56b0487cc 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,16 @@ 
+2018-09-24  John Baldwin  <jhb@FreeBSD.org>
+
+	* disasm-selftests.c (print_one_insn_test): Add bfd_arch_riscv to
+	case with explicit breakpoint kind.
+	* riscv-tdep.c (show_use_compressed_breakpoints): Remove
+	'additional_info' and related logic.
+	(riscv_debug_breakpoints): New variable.
+	(riscv_breakpoint_kind_from_pc): Use the length of the existing
+	instruction to determine the breakpoint kind.
+	(_initialize_riscv_tdep): Add 'set/show debug riscv breakpoints'
+	flag.  Update description of 'set/show riscv
+	use-compressed-breakpoints' flag.
+
 2018-09-24  John Baldwin  <jhb@FreeBSD.org>
 
 	* trad-frame.c (trad_frame_set_regmap, trad_frame_set_reg_regmap):
diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
index f7d0ecca0e..8cc267631e 100644
--- a/gdb/disasm-selftests.c
+++ b/gdb/disasm-selftests.c
@@ -77,9 +77,10 @@  print_one_insn_test (struct gdbarch *gdbarch)
       /* fall through */
     case bfd_arch_nios2:
     case bfd_arch_score:
-      /* nios2 and score need to know the current instruction to select
-	 breakpoint instruction.  Give the breakpoint instruction kind
-	 explicitly.  */
+    case bfd_arch_riscv:
+      /* nios2, riscv, and score need to know the current instruction
+	 to select breakpoint instruction.  Give the breakpoint
+	 instruction kind explicitly.  */
       int bplen;
       insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 4, &bplen);
       len = bplen;
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 254914c9c7..7faa7d01f0 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -210,20 +210,9 @@  show_use_compressed_breakpoints (struct ui_file *file, int from_tty,
 				 struct cmd_list_element *c,
 				 const char *value)
 {
-  const char *additional_info;
-  struct gdbarch *gdbarch = target_gdbarch ();
-
-  if (use_compressed_breakpoints == AUTO_BOOLEAN_AUTO)
-    if (riscv_has_feature (gdbarch, 'C'))
-      additional_info = _(" (currently on)");
-    else
-      additional_info = _(" (currently off)");
-  else
-    additional_info = "";
-
   fprintf_filtered (file,
 		    _("Debugger's use of compressed breakpoints is set "
-		      "to %s%s.\n"), value, additional_info);
+		      "to %s.\n"), value);
 }
 
 /* The set and show lists for 'set riscv' and 'show riscv' prefixes.  */
@@ -284,6 +273,11 @@  show_riscv_debug_variable (struct ui_file *file, int from_tty,
 		    c->name, value);
 }
 
+/* When this is set to non-zero debugging information about breakpoint
+   kinds will be printed.  */
+
+static unsigned int riscv_debug_breakpoints = 0;
+
 /* When this is set to non-zero debugging information about inferior calls
    will be printed.  */
 
@@ -417,7 +411,21 @@  riscv_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)
 {
   if (use_compressed_breakpoints == AUTO_BOOLEAN_AUTO)
     {
-      if (riscv_has_feature (gdbarch, 'C'))
+      gdb_byte buf[1];
+      int status;
+
+      /* Read the opcode byte to determine the instruction length.  */
+      status = target_read_code (*pcptr, buf, 1);
+      if (status)
+	memory_error (TARGET_XFER_E_IO, *pcptr);
+
+      if (riscv_debug_breakpoints)
+	fprintf_unfiltered
+	  (gdb_stdlog,
+	   "Using %s for breakpoint at %s (instruction length %d)\n",
+	   riscv_insn_length (buf[0]) == 2 ? "C.EBREAK" : "EBREAK",
+	   paddress (gdbarch, *pcptr), riscv_insn_length (buf[0]));
+      if (riscv_insn_length (buf[0]) == 2)
 	return 2;
       else
 	return 4;
@@ -2936,6 +2944,16 @@  _initialize_riscv_tdep (void)
 		  &showdebugriscvcmdlist, "show debug riscv ", 0,
 		  &showdebuglist);
 
+  add_setshow_zuinteger_cmd ("breakpoints", class_maintenance,
+			     &riscv_debug_breakpoints,  _("\
+Set riscv breakpoint debugging."), _("\
+Show riscv breakpoint debugging."), _("\
+When non-zero, print debugging information for the riscv specific parts\n\
+of the breakpoint mechanism."),
+			     NULL,
+			     show_riscv_debug_variable,
+			     &setdebugriscvcmdlist, &showdebugriscvcmdlist);
+
   add_setshow_zuinteger_cmd ("infcall", class_maintenance,
 			     &riscv_debug_infcall,  _("\
 Set riscv inferior call debugging."), _("\
@@ -2973,9 +2991,9 @@  of the stack unwinding mechanism."),
 Set debugger's use of compressed breakpoints."), _("	\
 Show debugger's use of compressed breakpoints."), _("\
 Debugging compressed code requires compressed breakpoints to be used. If\n \
-left to 'auto' then gdb will use them if $misa indicates the C extension\n \
-is supported. If that doesn't give the correct behavior, then this option\n\
-can be used."),
+left to 'auto' then gdb will use them if the existing instruction is a\n \
+compressed instruction. If that doesn't give the correct behavior, then\n \
+this option can be used."),
 				NULL,
 				show_use_compressed_breakpoints,
 				&setriscvcmdlist,