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
9 namespace v8 {
10 namespace internal {
11 namespace wasm {
12 namespace gdb_server {
13
Session(TransportBase * transport)14 Session::Session(TransportBase* transport)
15 : io_(transport), connected_(true), ack_enabled_(true) {}
16
WaitForDebugStubEvent()17 void Session::WaitForDebugStubEvent() { io_->WaitForDebugStubEvent(); }
18
SignalThreadEvent()19 bool Session::SignalThreadEvent() { return io_->SignalThreadEvent(); }
20
IsDataAvailable() const21 bool Session::IsDataAvailable() const { return io_->IsDataAvailable(); }
22
IsConnected() const23 bool Session::IsConnected() const { return connected_; }
24
Disconnect()25 void Session::Disconnect() {
26 io_->Disconnect();
27 connected_ = false;
28 }
29
GetChar(char * ch)30 bool Session::GetChar(char* ch) {
31 if (!io_->Read(ch, 1)) {
32 Disconnect();
33 return false;
34 }
35
36 return true;
37 }
38
SendPacket(Packet * pkt,bool expect_ack)39 bool 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
GetPayload(Packet * pkt,uint8_t * checksum)67 bool 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
GetPacket(Packet * pkt)91 bool 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