Adjusting logs in a few ways:
* The `DAP_LOG` and `DAP_LOG_ERROR` macros now include the file/line of
the log statement.
* Added support for creating a log with a prefix. This simplifies how we
create logs for the `lldb_dap::DAP` instance and `lldb_dap::Transport`
instance, allowing us to not have to pass the client name around as
much.
* Updated logging usage to take the `lldb_dap::Log` as a reference but
it now defaults to `llvm::raw_null_stream` if not configured. This
ensures more uniform access to the logger, even if its not written
anywhere.
The logs now look like:
```
1764896564.038788080 (stdio) --> {"command":"initialize","arguments":{...},"type":"request","seq":1}
1764896564.039064884 DAP.cpp:1007 (stdio) queued (command=initialize seq=1)
1764896564.039768934 (stdio) <-- {"body":{...},"command":"initialize","request_seq":1,"seq":1,"success":true,"type":"response"}
```
144 lines
4.8 KiB
C++
144 lines
4.8 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "DAPSessionManager.h"
|
|
#include "DAP.h"
|
|
#include "EventHelper.h"
|
|
#include "lldb/API/SBBroadcaster.h"
|
|
#include "lldb/API/SBEvent.h"
|
|
#include "lldb/API/SBTarget.h"
|
|
#include "lldb/Host/MainLoopBase.h"
|
|
#include "llvm/Support/Threading.h"
|
|
#include "llvm/Support/WithColor.h"
|
|
|
|
#include <chrono>
|
|
#include <mutex>
|
|
|
|
namespace lldb_dap {
|
|
|
|
ManagedEventThread::ManagedEventThread(lldb::SBBroadcaster broadcaster,
|
|
std::thread t)
|
|
: m_broadcaster(broadcaster), m_event_thread(std::move(t)) {}
|
|
|
|
ManagedEventThread::~ManagedEventThread() {
|
|
if (m_event_thread.joinable()) {
|
|
m_broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
|
|
m_event_thread.join();
|
|
}
|
|
}
|
|
|
|
DAPSessionManager &DAPSessionManager::GetInstance() {
|
|
static std::once_flag initialized;
|
|
static DAPSessionManager *instance =
|
|
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor
|
|
// chain
|
|
|
|
std::call_once(initialized, []() { instance = new DAPSessionManager(); });
|
|
|
|
return *instance;
|
|
}
|
|
|
|
void DAPSessionManager::RegisterSession(lldb_private::MainLoop *loop,
|
|
DAP *dap) {
|
|
std::lock_guard<std::mutex> lock(m_sessions_mutex);
|
|
m_active_sessions[loop] = dap;
|
|
}
|
|
|
|
void DAPSessionManager::UnregisterSession(lldb_private::MainLoop *loop) {
|
|
std::unique_lock<std::mutex> lock(m_sessions_mutex);
|
|
m_active_sessions.erase(loop);
|
|
std::notify_all_at_thread_exit(m_sessions_condition, std::move(lock));
|
|
}
|
|
|
|
std::vector<DAP *> DAPSessionManager::GetActiveSessions() {
|
|
std::lock_guard<std::mutex> lock(m_sessions_mutex);
|
|
std::vector<DAP *> sessions;
|
|
for (const auto &[loop, dap] : m_active_sessions)
|
|
if (dap)
|
|
sessions.emplace_back(dap);
|
|
return sessions;
|
|
}
|
|
|
|
void DAPSessionManager::DisconnectAllSessions() {
|
|
std::lock_guard<std::mutex> lock(m_sessions_mutex);
|
|
m_client_failed = false;
|
|
for (auto [loop, dap] : m_active_sessions) {
|
|
if (dap) {
|
|
if (llvm::Error error = dap->Disconnect()) {
|
|
m_client_failed = true;
|
|
llvm::WithColor::error() << "DAP client disconnected failed: "
|
|
<< llvm::toString(std::move(error)) << "\n";
|
|
}
|
|
loop->AddPendingCallback(
|
|
[](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::Error DAPSessionManager::WaitForAllSessionsToDisconnect() {
|
|
std::unique_lock<std::mutex> lock(m_sessions_mutex);
|
|
m_sessions_condition.wait(lock, [this] { return m_active_sessions.empty(); });
|
|
|
|
// Check if any disconnection failed and return appropriate error.
|
|
if (m_client_failed)
|
|
return llvm::make_error<llvm::StringError>(
|
|
"disconnecting all clients failed", llvm::inconvertibleErrorCode());
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
std::shared_ptr<ManagedEventThread>
|
|
DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger,
|
|
DAP *requesting_dap) {
|
|
lldb::user_id_t debugger_id = debugger.GetID();
|
|
std::lock_guard<std::mutex> lock(m_sessions_mutex);
|
|
|
|
// Try to use shared event thread, if it exists.
|
|
if (auto it = m_debugger_event_threads.find(debugger_id);
|
|
it != m_debugger_event_threads.end()) {
|
|
if (std::shared_ptr<ManagedEventThread> thread_sp = it->second.lock())
|
|
return thread_sp;
|
|
// Our weak pointer has expired.
|
|
m_debugger_event_threads.erase(it);
|
|
}
|
|
|
|
// Create a new event thread and store it.
|
|
auto new_thread_sp = std::make_shared<ManagedEventThread>(
|
|
requesting_dap->broadcaster,
|
|
std::thread(EventThread, debugger, requesting_dap->broadcaster,
|
|
requesting_dap->m_client_name,
|
|
std::ref(requesting_dap->log)));
|
|
m_debugger_event_threads[debugger_id] = new_thread_sp;
|
|
return new_thread_sp;
|
|
}
|
|
|
|
DAP *DAPSessionManager::FindDAPForTarget(lldb::SBTarget target) {
|
|
std::lock_guard<std::mutex> lock(m_sessions_mutex);
|
|
|
|
for (const auto &[loop, dap] : m_active_sessions)
|
|
if (dap && dap->target.IsValid() && dap->target == target)
|
|
return dap;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void DAPSessionManager::ReleaseExpiredEventThreads() {
|
|
std::lock_guard<std::mutex> lock(m_sessions_mutex);
|
|
for (auto it = m_debugger_event_threads.begin();
|
|
it != m_debugger_event_threads.end();) {
|
|
// Check if the weak_ptr has expired (no DAP instances are using it
|
|
// anymore).
|
|
if (it->second.expired()) {
|
|
it = m_debugger_event_threads.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace lldb_dap
|