Debugger stuff. Lots of wasted work :/
This commit is contained in:
319
src/xenia/debug/server/gdb/gdb_command_processor.cc
Normal file
319
src/xenia/debug/server/gdb/gdb_command_processor.cc
Normal file
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/debug/server/gdb/gdb_command_processor.h"
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/debug/server/gdb/gdb_server.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace server {
|
||||
namespace gdb {
|
||||
|
||||
constexpr size_t kReceiveBufferSize = 1 * 1024 * 1024;
|
||||
constexpr size_t kTransmitBufferSize = 1 * 1024 * 1024;
|
||||
|
||||
GdbCommandProcessor::GdbCommandProcessor(GdbServer* server, Debugger* debugger)
|
||||
: server_(server), debugger_(debugger) {
|
||||
receive_buffer_.resize(kReceiveBufferSize);
|
||||
transmit_buffer_.resize(kTransmitBufferSize);
|
||||
}
|
||||
|
||||
bool GdbCommandProcessor::ProcessBuffer(const uint8_t* buffer,
|
||||
size_t buffer_length) {
|
||||
// Grow and append the bytes to the receive buffer.
|
||||
while (receive_offset_ + buffer_length > receive_buffer_.capacity()) {
|
||||
receive_buffer_.resize(receive_buffer_.size() * 2);
|
||||
}
|
||||
std::memcpy(receive_buffer_.data() + receive_offset_, buffer, buffer_length);
|
||||
receive_offset_ += buffer_length;
|
||||
|
||||
// While there are bytes pending in the buffer we scan through looking for end
|
||||
// markers. When we find one, we emit the packet and move on.
|
||||
size_t process_offset = 0;
|
||||
while (process_offset < receive_offset_) {
|
||||
// Look for an end marker.
|
||||
size_t end_offset = -1;
|
||||
size_t buffer_end_offset = -1;
|
||||
for (size_t i = process_offset; i < receive_offset_; ++i) {
|
||||
if (receive_buffer_[i] == '#') {
|
||||
end_offset = i - 1;
|
||||
buffer_end_offset = i + 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end_offset == -1) {
|
||||
// No end marker found - break out for now.
|
||||
break;
|
||||
}
|
||||
|
||||
// Emit packet.
|
||||
if (!ProcessPacket((const char*)(receive_buffer_.data() + process_offset),
|
||||
end_offset - process_offset)) {
|
||||
// Failed to process line.
|
||||
std::string line(receive_buffer_.data() + process_offset,
|
||||
receive_buffer_.data() + end_offset);
|
||||
XELOGE("Failed to process incoming GDB line: %s", line.c_str());
|
||||
return false;
|
||||
}
|
||||
process_offset = buffer_end_offset + 1;
|
||||
}
|
||||
|
||||
// If we have leftover unprocessed bytes move them to the front of the buffer.
|
||||
if (process_offset < receive_offset_) {
|
||||
size_t remaining_bytes = receive_offset_ - process_offset;
|
||||
std::memmove(receive_buffer_.data(),
|
||||
receive_buffer_.data() + process_offset, remaining_bytes);
|
||||
receive_offset_ = remaining_bytes;
|
||||
} else {
|
||||
receive_offset_ = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GdbCommandProcessor::ProcessPacket(const char* buffer,
|
||||
size_t buffer_length) {
|
||||
// There may be any number of leading +'s or -'s.
|
||||
size_t offset = 0;
|
||||
for (; offset < buffer_length; ++offset) {
|
||||
if (buffer[offset] == '$') {
|
||||
// Command start.
|
||||
++offset;
|
||||
break;
|
||||
} else if (buffer[offset] == '+') {
|
||||
// Ack.
|
||||
} else if (buffer[offset] == '-') {
|
||||
// No good - means transmission error.
|
||||
XELOGE("GDB client remorted transmission error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string line((const char*)(buffer + offset), buffer_length - 1);
|
||||
XELOGI("GDB -> %s", line.c_str());
|
||||
|
||||
// Immediately send ACK.
|
||||
if (!no_ack_mode_) {
|
||||
server_->client()->Send("+", 1);
|
||||
}
|
||||
|
||||
const char* buffer_ptr = buffer + offset;
|
||||
bool handled = false;
|
||||
switch (buffer[offset]) {
|
||||
case '!':
|
||||
// Enable extended mode.
|
||||
SendResponse("OK");
|
||||
handled = true;
|
||||
break;
|
||||
case 'v':
|
||||
// Verbose packets.
|
||||
if (std::strstr(buffer_ptr, "vRun") == buffer_ptr) {
|
||||
SendResponse("S05");
|
||||
handled = true;
|
||||
} else if (std::strstr(buffer_ptr, "vCont") == buffer_ptr) {
|
||||
SendResponse("S05");
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
// Query packets.
|
||||
switch (buffer_ptr[1]) {
|
||||
case 'C':
|
||||
// Get current thread ID.
|
||||
SendResponse("QC01");
|
||||
handled = true;
|
||||
break;
|
||||
case 'R':
|
||||
// Command.
|
||||
SendResponse("OK");
|
||||
handled = true;
|
||||
break;
|
||||
default:
|
||||
if (std::strstr(buffer_ptr, "qSupported") == buffer_ptr) {
|
||||
// Get/set feature support.
|
||||
handled = Process_qSupported(line);
|
||||
} else if (std::strstr(buffer_ptr, "qAttached") == buffer_ptr) {
|
||||
// Check attach mode; 0 = new process, 1 = existing process.
|
||||
SendResponse("0");
|
||||
handled = true;
|
||||
} else if (std::strstr(buffer_ptr, "qfThreadInfo") == buffer_ptr) {
|
||||
// Start of thread list request.
|
||||
SendResponse("m01");
|
||||
handled = true;
|
||||
} else if (std::strstr(buffer_ptr, "qsThreadInfo") == buffer_ptr) {
|
||||
// Continuation of thread list request.
|
||||
SendResponse("l"); // l = last.
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Q':
|
||||
// Set packets.
|
||||
switch (buffer_ptr[1]) {
|
||||
default:
|
||||
if (std::strstr(buffer_ptr, "QStartNoAckMode") == buffer_ptr) {
|
||||
no_ack_mode_ = true;
|
||||
SendResponse("OK");
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
// Set thread for subsequent operations.
|
||||
SendResponse("OK");
|
||||
handled = true;
|
||||
break;
|
||||
case 'g':
|
||||
// Read all registers.
|
||||
handled = ProcessReadRegisters(line);
|
||||
break;
|
||||
case 'G':
|
||||
// Write all registers.
|
||||
handled = true;
|
||||
break;
|
||||
case 'p':
|
||||
// Read register.
|
||||
handled = true;
|
||||
break;
|
||||
case 'P':
|
||||
// Write register.
|
||||
handled = true;
|
||||
break;
|
||||
case 'm':
|
||||
// Read memory.
|
||||
handled = true;
|
||||
break;
|
||||
case 'M':
|
||||
// Write memory.
|
||||
handled = true;
|
||||
break;
|
||||
case 'Z':
|
||||
// Insert breakpoint.
|
||||
handled = true;
|
||||
break;
|
||||
case 'z':
|
||||
// Remove breakpoint.
|
||||
handled = true;
|
||||
break;
|
||||
case '?':
|
||||
// Query halt reason.
|
||||
SendResponse("S05");
|
||||
handled = true;
|
||||
break;
|
||||
case 'c':
|
||||
// Continue (vCont should be used instead).
|
||||
// NOTE: reply is sent on halt, not right now.
|
||||
SendResponse("S05");
|
||||
handled = true;
|
||||
break;
|
||||
case 's':
|
||||
// Single step.
|
||||
// NOTE: reply is sent on halt, not right now.
|
||||
SendResponse("S05");
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
if (!handled) {
|
||||
XELOGE("Unknown GDB packet: %s", buffer_ptr);
|
||||
SendResponse("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GdbCommandProcessor::SendResponse(const char* value, size_t length) {
|
||||
XELOGI("GDB <- %s", value);
|
||||
uint8_t checksum = 0;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
uint8_t c = uint8_t(value[i]);
|
||||
if (!c) {
|
||||
break;
|
||||
}
|
||||
checksum += c;
|
||||
}
|
||||
char crc[4];
|
||||
sprintf(crc, "#%.2X", checksum);
|
||||
std::pair<const void*, size_t> buffers[] = {
|
||||
{"$", 1}, {value, length}, {crc, 3},
|
||||
};
|
||||
server_->client()->Send(buffers, xe::countof(buffers));
|
||||
}
|
||||
|
||||
bool GdbCommandProcessor::Process_qSupported(const std::string& line) {
|
||||
StringBuffer response;
|
||||
|
||||
// Read in the features the client supports.
|
||||
// qSupported[:gdbfeature[;gdbfeature]...]
|
||||
size_t feature_offset = line.find(':');
|
||||
while (feature_offset != std::string::npos) {
|
||||
size_t next_offset = line.find(';', feature_offset + 1);
|
||||
std::string feature =
|
||||
line.substr(feature_offset + 1, next_offset - feature_offset - 1);
|
||||
feature_offset = next_offset;
|
||||
if (feature.find("multiprocess") == 0) {
|
||||
bool is_supported = feature[12] == '+';
|
||||
} else if (feature.find("xmlRegisters") == 0) {
|
||||
// xmlRegisters=A,B,C
|
||||
} else if (feature.find("qRelocInsn") == 0) {
|
||||
bool is_supported = feature[10] == '+';
|
||||
} else if (feature.find("swbreak") == 0) {
|
||||
bool is_supported = feature[7] == '+';
|
||||
} else if (feature.find("hwbreak") == 0) {
|
||||
bool is_supported = feature[7] == '+';
|
||||
} else {
|
||||
XELOGW("Unknown GDB client support feature: %s", feature.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
response.Append("PacketSize=4000;");
|
||||
response.Append("QStartNoAckMode+;");
|
||||
response.Append("qRelocInsn-;");
|
||||
response.Append("multiprocess-;");
|
||||
response.Append("ConditionalBreakpoints-;");
|
||||
response.Append("ConditionalTracepoints-;");
|
||||
response.Append("ReverseContinue-;");
|
||||
response.Append("ReverseStep-;");
|
||||
response.Append("swbreak+;");
|
||||
response.Append("hwbreak+;");
|
||||
|
||||
SendResponse(response.GetString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GdbCommandProcessor::ProcessReadRegisters(const std::string& line) {
|
||||
StringBuffer response;
|
||||
|
||||
for (int32_t n = 0; n < 32; n++) {
|
||||
// gpr
|
||||
response.AppendFormat("%08X", n);
|
||||
}
|
||||
for (int64_t n = 0; n < 32; n++) {
|
||||
// fpr
|
||||
response.AppendFormat("%016llX", n);
|
||||
}
|
||||
response.AppendFormat("%08X", 0x8202FB40); // pc
|
||||
response.AppendFormat("%08X", 65); // msr
|
||||
response.AppendFormat("%08X", 66); // cr
|
||||
response.AppendFormat("%08X", 67); // lr
|
||||
response.AppendFormat("%08X", 68); // ctr
|
||||
response.AppendFormat("%08X", 69); // xer
|
||||
response.AppendFormat("%08X", 70); // fpscr
|
||||
|
||||
SendResponse(response.GetString());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gdb
|
||||
} // namespace server
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
Reference in New Issue
Block a user