[v3,4/5] gdb/testsuite: Add gdb.arch/aarch64-mops-single-step.exp

Message ID 20240510052408.2173579-5-thiago.bauermann@linaro.org
State New
Headers
Series Add support for AArch64 MOPS instructions |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed

Commit Message

Thiago Jung Bauermann May 10, 2024, 5:24 a.m. UTC
  The testcase verifies that MOPS sequences are correctly single-stepped.
---
 .../gdb.arch/aarch64-mops-single-step.c       |  73 ++++++++++
 .../gdb.arch/aarch64-mops-single-step.exp     | 132 ++++++++++++++++++
 2 files changed, 205 insertions(+)
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-mops-single-step.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp

Changes in v3:
- Renamed gdb.arch/aarch64-mops-atomic-inst.exp to
  gdb.arch/aarch64-mops-single-step.exp.
- Adjusted test to expect the MOPS sequence to reset back to the prologue
  instruction.
- Set size variable before the cpyf and cpy sequences, because after each
  sequence the variable is set to zero.  This bug didn't affect the
  effectiveness of the test.

Changes in v2:
- Add prfm instruction after each MOPS sequence and look for it in the
  testcase to verify that the sequence was stepped through (Suggested by
  Christophe).
  

Comments

Luis Machado May 10, 2024, 1:07 p.m. UTC | #1
Thanks. Just one nit.

On 5/10/24 06:24, Thiago Jung Bauermann wrote:
> The testcase verifies that MOPS sequences are correctly single-stepped.
> ---
>  .../gdb.arch/aarch64-mops-single-step.c       |  73 ++++++++++
>  .../gdb.arch/aarch64-mops-single-step.exp     | 132 ++++++++++++++++++
>  2 files changed, 205 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-mops-single-step.c
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp
> 
> Changes in v3:
> - Renamed gdb.arch/aarch64-mops-atomic-inst.exp to
>   gdb.arch/aarch64-mops-single-step.exp.
> - Adjusted test to expect the MOPS sequence to reset back to the prologue
>   instruction.
> - Set size variable before the cpyf and cpy sequences, because after each
>   sequence the variable is set to zero.  This bug didn't affect the
>   effectiveness of the test.
> 
> Changes in v2:
> - Add prfm instruction after each MOPS sequence and look for it in the
>   testcase to verify that the sequence was stepped through (Suggested by
>   Christophe).
> 
> diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c
> new file mode 100644
> index 000000000000..4a27867d4b57
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c
> @@ -0,0 +1,73 @@
> +/* This file is part of GDB, the GNU debugger.
> +
> +   Copyright 2024 Free Software Foundation, Inc.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#define TEST_STRING "Just a test string."
> +#define BUF_SIZE sizeof(TEST_STRING)
> +
> +int
> +main (void)
> +{
> +  char source[BUF_SIZE] = TEST_STRING;
> +  char dest[BUF_SIZE];
> +  char *p, *q;
> +  long size, zero;
> +
> +  /* Note: The prfm instruction in the asm statements below is there just
> +     to allow the testcase to recognize when the PC is at the instruction
> +     right after the MOPS sequence.  */
> +
> +  p = dest;
> +  size = sizeof (dest);
> +  zero = 0;
> +  /* Break memset.  */
> +  /* memset implemented in MOPS instructions.  */
> +  __asm__ volatile ("setp [%0]!, %1!, %2\n\t"
> +		    "setm [%0]!, %1!, %2\n\t"
> +		    "sete [%0]!, %1!, %2\n\t"
> +		    "prfm pldl3keep, [%0, #0]\n\t"
> +		    : "+&r"(p), "+&r"(size)
> +		    : "r"(zero)
> +		    : "memory");
> +
> +  p = dest;
> +  q = source;
> +  size = sizeof (dest);
> +  /* Break memcpy.  */
> +  /* memcpy implemented in MOPS instructions.  */
> +  __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t"
> +		    "cpyfm [%0]!, [%1]!, %2!\n\t"
> +		    "cpyfe [%0]!, [%1]!, %2!\n\t"
> +		    "prfm pldl3keep, [%0, #0]\n\t"
> +		    : "+&r" (p), "+&r" (q), "+&r" (size)
> +		    :
> +		    : "memory");
> +
> +  p = dest;
> +  q = source;
> +  size = sizeof (dest);
> +  /* Break memmove.  */
> +  /* memmove implemented in MOPS instructions.  */
> +  __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t"
> +		    "cpym [%0]!, [%1]!, %2!\n\t"
> +		    "cpye [%0]!, [%1]!, %2!\n\t"
> +		    "prfm pldl3keep, [%0, #0]\n\t"
> +		    : "+&r" (p), "+&r" (q), "+&r" (size)
> +		    :
> +		    : "memory");
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp
> new file mode 100644
> index 000000000000..a6390d4bff7e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp
> @@ -0,0 +1,132 @@
> +# Copyright 2024 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +#
> +# This file is part of the GDB testsuite.
> +
> +# Test single stepping through MOPS (memory operations) instruction sequences.
> +
> +require allow_aarch64_mops_tests
> +
> +standard_testfile
> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> +	  [list debug additional_flags=-march=armv9.3-a]] } {
> +    return -1
> +}
> +
> +# INSTRUCTION should be just its mnemonic, without any arguments.
> +proc is_at_instruction { instruction } {
> +    global gdb_prompt hex
> +
> +    set test "pc points to $instruction"
> +    gdb_test_multiple {x/i $pc} $test {
> +	-re -wrap "=> $hex \[^\r\n\]+:\t$instruction\t\[^\r\n\]+" {
> +	    return 1
> +	}
> +	-re "\r\n$gdb_prompt $" {
> +	    return 0
> +	}
> +    }
> +
> +    return 0
> +}
> +
> +proc arrive_at_instruction { instruction } {
> +    set count 0
> +
> +    while { [is_at_instruction $instruction] != 1 } {
> +	gdb_test -nopass "stepi" ".*__asm__ volatile.*" \
> +	    "stepi #$count to reach $instruction"
> +	incr count
> +
> +	if { $count > 50 } {
> +	    fail "didn't reach $instruction"
> +	    return 0
> +	}
> +    }
> +
> +    return 1
> +}
> +
> +# If the inferior is reschedule to another CPU while a main or epilogue

