aarch64: detect atomic sequences like other ll/sc architectures

Message ID 20140324161056.GB23291@redacted.bos.redhat.com
State Superseded
Headers

Commit Message

Kyle McMartin March 24, 2014, 4:10 p.m. UTC
  Add similar single-stepping over atomic sequences support like other
load-locked/store-conditional architectures (alpha, powerpc, arm, etc.)
do. Verified the decode_masked_match, and decode_bcond works against the
atomic sequences used in the Linux kernel atomic.h, and also gcc
libatomic. Thanks to Richard Henderson for feedback on my initial
attempt at this patch!

2014-03-23  Kyle McMartin  <kyle@redhat.com>

	* aarch64-tdep.c (aarch64_deal_with_atomic_sequence): New function.
	(aarch64_gdbarch_init): Handle single stepping of atomic sequences
	with aarch64_deal_with_atomic_sequence.
  

Comments

Pedro Alves March 24, 2014, 4:24 p.m. UTC | #1
Hi!

On 03/24/2014 04:10 PM, Kyle McMartin wrote:
> Add similar single-stepping over atomic sequences support like other
> load-locked/store-conditional architectures (alpha, powerpc, arm, etc.)
> do. Verified the decode_masked_match, and decode_bcond works against the
> atomic sequences used in the Linux kernel atomic.h, and also gcc
> libatomic. Thanks to Richard Henderson for feedback on my initial
> attempt at this patch!

Thanks!  It'd be nice to have a test in the test suite.  Could you
add one?

PPC64's equivalent seems to be gdb.arch/ppc64-atomic-inst.c|exp.
  
Kyle McMartin March 24, 2014, 4:28 p.m. UTC | #2
On Mon, Mar 24, 2014 at 04:24:58PM +0000, Pedro Alves wrote:
> Hi!
> 
> On 03/24/2014 04:10 PM, Kyle McMartin wrote:
> > Add similar single-stepping over atomic sequences support like other
> > load-locked/store-conditional architectures (alpha, powerpc, arm, etc.)
> > do. Verified the decode_masked_match, and decode_bcond works against the
> > atomic sequences used in the Linux kernel atomic.h, and also gcc
> > libatomic. Thanks to Richard Henderson for feedback on my initial
> > attempt at this patch!
> 
> Thanks!  It'd be nice to have a test in the test suite.  Could you
> add one?
> 
> PPC64's equivalent seems to be gdb.arch/ppc64-atomic-inst.c|exp.
> 

Absolutely, I'll work on a set of tests for this today.

--Kyle
  
Joel Brobecker March 24, 2014, 4:57 p.m. UTC | #3
Hello Kyle,

> 2014-03-23  Kyle McMartin  <kyle@redhat.com>
> 
> 	* aarch64-tdep.c (aarch64_deal_with_atomic_sequence): New function.
> 	(aarch64_gdbarch_init): Handle single stepping of atomic sequences
> 	with aarch64_deal_with_atomic_sequence.

Some small commments...

The convention for functions implementing gdbarch hooks has been
to name the callback (nearly) the same as the gdbarch hook. In your
case, the function should be called aarch64_software_single_step.

The rest of my comments are mostly Coding Style. GDB follows the GNU
Coding Style with some additional requirements.
https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards

> +static int
> +aarch64_deal_with_atomic_sequence (struct frame_info *frame)

An introductory comment is required for all functions, now.
In this case, since it implements a gdbarch hook which is expected
to be already documented (in gdbarch.sh, gdbarch.h), you only need
to say:

/* Implements the "software_single_step" gdbarch method.  */

I would probably add a comment explaining that you only deal with
atomic sequences in this implementation.  That way, we know why
you had to implement that hook for AArch64, and what its scope is;
and you then won't need to add the comment next to the
set_gdbarch_software_single_step call.

