// 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/gdb-server-thread.h" #include "src/debug/wasm/gdb-server/gdb-server.h" #include "src/debug/wasm/gdb-server/session.h" namespace v8 { namespace internal { namespace wasm { namespace gdb_server { GdbServerThread::GdbServerThread(GdbServer* gdb_server) : Thread(v8::base::Thread::Options("GdbServerThread")), gdb_server_(gdb_server), start_semaphore_(0) {} bool GdbServerThread::StartAndInitialize() { // Executed in the Isolate thread. if (!Start()) { return false; } // We need to make sure that {Stop} is never called before the thread has // completely initialized {transport_} and {target_}. Otherwise there could be // a race condition where in the main thread {Stop} might get called before // the transport is created, and then in the GDBServer thread we may have time // to setup the transport and block on accept() before the main thread blocks // on joining the thread. // The small performance hit caused by this Wait should be negligeable because // this operation happensat most once per process and only when the // --wasm-gdb-remote flag is set. start_semaphore_.Wait(); return !!target_; } void GdbServerThread::CleanupThread() { // Executed in the GdbServer thread. v8::base::MutexGuard guard(&mutex_); target_ = nullptr; transport_ = nullptr; #if _WIN32 ::WSACleanup(); #endif } void GdbServerThread::Run() { // Executed in the GdbServer thread. #ifdef _WIN32 // Initialize Winsock WSADATA wsaData; int iResult = ::WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { TRACE_GDB_REMOTE("GdbServerThread::Run: WSAStartup failed\n"); return; } #endif // If the default port is not available, try any port. SocketBinding socket_binding = SocketBinding::Bind(FLAG_wasm_gdb_remote_port); if (!socket_binding.IsValid()) { socket_binding = SocketBinding::Bind(0); } if (!socket_binding.IsValid()) { TRACE_GDB_REMOTE("GdbServerThread::Run: Failed to bind any TCP port\n"); return; } TRACE_GDB_REMOTE("gdb-remote(%d) : Connect GDB with 'target remote :%d\n", __LINE__, socket_binding.GetBoundPort()); transport_ = socket_binding.CreateTransport(); target_ = std::make_unique<Target>(gdb_server_); // Here we have completed the initialization, and the thread that called // {StartAndInitialize} may resume execution. start_semaphore_.Signal(); while (!target_->IsTerminated()) { // Wait for incoming connections. if (!transport_->AcceptConnection()) { continue; } // Create a new session for this connection Session session(transport_.get()); TRACE_GDB_REMOTE("GdbServerThread: Connected\n"); // Run this session for as long as it lasts target_->Run(&session); } CleanupThread(); } void GdbServerThread::Stop() { // Executed in the Isolate thread. // Synchronized, becauses {Stop} might be called while {Run} is still // initializing {transport_} and {target_}. If this happens and the thread is // blocked waiting for an incoming connection or GdbServer for incoming // packets, it will unblocked when {transport_} is closed. v8::base::MutexGuard guard(&mutex_); if (target_) { target_->Terminate(); } if (transport_) { transport_->Close(); } } } // namespace gdb_server } // namespace wasm } // namespace internal } // namespace v8