fail to single step qemu
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;
}