> +{
> +  struct gdbarch *gdbarch = get_frame_arch (frame);
> +  struct address_space *aspace = get_frame_address_space (frame);
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  const int insn_size = 4;
> +  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
> +  CORE_ADDR pc = get_frame_pc (frame);
> +  CORE_ADDR breaks[2] = { -1, -1 };
> +  CORE_ADDR loc = pc;
> +  CORE_ADDR closing_insn = 0;
> +  uint32_t insn = read_memory_unsigned_integer (loc, insn_size, byte_order);
> +  int index;
> +  int insn_count;
> +  int bc_insn_count = 0; /* Conditional branch instruction count.  */
> +  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
> +
> +  /* look for a load-exclusive to begin the sequence... */
> +  if (!decode_masked_match(insn, 0x3fc00000, 0x08400000))

Missing space before the '('.

> +    return 0;
> +
> +  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
> +    {
> +      int32_t offset;
> +      unsigned cond;
> +
> +      loc += insn_size;
> +      insn = read_memory_unsigned_integer (loc, insn_size, byte_order);
> +
> +      /* look for a conditional branch to set a breakpoint on the destination. */

This line looks too long?

> +      if (decode_bcond(loc, insn, &cond, &offset))

Missing space before '('.

> +	{
> +
> +	  if (bc_insn_count >= 1)
> +	    return 0;
> +
> +	  breaks[1] = loc + offset;
> +
> +	  bc_insn_count++;
> +	  last_breakpoint++;
> +	}
> +
> +      /* and the matching store-exclusive to close it. */
> +      if (decode_masked_match(insn, 0x3fc00000, 0x08000000))

Same here...

> +	{
> +          closing_insn = loc;
> +	  break;
> +	}
> +    }
> +
> +  /* didn't find a stxr to end the sequence... */
> +  if (!closing_insn)
> +    return 0;
> +
> +  loc += insn_size;
> +  insn = read_memory_unsigned_integer (loc, insn_size, byte_order);
> +
> +  /* insert breakpoint at the end of the atomic sequence */
> +  breaks[0] = loc;
> +
> +  /* check for duplicated breakpoints. also check for a breakpoint placed on
> +   * the conditional branch destination isn't within the sequence. */

No '*' on the second line...

> +  if (last_breakpoint &&
> +      (breaks[1] == breaks[0] ||
> +       (breaks[1] >= pc && breaks[1] <= closing_insn)))

Binary operators should be at the start of the next line.

> +    last_breakpoint = 0;
> +
> +  /* insert the breakpoint at the end of the sequence, also possibly at the
> +     conditional branch destination */
> +  for (index = 0; index <= last_breakpoint; index++)
> +    insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
> +
> +  return 1;
> +}
> +
>  /* Initialize the current architecture based on INFO.  If possible,
>     re-use an architecture from ARCHES, which is a list of
>     architectures already created during this debugging session.
> @@ -2624,6 +2700,8 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_breakpoint_from_pc (gdbarch, aarch64_breakpoint_from_pc);
>    set_gdbarch_cannot_step_breakpoint (gdbarch, 1);
>    set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
> +  /* Handles single stepping of atomic sequences.  */
> +  set_gdbarch_software_single_step (gdbarch, aarch64_deal_with_atomic_sequence);
>  
>    /* Information about registers, etc.  */
>    set_gdbarch_sp_regnum (gdbarch, AARCH64_SP_REGNUM);

Thanks!
  
Richard Earnshaw March 24, 2014, 6 p.m. UTC | #4
On 24/03/14 16:57, Joel Brobecker wrote:
> Hello Kyle,
>> +      /* look for a conditional branch to set a breakpoint on the destination. */
> 
> This line looks too long?

Also, comments are full sentences, so begin with a capital letter and
end with a full stop and two spaces.

>> +      /* and the matching store-exclusive to close it. */
>> +      if (decode_masked_match(insn, 0x3fc00000, 0x08000000))
> 
> Same here...

If you really need a continuation comment like this, then end the
previous one with "..." and start the current one with "... and".
Otherwise, this should be re-written as a stand-alone sentence.
  
