Wasm debugging with LLDB: decode and execute GDB-remote commands

This changelist adds to the GDB stub (in class wasm::gdb_server::Target) the
logic to decode and execute GDB-remote commands and to format response packets
to be sent back to the debugger.
Here most of the commands still act as a NOOP; the actual implementation
requires interactions with the Wasm engine and will be implemented in the next
CL of this series.

Build with: v8_enable_wasm_gdb_remote_debugging = true
Run with: --wasm-gdb-remote

Bug: chromium:1010467
Change-Id: Icfa63be9e1eaa657c05876d0d4e86927e0885b90
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1938466
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67200}
This commit is contained in:
Paolo Severini 2020-04-17 01:52:07 -07:00 committed by Commit Bot
parent fcf2ce3501
commit b47893fc14
12 changed files with 704 additions and 32 deletions

View File

@ -54,6 +54,39 @@ bool NibbleToUInt8(char ch, uint8_t* byte) {
return false;
}
std::vector<std::string> StringSplit(const string& instr, const char* delim) {
std::vector<std::string> result;
const char* in = instr.data();
if (nullptr == in) return result;
// Check if we have nothing to do
if (nullptr == delim) {
result.push_back(string(in));
return result;
}
while (*in) {
// Toss all preceeding delimiters
while (*in && strchr(delim, *in)) in++;
// If we still have something to process
if (*in) {
const char* start = in;
int len = 0;
// Keep moving forward for all valid chars
while (*in && (strchr(delim, *in) == nullptr)) {
len++;
in++;
}
// Build this token and add it to the array.
result.push_back(string{start, len});
}
}
return result;
}
std::string Mem2Hex(const uint8_t* mem, size_t count) {
std::vector<char> result(count * 2 + 1);
for (size_t i = 0; i < count; i++) UInt8ToHex(*mem++, &result[i * 2]);

View File

@ -36,6 +36,35 @@ bool NibbleToUInt8(char ch, uint8_t* byte);
std::string Mem2Hex(const uint8_t* mem, size_t count);
std::string Mem2Hex(const std::string& str);
std::vector<std::string> V8_EXPORT_PRIVATE StringSplit(const std::string& instr,
const char* delim);
// For LLDB debugging, an address in a Wasm module code space is represented
// with 64 bits, where the first 32 bits identify the module id:
//
// 63 32 0
// +---------------+---------------+
// | module_id | offset |
// +---------------+---------------+
class wasm_addr_t {
public:
wasm_addr_t(uint32_t module_id, uint32_t offset)
: module_id_(module_id), offset_(offset) {}
explicit wasm_addr_t(uint64_t address)
: module_id_(address >> 32), offset_(address & 0xffffffff) {}
inline uint32_t ModuleId() const { return module_id_; }
inline uint32_t Offset() const { return offset_; }
inline operator uint64_t() const {
return static_cast<uint64_t>(module_id_) << 32 | offset_;
}
private:
uint32_t module_id_;
uint32_t offset_;
};
} // namespace gdb_server
} // namespace wasm
} // namespace internal

View File

@ -33,6 +33,53 @@ GdbServer::~GdbServer() {
}
}
// All the following methods require interaction with the actual V8 Wasm engine.
// They will be implemented later.
std::vector<GdbServer::WasmModuleInfo> GdbServer::GetLoadedModules() const {
// TODO(paolosev)
return {};
}
bool GdbServer::GetWasmGlobal(uint32_t wasm_module_id, uint32_t index,
uint64_t* value) {
// TODO(paolosev)
return false;
}
bool GdbServer::GetWasmLocal(uint32_t wasm_module_id, uint32_t frame_index,
uint32_t index, uint64_t* value) {
// TODO(paolosev)
return false;
}
uint32_t GdbServer::GetWasmMemory(uint32_t wasm_module_id, uint32_t offset,
uint8_t* buffer, uint32_t size) {
// TODO(paolosev)
return 0;
}
uint32_t GdbServer::GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer,
uint32_t size) {
// TODO(paolosev)
return 0;
}
bool GdbServer::AddBreakpoint(uint32_t module_id, uint32_t offset) {
// TODO(paolosev)
return false;
}
bool GdbServer::RemoveBreakpoint(uint32_t module_id, uint32_t offset) {
// TODO(paolosev)
return false;
}
std::vector<wasm_addr_t> GdbServer::GetWasmCallStack() const {
// TODO(paolosev)
return {};
}
} // namespace gdb_server
} // namespace wasm
} // namespace internal

