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/gdb-server-thread.h"
6 #include "src/debug/wasm/gdb-server/gdb-server.h"
7 #include "src/debug/wasm/gdb-server/session.h"
8
9 namespace v8 {
10 namespace internal {
11 namespace wasm {
12 namespace gdb_server {
13
GdbServerThread(GdbServer * gdb_server)14 GdbServerThread::GdbServerThread(GdbServer* gdb_server)
15 : Thread(v8::base::Thread::Options("GdbServerThread")),
16 gdb_server_(gdb_server),
17 start_semaphore_(0) {}
18
StartAndInitialize()19 bool GdbServerThread::StartAndInitialize() {
20 // Executed in the Isolate thread.
21 if (!Start()) {
22 return false;
23 }
24
25 // We need to make sure that {Stop} is never called before the thread has
26 // completely initialized {transport_} and {target_}. Otherwise there could be
27 // a race condition where in the main thread {Stop} might get called before
28 // the transport is created, and then in the GDBServer thread we may have time
29 // to setup the transport and block on accept() before the main thread blocks
30 // on joining the thread.
31 // The small performance hit caused by this Wait should be negligeable because
32 // this operation happensat most once per process and only when the
33 // --wasm-gdb-remote flag is set.
34 start_semaphore_.Wait();
35 return !!target_;
36 }
37
CleanupThread()38 void GdbServerThread::CleanupThread() {
39 // Executed in the GdbServer thread.
40 v8::base::MutexGuard guard(&mutex_);
41
42 target_ = nullptr;
43 transport_ = nullptr;
44
45 #if _WIN32
46 ::WSACleanup();
47 #endif
48 }
49
Run()50 void GdbServerThread::Run() {
51 // Executed in the GdbServer thread.
52 #ifdef _WIN32
53 // Initialize Winsock
54 WSADATA wsaData;
55 int iResult = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
56 if (iResult != 0) {
57 TRACE_GDB_REMOTE("GdbServerThread::Run: WSAStartup failed\n");
58 return;
59 }
60 #endif
61
62 // If the default port is not available, try any port.
63 SocketBinding socket_binding = SocketBinding::Bind(FLAG_wasm_gdb_remote_port);
64 if (!socket_binding.IsValid()) {
65 socket_binding = SocketBinding::Bind(0);
66 }
67 if (!socket_binding.IsValid()) {
68 TRACE_GDB_REMOTE("GdbServerThread::Run: Failed to bind any TCP port\n");
69 return;
70 }
71 TRACE_GDB_REMOTE("gdb-remote(%d) : Connect GDB with 'target remote :%d\n",
72 __LINE__, socket_binding.GetBoundPort());
73
74 transport_ = socket_binding.CreateTransport();
75 target_ = std::make_unique<Target>(gdb_server_);
76
77 // Here we have completed the initialization, and the thread that called
78 // {StartAndInitialize} may resume execution.
79 start_semaphore_.Signal();
80
81 while (!target_->IsTerminated()) {
82 // Wait for incoming connections.
83 if (!transport_->AcceptConnection()) {
84 continue;
85 }
86
87 // Create a new session for this connection
88 Session session(transport_.get());
89 TRACE_GDB_REMOTE("GdbServerThread: Connected\n");
90
91 // Run this session for as long as it lasts
92 target_->Run(&session);
93 }
94 CleanupThread();
95 }
96
Stop()97 void GdbServerThread::Stop() {
98 // Executed in the Isolate thread.
99
100 // Synchronized, becauses {Stop} might be called while {Run} is still
101 // initializing {transport_} and {target_}. If this happens and the thread is
102 // blocked waiting for an incoming connection or GdbServer for incoming
103 // packets, it will unblocked when {transport_} is closed.
104 v8::base::MutexGuard guard(&mutex_);
105
106 if (target_) {
107 target_->Terminate();
108 }
109
110 if (transport_) {
111 transport_->Close();
112 }
113 }
114
115 } // namespace gdb_server
116 } // namespace wasm
117 } // namespace internal
118 } // namespace v8
119