Message ID | 20240510052408.2173579-5-thiago.bauermann@linaro.org |
---|---|
State | New |
Headers | show |
Series | Add support for AArch64 MOPS instructions | expand |
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 > +}
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 >> +}
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 +}