Kyle McMartin March 27, 2014, 1:52 a.m. UTC | #5
On Mon, Mar 24, 2014 at 06:00:47PM +0000, Richard Earnshaw wrote:
> On 24/03/14 16:57, Joel Brobecker wrote:
> > Hello Kyle,
> >> +      /* look for a conditional branch to set a breakpoint on the destination. */
> > 
> > This line looks too long?
> 
> Also, comments are full sentences, so begin with a capital letter and
> end with a full stop and two spaces.
> 
> >> +      /* and the matching store-exclusive to close it. */
> >> +      if (decode_masked_match(insn, 0x3fc00000, 0x08000000))
> > 
> > Same here...
> 
> If you really need a continuation comment like this, then end the
> previous one with "..." and start the current one with "... and".
> Otherwise, this should be re-written as a stand-alone sentence.
> 

Thanks very much, I've just posted a v2 which I hope addresses your
issues with my patch (and adds a testcase.)

regards, Kyle
  

Patch

--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -2509,6 +2509,82 @@  value_of_aarch64_user_reg (struct frame_info *frame, const void *baton)
 }
 
 
+static int
+aarch64_deal_with_atomic_sequence (struct frame_info *frame)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct address_space *aspace = get_frame_address_space (frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  const int insn_size = 4;
+  const int atomic_sequence_length = 16; /* Instruction sequence length.  */
+  CORE_ADDR pc = get_frame_pc (frame);
+  CORE_ADDR breaks[2] = { -1, -1 };
+  CORE_ADDR loc = pc;
+  CORE_ADDR closing_insn = 0;
+  uint32_t insn = read_memory_unsigned_integer (loc, insn_size, byte_order);
+  int index;
+  int insn_count;
+  int bc_insn_count = 0; /* Conditional branch instruction count.  */
+  int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed).  */
+
+  /* look for a load-exclusive to begin the sequence... */
+  if (!decode_masked_match(insn, 0x3fc00000, 0x08400000))
+    return 0;
+
+  for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
+    {
+      int32_t offset;
+      unsigned cond;
+
+      loc += insn_size;
+      insn = read_memory_unsigned_integer (loc, insn_size, byte_order);
+
+      /* look for a conditional branch to set a breakpoint on the destination. */
+      if (decode_bcond(loc, insn, &cond, &offset))
+	{
+
+	  if (bc_insn_count >= 1)
+	    return 0;
+
+	  breaks[1] = loc + offset;
+
+	  bc_insn_count++;
+	  last_breakpoint++;
+	}
+
+      /* and the matching store-exclusive to close it. */
+      if (decode_masked_match(insn, 0x3fc00000, 0x08000000))
+	{
+          closing_insn = loc;
+	  break;
+	}
+    }
+
+  /* didn't find a stxr to end the sequence... */
+  if (!closing_insn)
+    return 0;
+
+  loc += insn_size;
+  insn = read_memory_unsigned_integer (loc, insn_size, byte_order);
+
+  /* insert breakpoint at the end of the atomic sequence */
+  breaks[0] = loc;
+
+  /* check for duplicated breakpoints. also check for a breakpoint placed on
+   * the conditional branch destination isn't within the sequence. */
+  if (last_breakpoint &&
+      (breaks[1] == breaks[0] ||
+       (breaks[1] >= pc && breaks[1] <= closing_insn)))
+    last_breakpoint = 0;
+
+  /* insert the breakpoint at the end of the sequence, also possibly at the
+     conditional branch destination */
+  for (index = 0; index <= last_breakpoint; index++)
+    insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
+
+  return 1;
+}
+
 /* Initialize the current architecture based on INFO.  If possible,
    re-use an architecture from ARCHES, which is a list of
    architectures already created during this debugging session.
@@ -2624,6 +2700,8 @@  aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_breakpoint_from_pc (gdbarch, aarch64_breakpoint_from_pc);
   set_gdbarch_cannot_step_breakpoint (gdbarch, 1);
   set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
+  /* Handles single stepping of atomic sequences.  */
+  set_gdbarch_software_single_step (gdbarch, aarch64_deal_with_atomic_sequence);
 
   /* Information about registers, etc.  */
   set_gdbarch_sp_regnum (gdbarch, AARCH64_SP_REGNUM);