text: // Copyright 2013 Dolphin Emulator Project
  // Licensed under GPLv2+
  // Refer to the license.txt file included.
  
  // Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
  // Integrated into Mephisto/CTUv2 by Cody Brocious
  
  #include "Ctu.h"
  
  const char GDB_STUB_START = '$';
  const char GDB_STUB_END = '#';
  const char GDB_STUB_ACK = '+';
  const char GDB_STUB_NACK = '-';
  
  #ifndef SIGTRAP
  const uint32_t SIGTRAP = 5;
  #endif
  
  #ifndef SIGTERM
  const uint32_t SIGTERM = 15;
  #endif
  
  #ifndef MSG_WAITALL
  const uint32_t MSG_WAITALL = 8;
  #endif
  
  // For sample XML files see the GDB source /gdb/features
  // GDB also wants the l character at the start
  // This XML defines what the registers are for this specific ARM device
  static const char* target_xml =
  	R"(<?xml version="1.0"?>
  <!DOCTYPE target SYSTEM "gdb-target.dtd">
  <target version="1.0">
  	<feature name="org.gnu.gdb.aarch64.core">
  		<reg name="x0" bitsize="64"/>
  		<reg name="x1" bitsize="64"/>
  		<reg name="x2" bitsize="64"/>
  		<reg name="x3" bitsize="64"/>
  		<reg name="x4" bitsize="64"/>
  		<reg name="x5" bitsize="64"/>
  		<reg name="x6" bitsize="64"/>
  		<reg name="x7" bitsize="64"/>
  		<reg name="x8" bitsize="64"/>
  		<reg name="x9" bitsize="64"/>
  		<reg name="x10" bitsize="64"/>
  		<reg name="x11" bitsize="64"/>
  		<reg name="x12" bitsize="64"/>
  		<reg name="x13" bitsize="64"/>
  		<reg name="x14" bitsize="64"/>
  		<reg name="x15" bitsize="64"/>
  		<reg name="x16" bitsize="64"/>
  		<reg name="x17" bitsize="64"/>
  		<reg name="x18" bitsize="64"/>
  		<reg name="x19" bitsize="64"/>
  		<reg name="x20" bitsize="64"/>
  		<reg name="x21" bitsize="64"/>
  		<reg name="x22" bitsize="64"/>
  		<reg name="x23" bitsize="64"/>
  		<reg name="x24" bitsize="64"/>
  		<reg name="x25" bitsize="64"/>
  		<reg name="x26" bitsize="64"/>
  		<reg name="x27" bitsize="64"/>
  		<reg name="x28" bitsize="64"/>
  		<reg name="x29" bitsize="64"/>
  		<reg name="x30" bitsize="64"/>
  		<reg name="sp" bitsize="64" type="data_ptr"/>
  		<reg name="pc" bitsize="64" type="code_ptr"/>
  		<reg name="cpsr" bitsize="32"/>
  	</feature>
  </target>)";
  
  uint8_t hexCharToValue(uint8_t hex) {
  	if(hex >= '0' && hex <= '9')
  		return hex - '0';
  	else if(hex >= 'a' && hex <= 'f')
  		return hex - 'a' + 0xA;
  	else if(hex >= 'A' && hex <= 'F')
  		return hex - 'A' + 0xA;
  
  	LOG_ERROR(GdbStub, "Invalid nibble: %c (%02x)", hex, hex);
  }
  
  uint8_t nibbleToHex(uint8_t n) {
  	n &= 0xF;
  	if(n < 0xA)
  		return '0' + n;
  	else
  		return 'a' + n - 0xA;
  }
  
  uint64_t hexToInt(const uint8_t* src, size_t len) {
  	uint64_t output = 0;
  	while(len-- > 0) {
  		output = (output << 4) | hexCharToValue(src[0]);
  		src++;
  	}
  	return output;
  }
  
  void memToGdbHex(uint8_t* dest, const uint8_t* src, size_t len) {
  	while(len-- > 0) {
  		auto tmp = *src++;
  		*dest++ = nibbleToHex(tmp >> 4);
  		*dest++ = nibbleToHex(tmp);
  	}
  }
  
  void gdbHexToMem(uint8_t* dest, const uint8_t* src, size_t len) {
  	while(len-- > 0) {
  		*dest++ = (uint8_t) ((hexCharToValue(src[0]) << 4) | hexCharToValue(src[1]));
  		src += 2;
  	}
  }
  
  void intToGdbHex(uint8_t* dest, uint64_t v) {
  	for(auto i = 0; i < 16; i += 2) {
  		dest[i + 1] = nibbleToHex((uint8_t) (v >> (4 * i)));
  		dest[i] = nibbleToHex((uint8_t) (v >> (4 * (i + 1))));
  	}
  }
  
  uint64_t gdbHexToInt(const uint8_t* src) {
  	uint64_t output = 0;
  
  	for(int i = 0; i < 16; i += 2) {
  		output = (output << 4) | hexCharToValue(src[15 - i - 1]);
  		output = (output << 4) | hexCharToValue(src[15 - i]);
  	}
  
  	return output;
  }
  
  uint8_t calculateChecksum(const uint8_t* buffer, size_t length) {
  	return static_cast<uint8_t>(accumulate(buffer, buffer + length, 0, plus<uint8_t>()));
  }
  
  GdbStub::GdbStub(Ctu *_ctu) : ctu(_ctu) {
  	memoryBreak = false;
  	haltLoop = stepLoop = false;
  	enabled = false;
  	latestSignal = 0;
  }
  
  void GdbStub::enable(uint16_t port) {
  	LOG_INFO(GdbStub, "Starting GDB server on port %d...", port);
  
  	sockaddr_in saddr_server = {};
  	saddr_server.sin_family = AF_INET;
  	saddr_server.sin_port = htons(port);
  	saddr_server.sin_addr.s_addr = INADDR_ANY;
  
  	auto tmpsock = socket(PF_INET, SOCK_STREAM, 0);
  	if(tmpsock == -1)
  		LOG_ERROR(GdbStub, "Failed to create gdb socket");
  
  	auto reuse_enabled = 1;
  	if(setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0)
  		LOG_ERROR(GdbStub, "Failed to set gdb socket option");
  
  	auto server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
  	socklen_t server_addrlen = sizeof(saddr_server);
  	if(bind(tmpsock, server_addr, server_addrlen) < 0)
  		LOG_ERROR(GdbStub, "Failed to bind gdb socket");
  
  	if(listen(tmpsock, 1) < 0)
  		LOG_ERROR(GdbStub, "Failed to listen to gdb socket");
  
  	LOG_INFO(GdbStub, "Waiting for gdb to connect...");
  	sockaddr_in saddr_client;
  	sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
  	socklen_t client_addrlen = sizeof(saddr_client);
  	client = accept(tmpsock, client_addr, &client_addrlen);
  	if(client < 0)
  		LOG_ERROR(GdbStub, "Failed to accept gdb client");
  	else
  		LOG_INFO(GdbStub, "Client connected.");
  
  	enabled = true;
  	haltLoop = true;
  	remoteBreak = false;
  }
  
  uint8_t GdbStub::readByte() {
  	uint8_t c;
  	auto size = recv(client, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
  	if(size != 1)
  		LOG_ERROR(GdbStub, "recv failed : %ld", size);
  
  	return c;
  }
  
  guint GdbStub::reg(int x) {
  	auto thread = ctu->tm.current();
  	if(thread == nullptr)
  		thread = ctu->tm.last();
  	if(thread == nullptr)
  		return 0;
  	switch(x) {
  	case 31:
  		return thread->regs.SP;
  	case 32:
  		return thread->regs.PC;
  	default:
  		assert(x < 31);
  		return thread->regs.gprs[x];
  	}
  }
  
  void GdbStub::reg(int x, guint v) {
  	auto thread = ctu->tm.current();
  	if(thread == nullptr)
  		thread = ctu->tm.last();
  	if(thread == nullptr)
  		return;
  	switch(x) {
  	case 31:
  		thread->regs.SP = v;
  		break;
  	case 32:
  		thread->regs.PC = v;
  		break;
  	default:
  		assert(x < 31);
  		thread->regs.gprs[x] = v;
  	}
  }
  
  auto& GdbStub::getBreakpointList(BreakpointType type) {
  	switch(type) {
  	case BreakpointType::Execute:
  		return breakpointsExecute;
  	case BreakpointType::Write:
  		return breakpointsWrite;
  	case BreakpointType::Read:
  	case BreakpointType::Access:
  	case BreakpointType::None: // Should never happen
  		return breakpointsRead;
  	}
  }
  
  void GdbStub::removeBreakpoint(BreakpointType type, gptr addr) {
  	auto& p = getBreakpointList(type);
  
  	auto bp = p.find(addr);
  	if(bp != p.end()) {
  		LOG_DEBUG(GdbStub, "gdb: removed a breakpoint: " ADDRFMT " bytes at " ADDRFMT " of type %d",
  				  bp->second.len, bp->second.addr, type);
  		ctu->cpu.removeBreakpoint(bp->second.hook);
  		p.erase(addr);
  	}
  }
  
  auto GdbStub::getNextBreakpointFromAddress(gptr addr, BreakpointType type) {
  	auto& p = getBreakpointList(type);
  	auto next_breakpoint = p.lower_bound(addr);
  	BreakpointAddress breakpoint;
  
  	if(next_breakpoint != p.end()) {
  		breakpoint.address = next_breakpoint->first;
  		breakpoint.type = type;
  	} else {
  		breakpoint.address = 0;
  		breakpoint.type = BreakpointType::None;
  	}
  
  	return breakpoint;
  }
  
  bool GdbStub::checkBreakpoint(gptr addr, BreakpointType type) {
  	auto& p = getBreakpointList(type);
  
  	auto bp = p.find(addr);
  	if(bp != p.end()) {
  		guint len = bp->second.len;
  
  		if(bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
  			LOG_DEBUG(GdbStub,
  					  "Found breakpoint type %d @ " ADDRFMT ", range: " ADDRFMT " - " ADDRFMT " (%d bytes)", type,
  					  addr, bp->second.addr, bp->second.addr + len, (uint32_t) len);
  			return true;
  		}
  	}
  
  	return false;
  }
  
  void GdbStub::sendPacket(const char packet) {
  	if(send(client, &packet, 1, 0) != 1)
  		LOG_ERROR(GdbStub, "send failed");
  }
  
  void GdbStub::sendReply(const char* reply) {
  	LOG_DEBUG(GdbStub, "Reply: %s", reply);
  	memset(commandBuffer, 0, sizeof(commandBuffer));
  
  	commandLength = static_cast<uint32_t>(strlen(reply));
  	if(commandLength + 4 > sizeof(commandBuffer)) {
  		LOG_DEBUG(GdbStub, "commandBuffer overflow in sendReply");
  		return;
  	}
  
  	memcpy(commandBuffer + 1, reply, commandLength);
  
  	auto checksum = calculateChecksum(commandBuffer, commandLength + 1);
  	commandBuffer[0] = GDB_STUB_START;
  	commandBuffer[commandLength + 1] = GDB_STUB_END;
  	commandBuffer[commandLength + 2] = nibbleToHex(checksum >> 4);
  	commandBuffer[commandLength + 3] = nibbleToHex(checksum);
  
  	auto ptr = commandBuffer;
  	auto left = commandLength + 4;
  	while(left > 0) {
  		auto sent_size = send(client, reinterpret_cast<char*>(ptr), left, 0);
  		if(sent_size < 0)
  			LOG_ERROR(GdbStub, "gdb: send failed");
  
  		left -= sent_size;
  		ptr += sent_size;
  	}
  }
  
  void GdbStub::handleQuery() {
  	LOG_DEBUG(GdbStub, "gdb: query '%s'", commandBuffer + 1);
  
  	auto query = reinterpret_cast<const char*>(commandBuffer + 1);
  
  	if(strcmp(query, "TStatus") == 0)
  		sendReply("T0");
  	else if(strncmp(query, "Supported", strlen("Supported")) == 0)
  		sendReply("PacketSize=1600");
  	else if(strncmp(query, "Xfer:features:read:target.xml:",
  					   strlen("Xfer:features:read:target.xml:")) == 0)
  		sendReply(target_xml);
  	else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
  		auto list = ctu->tm.thread_list();
  		char tmp[17] = {0};
  		string val = "m";
  		for (auto it = list.begin(); it != list.end(); it++) {
  			if (!(*it)->started)
  				continue;
  			memset(tmp, 0, sizeof(tmp));
  			sprintf(tmp, "%x", (*it)->id);
  			val += (char*)tmp;
  			val += ",";
  		}
  		val.pop_back();
  		sendReply(val.c_str());
  	} else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0)
  		sendReply("l");
  	else
  		sendReply("");
  }
  
  void GdbStub::handleSetThread() {
  	// TODO: allow actually changing threads now :|
  	if(memcmp(commandBuffer, "Hg", 2) == 0 || memcmp(commandBuffer, "Hc", 2) == 0) {
  		// Get thread id
  		if (commandBuffer[2] != '-') {
  			int threadid = (int)hexToInt(commandBuffer + 2, strlen((char*)commandBuffer + 2));
  			ctu->tm.setCurrent(threadid);
  		}
  		sendReply("OK");
  	} else
  		sendReply("E01");
  }
  
  auto stringFromFormat(const char* format, ...) {
  	char *buf;
  	va_list args;
  	va_start(args, format);
  	if(vasprintf(&buf, format, args) < 0)
  		LOG_ERROR(GdbStub, "Unable to allocate memory for string");
  	va_end(args);
  
  	string ret = buf;
  	free(buf);
  	return ret;
  }
  
  void GdbStub::sendSignal(uint32_t signal) {
  	latestSignal = signal;
  
  	uint8_t sp[16];
  	uint8_t pc[16];
  	intToGdbHex(sp, reg(31));
  	intToGdbHex(pc, reg(32));
  
  	string buffer = stringFromFormat("T%02x%02x:%.16s;%02x:%.16s;", latestSignal, 32, pc, 31, sp);
  
  	auto curthread = ctu->tm.current();
  	if(curthread == nullptr)
  		curthread = ctu->tm.last();
  	if (curthread != nullptr)
  		buffer += stringFromFormat("thread:%x;", curthread->id);
  
  	LOG_DEBUG(GdbStub, "Response: %s", buffer.c_str());
  	sendReply(buffer.c_str());
  }
  
  void GdbStub::readCommand() {
  	commandLength = 0;
  	memset(commandBuffer, 0, sizeof(commandBuffer));
  
  	uint8_t c = readByte();
  	if(c == '+') {
  		// ignore ack
  		return;
  	} else if(c == 0x03) {
  		LOG_INFO(GdbStub, "gdb: found break command");
  		haltLoop = true;
  		remoteBreak = true;
  		return;
  	} else if(c != GDB_STUB_START) {
  		LOG_DEBUG(GdbStub, "gdb: read invalid byte %02x", c);
  		return;
  	}
  
  	while((c = readByte()) != GDB_STUB_END) {
  		if(commandLength >= sizeof(commandBuffer)) {
  			LOG_ERROR(GdbStub, "gdb: commandBuffer overflow");
  			sendPacket(GDB_STUB_NACK);
  			return;
  		}
  		commandBuffer[commandLength++] = c;
  	}
  
  	auto checksum_received = hexCharToValue(readByte()) << 4;
  	checksum_received |= hexCharToValue(readByte());
  
  	auto checksum_calculated = calculateChecksum(commandBuffer, commandLength);
  
  	if(checksum_received != checksum_calculated)
  		LOG_ERROR(GdbStub,
  				  "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)",
  				  checksum_calculated, checksum_received, commandBuffer, commandLength);
  
  	sendPacket(GDB_STUB_ACK);
  }
  
  bool GdbStub::isDataAvailable() {
  	fd_set fd_socket;
  
  	FD_ZERO(&fd_socket);
  	FD_SET(client, &fd_socket);
  
  	struct timeval t;
  	t.tv_sec = 0;
  	t.tv_usec = 0;
  
  	if(select(client + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
  		LOG_ERROR(GdbStub, "select failed");
  		return false;
  	}
  
  	return FD_ISSET(client, &fd_socket) != 0;
  }
  
  void GdbStub::readRegister() {
  	uint8_t reply[64];
  	memset(reply, 0, sizeof(reply));
  
  	uint32_t id = hexCharToValue(commandBuffer[1]);
  	if(commandBuffer[2] != '\0') {
  		id <<= 4;
  		id |= hexCharToValue(commandBuffer[2]);
  	}
  
  	if(id <= 32)
  		intToGdbHex(reply, reg(id));
  	else if(id == 33)
  		memset(reply, '0', 8);
  	else
  		return sendReply("E01");
  
  	sendReply(reinterpret_cast<char*>(reply));
  }
  
  void GdbStub::readRegisters() {
  	uint8_t buffer[GDB_BUFFER_SIZE - 4 + 1];
  	memset(buffer, 0, sizeof(buffer));
  
  	uint8_t* bufptr = buffer;
  	for(int i = 0; i <= 32; i++) {
  		intToGdbHex(bufptr + i * 16, reg(i));
  	}
  
  	bufptr += (33 * 16);
  
  	memset(bufptr, '0', 8);
  	bufptr[8] = '\0';
  
  	sendReply(reinterpret_cast<char*>(buffer));
  }
  
  void GdbStub::writeRegister() {
  	const uint8_t* buffer_ptr = commandBuffer + 3;
  
  	uint32_t id = hexCharToValue(commandBuffer[1]);
  	if(commandBuffer[2] != '=') {
  		++buffer_ptr;
  		id <<= 4;
  		id |= hexCharToValue(commandBuffer[2]);
  	}
  
  	auto val = gdbHexToInt(buffer_ptr);
  
  	if(id <= 32)
  		reg(id, val);
  	else if(id == 33) {
  	}
  	else
  		return sendReply("E01");
  
  	sendReply("OK");
  }
  
  void GdbStub::writeRegisters() {
  	const uint8_t* buffer_ptr = commandBuffer + 1;
  
  	if(commandBuffer[0] != 'G')
  		return sendReply("E01");
  	for(auto i = 0; i < 33; ++i)
  		if(i <= 32)
  			reg(i, gdbHexToInt(buffer_ptr + i * 16));
  
  	sendReply("OK");
  }
  
  void GdbStub::readMemory() {
  	uint8_t reply[GDB_BUFFER_SIZE - 4];
  
  	auto start_offset = commandBuffer + 1;
  	auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
  	auto addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
  
  	start_offset = addr_pos + 1;
  	auto len = hexToInt(start_offset, static_cast<uint32_t>((commandBuffer + commandLength) - start_offset));
  
  	LOG_DEBUG(GdbStub, "gdb: addr: " ADDRFMT " len: " ADDRFMT, addr, len);
  
  	if(len * 2 > sizeof(reply)) {
  		sendReply("E01");
  	}
  
  	auto data = new uint8_t[len];
  	if(ctu->cpu.readmem(addr, data, len)) {
  		memToGdbHex(reply, data, len);
  		reply[len * 2] = '\0';
  		sendReply(reinterpret_cast<char*>(reply));
  	} else
  		sendReply("E00");
  
  	delete[] data;
  }
  
  void GdbStub::writeMemory() {
  	auto start_offset = commandBuffer + 1;
  	auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
  	gptr addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
  
  	start_offset = addr_pos + 1;
  	auto len_pos = find(start_offset, commandBuffer + commandLength, ':');
  	auto len = hexToInt(start_offset, static_cast<uint32_t>(len_pos - start_offset));
  
  	auto dst = new uint8_t[len];
  	gdbHexToMem(dst, len_pos + 1, len);
  
  	if(ctu->cpu.writemem(addr, dst, len))
  		sendReply("OK");
  	else
  		sendReply("E00");
  
  	delete[] dst;
  }
  
  void GdbStub::_break(bool is_memoryBreak) {
  	if(!haltLoop) {
  		haltLoop = true;
  		sendSignal(SIGTRAP);
  	}
  
  	memoryBreak = is_memoryBreak;
  }
  
  void GdbStub::step() {
  	stepLoop = true;
  	haltLoop = true;
  }
  
  void GdbStub::_continue() {
  	memoryBreak = false;
  	stepLoop = false;
  	haltLoop = false;
  }
  
  bool GdbStub::commitBreakpoint(BreakpointType type, gptr addr, uint32_t len) {
  	auto& p = getBreakpointList(type);
  
  	Breakpoint breakpoint;
  	breakpoint.active = true;
  	breakpoint.addr = addr;
  	breakpoint.len = len;
  
  	if(type == BreakpointType::Execute)
  		breakpoint.hook = ctu->cpu.addCodeBreakpoint(addr);
  	else
  		breakpoint.hook = ctu->cpu.addMemoryBreakpoint(addr, len, type);
  
  	p.insert({addr, breakpoint});
  
  	LOG_DEBUG(GdbStub, "gdb: added %d breakpoint: " ADDRFMT " bytes at " ADDRFMT, type, breakpoint.len,
  			  breakpoint.addr);
  
  	return true;
  }
  
  void GdbStub::addBreakpoint() {
  	BreakpointType type;
  
  	uint8_t type_id = hexCharToValue(commandBuffer[1]);
  	switch (type_id) {
  	case 0:
  	case 1:
  		type = BreakpointType::Execute;
  		break;
  	case 2:
  		type = BreakpointType::Write;
  		break;
  	case 3:
  		type = BreakpointType::Read;
  		break;
  	case 4:
  		type = BreakpointType::Access;
  		break;
  	default:
  		return sendReply("E01");
  	}
  
  	auto start_offset = commandBuffer + 3;
  	auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
  	gptr addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
  
  	start_offset = addr_pos + 1;
  	auto len = (uint32_t) hexToInt(start_offset, static_cast<uint32_t>((commandBuffer + commandLength) - start_offset));
  
  	if(type == BreakpointType::Access) {
  		type = BreakpointType::Read;
  
  		if(!commitBreakpoint(type, addr, len)) {
  			return sendReply("E02");
  		}
  
  		type = BreakpointType::Write;
  	}
  
  	if(!commitBreakpoint(type, addr, len)) {
  		return sendReply("E02");
  	}
  
  	sendReply("OK");
  }
  
  void GdbStub::removeBreakpoint() {
  	BreakpointType type;
  
  	uint8_t type_id = hexCharToValue(commandBuffer[1]);
  	switch (type_id) {
  	case 0:
  	case 1:
  		type = BreakpointType::Execute;
  		break;
  	case 2:
  		type = BreakpointType::Write;
  		break;
  	case 3:
  		type = BreakpointType::Read;
  		break;
  	case 4:
  		type = BreakpointType::Access;
  		break;
  	default:
  		return sendReply("E01");
  	}
  
  	auto start_offset = commandBuffer + 3;
  	auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
  	gptr addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
  
  	if(type == BreakpointType::Access) {
  		type = BreakpointType::Read;
  		removeBreakpoint(type, addr);
  
  		type = BreakpointType::Write;
  	}
  
  	removeBreakpoint(type, addr);
  	sendReply("OK");
  }
  
  void GdbStub::isThreadAlive() {
  	int threadid = (int)hexToInt(commandBuffer + 1, strlen((char*)commandBuffer + 1));
  	auto threads = ctu->tm.thread_list();
  	for (auto it = threads.begin(); it != threads.end(); it++) {
  		if ((*it)->id == threadid) {
  			sendReply("OK");
  			return;
  		}
  	}
  	sendReply("E01");
  }
  
  void GdbStub::handlePacket() {
  	if(!isDataAvailable())
  		return;
  
  	readCommand();
  	if(commandLength == 0)
  		return;
  
  	LOG_DEBUG(GdbStub, "Packet: %s", commandBuffer);
  
  	switch(commandBuffer[0]) {
  	case 'q':
  		handleQuery();
  		break;
  	case 'H':
  		handleSetThread();
  		break;
  	case '?':
  		sendSignal(latestSignal);
  		break;
  	case 'k':
  		LOG_ERROR(GdbStub, "killed by gdb");
  	case 'g':
  		readRegisters();
  		break;
  	case 'G':
  		writeRegisters();
  		break;
  	case 'p':
  		readRegister();
  		break;
  	case 'P':
  		writeRegister();
  		break;
  	case 'm':
  		readMemory();
  		break;
  	case 'M':
  		writeMemory();
  		break;
  	case 's':
  		step();
  		return;
  	case 'C':
  	case 'c':
  		_continue();
  		return;
  	case 'z':
  		removeBreakpoint();
  		break;
  	case 'T':
  		isThreadAlive();
  		break;
  	case 'Z':
  		addBreakpoint();
  		break;
  	default:
  		sendReply("");
  		break;
  	}
  }
  