s/reschedule/rescheduled

> +# instruction is executed, the OS resets the inferior back to the prologue
> +# instruction, so we need to allow for that possibility.
> +proc step_through_sequence { prefix } {
> +    set count 0
> +
> +    while { [is_at_instruction ${prefix}p] == 1 && $count < 50 } {
> +	incr count
> +
> +	# The stepi output isn't useful to detect whether we stepped over
> +	# the instruction.
> +	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}p"
> +	if { [is_at_instruction ${prefix}m] == 1 } {
> +	    pass "stepped over ${prefix}p"
> +	} else {
> +	    fail "stepped over ${prefix}e"
> +	    return 0
> +	}
> +
> +	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}m"
> +	if { [is_at_instruction ${prefix}e] == 1 } {
> +	    pass "stepped over ${prefix}m"
> +	} elseif { [is_at_instruction ${prefix}p] == 1 } {
> +	    # The inferior was rescheduled to another CPU.
> +	    pass "${prefix}m: reset back to prologue"
> +	    continue
> +	} else {
> +	    fail "stepped over ${prefix}m"
> +	    return 0
> +	}
> +
> +	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}e"
> +	if { [is_at_instruction prfm] == 1 } {
> +	    pass "stepped over ${prefix}e"
> +	    return 1
> +	} elseif { [is_at_instruction ${prefix}p] == 1 } {
> +	    # The inferior was rescheduled to another CPU.
> +	    pass "${prefix}e: reset back to prologue"
> +	    continue
> +	}
> +    }
> +
> +    fail "step through $prefix sequence"
> +    return 0
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memset"]
> +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memcpy"]
> +gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memmove"]
> +
> +gdb_continue_to_breakpoint "memset breakpoint"
> +
> +if { [arrive_at_instruction setp] } {
> +    step_through_sequence set
> +}
> +
> +gdb_continue_to_breakpoint "memcpy breakpoint"
> +
> +if { [arrive_at_instruction cpyfp] } {
> +    step_through_sequence cpyf
> +}
> +
> +gdb_continue_to_breakpoint "memmove breakpoint"
> +
> +if { [arrive_at_instruction cpyp] } {
> +    step_through_sequence cpy
> +}
  
Thiago Jung Bauermann May 23, 2024, 2:08 a.m. UTC | #2
Luis Machado <luis.machado@arm.com> writes:

