[v2,2/4] gdb: LoongArch: Add basic process record/replay support

Message ID 20241122090624.2355-3-lihui@loongson.cn
State New
Headers
Series gdb: LoongArch: Add process record/replay and reverse debugging 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

Hui Li Nov. 22, 2024, 9:06 a.m. UTC
  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

Guinevere Larsen Nov. 22, 2024, 2:14 p.m. UTC | #1
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
  

Patch

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