v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
transport.cc
Go to the documentation of this file.
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
6
7#include <fcntl.h>
8
9#ifndef SD_BOTH
10#define SD_BOTH 2
11#endif
12
13namespace v8 {
14namespace internal {
15namespace wasm {
16namespace gdb_server {
17
19 : socket_handle_(socket_handle) {}
20
21// static
23 SocketHandle socket_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
24 if (socket_handle == InvalidSocket) {
25 TRACE_GDB_REMOTE("Failed to create socket.\n");
27 }
28 struct sockaddr_in sockaddr;
29 // Clearing sockaddr_in first appears to be necessary on Mac OS X.
30 memset(&sockaddr, 0, sizeof(sockaddr));
31 socklen_t addrlen = static_cast<socklen_t>(sizeof(sockaddr));
32 sockaddr.sin_family = AF_INET;
33 sockaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
34 sockaddr.sin_port = htons(tcp_port);
35
36#if _WIN32
37 // On Windows, SO_REUSEADDR has a different meaning than on POSIX systems.
38 // SO_REUSEADDR allows hijacking of an open socket by another process.
39 // The SO_EXCLUSIVEADDRUSE flag prevents this behavior.
40 // See:
41 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
42 //
43 // Additionally, unlike POSIX, TCP server sockets can be bound to
44 // ports in the TIME_WAIT state, without setting SO_REUSEADDR.
45 int exclusive_address = 1;
46 if (setsockopt(socket_handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
47 reinterpret_cast<char*>(&exclusive_address),
48 sizeof(exclusive_address))) {
49 TRACE_GDB_REMOTE("Failed to set SO_EXCLUSIVEADDRUSE option.\n");
50 }
51#else
52 // On POSIX, this is necessary to ensure that the TCP port is released
53 // promptly when sel_ldr exits. Without this, the TCP port might
54 // only be released after a timeout, and later processes can fail
55 // to bind it.
56 int reuse_address = 1;
57 if (setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR,
58 reinterpret_cast<char*>(&reuse_address),
59 sizeof(reuse_address))) {
60 TRACE_GDB_REMOTE("Failed to set SO_REUSEADDR option.\n");
61 }
62#endif
63
64 if (bind(socket_handle, reinterpret_cast<struct sockaddr*>(&sockaddr),
65 addrlen)) {
66 TRACE_GDB_REMOTE("Failed to bind server.\n");
68 }
69
70 if (listen(socket_handle, 1)) {
71 TRACE_GDB_REMOTE("Failed to listen.\n");
73 }
74 return SocketBinding(socket_handle);
75}
76
77std::unique_ptr<SocketTransport> SocketBinding::CreateTransport() {
78 return std::make_unique<SocketTransport>(socket_handle_);
79}
80
82 struct sockaddr_in saddr;
83 struct sockaddr* psaddr = reinterpret_cast<struct sockaddr*>(&saddr);
84 // Clearing sockaddr_in first appears to be necessary on Mac OS X.
85 memset(&saddr, 0, sizeof(saddr));
86 socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
87 if (::getsockname(socket_handle_, psaddr, &addrlen)) {
88 TRACE_GDB_REMOTE("Failed to retrieve bound address.\n");
89 return 0;
90 }
91 return ntohs(saddr.sin_port);
92}
93
94// Do not delay sending small packets. This significantly speeds up
95// remote debugging. Debug stub uses buffering to send outgoing packets
96// so they are not split into more TCP packets than necessary.
98 int nodelay = 1;
99 if (::setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
100 reinterpret_cast<char*>(&nodelay), sizeof(nodelay))) {
101 TRACE_GDB_REMOTE("Failed to set TCP_NODELAY option.\n");
102 }
103}
104
106 : buf_(new char[kBufSize]),
107 pos_(0),
108 size_(0),
109 handle_bind_(s),
110 handle_accept_(InvalidSocket) {}
111
117
118void Transport::CopyFromBuffer(char** dst, int32_t* len) {
119 int32_t copy_bytes = std::min(*len, size_ - pos_);
120 memcpy(*dst, buf_.get() + pos_, copy_bytes);
121 pos_ += copy_bytes;
122 *len -= copy_bytes;
123 *dst += copy_bytes;
124}
125
126bool Transport::Read(char* dst, int32_t len) {
127 if (pos_ < size_) {
128 CopyFromBuffer(&dst, &len);
129 }
130 while (len > 0) {
131 pos_ = 0;
132 size_ = 0;
133 if (!ReadSomeData()) {
134 return false;
135 }
136 CopyFromBuffer(&dst, &len);
137 }
138 return true;
139}
140
141bool Transport::Write(const char* src, int32_t len) {
142 while (len > 0) {
143 ssize_t result = ::send(handle_accept_, src, len, 0);
144 if (result > 0) {
145 src += result;
146 len -= result;
147 continue;
148 }
149 if (result == 0) {
150 return false;
151 }
153 return false;
154 }
155 }
156 return true;
157}
158
159// Return true if there is data to read.
161 if (pos_ < size_) {
162 return true;
163 }
164 fd_set fds;
165
166 FD_ZERO(&fds);
167 FD_SET(handle_accept_, &fds);
168
169 // We want a "non-blocking" check
170 struct timeval timeout;
171 timeout.tv_sec = 0;
172 timeout.tv_usec = 0;
173
174 // Check if this file handle can select on read
175 int cnt = select(static_cast<int>(handle_accept_) + 1, &fds, 0, 0, &timeout);
176
177 // If we are ready, or if there is an error. We return true
178 // on error, to let the next IO request fail.
179 if (cnt != 0) return true;
180
181 return false;
182}
183
185 ::shutdown(handle_bind_, SD_BOTH);
187 Disconnect();
188}
189
192 // Shutdown the connection in both directions. This should
193 // always succeed, and nothing we can do if this fails.
194 ::shutdown(handle_accept_, SD_BOTH);
197 }
198}
199
200#if _WIN32
201
203 socket_event_ = WSA_INVALID_EVENT;
204 faulted_thread_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
205 if (faulted_thread_event_ == NULL) {
207 "SocketTransport::SocketTransport: Failed to create event object for "
208 "faulted thread\n");
209 }
210}
211
213 if (!CloseHandle(faulted_thread_event_)) {
215 "SocketTransport::~SocketTransport: Failed to close "
216 "event\n");
217 }
218
219 if (socket_event_) {
220 if (!::WSACloseEvent(socket_event_)) {
222 "SocketTransport::~SocketTransport: Failed to close "
223 "socket event\n");
224 }
225 }
226}
227
230 handle_accept_ = ::accept(handle_bind_, NULL, 0);
233
234 // Create socket event
235 socket_event_ = ::WSACreateEvent();
236 if (socket_event_ == WSA_INVALID_EVENT) {
238 "SocketTransport::AcceptConnection: Failed to create socket event\n");
239 }
240
241 // Listen for close events in order to handle them correctly.
242 // Additionally listen for read readiness as WSAEventSelect sets the socket
243 // to non-blocking mode.
244 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms738547(v=vs.85).aspx
245 if (::WSAEventSelect(handle_accept_, socket_event_, FD_CLOSE | FD_READ) ==
246 SOCKET_ERROR) {
248 "SocketTransport::AcceptConnection: Failed to bind event to "
249 "socket\n");
250 }
251 return true;
252 }
253 return false;
254}
255
257 while (true) {
258 ssize_t result =
259 ::recv(handle_accept_, buf_.get() + size_, kBufSize - size_, 0);
260 if (result > 0) {
261 size_ += result;
262 return true;
263 }
264 if (result == 0) {
265 return false; // The connection was gracefully closed.
266 }
267 // WSAEventSelect sets socket to non-blocking mode. This is essential
268 // for socket event notification to work, there is no workaround.
269 // See remarks section at the page
270 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
271 if (SocketGetLastError() == WSAEWOULDBLOCK) {
272 if (::WaitForSingleObject(socket_event_, INFINITE) == WAIT_FAILED) {
274 "SocketTransport::ReadSomeData: Failed to wait on socket event\n");
275 }
276 if (!::ResetEvent(socket_event_)) {
278 "SocketTransport::ReadSomeData: Failed to reset socket event\n");
279 }
280 continue;
281 }
282
284 return false;
285 }
286 }
287}
288
290 // Don't wait if we already have data to read.
291 bool wait = !(pos_ < size_);
292
293 HANDLE handles[2];
294 handles[0] = faulted_thread_event_;
295 handles[1] = socket_event_;
296 int count = size_ < kBufSize ? 2 : 1;
297 int result =
298 WaitForMultipleObjects(count, handles, FALSE, wait ? INFINITE : 0);
299 if (result == WAIT_OBJECT_0 + 1) {
300 if (!ResetEvent(socket_event_)) {
302 "SocketTransport::WaitForDebugStubEvent: Failed to reset socket "
303 "event\n");
304 }
305 return;
306 } else if (result == WAIT_OBJECT_0) {
307 if (!ResetEvent(faulted_thread_event_)) {
309 "SocketTransport::WaitForDebugStubEvent: Failed to reset event\n");
310 }
311 return;
312 } else if (result == WAIT_TIMEOUT) {
313 return;
314 }
316 "SocketTransport::WaitForDebugStubEvent: Wait for events failed\n");
317}
318
320 if (!SetEvent(faulted_thread_event_)) {
321 return false;
322 }
323 return true;
324}
325
328
329 if (socket_event_ != WSA_INVALID_EVENT && !::WSACloseEvent(socket_event_)) {
331 "SocketTransport::~SocketTransport: Failed to close "
332 "socket event\n");
333 }
334 socket_event_ = WSA_INVALID_EVENT;
336}
337
338#else // _WIN32
339
341 int fds[2];
342#if defined(__linux__)
343 int ret = pipe2(fds, O_CLOEXEC);
344#else
345 int ret = pipe(fds);
346#endif
347 if (ret < 0) {
349 "SocketTransport::SocketTransport: Failed to allocate pipe for faulted "
350 "thread\n");
351 }
354}
355
357 if (close(faulted_thread_fd_read_) != 0) {
359 "SocketTransport::~SocketTransport: Failed to close "
360 "event\n");
361 }
362 if (close(faulted_thread_fd_write_) != 0) {
364 "SocketTransport::~SocketTransport: Failed to close "
365 "event\n");
366 }
367}
368
371 handle_accept_ = ::accept(handle_bind_, NULL, 0);
374 return true;
375 }
376 return false;
377}
378
380 while (true) {
381 ssize_t result =
382 ::recv(handle_accept_, buf_.get() + size_, kBufSize - size_, 0);
383 if (result > 0) {
384 size_ += result;
385 return true;
386 }
387 if (result == 0) {
388 return false; // The connection was gracefully closed.
389 }
391 return false;
392 }
393 }
394}
395
397 // Don't wait if we already have data to read.
398 bool wait = !(pos_ < size_);
399
400 fd_set fds;
401 FD_ZERO(&fds);
402 FD_SET(faulted_thread_fd_read_, &fds);
403 int max_fd = faulted_thread_fd_read_;
404 if (size_ < kBufSize) {
405 FD_SET(handle_accept_, &fds);
406 max_fd = std::max(max_fd, handle_accept_);
407 }
408
409 int ret;
410 // We don't need sleep-polling on Linux now, so we set either zero or infinite
411 // timeout.
412 if (wait) {
413 ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
414 } else {
415 struct timeval timeout;
416 timeout.tv_sec = 0;
417 timeout.tv_usec = 0;
418 ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
419 }
420 if (ret < 0) {
422 "SocketTransport::WaitForDebugStubEvent: Failed to wait for "
423 "debug stub event\n");
424 }
425
426 if (ret > 0) {
427 if (FD_ISSET(faulted_thread_fd_read_, &fds)) {
428 char buf[16];
429 if (read(faulted_thread_fd_read_, &buf, sizeof(buf)) < 0) {
431 "SocketTransport::WaitForDebugStubEvent: Failed to read from "
432 "debug stub event pipe fd\n");
433 }
434 }
435 if (FD_ISSET(handle_accept_, &fds)) ReadSomeData();
436 }
437}
438
440 // Notify the debug stub by marking the thread as faulted.
441 char buf = 0;
442 if (write(faulted_thread_fd_write_, &buf, sizeof(buf)) != sizeof(buf)) {
444 "SocketTransport:SignalThreadEvent: Can't send debug stub "
445 "event\n");
446 return false;
447 }
448 return true;
449}
450
451#endif // _WIN32
452
453} // namespace gdb_server
454} // namespace wasm
455} // namespace internal
456} // namespace v8
457
458#undef SD_BOTH
int pos_
uint8_t * buf_
SocketBinding(SocketHandle socket_handle)
Definition transport.cc:18
static SocketBinding Bind(uint16_t tcp_port)
Definition transport.cc:22
std::unique_ptr< SocketTransport > CreateTransport()
Definition transport.cc:77
bool Read(char *dst, int32_t len) override
Definition transport.cc:126
void CopyFromBuffer(char **dst, int32_t *len)
Definition transport.cc:118
bool Write(const char *src, int32_t len) override
Definition transport.cc:141
const int size_
Definition assembler.cc:132
#define TRACE_GDB_REMOTE(...)
ZoneVector< RpoNumber > & result
void DisableNagleAlgorithm(SocketHandle socket)
Definition transport.cc:97
Definition c-api.cc:87
#define CHECK(condition)
Definition logging.h:124
#define SD_BOTH
Definition transport.cc:10
#define CloseSocket
Definition transport.h:37
#define InvalidSocket
Definition transport.h:38
static const int kErrInterrupt
Definition transport.h:40
#define SocketGetLastError()
Definition transport.h:39
int SocketHandle
Definition transport.h:35
void * HANDLE