Platform-specific chanches to i386 simulation for deterministic replay and reverse debugging.
We insert instruction-counting code before executing every instruction. In replay mode this code also breaks execution of TB, when any of the events are found in the replay log. Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@gmail.com> --- diff --git a/target-arm/helper.h b/target-arm/helper.h index facfcd2..3f7a4f5 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -1,3 +1,4 @@ +DEF_HELPER_1(assert_cpu, void, env) DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_NO_RWG_SE, i32, i32) @@ -506,6 +507,9 @@ DEF_HELPER_3(neon_zip16, void, env, i32, i32) DEF_HELPER_3(neon_qzip8, void, env, i32, i32) DEF_HELPER_3(neon_qzip16, void, env, i32, i32) DEF_HELPER_3(neon_qzip32, void, env, i32, i32) +DEF_HELPER_1(replay_instruction, i32, env) +DEF_HELPER_0(instruction_changed, void) +DEF_HELPER_0(reverse_breakpoint, void) DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32) DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32) diff --git a/target-arm/machine.c b/target-arm/machine.c index 3bcc7cc..18b4dd5 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -259,6 +259,10 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT64(env.exception.vaddress, ARMCPU), VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), + /* Fields required by replay */ + VMSTATE_UINT32(parent_obj.interrupt_request, ARMCPU), + VMSTATE_INT32(parent_obj.exit_request, ARMCPU), + VMSTATE_INT32(parent_obj.exception_index, ARMCPU), VMSTATE_END_OF_LIST() }, .subsections = (VMStateSubsection[]) { diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 9c1ef52..3452e35 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -500,3 +500,58 @@ uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i) return ((uint32_t)x >> shift) | (x << (32 - shift)); } } + +uint32_t helper_replay_instruction(CPUARMState *env) +{ + CPUState *cpu = ENV_GET_CPU(env); + if (replay_mode == REPLAY_PLAY + && !replay_has_instruction()) { + cpu->exception_index = EXCP_REPLAY; + return 1; + } + + if (cpu->exit_request) { + cpu->exception_index = EXCP_REPLAY; + return 1; + } + + int timer = replay_has_timer_request(); + replay_instruction(timer); + return timer; +} + +void helper_reverse_breakpoint(void) +{ + replay_reverse_breakpoint(); +} + +void helper_assert_cpu(CPUARMState *env) +{ + if (replay_get_current_step() > 16000000000) + { + //int i; + //REPLAY_ASSERT_M(env->regs[0], "r0"); + //REPLAY_ASSERT_M(env->regs[1], "r1"); + //REPLAY_ASSERT_M(env->regs[2], "r2"); + //REPLAY_ASSERT_M(env->regs[3], "r3"); + //REPLAY_ASSERT_M(env->regs[4], "r4"); + //REPLAY_ASSERT_M(env->regs[5], "r5"); + //REPLAY_ASSERT_M(env->regs[6], "r6"); + //REPLAY_ASSERT_M(env->regs[7], "r7"); + REPLAY_ASSERT_M(env->regs[8], "r8"); + REPLAY_ASSERT_M(env->regs[9], "r9"); + REPLAY_ASSERT_M(env->regs[10], "r10"); + REPLAY_ASSERT_M(env->regs[11], "r11"); + REPLAY_ASSERT_M(env->regs[12], "r12"); + REPLAY_ASSERT_M(env->regs[13], "r13"); + REPLAY_ASSERT_M(env->regs[14], "r14"); + } + //REPLAY_ASSERT_M(env->regs[15], "r15"); + //REPLAY_ASSERT_M(env->fp_status.floatx80_rounding_precision, "floatx80_rounding_precision"); +} + +void helper_instruction_changed(void) +{ + replay_instruction(0); +} + diff --git a/target-arm/translate.c b/target-arm/translate.c index cf4e767..4df38f7 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -35,6 +35,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" + #define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T) #define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5) /* currently all emulated v5 cores are also v5TE, so don't bother */ @@ -509,6 +510,16 @@ static void shifter_out_im(TCGv_i32 var, int shift) } } +static void gen_instructions_count(void) +{ + TCGv_i32 cpu_tmp_i32 = tcg_temp_new(); + tcg_gen_ld_i32(cpu_tmp_i32, cpu_env, offsetof(CPUState, instructions_count) - ENV_OFFSET); + tcg_gen_addi_i32(cpu_tmp_i32, cpu_tmp_i32, 1); + tcg_gen_st_i32(cpu_tmp_i32, cpu_env, offsetof(CPUState, instructions_count) - ENV_OFFSET); + tcg_temp_free(cpu_tmp_i32); +} + + /* Shift by immediate. Includes special handling for shift == 0. */ static inline void gen_arm_shift_im(TCGv_i32 var, int shiftop, int shift, int flags) @@ -10858,6 +10869,24 @@ undef: gen_exception_insn(s, 2, EXCP_UDEF, syn_uncategorized()); } +static void gen_icount_step(DisasContext *s, target_ulong pc_ptr); +static void gen_instr_replay(DisasContext *s, target_ulong pc_ptr); +static void gen_instr_exit(DisasContext *s, target_ulong pc_ptr, long tb); + +static void gen_replay_run_tb_start(TranslationBlock *tb) +{ + TCGv_i32 flag = tcg_temp_new_i32(); + int label = gen_new_label(); + + tcg_gen_ld_i32(flag, cpu_env, + offsetof(CPUState, tcg_exit_req) - ENV_OFFSET); + tcg_gen_brcondi_i32(TCG_COND_EQ, flag, 0, label); + tcg_temp_free_i32(flag); + tcg_gen_exit_tb((tcg_target_long)tb + TB_EXIT_REQUESTED); + gen_set_label(label); + gen_helper_instruction_changed(); +} + /* generate intermediate code in gen_opc_buf and gen_opparam_buf for basic block 'tb'. If search_pc is TRUE, also generate PC information for each intermediate instruction. */ @@ -10928,7 +10957,15 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + if (replay_mode == REPLAY_NONE) { + gen_tb_start(); + } + + if (replay_mode == REPLAY_PLAY + && replay_get_play_submode() == REPLAY_PLAY_CHANGED) { + gen_replay_run_tb_start(tb); + } + tcg_clear_temp_count(); @@ -11015,12 +11052,29 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, tcg_ctx.gen_opc_icount[lj] = num_insns; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO) && replay_mode == REPLAY_NONE) gen_io_start(); if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); } + if (replay_mode != REPLAY_NONE) { + /* In PLAY mode check timer event at every instruction - not only at the beginning of the block. + This is needed, when replaying has changed the bounds of translation blocks. + */ + if ((dc->pc == pc_start || replay_mode == REPLAY_PLAY) + && replay_get_play_submode() != REPLAY_PLAY_CHANGED) { + gen_instr_replay(dc, dc->pc); + } else { + gen_instructions_count(); + } + } + + /* icount mode support */ + if (replay_mode != REPLAY_NONE) { + gen_icount_step(dc, dc->pc); + } + if (dc->thumb) { disas_thumb_insn(env, dc); @@ -11054,7 +11108,8 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && !cs->singlestep_enabled && !singlestep && - dc->pc < next_page_start && + /* +3 is for unaligned Thumb2 instructions */ + dc->pc + 3 < next_page_start && num_insns < max_insns); if (tb->cflags & CF_LAST_IO) { @@ -11134,7 +11189,11 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } done_generating: - gen_tb_end(tb, num_insns); + if (replay_mode == REPLAY_NONE) { + if (tb->cflags & CF_LAST_IO) + gen_io_end(); + gen_tb_end(tb, num_insns); + } *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS @@ -11155,6 +11214,10 @@ done_generating: tb->size = dc->pc - pc_start; tb->icount = num_insns; } + if (tcg_ctx.gen_opc_ptr > tcg_ctx.gen_opc_buf + OPC_BUF_SIZE) { + fprintf(stderr, "TCG buffer overflow\n"); + exit(1); + } } void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) @@ -11231,3 +11294,41 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos) env->condexec_bits = gen_opc_condexec_bits[pc_pos]; } } + +static void gen_instr_exit(DisasContext *s, target_ulong pc_ptr, long tb) +{ + gen_set_condexec(s); + gen_set_pc_im(s, pc_ptr); + tcg_gen_exit_tb(tb); +} + +static void gen_instr_replay(DisasContext *s, target_ulong pc_ptr) +{ + int l1 = gen_new_label(); + TCGv_i32 cpu_tmp2_i32 = tcg_temp_new(); + + gen_helper_replay_instruction(cpu_tmp2_i32, cpu_env); + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_tmp2_i32, 0, l1); + + gen_instr_exit(s, pc_ptr, 0); + gen_set_label(l1); + tcg_temp_free(cpu_tmp2_i32); +} + +static void gen_icount_step(DisasContext *s, target_ulong pc_ptr) +{ + if (!use_icount) + return; + + int l1 = gen_new_label(); + TCGv_i32 cpu_tmp2_i32 = tcg_temp_new(); + tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUState, icount_decr.u32) - ENV_OFFSET); + tcg_gen_brcondi_i32(TCG_COND_NE, cpu_tmp2_i32, 0, l1); + + gen_instr_exit(s, pc_ptr, 2); + + gen_set_label(l1); + tcg_gen_subi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 1); + tcg_gen_st16_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUState, icount_decr.u16.low) - ENV_OFFSET); + tcg_temp_free(cpu_tmp2_i32); +}