> Thanks. Just one nit.

Thanks!

> On 5/10/24 06:24, Thiago Jung Bauermann wrote:
>> +# INSTRUCTION should be just its mnemonic, without any arguments.
>> +proc is_at_instruction { instruction } {
>> +    global gdb_prompt hex
>> +
>> +    set test "pc points to $instruction"
>> +    gdb_test_multiple {x/i $pc} $test {
>> +	-re -wrap "=> $hex \[^\r\n\]+:\t$instruction\t\[^\r\n\]+" {
>> +	    return 1
>> +	}
>> +	-re "\r\n$gdb_prompt $" {
>> +	    return 0
>> +	}
>> +    }
>> +
>> +    return 0
>> +}
>> +
>> +proc arrive_at_instruction { instruction } {
>> +    set count 0
>> +
>> +    while { [is_at_instruction $instruction] != 1 } {
>> +	gdb_test -nopass "stepi" ".*__asm__ volatile.*" \
>> +	    "stepi #$count to reach $instruction"
>> +	incr count
>> +
>> +	if { $count > 50 } {
>> +	    fail "didn't reach $instruction"
>> +	    return 0
>> +	}
>> +    }
>> +
>> +    return 1
>> +}
>> +
>> +# If the inferior is reschedule to another CPU while a main or epilogue
>
> s/reschedule/rescheduled

Thanks. Fixed in v4.

>> +# instruction is executed, the OS resets the inferior back to the prologue
>> +# instruction, so we need to allow for that possibility.
>> +proc step_through_sequence { prefix } {
>> +    set count 0
>> +
>> +    while { [is_at_instruction ${prefix}p] == 1 && $count < 50 } {
>> +	incr count
>> +
>> +	# The stepi output isn't useful to detect whether we stepped over
>> +	# the instruction.
>> +	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}p"
>> +	if { [is_at_instruction ${prefix}m] == 1 } {
>> +	    pass "stepped over ${prefix}p"
>> +	} else {
>> +	    fail "stepped over ${prefix}e"
>> +	    return 0
>> +	}
>> +
>> +	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}m"
>> +	if { [is_at_instruction ${prefix}e] == 1 } {
>> +	    pass "stepped over ${prefix}m"
>> +	} elseif { [is_at_instruction ${prefix}p] == 1 } {
>> +	    # The inferior was rescheduled to another CPU.
>> +	    pass "${prefix}m: reset back to prologue"
>> +	    continue
>> +	} else {
>> +	    fail "stepped over ${prefix}m"
>> +	    return 0
>> +	}
>> +
>> +	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}e"
>> +	if { [is_at_instruction prfm] == 1 } {
>> +	    pass "stepped over ${prefix}e"
>> +	    return 1
>> +	} elseif { [is_at_instruction ${prefix}p] == 1 } {
>> +	    # The inferior was rescheduled to another CPU.
>> +	    pass "${prefix}e: reset back to prologue"
>> +	    continue
>> +	}
>> +    }
>> +
>> +    fail "step through $prefix sequence"
>> +    return 0
>> +}
  

Patch

diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c
new file mode 100644
index 000000000000..4a27867d4b57
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.c
@@ -0,0 +1,73 @@ 
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#define TEST_STRING "Just a test string."
+#define BUF_SIZE sizeof(TEST_STRING)
+
+int
+main (void)
+{
+  char source[BUF_SIZE] = TEST_STRING;
+  char dest[BUF_SIZE];
+  char *p, *q;
+  long size, zero;
+
+  /* Note: The prfm instruction in the asm statements below is there just
+     to allow the testcase to recognize when the PC is at the instruction
+     right after the MOPS sequence.  */
+
+  p = dest;
+  size = sizeof (dest);
+  zero = 0;
+  /* Break memset.  */
+  /* memset implemented in MOPS instructions.  */
+  __asm__ volatile ("setp [%0]!, %1!, %2\n\t"
+		    "setm [%0]!, %1!, %2\n\t"
+		    "sete [%0]!, %1!, %2\n\t"
+		    "prfm pldl3keep, [%0, #0]\n\t"
+		    : "+&r"(p), "+&r"(size)
+		    : "r"(zero)
+		    : "memory");
+
+  p = dest;
+  q = source;
+  size = sizeof (dest);
+  /* Break memcpy.  */
+  /* memcpy implemented in MOPS instructions.  */
+  __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t"
+		    "cpyfm [%0]!, [%1]!, %2!\n\t"
+		    "cpyfe [%0]!, [%1]!, %2!\n\t"
+		    "prfm pldl3keep, [%0, #0]\n\t"
+		    : "+&r" (p), "+&r" (q), "+&r" (size)
+		    :
+		    : "memory");
+
+  p = dest;
+  q = source;
+  size = sizeof (dest);
+  /* Break memmove.  */
+  /* memmove implemented in MOPS instructions.  */
+  __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t"
+		    "cpym [%0]!, [%1]!, %2!\n\t"
+		    "cpye [%0]!, [%1]!, %2!\n\t"
+		    "prfm pldl3keep, [%0, #0]\n\t"
+		    : "+&r" (p), "+&r" (q), "+&r" (size)
+		    :
+		    : "memory");
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp
new file mode 100644
index 000000000000..a6390d4bff7e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-mops-single-step.exp
@@ -0,0 +1,132 @@ 
+# Copyright 2024 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# This file is part of the GDB testsuite.
+
+# Test single stepping through MOPS (memory operations) instruction sequences.
+
+require allow_aarch64_mops_tests
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+	  [list debug additional_flags=-march=armv9.3-a]] } {
+    return -1
+}
+
+# INSTRUCTION should be just its mnemonic, without any arguments.
+proc is_at_instruction { instruction } {
+    global gdb_prompt hex
+
+    set test "pc points to $instruction"
+    gdb_test_multiple {x/i $pc} $test {
+	-re -wrap "=> $hex \[^\r\n\]+:\t$instruction\t\[^\r\n\]+" {
+	    return 1
+	}
+	-re "\r\n$gdb_prompt $" {
+	    return 0
+	}
+    }
+
+    return 0
+}
+
+proc arrive_at_instruction { instruction } {
+    set count 0
+
+    while { [is_at_instruction $instruction] != 1 } {
+	gdb_test -nopass "stepi" ".*__asm__ volatile.*" \
+	    "stepi #$count to reach $instruction"
+	incr count
+
+	if { $count > 50 } {
+	    fail "didn't reach $instruction"
+	    return 0
+	}
+    }
+
+    return 1
+}
+
+# If the inferior is reschedule to another CPU while a main or epilogue
+# instruction is executed, the OS resets the inferior back to the prologue
+# instruction, so we need to allow for that possibility.
+proc step_through_sequence { prefix } {
+    set count 0
+
+    while { [is_at_instruction ${prefix}p] == 1 && $count < 50 } {
+	incr count
+
+	# The stepi output isn't useful to detect whether we stepped over
+	# the instruction.
+	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}p"
+	if { [is_at_instruction ${prefix}m] == 1 } {
+	    pass "stepped over ${prefix}p"
+	} else {
+	    fail "stepped over ${prefix}e"
+	    return 0
+	}
+
+	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}m"
+	if { [is_at_instruction ${prefix}e] == 1 } {
+	    pass "stepped over ${prefix}m"
+	} elseif { [is_at_instruction ${prefix}p] == 1 } {
+	    # The inferior was rescheduled to another CPU.
+	    pass "${prefix}m: reset back to prologue"
+	    continue
+	} else {
+	    fail "stepped over ${prefix}m"
+	    return 0
+	}
+
+	gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}e"
+	if { [is_at_instruction prfm] == 1 } {
+	    pass "stepped over ${prefix}e"
+	    return 1
+	} elseif { [is_at_instruction ${prefix}p] == 1 } {
+	    # The inferior was rescheduled to another CPU.
+	    pass "${prefix}e: reset back to prologue"
+	    continue
+	}
+    }
+
+    fail "step through $prefix sequence"
+    return 0
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memset"]
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memcpy"]
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memmove"]
+
+gdb_continue_to_breakpoint "memset breakpoint"
+
+if { [arrive_at_instruction setp] } {
+    step_through_sequence set
+}
+
+gdb_continue_to_breakpoint "memcpy breakpoint"
+
+if { [arrive_at_instruction cpyfp] } {
+    step_through_sequence cpyf
+}
+
+gdb_continue_to_breakpoint "memmove breakpoint"
+
+if { [arrive_at_instruction cpyp] } {
+    step_through_sequence cpy
+}