// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/debug/wasm/gdb-server/session.h" #include "src/debug/wasm/gdb-server/packet.h" #include "src/debug/wasm/gdb-server/transport.h" namespace v8 { namespace internal { namespace wasm { namespace gdb_server { Session::Session(TransportBase* transport) : io_(transport), connected_(true), ack_enabled_(true) {} void Session::WaitForDebugStubEvent() { io_->WaitForDebugStubEvent(); } bool Session::SignalThreadEvent() { return io_->SignalThreadEvent(); } bool Session::IsDataAvailable() const { return io_->IsDataAvailable(); } bool Session::IsConnected() const { return connected_; } void Session::Disconnect() { io_->Disconnect(); connected_ = false; } bool Session::GetChar(char* ch) { if (!io_->Read(ch, 1)) { Disconnect(); return false; } return true; } bool Session::SendPacket(Packet* pkt, bool expect_ack) { char ch; do { std::string data = pkt->GetPacketData(); TRACE_GDB_REMOTE("TX %s\n", data.size() < 160 ? data.c_str() : (data.substr(0, 160) + "...").c_str()); if (!io_->Write(data.data(), static_cast<int32_t>(data.length()))) { return false; } // If ACKs are off, we are done. if (!expect_ack || !ack_enabled_) { break; } // Otherwise, poll for '+' if (!GetChar(&ch)) { return false; } // Retry if we didn't get a '+' } while (ch != '+'); return true; } bool Session::GetPayload(Packet* pkt, uint8_t* checksum) { pkt->Clear(); *checksum = 0; // Stream in the characters char ch; while (GetChar(&ch)) { if (ch == '#') { // If we see a '#' we must be done with the data. return true; } else if (ch == '$') { // If we see a '$' we must have missed the last cmd, let's retry. TRACE_GDB_REMOTE("RX Missing $, retry.\n"); *checksum = 0; pkt->Clear(); } else { // Keep a running XSUM. *checksum += ch; pkt->AddRawChar(ch); } } return false; } bool Session::GetPacket(Packet* pkt) { while (true) { // Toss characters until we see a start of command char ch; do { if (!GetChar(&ch)) { return false; } } while (ch != '$'); uint8_t running_checksum = 0; if (!GetPayload(pkt, &running_checksum)) { return false; } // Get two nibble checksum uint8_t trailing_checksum = 0; char chars[2]; if (!GetChar(&chars[0]) || !GetChar(&chars[1]) || !HexToUInt8(chars, &trailing_checksum)) { return false; } TRACE_GDB_REMOTE("RX $%s#%c%c\n", pkt->GetPayload(), chars[0], chars[1]); pkt->ParseSequence(); // If ACKs are off, we are done. if (!ack_enabled_) { return true; } // If the XSUMs don't match, signal bad packet if (trailing_checksum == running_checksum) { char out[3] = {'+', 0, 0}; // If we have a sequence number int32_t seq; if (pkt->GetSequence(&seq)) { // Respond with sequence number UInt8ToHex(seq, &out[1]); return io_->Write(out, 3); } else { return io_->Write(out, 1); } } else { // Resend a bad XSUM and look for retransmit TRACE_GDB_REMOTE("RX Bad XSUM, retry\n"); io_->Write("-", 1); // retry... } } } } // namespace gdb_server } // namespace wasm } // namespace internal } // namespace v8