View File

@ -31,6 +31,49 @@ class GdbServer {
// called once, when the Wasm engine shuts down.
~GdbServer();
// Queries the set of the Wasm modules currently loaded. Each module is
// identified by a unique integer module id.
struct WasmModuleInfo {
uint32_t module_id;
std::string module_name;
};
std::vector<WasmModuleInfo> GetLoadedModules() const;
// Queries the value of the {index} global value in the Wasm module identified
// by {wasm_module_id}.
bool GetWasmGlobal(uint32_t wasm_module_id, uint32_t index, uint64_t* value);
// Queries the value of the {index} local value in the {frame_index}th stack
// frame in the Wasm module identified by {wasm_module_id}.
bool GetWasmLocal(uint32_t wasm_module_id, uint32_t frame_index,
uint32_t index, uint64_t* value);
// Reads {size} bytes, starting from {offset}, from the Memory instance
// associated to the Wasm module identified by {wasm_module_id}.
// Returns the number of bytes copied to {buffer}, or 0 is case of error.
// Note: only one Memory for Module is currently supported.
uint32_t GetWasmMemory(uint32_t wasm_module_id, uint32_t offset,
uint8_t* buffer, uint32_t size);
// Reads {size} bytes, starting from {address}, from the Code space of the
// Wasm module identified by {wasm_module_id}.
// Returns the number of bytes copied to {buffer}, or 0 is case of error.
uint32_t GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer,
uint32_t size);
// Inserts a breakpoint at the offset {offset} of the Wasm module identified
// by {wasm_module_id}.
// Returns true if the breakpoint was successfully added.
bool AddBreakpoint(uint32_t module_id, uint32_t offset);
// Removes a breakpoint at the offset {offset} of the Wasm module identified
// by {wasm_module_id}.
// Returns true if the breakpoint was successfully removed.
bool RemoveBreakpoint(uint32_t module_id, uint32_t offset);
// Returns the current call stack as a vector of program counters.
std::vector<wasm_addr_t> GetWasmCallStack() const;
private:
GdbServer() {}

View File

