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
9namespace v8 {
10namespace internal {
11namespace wasm {
12namespace gdb_server {
13
14GdbServerThread::GdbServerThread(GdbServer* gdb_server)
15    : Thread(v8::base::Thread::Options("GdbServerThread")),
16      gdb_server_(gdb_server),
17      start_semaphore_(0) {}
18
19bool 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
38void 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
50void 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
97void 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