fail to single step qemu
2012-11-21
Hi all
I created a simple debug server on qemu, it receives command from tcp, but when i try to single step qemu, it fails, it will keep running rather than stop in the next instruction. I have traced the code a little bit, for gdb, when gdb do single step, the process will throw a debug exception and control will return GDB. For my debug server, it won’t. Why? Following is my debug server:
#include "config.h" #include "qemu-common.h" #ifdef CONFIG_USER_ONLY #include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include "qemu.h" #else #include "monitor.h" #include "qemu-char.h" #include "sysemu.h" #include "gdbstub.h" #endif #include "gkd.h" #define MAC_RECEIVE_SIZE 128 char command[MAC_RECEIVE_SIZE]; int commandNo; static int sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; static int gkd_chr_can_receive(void *opaque) { return MAC_RECEIVE_SIZE; } static void gkd_chr_receive(void *opaque, const uint8_t *buf, int size) { // printf("gdb_chr_receive, size=%d, buf=%s\n", size, buf); if (runstate_is_running()) { /* when the CPU is running, we cannot do anything except stop it when receiving a char */ printf("vm_stop2\n"); vm_stop(RUN_STATE_PAUSED); } else { if (size < MAC_RECEIVE_SIZE - 1) { int commandIndex = 0; bool toggle = FALSE; memset(command, 0, MAC_RECEIVE_SIZE); int x; for (x = 0; x < size; x++) { if (toggle) { if (buf[x] == '-') { toggle = FALSE; command[commandIndex] = '\0'; processCommand(); memset(command, 0, MAC_RECEIVE_SIZE); commandIndex = 0; } else { command[commandIndex] = buf[x]; commandIndex++; } } else { if (buf[x] == '+') { toggle = TRUE; } } } //strncpy((char *) receiveBuffer, (char *) buf, size - 1); //receiveBuffer[size - 1] = '\0'; } } } static void gkd_chr_event(void *opaque, int event) { // if (event == CHR_EVENT_BREAK) { // printf("CHR_EVENT_BREAK\n"); // } else if (event == CHR_EVENT_FOCUS) { // printf("CHR_EVENT_FOCUS\n"); // } else if (event == CHR_EVENT_OPENED) { // printf("CHR_EVENT_OPENED\n"); // } else if (event == CHR_EVENT_MUX_IN) { // printf("CHR_EVENT_MUX_IN\n"); // } else if (event == CHR_EVENT_MUX_OUT) { // printf("CHR_EVENT_MUX_OUT\n"); // } else if (event == CHR_EVENT_CLOSED) { // printf("CHR_EVENT_CLOSED\n"); // // processCommand(); // } else { // printf("error command, event=%d\n", event); // } switch (event) { case CHR_EVENT_OPENED: printf("vm_stop3\n"); vm_stop(RUN_STATE_PAUSED); break; default: break; } } static CPUArchState *find_cpu(uint32_t thread_id) { CPUArchState * env; for (env = first_cpu; env != NULL; env = env->next_cpu) { if (cpu_index(env) == thread_id) { return env; } } return NULL; } void processCommand() { printf("processCommand, %d) command=%s\n", commandNo, command); commandNo++; if (strcmp(command, "c") == 0) { printf("continue\n"); vm_start(); } else if (strcmp(command, "pause") == 0) { printf("pause\n"); vm_stop(RUN_STATE_PAUSED); } else if (strcmp(command, "s") == 0) { printf("single step\n"); CPUArchState *cpu = find_cpu(1); cpu_single_step(cpu, sstep_flags); printf("eip=%x, eax=%x\n", cpu->eip, cpu->regs[0]); vm_start(); } } static void gkd_vm_state_change(void *opaque, int running, RunState state) { printf("gkd_vm_state_change, state=%d\n", state); // if (running) { // return; // } CPUArchState *cpu = find_cpu(1); switch (state) { case RUN_STATE_DEBUG: tb_flush(cpu); break; } cpu_single_step(cpu, 0); } int gkd_start(const char *device) { commandNo = 0; char gdbstub_device_name[128]; printf("gkd_start, cmdline=%s\n", device); CharDriverState *chr = NULL; if (strcmp(device, "none") != 0) { if (strstart(device, "tcp:", NULL)) { /* enforce required TCP attributes */ snprintf(gdbstub_device_name, sizeof(gdbstub_device_name), "%s,nowait,nodelay,server", device); device = gdbstub_device_name; } } chr = qemu_chr_new("gkd", device, NULL); if (!chr) return -1; qemu_chr_add_handlers(chr, gkd_chr_can_receive, gkd_chr_receive, gkd_chr_event, NULL); qemu_add_vm_change_state_handler(gkd_vm_state_change, NULL); return 0; }