blob: 03b9b8fd3e3fff4f3702013e1c55310663250378 [file] [log] [blame]
// 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