diff mbox

i386: fix breakpoints handling in icount mode

Message ID 5561E380.5010003@web.de
State New
Headers show

Commit Message

Jan Kiszka May 24, 2015, 2:43 p.m. UTC
On 2015-01-12 09:55, Paolo Bonzini wrote:
> On 12/01/2015 09:30, Jan Kiszka wrote:
>> I think this would only cure a symptom, but it doesn't explain why we
>> now hit cpu_handle_guest_debug which we do not before the patch:
> 
> That means we now exit with EXCP_DEBUG and we didn't before?
> 
> Something like this would be a more complete fix (it works if you have
> both gdb and CPU breakpoints), but I'm not sure if it's also a band-aid
> for the symptoms.
> 
> diff --git a/cpu-exec.c b/cpu-exec.c
> index a4f0eff..56139ac 100644
> --- a/cpu-exec.c
> +++ b/cpu-exec.c
> @@ -302,7 +302,7 @@ static inline TranslationBlock *tb_find_fast(CPUArchState *env)
>      return tb;
>  }
>  
> -static void cpu_handle_debug_exception(CPUArchState *env)
> +static int cpu_handle_debug_exception(CPUArchState *env)
>  {
>      CPUState *cpu = ENV_GET_CPU(env);
>      CPUClass *cc = CPU_GET_CLASS(cpu);
> @@ -314,7 +314,7 @@ static void cpu_handle_debug_exception(CPUArchState *env)
>          }
>      }
>  
> -    cc->debug_excp_handler(cpu);
> +    return cc->debug_excp_handler(cpu);
>  }
>  
>  /* main execution loop */
> @@ -375,12 +375,15 @@ int cpu_exec(CPUArchState *env)
>              if (cpu->exception_index >= 0) {
>                  if (cpu->exception_index >= EXCP_INTERRUPT) {
>                      /* exit request from the cpu execution loop */
> -                    ret = cpu->exception_index;
> -                    if (ret == EXCP_DEBUG) {
> -                        cpu_handle_debug_exception(env);
> +                    if (cpu->exception_index == EXCP_DEBUG) {
> +                        ret = cpu_handle_debug_exception(env);
> +                    } else {
> +                        ret = cpu->exception_index;
> +                    }
> +                    if (ret >= 0) {

This condition is always true for both 0 and EXCP_DEBUG.

> +                        cpu->exception_index = -1;
> +                        break;
>                      }
> -                    cpu->exception_index = -1;
> -                    break;
>                  } else {
>  #if defined(CONFIG_USER_ONLY)
>                      /* if user mode only, we simulate a fake exception
> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
> index 2098f1c..c1d6c20 100644
> --- a/include/qom/cpu.h
> +++ b/include/qom/cpu.h
> @@ -95,7 +95,8 @@ struct TranslationBlock;
>   * @get_phys_page_debug: Callback for obtaining a physical address.
>   * @gdb_read_register: Callback for letting GDB read a register.
>   * @gdb_write_register: Callback for letting GDB write a register.
> - * @debug_excp_handler: Callback for handling debug exceptions.
> + * @debug_excp_handler: Callback for handling debug exceptions.  Should
> + * return either #EXCP_DEBUG or zero.
>   * @vmsd: State description for migration.
>   * @gdb_num_core_regs: Number of core registers accessible to GDB.
>   * @gdb_core_xml_file: File name for core registers GDB XML description.
> @@ -140,7 +141,7 @@ typedef struct CPUClass {
>      hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr);
>      int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg);
>      int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg);
> -    void (*debug_excp_handler)(CPUState *cpu);
> +    int (*debug_excp_handler)(CPUState *cpu);
>  
>      int (*write_elf64_note)(WriteCoreDumpFunction f, CPUState *cpu,
>                              int cpuid, void *opaque);
> diff --git a/qom/cpu.c b/qom/cpu.c
> index 9c68fa4..e86fec5 100644
> --- a/qom/cpu.c
> +++ b/qom/cpu.c
> @@ -193,6 +193,11 @@ static bool cpu_common_virtio_is_big_endian(CPUState *cpu)
>      return target_words_bigendian();
>  }
>  
> +static int cpu_common_debug_excp_handler(CPUState *cpu)
> +{
> +    return EXCP_DEBUG;
> +}
> +
>  static void cpu_common_noop(CPUState *cpu)
>  {
>  }
> @@ -340,7 +345,7 @@ static void cpu_class_init(ObjectClass *klass, void *data)
>      k->gdb_read_register = cpu_common_gdb_read_register;
>      k->gdb_write_register = cpu_common_gdb_write_register;
>      k->virtio_is_big_endian = cpu_common_virtio_is_big_endian;
> -    k->debug_excp_handler = cpu_common_noop;
> +    k->debug_excp_handler = cpu_common_debug_excp_handler;
>      k->cpu_exec_enter = cpu_common_noop;
>      k->cpu_exec_exit = cpu_common_noop;
>      k->cpu_exec_interrupt = cpu_common_exec_interrupt;
> diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
> index 2bed914..40b7f79 100644
> --- a/target-arm/op_helper.c
> +++ b/target-arm/op_helper.c
> @@ -732,7 +732,7 @@ static bool check_breakpoints(ARMCPU *cpu)
>      return false;
>  }
>  
> -void arm_debug_excp_handler(CPUState *cs)
> +int arm_debug_excp_handler(CPUState *cs)
>  {
>      /* Called by core code when a watchpoint or breakpoint fires;
>       * need to check which one and raise the appropriate exception.
> @@ -756,9 +756,9 @@ void arm_debug_excp_handler(CPUState *cs)
>                  }
>                  env->exception.vaddress = wp_hit->hitaddr;
>                  raise_exception(env, EXCP_DATA_ABORT);
> -            } else {
> -                cpu_resume_from_signal(cs, NULL);
> +                return 0;
>              }
> +            cpu_resume_from_signal(cs, NULL);
>          }
>      } else {
>          if (check_breakpoints(cpu)) {
> @@ -771,8 +771,10 @@ void arm_debug_excp_handler(CPUState *cs)
>              }
>              /* FAR is UNKNOWN, so doesn't need setting */
>              raise_exception(env, EXCP_PREFETCH_ABORT);
> +            return 0;
>          }
>      }
> +    return EXCP_DEBUG;
>  }
>  
>  /* ??? Flag setting arithmetic is awkward because we need to do comparisons.
> diff --git a/target-i386/helper.c b/target-i386/helper.c
> index 4f1ddf7..a313424 100644
> --- a/target-i386/helper.c
> +++ b/target-i386/helper.c
> @@ -1004,17 +1004,19 @@ bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update)
>      return hit_enabled;
>  }
>  
> -void breakpoint_handler(CPUState *cs)
> +int breakpoint_handler(CPUState *cs)
>  {
>      X86CPU *cpu = X86_CPU(cs);
>      CPUX86State *env = &cpu->env;
>      CPUBreakpoint *bp;
> +    int ret = EXCP_DEBUG;
>  
>      if (cs->watchpoint_hit) {
>          if (cs->watchpoint_hit->flags & BP_CPU) {
>              cs->watchpoint_hit = NULL;
>              if (check_hw_breakpoints(env, false)) {
>                  raise_exception(env, EXCP01_DB);
> +                ret = 0;
>              } else {
>                  cpu_resume_from_signal(cs, NULL);
>              }
> @@ -1025,11 +1027,13 @@ void breakpoint_handler(CPUState *cs)
>                  if (bp->flags & BP_CPU) {
>                      check_hw_breakpoints(env, true);
>                      raise_exception(env, EXCP01_DB);
> +                    ret = 0;
>                  }
>                  break;
>              }
>          }
>      }
> +    return ret;
>  }
>  
>  typedef struct MCEInjectionParams {
> diff --git a/target-lm32/helper.c b/target-lm32/helper.c
> index 7a41f29..088d3fa 100644
> --- a/target-lm32/helper.c
> +++ b/target-lm32/helper.c
> @@ -125,17 +125,19 @@ static bool check_watchpoints(CPULM32State *env)
>      return false;
>  }
>  
> -void lm32_debug_excp_handler(CPUState *cs)
> +int lm32_debug_excp_handler(CPUState *cs)
>  {
>      LM32CPU *cpu = LM32_CPU(cs);
>      CPULM32State *env = &cpu->env;
>      CPUBreakpoint *bp;
> +    int ret = EXCP_DEBUG;
>  
>      if (cs->watchpoint_hit) {
>          if (cs->watchpoint_hit->flags & BP_CPU) {
>              cs->watchpoint_hit = NULL;
>              if (check_watchpoints(env)) {
>                  raise_exception(env, EXCP_WATCHPOINT);
> +                ret = 0;
>              } else {
>                  cpu_resume_from_signal(cs, NULL);
>              }
> @@ -145,11 +147,13 @@ void lm32_debug_excp_handler(CPUState *cs)
>              if (bp->pc == env->pc) {
>                  if (bp->flags & BP_CPU) {
>                      raise_exception(env, EXCP_BREAKPOINT);
> +                    ret = 0;
>                  }
>                  break;
>              }
>          }
>      }
> +    return ret;
>  }
>  
>  void lm32_cpu_do_interrupt(CPUState *cs)
> diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c
> index d84d259..52f50a2 100644
> --- a/target-xtensa/helper.c
> +++ b/target-xtensa/helper.c
> @@ -79,7 +79,7 @@ static uint32_t check_hw_breakpoints(CPUXtensaState *env)
>      return 0;
>  }
>  
> -void xtensa_breakpoint_handler(CPUState *cs)
> +int xtensa_breakpoint_handler(CPUState *cs)
>  {
>      XtensaCPU *cpu = XTENSA_CPU(cs);
>      CPUXtensaState *env = &cpu->env;
> @@ -92,10 +92,12 @@ void xtensa_breakpoint_handler(CPUState *cs)
>              cause = check_hw_breakpoints(env);
>              if (cause) {
>                  debug_exception_env(env, cause);
> +                return 0;
>              }
>              cpu_resume_from_signal(cs, NULL);
>          }
>      }
> +    return EXCP_DEBUG;
>  }
>  
>  XtensaCPU *cpu_xtensa_init(const char *cpu_model)
> 
> 

Lost track of this: This doesn't build (EXCP_DEBUG not available to
qom/cpu.c, wrong breakpoint_handler prototype) and then, when the build
issues are fixed, it doesn't work.

Playing a bit with the code, I found out that this cures the issue:


pc_ptr is used at the end of the function to calculate the tb size. I
suspect that the difference prevents that the breakpoint event is
associated with the stored location. Can someone explain this more
properly? Then I would happily pass patch credits.

Jan
diff mbox

Patch

diff --git a/target-i386/translate.c b/target-i386/translate.c
index 305ce50..57b607d 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -8006,6 +8006,7 @@  static inline void gen_intermediate_code_internal(X86CPU *cpu,
                 if (bp->pc == pc_ptr &&
                     !((bp->flags & BP_CPU) && (tb->flags & HF_RF_MASK))) {
                     gen_debug(dc, pc_ptr - dc->cs_base);
+                    pc_ptr = disas_insn(env, dc, pc_ptr);
                     goto done_generating;
                 }
             }