QEMU支持GDB, 它内置了一个GDB server,就是说如果你懂GDB protocol的话,你可以直接向GDB发送指令,控制GDB。
但GDB不是完美, 所以你有理主去搞一个自己的debug server, 如果你是在开发调试器的话, 你更加需要一个自己的debug protocol.
这编文章我会示范怎样去创造一个debug server, 经TCP去接收指令. 我会以qemu-kvm来示范, qemu-kvm代码来讲和qemu不同, 但加debug server来说都是差不多.
我会用eclipse+CDT来调qemu,我不会用gdb了, 因为命令行对我来说是灾难.
Here we go:
所有我改过的variable和function, 我都会以peter为开头,我在代码里搜peter就可以知道我改过什么地方了。
Step 1) Qemu 的开始是在文件vl.c, 行2041, 我们需要加一个identifier去enum, 就叫它 "DEV_PETER"
struct device_config {
enum {
DEV_USB, /* -usbdevice */
DEV_BT, /* -bt */
DEV_SERIAL, /* -serial */
DEV_PARALLEL, /* -parallel */
DEV_VIRTCON, /* -virtioconsole */
DEV_DEBUGCON, /* -debugcon */
DEV_GDB, /* -gdb, -s */
DEV_PETER /* peter */
} type;
const char *cmdline;
Location loc;
QTAILQ_ENTRY(device_config) next;
};
Step 2) 在行2793, 你可以见到一个"switch loop", 它是用来parse命令行的参数的, 因为我想指定我的debug server的tcp port, 所以我在执行qemu时会加一个参数"-peter 1234"
case QEMU_OPTION_s:
add_device_config(DEV_GDB, "tcp::" DEFAULT_GDBSTUB_PORT);
break;
case QEMU_OPTION_gdb:
add_device_config(DEV_GDB, optarg);
break;
case QEMU_OPTION_peter:
peter_cmdline=optarg;
break;
case QEMU_OPTION_L:
data_dir = optarg;
break;
請在vl.c的頭頂加入const char *peter_cmdline;
我们需要改文件qemu-options.hx, 它决定command-line-argument-parser 怎样 parse 我们的参数"-peter 1234". 所以要在行2390加入:
DEF("peter", HAS_ARG, QEMU_OPTION_peter, \
"-peter dev wait for peter connection on 'dev'\n", QEMU_ARCH_ALL)
STEXI
@findex -peter
Wait for peter
@example
(peter) target remote | exec qemu-system-i386 -peter stdio ...
@end example
ETEXI
Step 3) 在main(), 行239
const char *peter_cmdline;
在main(), 行3788
if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
exit(1);
}
if (peter_start(peter_cmdline)>0){
fprintf(stderr, "start peter error\n");
exit(0);
}
qdev_machine_creation_done();
if (rom_load_all() != 0) {
fprintf(stderr, "rom loading failed\n");
exit(1);
}
Step 4) 这里是peterstub.h 和 peterstub.h, 请放它们到代码树的最顶层 (和文件gdbserver.c相同):
peterstub.c:
#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 "peterstub.h"
static int peter_chr_can_receive(void *opaque) {
printf("peter_chr_can_receive\n");
return 4096;
}
static void peter_chr_receive(void *opaque, const uint8_t *buf, int size) {
printf("gdb_chr_receive, size=%d, buf=%s\n", size, buf);
}
static void peter_chr_event(void *opaque, int event) {
printf("gdb_chr_event, event=%d\n", event);
}
int peter_start(const char *device) {
char gdbstub_device_name[128];
printf("peter_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("peter", device, NULL);
if (!chr)
return -1;
qemu_chr_add_handlers(chr, peter_chr_can_receive, peter_chr_receive,
peter_chr_event, NULL);
return 0;
}
peterstub.h:
#ifndef PETERSTUB_H_
#define PETERSTUB_H_
int peter_start(const char *device);
#endif /* PETERSTUB_H_ */
Step 5)
加你的.o到Makefile.target
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
CONFIG_NO_GET_MEMORY_MAPPING = $(if $(subst n,,$(CONFIG_HAVE_GET_MEMORY_MAPPING)),n,y)
CONFIG_NO_CORE_DUMP = $(if $(subst n,,$(CONFIG_HAVE_CORE_DUMP)),n,y)
obj-y += arch_init.o cpus.o monitor.o gdbstub.o gkd.o balloon.o ioport.o
obj-y += hw/
obj-$(CONFIG_KVM) += kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-y += memory.o savevm.o cputlb.o
obj-$(CONFIG_HAVE_GET_MEMORY_MAPPING) += memory_mapping.o
obj-$(CONFIG_HAVE_CORE_DUMP) += dump.o
obj-$(CONFIG_NO_GET_MEMORY_MAPPING) += memory_mapping-stub.o
obj-$(CONFIG_NO_CORE_DUMP) += dump-stub.o
LIBS+=-lz
Step 6) 最后一步, 执行qemu, 把"-peter 1234"加到参数去
QEMU成功执行之后, 可以在linux里用nv指令发送一些string去debug server以作测试