Search Posts

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;
}

Leave a Reply

Your email address will not be published. Required fields are marked *