1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
// 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
|