1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/debug/wasm/gdb-server/session.h" 6#include "src/debug/wasm/gdb-server/packet.h" 7#include "src/debug/wasm/gdb-server/transport.h" 8 9namespace v8 { 10namespace internal { 11namespace wasm { 12namespace gdb_server { 13 14Session::Session(TransportBase* transport) 15 : io_(transport), connected_(true), ack_enabled_(true) {} 16 17void Session::WaitForDebugStubEvent() { io_->WaitForDebugStubEvent(); } 18 19bool Session::SignalThreadEvent() { return io_->SignalThreadEvent(); } 20 21bool Session::IsDataAvailable() const { return io_->IsDataAvailable(); } 22 23bool Session::IsConnected() const { return connected_; } 24 25void Session::Disconnect() { 26 io_->Disconnect(); 27 connected_ = false; 28} 29 30bool Session::GetChar(char* ch) { 31 if (!io_->Read(ch, 1)) { 32 Disconnect(); 33 return false; 34 } 35 36 return true; 37} 38 39bool Session::SendPacket(Packet* pkt, bool expect_ack) { 40 char ch; 41 do { 42 std::string data = pkt->GetPacketData(); 43 44 TRACE_GDB_REMOTE("TX %s\n", data.size() < 160 45 ? data.c_str() 46 : (data.substr(0, 160) + "...").c_str()); 47 if (!io_->Write(data.data(), static_cast<int32_t>(data.length()))) { 48 return false; 49 } 50 51 // If ACKs are off, we are done. 52 if (!expect_ack || !ack_enabled_) { 53 break; 54 } 55 56 // Otherwise, poll for '+' 57 if (!GetChar(&ch)) { 58 return false; 59 } 60 61 // Retry if we didn't get a '+' 62 } while (ch != '+'); 63 64 return true; 65} 66 67bool Session::GetPayload(Packet* pkt, uint8_t* checksum) { 68 pkt->Clear(); 69 *checksum = 0; 70 71 // Stream in the characters 72 char ch; 73 while (GetChar(&ch)) { 74 if (ch == '#') { 75 // If we see a '#' we must be done with the data. 76 return true; 77 } else if (ch == '$') { 78 // If we see a '$' we must have missed the last cmd, let's retry. 79 TRACE_GDB_REMOTE("RX Missing $, retry.\n"); 80 *checksum = 0; 81 pkt->Clear(); 82 } else { 83 // Keep a running XSUM. 84 *checksum += ch; 85 pkt->AddRawChar(ch); 86 } 87 } 88 return false; 89} 90 91bool Session::GetPacket(Packet* pkt) { 92 while (true) { 93 // Toss characters until we see a start of command 94 char ch; 95 do { 96 if (!GetChar(&ch)) { 97 return false; 98 } 99 } while (ch != '$'); 100 101 uint8_t running_checksum = 0; 102 if (!GetPayload(pkt, &running_checksum)) { 103 return false; 104 } 105 106 // Get two nibble checksum 107 uint8_t trailing_checksum = 0; 108 char chars[2]; 109 if (!GetChar(&chars[0]) || !GetChar(&chars[1]) || 110 !HexToUInt8(chars, &trailing_checksum)) { 111 return false; 112 } 113 114 TRACE_GDB_REMOTE("RX $%s#%c%c\n", pkt->GetPayload(), chars[0], chars[1]); 115 116 pkt->ParseSequence(); 117 118 // If ACKs are off, we are done. 119 if (!ack_enabled_) { 120 return true; 121 } 122 123 // If the XSUMs don't match, signal bad packet 124 if (trailing_checksum == running_checksum) { 125 char out[3] = {'+', 0, 0}; 126 127 // If we have a sequence number 128 int32_t seq; 129 if (pkt->GetSequence(&seq)) { 130 // Respond with sequence number 131 UInt8ToHex(seq, &out[1]); 132 return io_->Write(out, 3); 133 } else { 134 return io_->Write(out, 1); 135 } 136 } else { 137 // Resend a bad XSUM and look for retransmit 138 TRACE_GDB_REMOTE("RX Bad XSUM, retry\n"); 139 io_->Write("-", 1); 140 // retry... 141 } 142 } 143} 144 145} // namespace gdb_server 146} // namespace wasm 147} // namespace internal 148} // namespace v8 149