[v2,2/4] gdb: LoongArch: Add basic process record/replay support
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gdb_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gdb_check--master-arm |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 |
success
|
Test passed
|
Commit Message
GDB provides a special process record and replay target that can
record a log of the process execution, and replay it later with
both forward and reverse execution commands. This patch adds the
basic support of process record and replay on LoongArch, it allows
users to debug basic LoongArch instructions and provides reverse
debugging support.
Here is a simple example on LoongArch:
$ cat test.c
int a = 0;
int main()
{
a = 1;
a = 2;
return 0;
}
$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:4
4 a = 1;
(gdb) record
(gdb) p a
$1 = 0
(gdb) n
5 a = 2;
(gdb) n
6 return 0;
(gdb) p a
$2 = 2
(gdb) rn
5 a = 2;
(gdb) rn
Reached end of recorded history; stopping.
Backward execution from here not possible.
main () at test.c:4
4 a = 1;
(gdb) p a
$3 = 0
(gdb) record stop
Process record is stopped and all execution logs are deleted.
(gdb) c
Continuing.
[Inferior 1 (process 129178) exited normally]
Signed-off-by: Hui Li <lihui@loongson.cn>
---
gdb/configure.tgt | 2 +-
gdb/loongarch-linux-tdep.c | 3 +
gdb/loongarch-tdep.c | 495 +++++++++++++++++++++++++++++++++++++
gdb/loongarch-tdep.h | 3 +
gdb/testsuite/lib/gdb.exp | 2 +
5 files changed, 504 insertions(+), 1 deletion(-)
Comments
On 11/22/24 6:06 AM, Hui Li wrote:
> GDB provides a special process record and replay target that can
> record a log of the process execution, and replay it later with
> both forward and reverse execution commands. This patch adds the
> basic support of process record and replay on LoongArch, it allows
> users to debug basic LoongArch instructions and provides reverse
> debugging support.
>
> Here is a simple example on LoongArch:
>
> $ cat test.c
> int a = 0;
> int main()
> {
> a = 1;
> a = 2;
> return 0;
> }
> $ gdb test
> ...
> (gdb) start
> ...
> Temporary breakpoint 1, main () at test.c:4
> 4 a = 1;
> (gdb) record
> (gdb) p a
> $1 = 0
> (gdb) n
> 5 a = 2;
> (gdb) n
> 6 return 0;
> (gdb) p a
> $2 = 2
> (gdb) rn
> 5 a = 2;
> (gdb) rn
>
> Reached end of recorded history; stopping.
> Backward execution from here not possible.
> main () at test.c:4
> 4 a = 1;
> (gdb) p a
> $3 = 0
> (gdb) record stop
> Process record is stopped and all execution logs are deleted.
> (gdb) c
> Continuing.
> [Inferior 1 (process 129178) exited normally]
>
> Signed-off-by: Hui Li <lihui@loongson.cn>
> ---
Hi!
thanks for the update! This version has addressed all of my concerns. I
haven't looked into the instructions themselves, and I'll leave that to
Tiezhu, but as it relates to recoding, I'm happy with this
Approved-By: Guinevere Larsen <guinevere@redhat.com> (record-full)
Hopefully this series is approved soon.
> gdb/configure.tgt | 2 +-
> gdb/loongarch-linux-tdep.c | 3 +
> gdb/loongarch-tdep.c | 495 +++++++++++++++++++++++++++++++++++++
> gdb/loongarch-tdep.h | 3 +
> gdb/testsuite/lib/gdb.exp | 2 +
> 5 files changed, 504 insertions(+), 1 deletion(-)
>
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..2d78c1182fa 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -363,7 +363,7 @@ lm32-*-*)
> loongarch*-*-linux*)
> # Target: LoongArch running Linux
> gdb_target_obs="loongarch-linux-tdep.o glibc-tdep.o \
> - linux-tdep.o solib-svr4.o"
> + linux-tdep.o solib-svr4.o linux-record.o"
> ;;
>
> m32c-*-*)
> diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c
> index 71578060303..86e7ed8a0bc 100644
> --- a/gdb/loongarch-linux-tdep.c
> +++ b/gdb/loongarch-linux-tdep.c
> @@ -602,6 +602,9 @@ loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>
> /* Get the syscall number from the arch's register. */
> set_gdbarch_get_syscall_number (gdbarch, loongarch_linux_get_syscall_number);
> +
> + /* Reversible debugging, process record. */
> + set_gdbarch_process_record (gdbarch, loongarch_process_record);
> }
>
> /* Initialize LoongArch Linux target support. */
> diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
> index c50dd7f4b78..c1b3a1f239c 100644
> --- a/gdb/loongarch-tdep.c
> +++ b/gdb/loongarch-tdep.c
> @@ -18,12 +18,16 @@
> along with this program. If not, see <http://www.gnu.org/licenses/>. */
>
> #include "arch-utils.h"
> +#include "arch/loongarch-insn.h"
> #include "dwarf2/frame.h"
> #include "elf-bfd.h"
> #include "extract-store-integer.h"
> #include "frame-unwind.h"
> #include "gdbcore.h"
> +#include "linux-record.h"
> #include "loongarch-tdep.h"
> +#include "record.h"
> +#include "record-full.h"
> #include "reggroups.h"
> #include "target.h"
> #include "target-descriptions.h"
> @@ -1889,6 +1893,497 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
> return gdbarch;
> }
>
> +/* LoongArch record/replay enumerations and structures. */
> +
> +enum loongarch_record_result
> +{
> + LOONGARCH_RECORD_SUCCESS,
> + LOONGARCH_RECORD_UNSUPPORTED,
> + LOONGARCH_RECORD_UNKNOWN
> +};
> +
> +struct loongarch_record_s
> +{
> + struct gdbarch *gdbarch;
> + struct regcache *regcache;
> + CORE_ADDR this_addr; /* Addr of insn to be recorded. */
> + uint32_t insn; /* Insn to be recorded. */
> +};
> +
> +/* Record handler for data processing instructions. */
> +
> +static int
> +loongarch_record_data_proc_insn (loongarch_record_s *loongarch_record)
> +{
> + int rd_num;
> + rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
> + return -1;
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for read time instructions. */
> +
> +static int
> +loongarch_record_read_time_insn (loongarch_record_s *loongarch_record)
> +{
> + int rd_num, rj_num;
> + rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
> + rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
> + return -1;
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rj_num))
> + return -1;
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for branch instructions. */
> +
> +static int
> +loongarch_record_branch_insn (loongarch_record_s *loongarch_record)
> +{
> + if (is_jirl_insn (loongarch_record->insn))
> + {
> + int rd_num;
> + rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
> + return -1;
> + }
> + else if (is_bl_insn (loongarch_record->insn))
> + {
> + if (record_full_arch_list_add_reg (loongarch_record->regcache,
> + LOONGARCH_RA_REGNUM))
> + return -1;
> + }
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for float data processing instructions. */
> +
> +static int
> +loongarch_record_float_data_proc_insn (loongarch_record_s *loongarch_record)
> +{
> + int fd_num;
> + fd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0)
> + + LOONGARCH_FIRST_FP_REGNUM;
> +
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, fd_num))
> + return -1;
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for move gr to fcsr instructions. */
> +
> +static int
> +loongarch_record_movgr2fcsr_insn (loongarch_record_s *loongarch_record)
> +{
> + if (record_full_arch_list_add_reg (loongarch_record->regcache,
> + LOONGARCH_FCSR_REGNUM))
> + return -1;
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for move gr/fr to fcc instructions. */
> +
> +static int
> +loongarch_record_mov2cf_insn (loongarch_record_s *loongarch_record)
> +{
> + int cd;
> + cd = loongarch_decode_imm ("0:3", loongarch_record->insn, 0);
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, cd))
> + return -1;
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for float instructions. */
> +
> +static unsigned int
> +loongarch_record_float_insn (loongarch_record_s *loongarch_record)
> +{
> + if (is_movgr2fcsr_insn (loongarch_record->insn))
> + return loongarch_record_movgr2fcsr_insn (loongarch_record);
> + else if (is_mov2cf_insn (loongarch_record->insn))
> + return loongarch_record_mov2cf_insn (loongarch_record);
> + else
> + return loongarch_record_float_data_proc_insn (loongarch_record);
> +}
> +
> +/* Record handler for store instructions. */
> +
> +static int
> +loongarch_record_store_insn (loongarch_record_s *loongarch_record)
> +{
> + enum store_types
> + {
> + STB, STH, STW, STD, STXB, STXH, STXW, STXD, STPTRW, STPTRD,
> + SCW, SCD, FSTS, FSTD, FSTXS, FSTXD, VST, XVST, NOT_STORE
> + };
> + int store_type, data_size, rj_num;
> + uint64_t address, rj_val;
> +
> + store_type = is_st_b_insn (loongarch_record->insn) ? STB :
> + is_st_h_insn (loongarch_record->insn) ? STH :
> + is_st_w_insn (loongarch_record->insn) ? STW :
> + is_st_d_insn (loongarch_record->insn) ? STD :
> + is_stx_b_insn (loongarch_record->insn) ? STXB :
> + is_stx_h_insn (loongarch_record->insn) ? STXH :
> + is_stx_w_insn (loongarch_record->insn) ? STXW :
> + is_stx_d_insn (loongarch_record->insn) ? STXD :
> + is_stptr_w_insn (loongarch_record->insn) ? STPTRW :
> + is_stptr_d_insn (loongarch_record->insn) ? STPTRD :
> + is_sc_w_insn (loongarch_record->insn) ? SCW :
> + is_sc_d_insn (loongarch_record->insn) ? SCD :
> + is_fst_s_insn (loongarch_record->insn) ? FSTS :
> + is_fst_d_insn (loongarch_record->insn) ? FSTD :
> + is_fstx_s_insn (loongarch_record->insn) ? FSTXS :
> + is_fstx_d_insn (loongarch_record->insn) ? FSTXD :
> + is_vst_insn (loongarch_record->insn) ? VST :
> + is_xvst_insn (loongarch_record->insn) ? XVST :
> + NOT_STORE;
> + rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
> +
> + if (store_type == STB || store_type == STH || store_type == STW
> + || store_type == STD || store_type == FSTS || store_type == FSTD
> + || store_type == VST || store_type == XVST)
> + {
> + int imm;
> + imm = loongarch_decode_imm ("10:12", loongarch_record->insn, 1);
> + address = rj_val + imm;
> + switch (store_type)
> + {
> + case STB:
> + data_size = 1;
> + break;
> + case STH:
> + data_size = 2;
> + break;
> + case STW:
> + case FSTS:
> + data_size = 4;
> + break;
> + case STD:
> + case FSTD:
> + data_size = 8;
> + break;
> + case VST:
> + data_size = 16;
> + break;
> + case XVST:
> + data_size = 32;
> + break;
> + default:
> + data_size = 0;
> + break;
> + }
> +
> + if (record_full_arch_list_add_mem (address, data_size))
> + return -1;
> + }
> + else if (store_type == STXB || store_type == STXH || store_type == STXW
> + || store_type == STXD || store_type == FSTXS || store_type == FSTXD)
> + {
> + int rk_num;
> + uint64_t rk_val;
> + rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
> + address = rj_val + rk_val;
> + switch (store_type)
> + {
> + case STXB:
> + data_size = 1;
> + break;
> + case STXH:
> + data_size = 2;
> + break;
> + case STXW:
> + case FSTXS:
> + data_size = 4;
> + break;
> + case STXD:
> + case FSTXD:
> + data_size = 8;
> + break;
> + default:
> + data_size = 0;
> + break;
> + }
> +
> + if (record_full_arch_list_add_mem (address, data_size))
> + return -1;
> + }
> + else if (store_type == STPTRW || store_type == STPTRD || store_type == SCW
> + || store_type == SCD)
> + {
> + int imm;
> + imm = loongarch_decode_imm ("10:14<<2", loongarch_record->insn, 1);
> + address = rj_val + imm;
> + switch (store_type)
> + {
> + case STPTRW:
> + case SCW:
> + data_size = 4;
> + break;
> + case STPTRD:
> + case SCD:
> + data_size = 8;
> + break;
> + default:
> + data_size = 0;
> + break;
> + }
> +
> + if (record_full_arch_list_add_mem (address, data_size))
> + return -1;
> + }
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for atomic memory access instructions. */
> +
> +static int
> +loongarch_record_atomic_access_insn (loongarch_record_s *loongarch_record)
> +{
> + int rj_num, rd_num, rk_num, length;
> + int data_size;
> + uint64_t address;
> + rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
> + rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
> + rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &address);
> + if (is_basic_am_w_d_insn (loongarch_record->insn))
> + {
> + length = loongarch_decode_imm ("15:1", loongarch_record->insn, 0);
> + data_size = length == 1 ? 8 : 4;
> + if (record_full_arch_list_add_mem (address, data_size))
> + return -1;
> + }
> + if (is_am_b_h_insn (loongarch_record->insn))
> + {
> + length = loongarch_decode_imm ("15:1", loongarch_record->insn, 0);
> + data_size = length == 1 ? 2 : 1;
> + if (record_full_arch_list_add_mem (address, data_size))
> + return -1;
> + }
> + if (is_amcas_insn (loongarch_record->insn))
> + {
> + length = loongarch_decode_imm ("15:2", loongarch_record->insn, 0);
> + switch (length)
> + {
> + case 0x0:
> + data_size = 1;
> + break;
> + case 0x1:
> + data_size = 2;
> + break;
> + case 0x2:
> + data_size = 4;
> + break;
> + case 0x3:
> + data_size = 8;
> + break;
> + default:
> + data_size = 0;
> + break;
> + }
> + if (record_full_arch_list_add_mem (address, data_size))
> + return -1;
> + }
> +
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
> + return -1;
> +
> + if (is_amswap_insn (loongarch_record->insn))
> + {
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rk_num))
> + return -1;
> + }
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for bound check load instructions. */
> +
> +static int
> +loongarch_record_bound_check_load_insn (loongarch_record_s *loongarch_record)
> +{
> + int rd_num, rj_num, rk_num, fd_num;
> + uint64_t rj_val, rk_val;
> + rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
> + fd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
> + rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
> + rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
> +
> + if ((is_ldgt_insn (loongarch_record->insn) && (rj_val > rk_val))
> + || (is_ldle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
> + {
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
> + return -1;
> + }
> + else if ((is_fldgt_insn (loongarch_record->insn) && (rj_val > rk_val))
> + || (is_fldle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
> + {
> + if (record_full_arch_list_add_reg (loongarch_record->regcache, fd_num))
> + return -1;
> + }
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for bound check store instructions. */
> +
> +static int
> +loongarch_record_bound_check_store_insn (loongarch_record_s *loongarch_record)
> +{
> + int rj_num, rk_num;
> + int data_size;
> + uint64_t rj_val, rk_val;
> + uint32_t length_opcode;
> + rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
> + rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
> + regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
> +
> + if ((is_stgt_insn (loongarch_record->insn) && (rj_val > rk_val))
> + || (is_stle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
> + {
> + length_opcode = loongarch_record->insn & 0x00018000;
> + switch (length_opcode)
> + {
> + case 0x00000000:
> + data_size = 1;
> + break;
> + case 0x00008000:
> + data_size = 2;
> + break;
> + case 0x00010000:
> + data_size = 4;
> + break;
> + case 0x00018000:
> + data_size = 8;
> + break;
> + default:
> + data_size = 0;
> + break;
> + }
> +
> + if (record_full_arch_list_add_mem (rj_val, data_size))
> + return -1;
> + }
> + else if ((is_fstgt_insn (loongarch_record->insn) && (rj_val > rk_val))
> + || (is_fstle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
> + {
> + length_opcode = loongarch_record->insn & 0x00008000;
> + switch (length_opcode)
> + {
> + case 0x00000000:
> + data_size = 4;
> + break;
> + case 0x00008000:
> + data_size = 8;
> + break;
> + default:
> + data_size = 0;
> + break;
> + }
> +
> + if (record_full_arch_list_add_mem (rj_val, data_size))
> + return -1;
> + }
> +
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Record handler for special instructions like privilege instructions,
> + barrier instructions and cache related instructions etc. */
> +
> +static int
> +loongarch_record_special_insn (loongarch_record_s *loongarch_record)
> +{
> + return LOONGARCH_RECORD_SUCCESS;
> +}
> +
> +/* Decode insns type and invoke its record handler. */
> +
> +static int
> +loongarch_record_decode_insn_handler (loongarch_record_s *loongarch_record)
> +{
> + if (is_data_process_insn (loongarch_record->insn))
> + return loongarch_record_data_proc_insn (loongarch_record);
> + else if (is_branch_insn (loongarch_record->insn))
> + return loongarch_record_branch_insn (loongarch_record);
> + else if (is_store_insn (loongarch_record->insn))
> + return loongarch_record_store_insn (loongarch_record);
> + else if (is_read_time_insn (loongarch_record->insn))
> + return loongarch_record_read_time_insn (loongarch_record);
> + else if (is_float_insn (loongarch_record->insn))
> + return loongarch_record_float_insn (loongarch_record);
> + else if (is_special_insn (loongarch_record->insn))
> + return loongarch_record_special_insn (loongarch_record);
> + else if (is_atomic_access_insn (loongarch_record->insn))
> + return loongarch_record_atomic_access_insn (loongarch_record);
> + else if (is_bound_check_load_insn (loongarch_record->insn))
> + return loongarch_record_bound_check_load_insn (loongarch_record);
> + else if (is_bound_check_store_insn (loongarch_record->insn))
> + return loongarch_record_bound_check_store_insn (loongarch_record);
> +
> + return LOONGARCH_RECORD_UNSUPPORTED;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list
> + return -1 if something is wrong. */
> +
> +int
> +loongarch_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR insn_addr)
> +{
> + int ret = 0;
> + loongarch_record_s loongarch_record;
> +
> + /* reset the content of loongarch_record */
> + memset (&loongarch_record, 0, sizeof (loongarch_record_s));
> +
> + /* write the loongarch_record */
> + loongarch_record.gdbarch = gdbarch;
> + loongarch_record.regcache = regcache;
> + loongarch_record.this_addr = insn_addr;
> +
> + /* Get the current instruction */
> + loongarch_record.insn = (uint32_t) loongarch_fetch_instruction (insn_addr);
> + ret = loongarch_record_decode_insn_handler (&loongarch_record);
> + if (ret == LOONGARCH_RECORD_UNSUPPORTED)
> + {
> + gdb_printf (gdb_stderr,
> + _("Process record does not support instruction "
> + "0x%0x at address %s.\n"),
> + loongarch_record.insn,
> + paddress (gdbarch, insn_addr));
> + return -1;
> + }
> + if (ret == LOONGARCH_RECORD_SUCCESS)
> + {
> + /* Record PC registers. */
> + if (record_full_arch_list_add_reg (loongarch_record.regcache,
> + LOONGARCH_PC_REGNUM))
> + return -1;
> +
> + if (record_full_arch_list_add_end ())
> + return -1;
> + }
> +
> + return ret;
> +}
> +
> void _initialize_loongarch_tdep ();
> void
> _initialize_loongarch_tdep ()
> diff --git a/gdb/loongarch-tdep.h b/gdb/loongarch-tdep.h
> index 5c8108182ad..a148363c795 100644
> --- a/gdb/loongarch-tdep.h
> +++ b/gdb/loongarch-tdep.h
> @@ -44,4 +44,7 @@ struct loongarch_gdbarch_tdep : gdbarch_tdep_base
> CORE_ADDR (*syscall_next_pc) (const frame_info_ptr &frame) = nullptr;
> };
>
> +extern int loongarch_process_record (struct gdbarch *gdbarch,
> + struct regcache *regcache, CORE_ADDR addr);
> +
> #endif /* LOONGARCH_TDEP_H */
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 2b27d7fc9e7..127ab272100 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3700,6 +3700,7 @@ proc supports_process_record {} {
> if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"]
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> + || [istarget "loongarch*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> || [istarget "s390*-*-linux*"] } {
> return 1
> @@ -3719,6 +3720,7 @@ proc supports_reverse {} {
> if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"]
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> + || [istarget "loongarch*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> || [istarget "s390*-*-linux*"] } {
> return 1
@@ -363,7 +363,7 @@ lm32-*-*)
loongarch*-*-linux*)
# Target: LoongArch running Linux
gdb_target_obs="loongarch-linux-tdep.o glibc-tdep.o \
- linux-tdep.o solib-svr4.o"
+ linux-tdep.o solib-svr4.o linux-record.o"
;;
m32c-*-*)
@@ -602,6 +602,9 @@ loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Get the syscall number from the arch's register. */
set_gdbarch_get_syscall_number (gdbarch, loongarch_linux_get_syscall_number);
+
+ /* Reversible debugging, process record. */
+ set_gdbarch_process_record (gdbarch, loongarch_process_record);
}
/* Initialize LoongArch Linux target support. */
@@ -18,12 +18,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "arch-utils.h"
+#include "arch/loongarch-insn.h"
#include "dwarf2/frame.h"
#include "elf-bfd.h"
#include "extract-store-integer.h"
#include "frame-unwind.h"
#include "gdbcore.h"
+#include "linux-record.h"
#include "loongarch-tdep.h"
+#include "record.h"
+#include "record-full.h"
#include "reggroups.h"
#include "target.h"
#include "target-descriptions.h"
@@ -1889,6 +1893,497 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
return gdbarch;
}
+/* LoongArch record/replay enumerations and structures. */
+
+enum loongarch_record_result
+{
+ LOONGARCH_RECORD_SUCCESS,
+ LOONGARCH_RECORD_UNSUPPORTED,
+ LOONGARCH_RECORD_UNKNOWN
+};
+
+struct loongarch_record_s
+{
+ struct gdbarch *gdbarch;
+ struct regcache *regcache;
+ CORE_ADDR this_addr; /* Addr of insn to be recorded. */
+ uint32_t insn; /* Insn to be recorded. */
+};
+
+/* Record handler for data processing instructions. */
+
+static int
+loongarch_record_data_proc_insn (loongarch_record_s *loongarch_record)
+{
+ int rd_num;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for read time instructions. */
+
+static int
+loongarch_record_read_time_insn (loongarch_record_s *loongarch_record)
+{
+ int rd_num, rj_num;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rj_num))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for branch instructions. */
+
+static int
+loongarch_record_branch_insn (loongarch_record_s *loongarch_record)
+{
+ if (is_jirl_insn (loongarch_record->insn))
+ {
+ int rd_num;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+ }
+ else if (is_bl_insn (loongarch_record->insn))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache,
+ LOONGARCH_RA_REGNUM))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for float data processing instructions. */
+
+static int
+loongarch_record_float_data_proc_insn (loongarch_record_s *loongarch_record)
+{
+ int fd_num;
+ fd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0)
+ + LOONGARCH_FIRST_FP_REGNUM;
+
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, fd_num))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for move gr to fcsr instructions. */
+
+static int
+loongarch_record_movgr2fcsr_insn (loongarch_record_s *loongarch_record)
+{
+ if (record_full_arch_list_add_reg (loongarch_record->regcache,
+ LOONGARCH_FCSR_REGNUM))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for move gr/fr to fcc instructions. */
+
+static int
+loongarch_record_mov2cf_insn (loongarch_record_s *loongarch_record)
+{
+ int cd;
+ cd = loongarch_decode_imm ("0:3", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, cd))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for float instructions. */
+
+static unsigned int
+loongarch_record_float_insn (loongarch_record_s *loongarch_record)
+{
+ if (is_movgr2fcsr_insn (loongarch_record->insn))
+ return loongarch_record_movgr2fcsr_insn (loongarch_record);
+ else if (is_mov2cf_insn (loongarch_record->insn))
+ return loongarch_record_mov2cf_insn (loongarch_record);
+ else
+ return loongarch_record_float_data_proc_insn (loongarch_record);
+}
+
+/* Record handler for store instructions. */
+
+static int
+loongarch_record_store_insn (loongarch_record_s *loongarch_record)
+{
+ enum store_types
+ {
+ STB, STH, STW, STD, STXB, STXH, STXW, STXD, STPTRW, STPTRD,
+ SCW, SCD, FSTS, FSTD, FSTXS, FSTXD, VST, XVST, NOT_STORE
+ };
+ int store_type, data_size, rj_num;
+ uint64_t address, rj_val;
+
+ store_type = is_st_b_insn (loongarch_record->insn) ? STB :
+ is_st_h_insn (loongarch_record->insn) ? STH :
+ is_st_w_insn (loongarch_record->insn) ? STW :
+ is_st_d_insn (loongarch_record->insn) ? STD :
+ is_stx_b_insn (loongarch_record->insn) ? STXB :
+ is_stx_h_insn (loongarch_record->insn) ? STXH :
+ is_stx_w_insn (loongarch_record->insn) ? STXW :
+ is_stx_d_insn (loongarch_record->insn) ? STXD :
+ is_stptr_w_insn (loongarch_record->insn) ? STPTRW :
+ is_stptr_d_insn (loongarch_record->insn) ? STPTRD :
+ is_sc_w_insn (loongarch_record->insn) ? SCW :
+ is_sc_d_insn (loongarch_record->insn) ? SCD :
+ is_fst_s_insn (loongarch_record->insn) ? FSTS :
+ is_fst_d_insn (loongarch_record->insn) ? FSTD :
+ is_fstx_s_insn (loongarch_record->insn) ? FSTXS :
+ is_fstx_d_insn (loongarch_record->insn) ? FSTXD :
+ is_vst_insn (loongarch_record->insn) ? VST :
+ is_xvst_insn (loongarch_record->insn) ? XVST :
+ NOT_STORE;
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
+
+ if (store_type == STB || store_type == STH || store_type == STW
+ || store_type == STD || store_type == FSTS || store_type == FSTD
+ || store_type == VST || store_type == XVST)
+ {
+ int imm;
+ imm = loongarch_decode_imm ("10:12", loongarch_record->insn, 1);
+ address = rj_val + imm;
+ switch (store_type)
+ {
+ case STB:
+ data_size = 1;
+ break;
+ case STH:
+ data_size = 2;
+ break;
+ case STW:
+ case FSTS:
+ data_size = 4;
+ break;
+ case STD:
+ case FSTD:
+ data_size = 8;
+ break;
+ case VST:
+ data_size = 16;
+ break;
+ case XVST:
+ data_size = 32;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ else if (store_type == STXB || store_type == STXH || store_type == STXW
+ || store_type == STXD || store_type == FSTXS || store_type == FSTXD)
+ {
+ int rk_num;
+ uint64_t rk_val;
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
+ address = rj_val + rk_val;
+ switch (store_type)
+ {
+ case STXB:
+ data_size = 1;
+ break;
+ case STXH:
+ data_size = 2;
+ break;
+ case STXW:
+ case FSTXS:
+ data_size = 4;
+ break;
+ case STXD:
+ case FSTXD:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ else if (store_type == STPTRW || store_type == STPTRD || store_type == SCW
+ || store_type == SCD)
+ {
+ int imm;
+ imm = loongarch_decode_imm ("10:14<<2", loongarch_record->insn, 1);
+ address = rj_val + imm;
+ switch (store_type)
+ {
+ case STPTRW:
+ case SCW:
+ data_size = 4;
+ break;
+ case STPTRD:
+ case SCD:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for atomic memory access instructions. */
+
+static int
+loongarch_record_atomic_access_insn (loongarch_record_s *loongarch_record)
+{
+ int rj_num, rd_num, rk_num, length;
+ int data_size;
+ uint64_t address;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &address);
+ if (is_basic_am_w_d_insn (loongarch_record->insn))
+ {
+ length = loongarch_decode_imm ("15:1", loongarch_record->insn, 0);
+ data_size = length == 1 ? 8 : 4;
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ if (is_am_b_h_insn (loongarch_record->insn))
+ {
+ length = loongarch_decode_imm ("15:1", loongarch_record->insn, 0);
+ data_size = length == 1 ? 2 : 1;
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ if (is_amcas_insn (loongarch_record->insn))
+ {
+ length = loongarch_decode_imm ("15:2", loongarch_record->insn, 0);
+ switch (length)
+ {
+ case 0x0:
+ data_size = 1;
+ break;
+ case 0x1:
+ data_size = 2;
+ break;
+ case 0x2:
+ data_size = 4;
+ break;
+ case 0x3:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+
+ if (is_amswap_insn (loongarch_record->insn))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rk_num))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for bound check load instructions. */
+
+static int
+loongarch_record_bound_check_load_insn (loongarch_record_s *loongarch_record)
+{
+ int rd_num, rj_num, rk_num, fd_num;
+ uint64_t rj_val, rk_val;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ fd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
+
+ if ((is_ldgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_ldle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+ }
+ else if ((is_fldgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_fldle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, fd_num))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for bound check store instructions. */
+
+static int
+loongarch_record_bound_check_store_insn (loongarch_record_s *loongarch_record)
+{
+ int rj_num, rk_num;
+ int data_size;
+ uint64_t rj_val, rk_val;
+ uint32_t length_opcode;
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
+
+ if ((is_stgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_stle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ length_opcode = loongarch_record->insn & 0x00018000;
+ switch (length_opcode)
+ {
+ case 0x00000000:
+ data_size = 1;
+ break;
+ case 0x00008000:
+ data_size = 2;
+ break;
+ case 0x00010000:
+ data_size = 4;
+ break;
+ case 0x00018000:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (rj_val, data_size))
+ return -1;
+ }
+ else if ((is_fstgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_fstle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ length_opcode = loongarch_record->insn & 0x00008000;
+ switch (length_opcode)
+ {
+ case 0x00000000:
+ data_size = 4;
+ break;
+ case 0x00008000:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (rj_val, data_size))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for special instructions like privilege instructions,
+ barrier instructions and cache related instructions etc. */
+
+static int
+loongarch_record_special_insn (loongarch_record_s *loongarch_record)
+{
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Decode insns type and invoke its record handler. */
+
+static int
+loongarch_record_decode_insn_handler (loongarch_record_s *loongarch_record)
+{
+ if (is_data_process_insn (loongarch_record->insn))
+ return loongarch_record_data_proc_insn (loongarch_record);
+ else if (is_branch_insn (loongarch_record->insn))
+ return loongarch_record_branch_insn (loongarch_record);
+ else if (is_store_insn (loongarch_record->insn))
+ return loongarch_record_store_insn (loongarch_record);
+ else if (is_read_time_insn (loongarch_record->insn))
+ return loongarch_record_read_time_insn (loongarch_record);
+ else if (is_float_insn (loongarch_record->insn))
+ return loongarch_record_float_insn (loongarch_record);
+ else if (is_special_insn (loongarch_record->insn))
+ return loongarch_record_special_insn (loongarch_record);
+ else if (is_atomic_access_insn (loongarch_record->insn))
+ return loongarch_record_atomic_access_insn (loongarch_record);
+ else if (is_bound_check_load_insn (loongarch_record->insn))
+ return loongarch_record_bound_check_load_insn (loongarch_record);
+ else if (is_bound_check_store_insn (loongarch_record->insn))
+ return loongarch_record_bound_check_store_insn (loongarch_record);
+
+ return LOONGARCH_RECORD_UNSUPPORTED;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list
+ return -1 if something is wrong. */
+
+int
+loongarch_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR insn_addr)
+{
+ int ret = 0;
+ loongarch_record_s loongarch_record;
+
+ /* reset the content of loongarch_record */
+ memset (&loongarch_record, 0, sizeof (loongarch_record_s));
+
+ /* write the loongarch_record */
+ loongarch_record.gdbarch = gdbarch;
+ loongarch_record.regcache = regcache;
+ loongarch_record.this_addr = insn_addr;
+
+ /* Get the current instruction */
+ loongarch_record.insn = (uint32_t) loongarch_fetch_instruction (insn_addr);
+ ret = loongarch_record_decode_insn_handler (&loongarch_record);
+ if (ret == LOONGARCH_RECORD_UNSUPPORTED)
+ {
+ gdb_printf (gdb_stderr,
+ _("Process record does not support instruction "
+ "0x%0x at address %s.\n"),
+ loongarch_record.insn,
+ paddress (gdbarch, insn_addr));
+ return -1;
+ }
+ if (ret == LOONGARCH_RECORD_SUCCESS)
+ {
+ /* Record PC registers. */
+ if (record_full_arch_list_add_reg (loongarch_record.regcache,
+ LOONGARCH_PC_REGNUM))
+ return -1;
+
+ if (record_full_arch_list_add_end ())
+ return -1;
+ }
+
+ return ret;
+}
+
void _initialize_loongarch_tdep ();
void
_initialize_loongarch_tdep ()
@@ -44,4 +44,7 @@ struct loongarch_gdbarch_tdep : gdbarch_tdep_base
CORE_ADDR (*syscall_next_pc) (const frame_info_ptr &frame) = nullptr;
};
+extern int loongarch_process_record (struct gdbarch *gdbarch,
+ struct regcache *regcache, CORE_ADDR addr);
+
#endif /* LOONGARCH_TDEP_H */
@@ -3700,6 +3700,7 @@ proc supports_process_record {} {
if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"]
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
+ || [istarget "loongarch*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
|| [istarget "s390*-*-linux*"] } {
return 1
@@ -3719,6 +3720,7 @@ proc supports_reverse {} {
if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"]
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
+ || [istarget "loongarch*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
|| [istarget "s390*-*-linux*"] } {
return 1