@ -43,6 +43,15 @@ void Packet::AddBlock(const void* ptr, uint32_t len) {
}
}
void Packet::AddString(const char* str) {
DCHECK(str);
while (*str) {
AddRawChar(*str);
str++;
}
}
void Packet::AddHexString(const char* str) {
DCHECK(str);

View File

@ -42,9 +42,12 @@ class V8_EXPORT_PRIVATE Packet {
// separator (non hex digit).
void AddNumberSep(uint64_t val, char sep);
// Add a raw string.
void AddString(const char* str);
// Add a string stored as a stream of ASCII hex digit pairs. It is safe
// to use any non-null character in this stream. If this does not terminate
// the packet, there should be a seperator (non hex digit) immediately
// the packet, there should be a separator (non hex digit) immediately
// following.
void AddHexString(const char* str);

View File

@ -4,6 +4,7 @@
#include "src/debug/wasm/gdb-server/target.h"
#include <inttypes.h>
#include "src/base/platform/time.h"
#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
#include "src/debug/wasm/gdb-server/gdb-server.h"
@ -16,8 +17,38 @@ namespace internal {
namespace wasm {
namespace gdb_server {
static const int kThreadId = 1;
Target::Target(GdbServer* gdb_server)
: status_(Status::Running), session_(nullptr) {}
: gdb_server_(gdb_server),
status_(Status::Running),
cur_signal_(0),
session_(nullptr) {}
void Target::InitQueryPropertyMap() {
// Request LLDB to send packets up to 4000 bytes for bulk transfers.
query_properties_["Supported"] =
"PacketSize=4000;vContSupported-;qXfer:libraries:read+;";
query_properties_["Attached"] = "1";
// There is only one register, named 'pc', in this architecture
query_properties_["RegisterInfo0"] =
"name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:"
"General Purpose Registers;gcc:16;dwarf:16;generic:pc;";
query_properties_["RegisterInfo1"] = "E45";
// ProcessInfo for wasm32
query_properties_["ProcessInfo"] =
"pid:1;ppid:1;uid:1;gid:1;euid:1;egid:1;name:6c6c6462;triple:" +
Mem2Hex("wasm32-unknown-unknown-wasm") + ";ptrsize:4;";
query_properties_["Symbol"] = "OK";
// Current thread info
char buff[16];
snprintf(buff, sizeof(buff), "QC%x", kThreadId);
query_properties_["C"] = buff;
}
void Target::Terminate() {
// Executed in the Isolate thread.
@ -26,7 +57,6 @@ void Target::Terminate() {
void Target::Run(Session* session) {
// Executed in the GdbServer thread.
session_ = session;
do {
WaitForDebugEvent();
@ -56,31 +86,458 @@ void Target::ProcessCommands() {
// Now we are ready to process commands.
// Loop through packets until we process a continue packet or a detach.
Packet recv, reply;
do {
if (!session_->GetPacket(&recv)) continue;
reply.Clear();
if (ProcessPacket(&recv, &reply)) {
// If this is a continue type command, break out of this loop.
break;
while (session_->IsConnected()) {
if (!session_->GetPacket(&recv)) {
continue;
}
// Otherwise send the response.
session_->SendPacket(&reply);
} while (session_->IsConnected());
reply.Clear();
ProcessPacketResult result = ProcessPacket(&recv, &reply);
switch (result) {
case ProcessPacketResult::Paused:
session_->SendPacket(&reply);
break;
case ProcessPacketResult::Continue:
// If this is a continue type command, break out of this loop.
return;
case ProcessPacketResult::Detach:
session_->SendPacket(&reply);
session_->Disconnect();
cur_signal_ = 0; // // Reset the signal value
return;
case ProcessPacketResult::Kill:
session_->SendPacket(&reply);
exit(-9);
default:
UNREACHABLE();
}
}
}
bool Target::ProcessPacket(const Packet* pktIn, Packet* pktOut) {
// Pull out the sequence.
Target::ProcessPacketResult Target::ProcessPacket(Packet* pkt_in,
Packet* pkt_out) {
ErrorCode err = ErrorCode::None;
// Clear the outbound message.
pkt_out->Clear();
// Set the sequence number, if present.
int32_t seq = -1;
if (pktIn->GetSequence(&seq)) {
pktOut->SetSequence(seq);
if (pkt_in->GetSequence(&seq)) {
pkt_out->SetSequence(seq);
}
// Ignore all commands and returns an error.
pktOut->SetError(Packet::ErrDef::Failed);
// A GDB-remote packet begins with an upper- or lower-case letter, which
// generally represents a single command.
// The letters 'q' and 'Q' introduce a "General query packets" and are used
// to extend the protocol with custom commands.
// The format of GDB-remote commands is documented here:
// https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview.
char cmd;
pkt_in->GetRawChar(&cmd);
return false;
switch (cmd) {
// Queries the reason the target halted.
// IN : $?
// OUT: A Stop-reply packet
case '?':
SetStopReply(pkt_out);
break;
// Resumes execution
// IN : $c
// OUT: A Stop-reply packet is sent later, when the execution halts.
case 'c':
// TODO(paolosev) - Not implemented yet
err = ErrorCode::Failed;
break;
// Detaches the debugger from this target
// IN : $D
// OUT: $OK
case 'D':
TRACE_GDB_REMOTE("Requested Detach.\n");
pkt_out->AddString("OK");
return ProcessPacketResult::Detach;
// Read general registers (We only support register 'pc' that contains
// the current instruction pointer).
// IN : $g
// OUT: $xx...xx
case 'g': {
uint64_t pc = GetCurrentPc();
pkt_out->AddBlock(&pc, sizeof(pc));
break;
}
// Write general registers - NOT SUPPORTED
// IN : $Gxx..xx
// OUT: $ (empty string)
case 'G': {
break;
}
// Set thread for subsequent operations. For Wasm targets, we currently
// assume that there is only one thread with id = kThreadId (= 1).
// IN : $H(c/g)(-1,0,xxxx)
// OUT: $OK
case 'H': {
// Type of the operation (m, M, g, G, ...)
char operation;
if (!pkt_in->GetRawChar(&operation)) {
err = ErrorCode::BadFormat;
break;
}
uint64_t thread_id;
if (!pkt_in->GetNumberSep(&thread_id, 0)) {
err = ErrorCode::BadFormat;
break;
}
// Ignore, only one thread supported for now.
pkt_out->AddString("OK");
break;
}
// Kills the debuggee.
// IN : $k
// OUT: $OK
case 'k':
TRACE_GDB_REMOTE("Requested Kill.\n");
pkt_out->AddString("OK");
return ProcessPacketResult::Kill;
// Reads {llll} addressable memory units starting at address {aaaa}.
// IN : $maaaa,llll
// OUT: $xx..xx
case 'm': {
uint64_t address;
if (!pkt_in->GetNumberSep(&address, 0)) {
err = ErrorCode::BadFormat;
break;
}
wasm_addr_t wasm_addr(address);
uint64_t len;
if (!pkt_in->GetNumberSep(&len, 0)) {
err = ErrorCode::BadFormat;
break;
}
if (len > Transport::kBufSize / 2) {
err = ErrorCode::BadArgs;
break;
}
uint32_t length = static_cast<uint32_t>(len);
uint8_t buff[Transport::kBufSize];
if (wasm_addr.ModuleId() > 0) {
uint32_t read =
gdb_server_->GetWasmModuleBytes(wasm_addr, buff, length);
if (read > 0) {
pkt_out->AddBlock(buff, read);
} else {
err = ErrorCode::Failed;
}
} else {
err = ErrorCode::BadArgs;
}
break;
}
// Writes {llll} addressable memory units starting at address {aaaa}.
// IN : $Maaaa,llll:xx..xx
// OUT: $OK
case 'M': {
// Writing to memory not supported for Wasm.
err = ErrorCode::Failed;
break;
}
// pN: Reads the value of register N.
// IN : $pxx
// OUT: $xx..xx
case 'p': {
uint64_t pc = GetCurrentPc();
pkt_out->AddBlock(&pc, sizeof(pc));
} break;
case 'q': {
err = ProcessQueryPacket(pkt_in, pkt_out);
break;
}
// Single step
// IN : $s
// OUT: A Stop-reply packet is sent later, when the execution halts.
case 's': {
// TODO(paolosev) - Not implemented yet
err = ErrorCode::Failed;
break;
}
// Find out if the thread 'id' is alive.
// IN : $T
// OUT: $OK if alive, $Enn if thread is dead.
case 'T': {
uint64_t id;
if (!pkt_in->GetNumberSep(&id, 0)) {
err = ErrorCode::BadFormat;
break;
}
if (id != kThreadId) {
err = ErrorCode::BadArgs;
break;
}
pkt_out->AddString("OK");
break;
}
// Z: Adds a breakpoint
// IN : $Ztype,addr,kind
// OUT: $OK (success) or $Enn (error)
case 'Z': {
// Only software breakpoints are supported.
uint64_t breakpoint_type; // 0: sw breakpoint,
// 1: hw breakpoint,
// 2: watchpoint
uint64_t breakpoint_address;
uint64_t breakpoint_kind; // Ignored for Wasm.
if (!pkt_in->GetNumberSep(&breakpoint_type, 0) || breakpoint_type != 0 ||
!pkt_in->GetNumberSep(&breakpoint_address, 0) ||
!pkt_in->GetNumberSep(&breakpoint_kind, 0)) {
err = ErrorCode::BadFormat;
break;
}
wasm_addr_t wasm_breakpoint_addr(breakpoint_address);
if (!gdb_server_->AddBreakpoint(wasm_breakpoint_addr.ModuleId(),
wasm_breakpoint_addr.Offset())) {
err = ErrorCode::Failed;
break;
}
pkt_out->AddString("OK");
break;
}
// z: Removes a breakpoint
// IN : $ztype,addr,kind
// OUT: $OK (success) or $Enn (error)
case 'z': {
uint64_t breakpoint_type;
uint64_t breakpoint_address;
uint64_t breakpoint_kind;
if (!pkt_in->GetNumberSep(&breakpoint_type, 0) || breakpoint_type != 0 ||
!pkt_in->GetNumberSep(&breakpoint_address, 0) ||
!pkt_in->GetNumberSep(&breakpoint_kind, 0)) {
err = ErrorCode::BadFormat;
break;
}
wasm_addr_t wasm_breakpoint_addr(breakpoint_address);
if (!gdb_server_->RemoveBreakpoint(wasm_breakpoint_addr.ModuleId(),
wasm_breakpoint_addr.Offset())) {
err = ErrorCode::Failed;
break;
}
pkt_out->AddString("OK");
break;
}
// If the command is not recognized, ignore it by sending an empty reply.
default: {
std::string str;
pkt_in->GetString(&str);
TRACE_GDB_REMOTE("Unknown command: %s\n", pkt_in->GetPayload());
}
}
// If there is an error, return the error code instead of a payload
if (err != ErrorCode::None) {
pkt_out->Clear();
pkt_out->AddRawChar('E');
pkt_out->AddWord8(static_cast<uint8_t>(err));
}
return ProcessPacketResult::Paused;
}
Target::ErrorCode Target::ProcessQueryPacket(const Packet* pkt_in,
Packet* pkt_out) {
const char* str = &pkt_in->GetPayload()[1];
// Get first thread query
// IN : $qfThreadInfo
// OUT: $m<tid>
//
// Get next thread query
// IN : $qsThreadInfo
// OUT: $m<tid> or l to denote end of list.
if (!strcmp(str, "fThreadInfo") || !strcmp(str, "sThreadInfo")) {
if (str[0] == 'f') {
pkt_out->AddString("m");
pkt_out->AddNumberSep(kThreadId, 0);
} else {
pkt_out->AddString("l");
}
return ErrorCode::None;
}
// Get a list of loaded libraries
// IN : $qXfer:libraries:read
// OUT: an XML document which lists loaded libraries, with this format:
// <library-list>
// <library name="foo.wasm">
// <section address="0x100000000"/>
// </library>
// <library name="bar.wasm">
// <section address="0x200000000"/>
// </library>
// </library-list>
// Note that LLDB must be compiled with libxml2 support to handle this packet.
std::string tmp = "Xfer:libraries:read";
if (!strncmp(str, tmp.data(), tmp.length())) {
std::vector<GdbServer::WasmModuleInfo> modules =
gdb_server_->GetLoadedModules();
std::string result("l<library-list>");
for (const auto& module : modules) {
wasm_addr_t address(module.module_id, 0);
char address_string[32];
snprintf(address_string, sizeof(address_string), "%" PRIu64,
static_cast<uint64_t>(address));
result += "<library name=\"";
result += module.module_name;
result += "\"><section address=\"";
result += address_string;
result += "\"/></library>";
}
result += "</library-list>";
pkt_out->AddString(result.c_str());
return ErrorCode::None;
}
// Get the current call stack.
// IN : $qWasmCallStack
// OUT: $xx..xxyy..yyzz..zz (A sequence of uint64_t values represented as
// consecutive 8-bytes blocks).
std::vector<std::string> toks = StringSplit(str, ":;");
if (toks[0] == "WasmCallStack") {
std::vector<wasm_addr_t> callStackPCs = gdb_server_->GetWasmCallStack();
pkt_out->AddBlock(
callStackPCs.data(),
static_cast<uint32_t>(sizeof(wasm_addr_t) * callStackPCs.size()));
return ErrorCode::None;
}
// Get a Wasm global value in the Wasm module specified.
// IN : $qWasmGlobal:moduleId;index
// OUT: $xx..xx
if (toks[0] == "WasmGlobal") {
if (toks.size() == 3) {
uint32_t module_id =
static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
uint32_t index =
static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
uint64_t value = 0;
if (gdb_server_->GetWasmGlobal(module_id, index, &value)) {
pkt_out->AddBlock(&value, sizeof(value));
return ErrorCode::None;
} else {
return ErrorCode::Failed;
}
}
return ErrorCode::BadFormat;
}
// Get a Wasm local value in the stack frame specified.
// IN : $qWasmLocal:moduleId;frameIndex;index
// OUT: $xx..xx
if (toks[0] == "WasmLocal") {
if (toks.size() == 4) {
uint32_t module_id =
static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
uint32_t frame_index =
static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
uint32_t index =
static_cast<uint32_t>(strtol(toks[3].data(), nullptr, 10));
uint64_t value = 0;
if (gdb_server_->GetWasmLocal(module_id, frame_index, index, &value)) {
pkt_out->AddBlock(&value, sizeof(value));
return ErrorCode::None;
} else {
return ErrorCode::Failed;
}
}
return ErrorCode::BadFormat;
}
// Read Wasm memory.
// IN : $qWasmMem:memId;addr;len
// OUT: $xx..xx
if (toks[0] == "WasmMem") {
if (toks.size() == 4) {
uint32_t module_id =
static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
uint32_t address =
static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 16));
uint32_t length =
static_cast<uint32_t>(strtol(toks[3].data(), nullptr, 16));
if (length > Transport::kBufSize / 2) {
return ErrorCode::BadArgs;
}
uint8_t buff[Transport::kBufSize];
uint32_t read =
gdb_server_->GetWasmMemory(module_id, address, buff, length);
if (read > 0) {
pkt_out->AddBlock(buff, read);
return ErrorCode::None;
} else {
return ErrorCode::Failed;
}
}
return ErrorCode::BadFormat;
}
// No match so far, check the property cache.
QueryPropertyMap::const_iterator it = query_properties_.find(toks[0]);
if (it != query_properties_.end()) {
pkt_out->AddString(it->second.data());
}
// If not found, just send an empty response.
return ErrorCode::None;
}
// A Stop-reply packet has the format:
// Sxx
// or:
// Txx<name1>:<value1>;...;<nameN>:<valueN>
// where 'xx' is a two-digit hex number that represents the stop signal
// and the <name>:<value> pairs are used to report additional information,
// like the thread id.
void Target::SetStopReply(Packet* pkt_out) const {
pkt_out->AddRawChar('T');
pkt_out->AddWord8(cur_signal_);
// Adds 'thread-pcs:<pc1>,...,<pcN>;' A list of pc values for all threads that
// currently exist in the process.
char buff[64];
snprintf(buff, sizeof(buff), "thread-pcs:%" PRIx64 ";",
static_cast<uint64_t>(GetCurrentPc()));
pkt_out->AddString(buff);
// Adds 'thread:<tid>;' pair. Note that a terminating ';' is required.
pkt_out->AddString("thread:");
pkt_out->AddNumberSep(kThreadId, ';');
}
wasm_addr_t Target::GetCurrentPc() const { return wasm_addr_t(0); }
} // namespace gdb_server
} // namespace wasm
} // namespace internal

View File

@ -6,7 +6,9 @@
#define V8_DEBUG_WASM_GDB_SERVER_TARGET_H_
#include <atomic>
#include <map>
#include "src/base/macros.h"
#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
namespace v8 {
namespace internal {
@ -32,6 +34,10 @@ class Target {
bool IsTerminated() const { return status_ == Status::Terminated; }
private:
// Initializes a map used to make fast lookups when handling query packets
// that have a constant response.
void InitQueryPropertyMap();
// Blocks waiting for one of these two events to occur:
// - A network packet arrives from the debugger, or the debugger connection is
// closed;
@ -42,19 +48,43 @@ class Target {
// This method should be called when the debuggee has suspended its execution.
void ProcessCommands();
// This function always succeedes, since all errors
// are reported as an error string of "E<##>" where
// the two digit number. The error codes are not
// not documented, so this implementation uses
// ErrDef as errors codes. This function returns
// true a request to continue (or step) is processed.
bool ProcessPacket(const Packet* pktIn, Packet* pktOut);
enum class ErrorCode { None = 0, BadFormat = 1, BadArgs = 2, Failed = 3 };
enum class ProcessPacketResult {
Paused, // The command was processed, debuggee still paused.
Continue, // The debuggee should resume execution.
Detach, // Request to detach from the debugger.
Kill // Request to terminate the debuggee process.
};
// This function always succeedes, since all errors are reported as an error
// string "Exx" where xx is a two digit number.
// The return value indicates if the target can resume execution or it is
// still paused.
ProcessPacketResult ProcessPacket(Packet* pkt_in, Packet* pkt_out);
// Processes a general query packet
ErrorCode ProcessQueryPacket(const Packet* pkt_in, Packet* pkt_out);
// Formats a 'Stop-reply' packet, which is sent in response of a 'c'
// (continue), 's' (step) and '?' (query halt reason) commands.
void SetStopReply(Packet* pkt_out) const;
wasm_addr_t GetCurrentPc() const;
GdbServer* gdb_server_;
enum class Status { Running, Terminated };
std::atomic<Status> status_;
// Signal being processed.
int8_t cur_signal_;
Session* session_; // Session object not owned by the Target.
// Map used to make fast lookups when handling query packets.
typedef std::map<std::string, std::string> QueryPropertyMap;
QueryPropertyMap query_properties_;
DISALLOW_COPY_AND_ASSIGN(Target);
};

View File

@ -263,7 +263,6 @@ bool SocketTransport::ReadSomeData() {
if (result == 0) {
return false; // The connection was gracefully closed.
}
// WSAEventSelect sets socket to non-blocking mode. This is essential
// for socket event notification to work, there is no workaround.
// See remarks section at the page

View File

@ -124,6 +124,8 @@ class Transport : public TransportBase {
void Disconnect() override;
void Close() override;
static const int kBufSize = 4096;
protected:
// Copy buffered data to *dst up to len bytes and update dst and len.
void CopyFromBuffer(char** dst, int32_t* len);
@ -131,7 +133,6 @@ class Transport : public TransportBase {
// Read available data from the socket. Return false on EOF or error.
virtual bool ReadSomeData() = 0;
static const int kBufSize = 4096;
std::unique_ptr<char[]> buf_;
int32_t pos_;
int32_t size_;

View File

@ -31,15 +31,17 @@ class JSArrayBuffer;
namespace wasm {
#ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
namespace gdb_server {
class GdbServer;
}
#endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING
class AsyncCompileJob;
class ErrorThrower;
struct ModuleWireBytes;
class WasmFeatures;
namespace gdb_server {
class GdbServer;
}
class V8_EXPORT_PRIVATE CompilationResultResolver {
public:
virtual void OnCompilationSucceeded(Handle<WasmModuleObject> result) = 0;

View File

@ -143,6 +143,25 @@ TEST_F(WasmGdbRemoteTest, GdbRemotePacketRunLengthEncoded) {
EXPECT_EQ("123333ab", std::string(packet2.GetPayload()));
}
TEST_F(WasmGdbRemoteTest, GdbRemoteUtilStringSplit) {
std::vector<std::string> parts1 = StringSplit({}, ",");
EXPECT_EQ(size_t(0), parts1.size());
auto parts2 = StringSplit("a", nullptr);
EXPECT_EQ(size_t(1), parts2.size());
EXPECT_EQ("a", parts2[0]);
auto parts3 = StringSplit(";a;bc;def;", ",");
EXPECT_EQ(size_t(1), parts3.size());
EXPECT_EQ(";a;bc;def;", parts3[0]);
auto parts4 = StringSplit(";a;bc;def;", ";");
EXPECT_EQ(size_t(3), parts4.size());
EXPECT_EQ("a", parts4[0]);
EXPECT_EQ("bc", parts4[1]);
EXPECT_EQ("def", parts4[2]);
}
class MockTransport : public TransportBase {
public:
MOCK_METHOD0(AcceptConnection, bool());