Commit 4c3a88a284b288e0ed3c097de7fc07111d848003
1 parent
d6b49367
gdb stub breakpoints support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@332 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
11 changed files
with
151 additions
and
25 deletions
cpu-all.h
| ... | ... | @@ -313,9 +313,12 @@ extern CPUState *cpu_single_env; |
| 313 | 313 | #define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */ |
| 314 | 314 | void cpu_interrupt(CPUState *s, int mask); |
| 315 | 315 | |
| 316 | +int cpu_breakpoint_insert(CPUState *env, uint32_t pc); | |
| 317 | +int cpu_breakpoint_remove(CPUState *env, uint32_t pc); | |
| 318 | + | |
| 316 | 319 | /* gdb stub API */ |
| 317 | 320 | extern int gdbstub_fd; |
| 318 | 321 | CPUState *cpu_gdbstub_get_env(void *opaque); |
| 319 | -int cpu_gdbstub(void *opaque, void (*main_loop)(void *opaque), int port); | |
| 322 | +int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port); | |
| 320 | 323 | |
| 321 | 324 | #endif /* CPU_ALL_H */ | ... | ... |
cpu-exec.c
| ... | ... | @@ -280,7 +280,7 @@ int cpu_exec(CPUState *env1) |
| 280 | 280 | tb->tc_ptr = tc_ptr; |
| 281 | 281 | tb->cs_base = (unsigned long)cs_base; |
| 282 | 282 | tb->flags = flags; |
| 283 | - ret = cpu_gen_code(tb, CODE_GEN_MAX_SIZE, &code_gen_size); | |
| 283 | + ret = cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size); | |
| 284 | 284 | #if defined(TARGET_I386) |
| 285 | 285 | /* XXX: suppress that, this is incorrect */ |
| 286 | 286 | /* if invalid instruction, signal it */ | ... | ... |
cpu-i386.h
| ... | ... | @@ -155,6 +155,9 @@ |
| 155 | 155 | |
| 156 | 156 | #define EXCP_INTERRUPT 256 /* async interruption */ |
| 157 | 157 | #define EXCP_HLT 257 /* hlt instruction reached */ |
| 158 | +#define EXCP_DEBUG 258 /* cpu stopped after a breakpoint or singlestep */ | |
| 159 | + | |
| 160 | +#define MAX_BREAKPOINTS 32 | |
| 158 | 161 | |
| 159 | 162 | enum { |
| 160 | 163 | CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ |
| ... | ... | @@ -270,6 +273,9 @@ typedef struct CPUX86State { |
| 270 | 273 | uint32_t dr[8]; /* debug registers */ |
| 271 | 274 | int interrupt_request; |
| 272 | 275 | int user_mode_only; /* user mode only simulation */ |
| 276 | + | |
| 277 | + uint32_t breakpoints[MAX_BREAKPOINTS]; | |
| 278 | + int nb_breakpoints; | |
| 273 | 279 | |
| 274 | 280 | /* user data */ |
| 275 | 281 | void *opaque; | ... | ... |
exec.c
| ... | ... | @@ -617,6 +617,48 @@ static void tb_reset_jump_recursive(TranslationBlock *tb) |
| 617 | 617 | tb_reset_jump_recursive2(tb, 1); |
| 618 | 618 | } |
| 619 | 619 | |
| 620 | +/* add a breakpoint */ | |
| 621 | +int cpu_breakpoint_insert(CPUState *env, uint32_t pc) | |
| 622 | +{ | |
| 623 | +#if defined(TARGET_I386) | |
| 624 | + int i; | |
| 625 | + | |
| 626 | + for(i = 0; i < env->nb_breakpoints; i++) { | |
| 627 | + if (env->breakpoints[i] == pc) | |
| 628 | + return 0; | |
| 629 | + } | |
| 630 | + | |
| 631 | + if (env->nb_breakpoints >= MAX_BREAKPOINTS) | |
| 632 | + return -1; | |
| 633 | + env->breakpoints[env->nb_breakpoints++] = pc; | |
| 634 | + tb_invalidate_page(pc); | |
| 635 | + return 0; | |
| 636 | +#else | |
| 637 | + return -1; | |
| 638 | +#endif | |
| 639 | +} | |
| 640 | + | |
| 641 | +/* remove a breakpoint */ | |
| 642 | +int cpu_breakpoint_remove(CPUState *env, uint32_t pc) | |
| 643 | +{ | |
| 644 | +#if defined(TARGET_I386) | |
| 645 | + int i; | |
| 646 | + for(i = 0; i < env->nb_breakpoints; i++) { | |
| 647 | + if (env->breakpoints[i] == pc) | |
| 648 | + goto found; | |
| 649 | + } | |
| 650 | + return -1; | |
| 651 | + found: | |
| 652 | + memmove(&env->breakpoints[i], &env->breakpoints[i + 1], | |
| 653 | + (env->nb_breakpoints - (i + 1)) * sizeof(env->breakpoints[0])); | |
| 654 | + env->nb_breakpoints--; | |
| 655 | + tb_invalidate_page(pc); | |
| 656 | + return 0; | |
| 657 | +#else | |
| 658 | + return -1; | |
| 659 | +#endif | |
| 660 | +} | |
| 661 | + | |
| 620 | 662 | /* mask must never be zero */ |
| 621 | 663 | void cpu_interrupt(CPUState *env, int mask) |
| 622 | 664 | { | ... | ... |
exec.h
| ... | ... | @@ -58,10 +58,10 @@ extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; |
| 58 | 58 | extern FILE *logfile; |
| 59 | 59 | extern int loglevel; |
| 60 | 60 | |
| 61 | -int gen_intermediate_code(struct TranslationBlock *tb); | |
| 62 | -int gen_intermediate_code_pc(struct TranslationBlock *tb); | |
| 61 | +int gen_intermediate_code(CPUState *env, struct TranslationBlock *tb); | |
| 62 | +int gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb); | |
| 63 | 63 | void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf); |
| 64 | -int cpu_gen_code(struct TranslationBlock *tb, | |
| 64 | +int cpu_gen_code(CPUState *env, struct TranslationBlock *tb, | |
| 65 | 65 | int max_code_size, int *gen_code_size_ptr); |
| 66 | 66 | int cpu_restore_state(struct TranslationBlock *tb, |
| 67 | 67 | CPUState *env, unsigned long searched_pc); | ... | ... |
gdbstub.c
| ... | ... | @@ -37,7 +37,7 @@ |
| 37 | 37 | #include "thunk.h" |
| 38 | 38 | #include "exec.h" |
| 39 | 39 | |
| 40 | -//#define DEBUG_GDB | |
| 40 | +#define DEBUG_GDB | |
| 41 | 41 | |
| 42 | 42 | int gdbstub_fd = -1; |
| 43 | 43 | |
| ... | ... | @@ -283,11 +283,11 @@ static int memory_rw(uint8_t *buf, uint32_t addr, int len, int is_write) |
| 283 | 283 | } |
| 284 | 284 | |
| 285 | 285 | /* port = 0 means default port */ |
| 286 | -int cpu_gdbstub(void *opaque, void (*main_loop)(void *opaque), int port) | |
| 286 | +int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port) | |
| 287 | 287 | { |
| 288 | 288 | CPUState *env; |
| 289 | 289 | const char *p; |
| 290 | - int ret, ch, nb_regs, i; | |
| 290 | + int ret, ch, nb_regs, i, type; | |
| 291 | 291 | char buf[4096]; |
| 292 | 292 | uint8_t mem_buf[2000]; |
| 293 | 293 | uint32_t *registers; |
| ... | ... | @@ -309,8 +309,19 @@ int cpu_gdbstub(void *opaque, void (*main_loop)(void *opaque), int port) |
| 309 | 309 | put_packet(buf); |
| 310 | 310 | break; |
| 311 | 311 | case 'c': |
| 312 | - main_loop(opaque); | |
| 313 | - snprintf(buf, sizeof(buf), "S%02x", 0); | |
| 312 | + if (*p != '\0') { | |
| 313 | + addr = strtoul(p, (char **)&p, 16); | |
| 314 | + env = cpu_gdbstub_get_env(opaque); | |
| 315 | +#if defined(TARGET_I386) | |
| 316 | + env->eip = addr; | |
| 317 | +#endif | |
| 318 | + } | |
| 319 | + ret = main_loop(opaque); | |
| 320 | + if (ret == EXCP_DEBUG) | |
| 321 | + ret = SIGTRAP; | |
| 322 | + else | |
| 323 | + ret = 0; | |
| 324 | + snprintf(buf, sizeof(buf), "S%02x", ret); | |
| 314 | 325 | put_packet(buf); |
| 315 | 326 | break; |
| 316 | 327 | case 'g': |
| ... | ... | @@ -379,6 +390,40 @@ int cpu_gdbstub(void *opaque, void (*main_loop)(void *opaque), int port) |
| 379 | 390 | else |
| 380 | 391 | put_packet("OK"); |
| 381 | 392 | break; |
| 393 | + case 'Z': | |
| 394 | + type = strtoul(p, (char **)&p, 16); | |
| 395 | + if (*p == ',') | |
| 396 | + p++; | |
| 397 | + addr = strtoul(p, (char **)&p, 16); | |
| 398 | + if (*p == ',') | |
| 399 | + p++; | |
| 400 | + len = strtoul(p, (char **)&p, 16); | |
| 401 | + if (type == 0 || type == 1) { | |
| 402 | + env = cpu_gdbstub_get_env(opaque); | |
| 403 | + if (cpu_breakpoint_insert(env, addr) < 0) | |
| 404 | + goto breakpoint_error; | |
| 405 | + put_packet("OK"); | |
| 406 | + } else { | |
| 407 | + breakpoint_error: | |
| 408 | + put_packet("ENN"); | |
| 409 | + } | |
| 410 | + break; | |
| 411 | + case 'z': | |
| 412 | + type = strtoul(p, (char **)&p, 16); | |
| 413 | + if (*p == ',') | |
| 414 | + p++; | |
| 415 | + addr = strtoul(p, (char **)&p, 16); | |
| 416 | + if (*p == ',') | |
| 417 | + p++; | |
| 418 | + len = strtoul(p, (char **)&p, 16); | |
| 419 | + if (type == 0 || type == 1) { | |
| 420 | + env = cpu_gdbstub_get_env(opaque); | |
| 421 | + cpu_breakpoint_remove(env, addr); | |
| 422 | + put_packet("OK"); | |
| 423 | + } else { | |
| 424 | + goto breakpoint_error; | |
| 425 | + } | |
| 426 | + break; | |
| 382 | 427 | default: |
| 383 | 428 | /* put empty packet */ |
| 384 | 429 | buf[0] = '\0'; | ... | ... |
op-i386.c
translate-arm.c
| ... | ... | @@ -786,7 +786,9 @@ static void disas_arm_insn(DisasContext *s) |
| 786 | 786 | /* generate intermediate code in gen_opc_buf and gen_opparam_buf for |
| 787 | 787 | basic block 'tb'. If search_pc is TRUE, also generate PC |
| 788 | 788 | information for each intermediate instruction. */ |
| 789 | -static inline int gen_intermediate_code_internal(TranslationBlock *tb, int search_pc) | |
| 789 | +static inline int gen_intermediate_code_internal(CPUState *env, | |
| 790 | + TranslationBlock *tb, | |
| 791 | + int search_pc) | |
| 790 | 792 | { |
| 791 | 793 | DisasContext dc1, *dc = &dc1; |
| 792 | 794 | uint16_t *gen_opc_end; |
| ... | ... | @@ -853,14 +855,14 @@ static inline int gen_intermediate_code_internal(TranslationBlock *tb, int searc |
| 853 | 855 | return 0; |
| 854 | 856 | } |
| 855 | 857 | |
| 856 | -int gen_intermediate_code(TranslationBlock *tb) | |
| 858 | +int gen_intermediate_code(CPUState *env, TranslationBlock *tb) | |
| 857 | 859 | { |
| 858 | - return gen_intermediate_code_internal(tb, 0); | |
| 860 | + return gen_intermediate_code_internal(env, tb, 0); | |
| 859 | 861 | } |
| 860 | 862 | |
| 861 | -int gen_intermediate_code_pc(TranslationBlock *tb) | |
| 863 | +int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) | |
| 862 | 864 | { |
| 863 | - return gen_intermediate_code_internal(tb, 1); | |
| 865 | + return gen_intermediate_code_internal(env, tb, 1); | |
| 864 | 866 | } |
| 865 | 867 | |
| 866 | 868 | CPUARMState *cpu_arm_init(void) | ... | ... |
translate-i386.c
| ... | ... | @@ -1463,6 +1463,15 @@ static void gen_interrupt(DisasContext *s, int intno, |
| 1463 | 1463 | s->is_jmp = 1; |
| 1464 | 1464 | } |
| 1465 | 1465 | |
| 1466 | +static void gen_debug(DisasContext *s, unsigned int cur_eip) | |
| 1467 | +{ | |
| 1468 | + if (s->cc_op != CC_OP_DYNAMIC) | |
| 1469 | + gen_op_set_cc_op(s->cc_op); | |
| 1470 | + gen_op_jmp_im(cur_eip); | |
| 1471 | + gen_op_debug(); | |
| 1472 | + s->is_jmp = 1; | |
| 1473 | +} | |
| 1474 | + | |
| 1466 | 1475 | /* generate a jump to eip. No segment change must happen before as a |
| 1467 | 1476 | direct call to the next block may occur */ |
| 1468 | 1477 | static void gen_jmp(DisasContext *s, unsigned int eip) |
| ... | ... | @@ -4080,7 +4089,9 @@ static void optimize_flags(uint16_t *opc_buf, int opc_buf_len) |
| 4080 | 4089 | /* generate intermediate code in gen_opc_buf and gen_opparam_buf for |
| 4081 | 4090 | basic block 'tb'. If search_pc is TRUE, also generate PC |
| 4082 | 4091 | information for each intermediate instruction. */ |
| 4083 | -static inline int gen_intermediate_code_internal(TranslationBlock *tb, int search_pc) | |
| 4092 | +static inline int gen_intermediate_code_internal(CPUState *env, | |
| 4093 | + TranslationBlock *tb, | |
| 4094 | + int search_pc) | |
| 4084 | 4095 | { |
| 4085 | 4096 | DisasContext dc1, *dc = &dc1; |
| 4086 | 4097 | uint8_t *pc_ptr; |
| ... | ... | @@ -4116,6 +4127,14 @@ static inline int gen_intermediate_code_internal(TranslationBlock *tb, int searc |
| 4116 | 4127 | pc_ptr = pc_start; |
| 4117 | 4128 | lj = -1; |
| 4118 | 4129 | do { |
| 4130 | + if (env->nb_breakpoints > 0) { | |
| 4131 | + for(j = 0; j < env->nb_breakpoints; j++) { | |
| 4132 | + if (env->breakpoints[j] == (unsigned long)pc_ptr) { | |
| 4133 | + gen_debug(dc, pc_ptr - dc->cs_base); | |
| 4134 | + goto the_end; | |
| 4135 | + } | |
| 4136 | + } | |
| 4137 | + } | |
| 4119 | 4138 | if (search_pc) { |
| 4120 | 4139 | j = gen_opc_ptr - gen_opc_buf; |
| 4121 | 4140 | if (lj < j) { |
| ... | ... | @@ -4160,6 +4179,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock *tb, int searc |
| 4160 | 4179 | if (dc->tf) { |
| 4161 | 4180 | gen_op_raise_exception(EXCP01_SSTP); |
| 4162 | 4181 | } |
| 4182 | + the_end: | |
| 4163 | 4183 | if (dc->is_jmp != DISAS_TB_JUMP) { |
| 4164 | 4184 | /* indicate that the hash table must be used to find the next TB */ |
| 4165 | 4185 | gen_op_movl_T0_0(); |
| ... | ... | @@ -4202,14 +4222,14 @@ static inline int gen_intermediate_code_internal(TranslationBlock *tb, int searc |
| 4202 | 4222 | return 0; |
| 4203 | 4223 | } |
| 4204 | 4224 | |
| 4205 | -int gen_intermediate_code(TranslationBlock *tb) | |
| 4225 | +int gen_intermediate_code(CPUState *env, TranslationBlock *tb) | |
| 4206 | 4226 | { |
| 4207 | - return gen_intermediate_code_internal(tb, 0); | |
| 4227 | + return gen_intermediate_code_internal(env, tb, 0); | |
| 4208 | 4228 | } |
| 4209 | 4229 | |
| 4210 | -int gen_intermediate_code_pc(TranslationBlock *tb) | |
| 4230 | +int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) | |
| 4211 | 4231 | { |
| 4212 | - return gen_intermediate_code_internal(tb, 1); | |
| 4232 | + return gen_intermediate_code_internal(env, tb, 1); | |
| 4213 | 4233 | } |
| 4214 | 4234 | |
| 4215 | 4235 | CPUX86State *cpu_x86_init(void) | ... | ... |
translate.c
| ... | ... | @@ -107,13 +107,13 @@ void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf) |
| 107 | 107 | '*gen_code_size_ptr' contains the size of the generated code (host |
| 108 | 108 | code). |
| 109 | 109 | */ |
| 110 | -int cpu_gen_code(TranslationBlock *tb, | |
| 110 | +int cpu_gen_code(CPUState *env, TranslationBlock *tb, | |
| 111 | 111 | int max_code_size, int *gen_code_size_ptr) |
| 112 | 112 | { |
| 113 | 113 | uint8_t *gen_code_buf; |
| 114 | 114 | int gen_code_size; |
| 115 | 115 | |
| 116 | - if (gen_intermediate_code(tb) < 0) | |
| 116 | + if (gen_intermediate_code(env, tb) < 0) | |
| 117 | 117 | return -1; |
| 118 | 118 | |
| 119 | 119 | /* generate machine code */ |
| ... | ... | @@ -154,7 +154,7 @@ int cpu_restore_state(TranslationBlock *tb, |
| 154 | 154 | unsigned long tc_ptr; |
| 155 | 155 | uint16_t *opc_ptr; |
| 156 | 156 | |
| 157 | - if (gen_intermediate_code_pc(tb) < 0) | |
| 157 | + if (gen_intermediate_code_pc(env, tb) < 0) | |
| 158 | 158 | return -1; |
| 159 | 159 | |
| 160 | 160 | /* find opc index corresponding to search_pc */ | ... | ... |
vl.c
| ... | ... | @@ -2540,7 +2540,7 @@ CPUState *cpu_gdbstub_get_env(void *opaque) |
| 2540 | 2540 | return global_env; |
| 2541 | 2541 | } |
| 2542 | 2542 | |
| 2543 | -void main_loop(void *opaque) | |
| 2543 | +int main_loop(void *opaque) | |
| 2544 | 2544 | { |
| 2545 | 2545 | struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd, *gdb_ufd; |
| 2546 | 2546 | int ret, n, timeout; |
| ... | ... | @@ -2552,7 +2552,8 @@ void main_loop(void *opaque) |
| 2552 | 2552 | ret = cpu_x86_exec(env); |
| 2553 | 2553 | if (reset_requested) |
| 2554 | 2554 | break; |
| 2555 | - | |
| 2555 | + if (ret == EXCP_DEBUG) | |
| 2556 | + return EXCP_DEBUG; | |
| 2556 | 2557 | /* if hlt instruction, we wait until the next IRQ */ |
| 2557 | 2558 | if (ret == EXCP_HLT) |
| 2558 | 2559 | timeout = 10; |
| ... | ... | @@ -2618,6 +2619,7 @@ void main_loop(void *opaque) |
| 2618 | 2619 | timer_irq_pending = 0; |
| 2619 | 2620 | } |
| 2620 | 2621 | } |
| 2622 | + return EXCP_INTERRUPT; | |
| 2621 | 2623 | } |
| 2622 | 2624 | |
| 2623 | 2625 | void help(void) | ... | ... |