https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/170731
>From ab5464aef6586a7d5e5722e44e9f0cab5eec9630 Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Thu, 4 Dec 2025 11:46:04 -0800 Subject: [PATCH 1/5] [lldb-dap] Improving logging support and formatting. 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 DAP instance and 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 `/dev/null` if not configured. This ensures more uniform access to the logger, even if its not written anywhere. --- lldb/tools/lldb-dap/DAP.cpp | 41 ++++++--------- lldb/tools/lldb-dap/DAP.h | 9 ++-- lldb/tools/lldb-dap/DAPLog.cpp | 20 ++++++-- lldb/tools/lldb-dap/DAPLog.h | 45 ++++++++++------ lldb/tools/lldb-dap/DAPSessionManager.cpp | 3 +- lldb/tools/lldb-dap/EventHelper.cpp | 14 ++--- lldb/tools/lldb-dap/EventHelper.h | 11 +--- .../lldb-dap/Handler/CompletionsHandler.cpp | 2 +- lldb/tools/lldb-dap/Transport.cpp | 10 ++-- lldb/tools/lldb-dap/Transport.h | 7 ++- lldb/tools/lldb-dap/tool/lldb-dap.cpp | 51 ++++++++++--------- lldb/unittests/DAP/CMakeLists.txt | 1 + lldb/unittests/DAP/DAPLogTest.cpp | 46 +++++++++++++++++ lldb/unittests/DAP/TestBase.cpp | 9 ++-- lldb/unittests/DAP/TestBase.h | 3 ++ 15 files changed, 162 insertions(+), 110 deletions(-) create mode 100644 lldb/unittests/DAP/DAPLogTest.cpp diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 6971bfea5c128..58c9922214583 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -122,7 +122,7 @@ static std::string capitalize(llvm::StringRef str) { llvm::StringRef DAP::debug_adapter_path = ""; -DAP::DAP(Log *log, const ReplMode default_repl_mode, +DAP::DAP(Log &log, const ReplMode default_repl_mode, std::vector<std::string> pre_init_commands, bool no_lldbinit, llvm::StringRef client_name, DAPTransport &transport, MainLoop &loop) : log(log), transport(transport), broadcaster("lldb-dap"), @@ -134,8 +134,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, RegisterRequests(); } -DAP::~DAP() = default; - void DAP::PopulateExceptionBreakpoints() { if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) { exception_breakpoints.emplace_back(*this, "cpp_catch", "C++ Catch", @@ -263,8 +261,7 @@ void DAP::SendJSON(const llvm::json::Value &json) { Message message; llvm::json::Path::Root root; if (!fromJSON(json, message, root)) { - DAP_LOG_ERROR(log, root.getError(), "({1}) encoding failed: {0}", - m_client_name); + DAP_LOG_ERROR(log, root.getError(), "encoding failed: {0}"); return; } Send(message); @@ -285,15 +282,13 @@ Id DAP::Send(const Message &message) { if (const protocol::Event *event = std::get_if<protocol::Event>(&msg)) { if (llvm::Error err = transport.Send(*event)) - DAP_LOG_ERROR(log, std::move(err), "({0}) sending event failed", - m_client_name); + DAP_LOG_ERROR(log, std::move(err), "sending event failed: {0}"); return event->seq; } if (const Request *req = std::get_if<Request>(&msg)) { if (llvm::Error err = transport.Send(*req)) - DAP_LOG_ERROR(log, std::move(err), "({0}) sending request failed", - m_client_name); + DAP_LOG_ERROR(log, std::move(err), "sending request failed: {0}"); return req->seq; } @@ -313,8 +308,7 @@ Id DAP::Send(const Message &message) { }) : transport.Send(*resp); if (err) - DAP_LOG_ERROR(log, std::move(err), "({0}) sending response failed", - m_client_name); + DAP_LOG_ERROR(log, std::move(err), "sending response failed: {0}"); return resp->seq; } @@ -857,8 +851,7 @@ bool DAP::HandleObject(const Message &M) { dispatcher.Set("error", llvm::Twine("unhandled-command:" + req->command).str()); - DAP_LOG(log, "({0}) error: unhandled command '{1}'", m_client_name, - req->command); + DAP_LOG(log, "error: unhandled command '{0}'", req->command); return false; // Fail } @@ -1004,35 +997,33 @@ void DAP::Received(const protocol::Request &request) { // effort attempt to interrupt. std::lock_guard<std::mutex> guard(m_active_request_mutex); if (m_active_request && cancel_args->requestId == m_active_request->seq) { - DAP_LOG(log, "({0}) interrupting inflight request (command={1} seq={2})", - m_client_name, m_active_request->command, m_active_request->seq); + DAP_LOG(log, "interrupting inflight request (command={0} seq={1})", + m_active_request->command, m_active_request->seq); debugger.RequestInterrupt(); } } std::lock_guard<std::mutex> guard(m_queue_mutex); - DAP_LOG(log, "({0}) queued (command={1} seq={2})", m_client_name, - request.command, request.seq); + DAP_LOG(log, "queued (command={0} seq={1})", request.command, request.seq); m_queue.push_back(request); m_queue_cv.notify_one(); } void DAP::Received(const protocol::Response &response) { std::lock_guard<std::mutex> guard(m_queue_mutex); - DAP_LOG(log, "({0}) queued (command={1} seq={2})", m_client_name, - response.command, response.request_seq); + DAP_LOG(log, "queued (command={0} seq={1})", response.command, + response.request_seq); m_queue.push_back(response); m_queue_cv.notify_one(); } void DAP::OnError(llvm::Error error) { - DAP_LOG_ERROR(log, std::move(error), "({1}) received error: {0}", - m_client_name); + DAP_LOG_ERROR(log, std::move(error), "transport error: {0}"); TerminateLoop(/*failed=*/true); } void DAP::OnClosed() { - DAP_LOG(log, "({0}) received EOF", m_client_name); + DAP_LOG(log, "transport closed"); TerminateLoop(); } @@ -1058,16 +1049,14 @@ void DAP::TransportHandler() { auto handle = transport.RegisterMessageHandler(m_loop, *this); if (!handle) { DAP_LOG_ERROR(log, handle.takeError(), - "({1}) registering message handler failed: {0}", - m_client_name); + "registering message handler failed: {0}"); std::lock_guard<std::mutex> guard(m_queue_mutex); m_error_occurred = true; return; } if (Status status = m_loop.Run(); status.Fail()) { - DAP_LOG_ERROR(log, status.takeError(), "({1}) MainLoop run failed: {0}", - m_client_name); + DAP_LOG_ERROR(log, status.takeError(), "MainLoop run failed: {0}"); std::lock_guard<std::mutex> guard(m_queue_mutex); m_error_occurred = true; return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index b5f2a57d9dc5f..231eb0b1cf2d1 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -23,7 +23,6 @@ #include "Transport.h" #include "Variables.h" #include "lldb/API/SBBroadcaster.h" -#include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBError.h" #include "lldb/API/SBFile.h" @@ -33,11 +32,9 @@ #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/Host/MainLoop.h" -#include "lldb/Utility/Status.h" #include "lldb/lldb-types.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -88,7 +85,7 @@ struct DAP final : public DAPTransport::MessageHandler { /// Path to the lldb-dap binary itself. static llvm::StringRef debug_adapter_path; - Log *log; + Log &log; DAPTransport &transport; lldb::SBFile in; OutputRedirector out; @@ -194,12 +191,12 @@ struct DAP final : public DAPTransport::MessageHandler { /// Transport for this debug session. /// \param[in] loop /// Main loop associated with this instance. - DAP(Log *log, const ReplMode default_repl_mode, + DAP(Log &log, const ReplMode default_repl_mode, std::vector<std::string> pre_init_commands, bool no_lldbinit, llvm::StringRef client_name, DAPTransport &transport, lldb_private::MainLoop &loop); - ~DAP(); + ~DAP() override = default; /// DAP is not copyable. /// @{ diff --git a/lldb/tools/lldb-dap/DAPLog.cpp b/lldb/tools/lldb-dap/DAPLog.cpp index f66784e197518..8907d02d3e9f4 100644 --- a/lldb/tools/lldb-dap/DAPLog.cpp +++ b/lldb/tools/lldb-dap/DAPLog.cpp @@ -8,22 +8,32 @@ #include "DAPLog.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include <chrono> #include <mutex> -#include <system_error> using namespace llvm; namespace lldb_dap { -Log::Log(StringRef filename, std::error_code &EC) : m_stream(filename, EC) {} +void Log::Emit(StringRef message) { + std::lock_guard<Log::Mutex> lock(m_mutex); + std::chrono::duration<double> now{ + std::chrono::system_clock::now().time_since_epoch()}; + m_stream << formatv("{0:f9} ", now.count()).str() << m_prefix << message + << "\n"; + m_stream.flush(); +} -void Log::WriteMessage(StringRef message) { - std::lock_guard<std::mutex> lock(m_mutex); +void Log::Emit(StringRef file, size_t line, StringRef message) { + std::lock_guard<Log::Mutex> lock(m_mutex); std::chrono::duration<double> now{ std::chrono::system_clock::now().time_since_epoch()}; - m_stream << formatv("{0:f9} ", now.count()).str() << message << "\n"; + m_stream << formatv("{0:f9} {1}:{2} ", now.count(), sys::path::filename(file), + line) + .str() + << m_prefix << message << "\n"; m_stream.flush(); } diff --git a/lldb/tools/lldb-dap/DAPLog.h b/lldb/tools/lldb-dap/DAPLog.h index 484001a9b1628..05cca7d4669a9 100644 --- a/lldb/tools/lldb-dap/DAPLog.h +++ b/lldb/tools/lldb-dap/DAPLog.h @@ -11,33 +11,28 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" -#include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include <mutex> #include <string> -#include <system_error> // Write a message to log, if logging is enabled. #define DAP_LOG(log, ...) \ do { \ - ::lldb_dap::Log *log_private = (log); \ - if (log_private) { \ - log_private->WriteMessage(::llvm::formatv(__VA_ARGS__).str()); \ - } \ + ::lldb_dap::Log &log_private = (log); \ + log_private.Emit(__FILE__, __LINE__, ::llvm::formatv(__VA_ARGS__).str()); \ } while (0) // Write message to log, if error is set. In the log message refer to the error // with {0}. Error is cleared regardless of whether logging is enabled. #define DAP_LOG_ERROR(log, error, ...) \ do { \ - ::lldb_dap::Log *log_private = (log); \ + ::lldb_dap::Log &log_private = (log); \ ::llvm::Error error_private = (error); \ - if (log_private && error_private) { \ - log_private->WriteMessage( \ + if (error_private) \ + log_private.Emit( \ + __FILE__, __LINE__, \ ::lldb_dap::FormatError(::std::move(error_private), __VA_ARGS__)); \ - } else \ - ::llvm::consumeError(::std::move(error_private)); \ } while (0) namespace lldb_dap { @@ -46,14 +41,32 @@ namespace lldb_dap { /// `DAP_LOG_ERROR` helpers. class Log final { public: - /// Creates a log file with the given filename. - Log(llvm::StringRef filename, std::error_code &EC); + using Mutex = std::recursive_mutex; - void WriteMessage(llvm::StringRef message); + Log(llvm::raw_ostream &stream, Mutex &mutex) + : m_stream(stream), m_mutex(mutex) {} + Log(llvm::StringRef prefix, const Log &log) + : m_prefix(prefix), m_stream(log.m_stream), m_mutex(log.m_mutex) {} + + /// Retuns a new Log instance with the associated prefix for all messages. + inline Log WithPrefix(llvm::StringRef prefix) const { + std::string full_prefix = + m_prefix.empty() ? prefix.str() : m_prefix + " " + prefix.str(); + full_prefix += " "; + return Log(full_prefix, *this); + } + + /// Emit writes a message to the underlying stream. + void Emit(llvm::StringRef message); + + /// Emit writes a message to the underlying stream, including the file and + /// line the message originated from. + void Emit(llvm::StringRef file, size_t line, llvm::StringRef message); private: - std::mutex m_mutex; - llvm::raw_fd_ostream m_stream; + std::string m_prefix; + llvm::raw_ostream &m_stream; + Mutex &m_mutex; }; template <typename... Args> diff --git a/lldb/tools/lldb-dap/DAPSessionManager.cpp b/lldb/tools/lldb-dap/DAPSessionManager.cpp index d5440ffd64597..3fbdc26fc4cda 100644 --- a/lldb/tools/lldb-dap/DAPSessionManager.cpp +++ b/lldb/tools/lldb-dap/DAPSessionManager.cpp @@ -110,7 +110,8 @@ DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger, auto new_thread_sp = std::make_shared<ManagedEventThread>( requesting_dap->broadcaster, std::thread(EventThread, debugger, requesting_dap->broadcaster, - requesting_dap->m_client_name, requesting_dap->log)); + requesting_dap->m_client_name, + std::ref(requesting_dap->log))); m_debugger_event_threads[debugger_id] = new_thread_sp; return new_thread_sp; } diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index bdb6bb55fe168..5fd13925be209 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -324,8 +324,8 @@ void SendMemoryEvent(DAP &dap, lldb::SBValue variable) { // the original DAP::Handle*Event pattern while supporting multi-session // debugging. -void HandleProcessEvent(const lldb::SBEvent &event, bool &process_exited, - Log *log) { +static void HandleProcessEvent(const lldb::SBEvent &event, bool &process_exited, + Log &log) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); // Find the DAP instance that owns this process's target. @@ -393,7 +393,7 @@ void HandleProcessEvent(const lldb::SBEvent &event, bool &process_exited, } } -void HandleTargetEvent(const lldb::SBEvent &event, Log *log) { +static void HandleTargetEvent(const lldb::SBEvent &event, Log &log) { lldb::SBTarget target = lldb::SBTarget::GetTargetFromEvent(event); // Find the DAP instance that owns this target. @@ -480,7 +480,7 @@ void HandleTargetEvent(const lldb::SBEvent &event, Log *log) { } } -void HandleBreakpointEvent(const lldb::SBEvent &event, Log *log) { +void HandleBreakpointEvent(const lldb::SBEvent &event, Log &log) { const uint32_t event_mask = event.GetType(); if (!(event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged)) return; @@ -529,7 +529,7 @@ void HandleBreakpointEvent(const lldb::SBEvent &event, Log *log) { } } -void HandleThreadEvent(const lldb::SBEvent &event, Log *log) { +static void HandleThreadEvent(const lldb::SBEvent &event, Log &log) { uint32_t event_type = event.GetType(); if (!(event_type & lldb::SBThread::eBroadcastBitStackChanged)) @@ -550,7 +550,7 @@ void HandleThreadEvent(const lldb::SBEvent &event, Log *log) { thread.GetThreadID()); } -void HandleDiagnosticEvent(const lldb::SBEvent &event, Log *log) { +static void HandleDiagnosticEvent(const lldb::SBEvent &event, Log &log) { // Global debugger events - send to all DAP instances. std::vector<DAP *> active_instances = DAPSessionManager::GetInstance().GetActiveSessions(); @@ -588,7 +588,7 @@ void HandleDiagnosticEvent(const lldb::SBEvent &event, Log *log) { // them prevent multiple threads from writing simultaneously so no locking // is required. void EventThread(lldb::SBDebugger debugger, lldb::SBBroadcaster broadcaster, - llvm::StringRef client_name, Log *log) { + llvm::StringRef client_name, Log &log) { llvm::set_thread_name("lldb.DAP.client." + client_name + ".event_handler"); lldb::SBListener listener = debugger.GetListener(); broadcaster.AddListener(listener, eBroadcastBitStopEventThread); diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h index 3beba2629b2e3..b46d5aef3581f 100644 --- a/lldb/tools/lldb-dap/EventHelper.h +++ b/lldb/tools/lldb-dap/EventHelper.h @@ -51,16 +51,7 @@ void SendMemoryEvent(DAP &dap, lldb::SBValue variable); /// \param client_name The client name for thread naming/logging purposes. /// \param log The log instance for logging. void EventThread(lldb::SBDebugger debugger, lldb::SBBroadcaster broadcaster, - llvm::StringRef client_name, Log *log); - -/// Event handler functions called by EventThread. -/// These handlers extract the necessary objects from events and find the -/// appropriate DAP instance to handle them. -void HandleProcessEvent(const lldb::SBEvent &event, bool &done, Log *log); -void HandleTargetEvent(const lldb::SBEvent &event, Log *log); -void HandleBreakpointEvent(const lldb::SBEvent &event, Log *log); -void HandleThreadEvent(const lldb::SBEvent &event, Log *log); -void HandleDiagnosticEvent(const lldb::SBEvent &event, Log *log); + llvm::StringRef client_name, Log &log); } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp index de9a15dcb73f4..27ac01a37e4de 100644 --- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "DAP.h" -#include "JSONUtils.h" #include "Protocol/ProtocolRequests.h" #include "Protocol/ProtocolTypes.h" #include "RequestHandler.h" #include "lldb/API/SBStringList.h" +#include "lldb/SBCommandInterpreter.h" using namespace llvm; using namespace lldb_dap; diff --git a/lldb/tools/lldb-dap/Transport.cpp b/lldb/tools/lldb-dap/Transport.cpp index 8f71f88cae1f7..b3512385d6579 100644 --- a/lldb/tools/lldb-dap/Transport.cpp +++ b/lldb/tools/lldb-dap/Transport.cpp @@ -17,13 +17,13 @@ using namespace lldb_private; namespace lldb_dap { -Transport::Transport(llvm::StringRef client_name, lldb_dap::Log *log, - lldb::IOObjectSP input, lldb::IOObjectSP output) - : HTTPDelimitedJSONTransport(input, output), m_client_name(client_name), - m_log(log) {} +Transport::Transport(lldb_dap::Log &log, lldb::IOObjectSP input, + lldb::IOObjectSP output) + : HTTPDelimitedJSONTransport(input, output), m_log(log) {} void Transport::Log(llvm::StringRef message) { - DAP_LOG(m_log, "({0}) {1}", m_client_name, message); + // Emit the message directly, since this log was forwarded. + m_log.Emit(message); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Transport.h b/lldb/tools/lldb-dap/Transport.h index 58c48c133f9cb..b20a93475d2dd 100644 --- a/lldb/tools/lldb-dap/Transport.h +++ b/lldb/tools/lldb-dap/Transport.h @@ -35,15 +35,14 @@ class Transport final : public lldb_private::transport::HTTPDelimitedJSONTransport< ProtocolDescriptor> { public: - Transport(llvm::StringRef client_name, lldb_dap::Log *log, - lldb::IOObjectSP input, lldb::IOObjectSP output); + Transport(lldb_dap::Log &log, lldb::IOObjectSP input, + lldb::IOObjectSP output); virtual ~Transport() = default; void Log(llvm::StringRef message) override; private: - llvm::StringRef m_client_name; - lldb_dap::Log *m_log; + lldb_dap::Log &m_log; }; } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index 27516b2a25678..a427ecdef5080 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -11,6 +11,7 @@ #include "DAPLog.h" #include "EventHelper.h" #include "Handler/RequestHandler.h" +#include "Handler/ResponseHandler.h" #include "RunInTerminal.h" #include "Transport.h" #include "lldb/API/SBDebugger.h" @@ -50,9 +51,7 @@ #include <cstddef> #include <cstdio> #include <cstdlib> -#include <exception> #include <fcntl.h> -#include <map> #include <memory> #include <mutex> #include <string> @@ -410,7 +409,7 @@ validateConnection(llvm::StringRef conn) { } static llvm::Error serveConnection( - const Socket::SocketProtocol &protocol, const std::string &name, Log *log, + const Socket::SocketProtocol &protocol, const std::string &name, Log &log, const ReplMode default_repl_mode, const std::vector<std::string> &pre_init_commands, bool no_lldbinit, std::optional<std::chrono::seconds> connection_timeout_seconds) { @@ -446,7 +445,7 @@ static llvm::Error serveConnection( connection_timeout_seconds.value()); std::condition_variable dap_sessions_condition; unsigned int clientCount = 0; - auto handle = listener->Accept(g_loop, [=, &clientCount]( + auto handle = listener->Accept(g_loop, [=, &log, &clientCount]( std::unique_ptr<Socket> sock) { // Reset the keep alive timer, because we won't be killing the server // while this connection is being served. @@ -454,17 +453,18 @@ static llvm::Error serveConnection( ResetConnectionTimeout(g_connection_timeout_mutex, g_connection_timeout_time_point); std::string client_name = llvm::formatv("client_{0}", clientCount++).str(); - DAP_LOG(log, "({0}) client connected", client_name); + Log client_log = log.WithPrefix("(" + client_name + ")"); + DAP_LOG(client_log, "client connected"); lldb::IOObjectSP io(std::move(sock)); // Move the client into a background thread to unblock accepting the next // client. - std::thread client([=]() { + std::thread client([=, &client_log]() { llvm::set_thread_name(client_name + ".runloop"); MainLoop loop; - Transport transport(client_name, log, io, io); - DAP dap(log, default_repl_mode, pre_init_commands, no_lldbinit, + Transport transport(client_log, io, io); + DAP dap(client_log, default_repl_mode, pre_init_commands, no_lldbinit, client_name, transport, loop); if (auto Err = dap.ConfigureIO()) { @@ -482,7 +482,7 @@ static llvm::Error serveConnection( ") error: "); } - DAP_LOG(log, "({0}) client disconnected", client_name); + DAP_LOG(client_log, "client disconnected"); // Unregister the DAP session from the global manager. DAPSessionManager::GetInstance().UnregisterSession(&loop); // Start the countdown to kill the server at the end of each connection. @@ -503,9 +503,7 @@ static llvm::Error serveConnection( return status.takeError(); } - DAP_LOG( - log, - "lldb-dap server shutdown requested, disconnecting remaining clients..."); + DAP_LOG(log, "server shutting down, disconnecting remaining clients"); // Disconnect all active sessions using the global manager. DAPSessionManager::GetInstance().DisconnectAllSessions(); @@ -642,17 +640,19 @@ int main(int argc, char *argv[]) { } #endif - std::unique_ptr<Log> log = nullptr; - const char *log_file_path = getenv("LLDBDAP_LOG"); - if (log_file_path) { - std::error_code EC; - log = std::make_unique<Log>(log_file_path, EC); - if (EC) { - llvm::logAllUnhandledErrors(llvm::errorCodeToError(EC), llvm::errs(), - "Failed to create log file: "); + std::unique_ptr<llvm::raw_ostream> log_os; + if (const char *log_file_path = getenv("LLDBDAP_LOG"); log_file_path) { + int FD; + if (std::error_code EC = + llvm::sys::fs::openFileForWrite(log_file_path, FD)) { + llvm::errs() << "Failed to open log file: " << log_file_path << ": " + << EC.message() << "\n"; return EXIT_FAILURE; } + log_os = std::make_unique<llvm::raw_fd_ostream>(FD, /*shouldClose=*/true); } + Log::Mutex mutex; + Log log(log_os ? *log_os.get() : llvm::nulls(), mutex); // Initialize LLDB first before we do anything. lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling(); @@ -666,7 +666,7 @@ int main(int argc, char *argv[]) { // Create a memory monitor. This can return nullptr if the host platform is // not supported. std::unique_ptr<lldb_private::MemoryMonitor> memory_monitor = - lldb_private::MemoryMonitor::Create([log = log.get()]() { + lldb_private::MemoryMonitor::Create([&log]() { DAP_LOG(log, "memory pressure detected"); lldb::SBDebugger::MemoryPressureDetected(); }); @@ -700,7 +700,7 @@ int main(int argc, char *argv[]) { Socket::SocketProtocol protocol; std::string name; std::tie(protocol, name) = *maybeProtoclAndName; - if (auto Err = serveConnection(protocol, name, log.get(), default_repl_mode, + if (auto Err = serveConnection(protocol, name, log, default_repl_mode, pre_init_commands, no_lldbinit, connection_timeout_seconds)) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), @@ -737,8 +737,9 @@ int main(int argc, char *argv[]) { constexpr llvm::StringLiteral client_name = "stdio"; MainLoop loop; - Transport transport(client_name, log.get(), input, output); - DAP dap(log.get(), default_repl_mode, pre_init_commands, no_lldbinit, + Log client_log = log.WithPrefix("(stdio)"); + Transport transport(client_log, input, output); + DAP dap(client_log, default_repl_mode, pre_init_commands, no_lldbinit, client_name, transport, loop); // stdout/stderr redirection to the IDE's console @@ -757,7 +758,7 @@ int main(int argc, char *argv[]) { redirection_test(); if (auto Err = dap.Loop()) { - DAP_LOG(log.get(), "({0}) DAP session error: {1}", client_name, + DAP_LOG(client_log, "DAP session error: {0}", llvm::toStringWithoutConsuming(Err)); llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "DAP session error: "); diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt index 0f8e9db2fab31..9fef37e00ed5d 100644 --- a/lldb/unittests/DAP/CMakeLists.txt +++ b/lldb/unittests/DAP/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_unittest(DAPTests ClientLauncherTest.cpp DAPErrorTest.cpp + DAPLogTest.cpp DAPSessionManagerTest.cpp DAPTest.cpp DAPTypesTest.cpp diff --git a/lldb/unittests/DAP/DAPLogTest.cpp b/lldb/unittests/DAP/DAPLogTest.cpp new file mode 100644 index 0000000000000..89fe5058c5f8e --- /dev/null +++ b/lldb/unittests/DAP/DAPLogTest.cpp @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// 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 "DAPLog.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace lldb_dap; +using namespace llvm; + +static llvm::StringRef last_line(llvm::StringRef str) { + size_t index = str.find_last_of('\n', str.size() - 1); + if (index == llvm::StringRef::npos) + return str; + return str.substr(index + 1); +} + +TEST(DAPLog, Emit) { + Log::Mutex mux; + std::string outs; + raw_string_ostream os(outs); + Log log(os, mux); + Log inner_log = log.WithPrefix("my_prefix:"); + + // Line includes a timestamp, only check the suffix. + log.Emit("Hi"); + EXPECT_TRUE(last_line(outs).ends_with(" Hi\n")) << outs; + + inner_log.Emit("foobar"); + EXPECT_TRUE(last_line(outs).ends_with(" my_prefix: foobar\n")) << outs; + + log.Emit("file.cpp", 42, "Hello from a file/line."); + EXPECT_TRUE( + last_line(outs).ends_with(" file.cpp:42 Hello from a file/line.\n")) + << outs; + + inner_log.Emit("file.cpp", 42, "Hello from a file/line."); + EXPECT_TRUE(last_line(outs).ends_with( + " file.cpp:42 my_prefix: Hello from a file/line.\n")) + << outs; +} \ No newline at end of file diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp index f4dde9559e9d3..2b8c6994e97bc 100644 --- a/lldb/unittests/DAP/TestBase.cpp +++ b/lldb/unittests/DAP/TestBase.cpp @@ -7,7 +7,10 @@ //===----------------------------------------------------------------------===// #include "TestBase.h" +#include "DAP.h" #include "DAPLog.h" +#include "Handler/RequestHandler.h" +#include "Handler/ResponseHandler.h" #include "TestingSupport/TestUtilities.h" #include "lldb/API/SBDefines.h" #include "lldb/API/SBStructuredData.h" @@ -19,7 +22,6 @@ #include "gtest/gtest.h" #include <cstdio> #include <memory> -#include <system_error> using namespace llvm; using namespace lldb; @@ -35,10 +37,9 @@ using lldb_private::Pipe; void TransportBase::SetUp() { std::tie(to_client, to_server) = TestDAPTransport::createPair(); - std::error_code EC; - log = std::make_unique<Log>("-", EC); + log = std::make_unique<Log>(llvm::outs(), log_mutex); dap = std::make_unique<DAP>( - /*log=*/log.get(), + /*log=*/*log.get(), /*default_repl_mode=*/ReplMode::Auto, /*pre_init_commands=*/std::vector<std::string>(), /*no_lldbinit=*/false, diff --git a/lldb/unittests/DAP/TestBase.h b/lldb/unittests/DAP/TestBase.h index c32f3a769c737..f1c7e6b989729 100644 --- a/lldb/unittests/DAP/TestBase.h +++ b/lldb/unittests/DAP/TestBase.h @@ -8,6 +8,8 @@ #include "DAP.h" #include "DAPLog.h" +#include "Handler/RequestHandler.h" +#include "Handler/ResponseHandler.h" #include "Protocol/ProtocolBase.h" #include "TestingSupport/Host/JSONTransportTestUtilities.h" #include "TestingSupport/SubsystemRAII.h" @@ -60,6 +62,7 @@ class TransportBase : public testing::Test { lldb_private::MainLoop::ReadHandleUP handles[2]; std::unique_ptr<lldb_dap::Log> log; + lldb_dap::Log::Mutex log_mutex; std::unique_ptr<TestDAPTransport> to_client; MockMessageHandler<lldb_dap::ProtocolDescriptor> client; >From 0c2054b9d49243183233d39b49ec33bd701de239 Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Thu, 4 Dec 2025 15:56:47 -0800 Subject: [PATCH 2/5] Addressing reviewer feedback and fixing client_log having the incorrect scope when lldb-dap is running in server mode on accept. --- lldb/tools/lldb-dap/DAP.h | 1 + lldb/tools/lldb-dap/DAPLog.h | 4 +-- lldb/tools/lldb-dap/EventHelper.cpp | 2 +- .../lldb-dap/Handler/CompletionsHandler.cpp | 2 +- lldb/tools/lldb-dap/tool/lldb-dap.cpp | 11 ++++---- lldb/unittests/DAP/DAPLogTest.cpp | 27 ++++++++++++------- lldb/unittests/DAP/TestBase.cpp | 2 +- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 231eb0b1cf2d1..01139221ea37f 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -23,6 +23,7 @@ #include "Transport.h" #include "Variables.h" #include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBError.h" #include "lldb/API/SBFile.h" diff --git a/lldb/tools/lldb-dap/DAPLog.h b/lldb/tools/lldb-dap/DAPLog.h index 05cca7d4669a9..c2188c6b73278 100644 --- a/lldb/tools/lldb-dap/DAPLog.h +++ b/lldb/tools/lldb-dap/DAPLog.h @@ -41,7 +41,7 @@ namespace lldb_dap { /// `DAP_LOG_ERROR` helpers. class Log final { public: - using Mutex = std::recursive_mutex; + using Mutex = std::mutex; Log(llvm::raw_ostream &stream, Mutex &mutex) : m_stream(stream), m_mutex(mutex) {} @@ -51,7 +51,7 @@ class Log final { /// Retuns a new Log instance with the associated prefix for all messages. inline Log WithPrefix(llvm::StringRef prefix) const { std::string full_prefix = - m_prefix.empty() ? prefix.str() : m_prefix + " " + prefix.str(); + m_prefix.empty() ? prefix.str() : m_prefix + prefix.str(); full_prefix += " "; return Log(full_prefix, *this); } diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 5fd13925be209..01d4547e2d228 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -480,7 +480,7 @@ static void HandleTargetEvent(const lldb::SBEvent &event, Log &log) { } } -void HandleBreakpointEvent(const lldb::SBEvent &event, Log &log) { +static void HandleBreakpointEvent(const lldb::SBEvent &event, Log &log) { const uint32_t event_mask = event.GetType(); if (!(event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged)) return; diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp index 27ac01a37e4de..de9a15dcb73f4 100644 --- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "DAP.h" +#include "JSONUtils.h" #include "Protocol/ProtocolRequests.h" #include "Protocol/ProtocolTypes.h" #include "RequestHandler.h" #include "lldb/API/SBStringList.h" -#include "lldb/SBCommandInterpreter.h" using namespace llvm; using namespace lldb_dap; diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index a427ecdef5080..e9b0c4993309d 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -453,15 +453,16 @@ static llvm::Error serveConnection( ResetConnectionTimeout(g_connection_timeout_mutex, g_connection_timeout_time_point); std::string client_name = llvm::formatv("client_{0}", clientCount++).str(); - Log client_log = log.WithPrefix("(" + client_name + ")"); - DAP_LOG(client_log, "client connected"); - lldb::IOObjectSP io(std::move(sock)); // Move the client into a background thread to unblock accepting the next // client. - std::thread client([=, &client_log]() { + std::thread client([=, &log]() { llvm::set_thread_name(client_name + ".runloop"); + + Log client_log = log.WithPrefix("(" + client_name + ")"); + DAP_LOG(client_log, "client connected"); + MainLoop loop; Transport transport(client_log, io, io); DAP dap(client_log, default_repl_mode, pre_init_commands, no_lldbinit, @@ -652,7 +653,7 @@ int main(int argc, char *argv[]) { log_os = std::make_unique<llvm::raw_fd_ostream>(FD, /*shouldClose=*/true); } Log::Mutex mutex; - Log log(log_os ? *log_os.get() : llvm::nulls(), mutex); + Log log(log_os ? *log_os : llvm::nulls(), mutex); // Initialize LLDB first before we do anything. lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling(); diff --git a/lldb/unittests/DAP/DAPLogTest.cpp b/lldb/unittests/DAP/DAPLogTest.cpp index 89fe5058c5f8e..c101b9ab7a59c 100644 --- a/lldb/unittests/DAP/DAPLogTest.cpp +++ b/lldb/unittests/DAP/DAPLogTest.cpp @@ -8,10 +8,12 @@ #include "DAPLog.h" #include "llvm/Support/raw_ostream.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using namespace lldb_dap; using namespace llvm; +using namespace testing; static llvm::StringRef last_line(llvm::StringRef str) { size_t index = str.find_last_of('\n', str.size() - 1); @@ -20,6 +22,8 @@ static llvm::StringRef last_line(llvm::StringRef str) { return str.substr(index + 1); } +#define TIMESTAMP_PATTERN "[0-9]+\\.[0-9]+ " + TEST(DAPLog, Emit) { Log::Mutex mux; std::string outs; @@ -27,20 +31,23 @@ TEST(DAPLog, Emit) { Log log(os, mux); Log inner_log = log.WithPrefix("my_prefix:"); - // Line includes a timestamp, only check the suffix. log.Emit("Hi"); - EXPECT_TRUE(last_line(outs).ends_with(" Hi\n")) << outs; + EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "Hi\n")); inner_log.Emit("foobar"); - EXPECT_TRUE(last_line(outs).ends_with(" my_prefix: foobar\n")) << outs; + EXPECT_THAT(last_line(outs), + MatchesRegex(TIMESTAMP_PATTERN "my_prefix: foobar\n")); log.Emit("file.cpp", 42, "Hello from a file/line."); - EXPECT_TRUE( - last_line(outs).ends_with(" file.cpp:42 Hello from a file/line.\n")) - << outs; + EXPECT_THAT( + last_line(outs), + MatchesRegex(TIMESTAMP_PATTERN "file.cpp:42 Hello from a file/line.\n")); inner_log.Emit("file.cpp", 42, "Hello from a file/line."); - EXPECT_TRUE(last_line(outs).ends_with( - " file.cpp:42 my_prefix: Hello from a file/line.\n")) - << outs; -} \ No newline at end of file + EXPECT_THAT(last_line(outs), + MatchesRegex(TIMESTAMP_PATTERN + "file.cpp:42 my_prefix: Hello from a file/line.\n")); + + log.WithPrefix("a").WithPrefix("b").WithPrefix("c").Emit("msg"); + EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "a b c msg\n")); +} diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp index 2b8c6994e97bc..e57963e37f68a 100644 --- a/lldb/unittests/DAP/TestBase.cpp +++ b/lldb/unittests/DAP/TestBase.cpp @@ -39,7 +39,7 @@ void TransportBase::SetUp() { log = std::make_unique<Log>(llvm::outs(), log_mutex); dap = std::make_unique<DAP>( - /*log=*/*log.get(), + /*log=*/*log, /*default_repl_mode=*/ReplMode::Auto, /*pre_init_commands=*/std::vector<std::string>(), /*no_lldbinit=*/false, >From cc40fb3fb3ee42f38b71daefe348f4144c69ed1c Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Thu, 4 Dec 2025 17:06:49 -0800 Subject: [PATCH 3/5] Deduping the emit functions, one now calls the other. --- lldb/tools/lldb-dap/DAPLog.cpp | 21 ++++++++------------- lldb/tools/lldb-dap/DAPLog.h | 8 ++++---- lldb/unittests/DAP/DAPLogTest.cpp | 4 ++-- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lldb/tools/lldb-dap/DAPLog.cpp b/lldb/tools/lldb-dap/DAPLog.cpp index 8907d02d3e9f4..8cd2bed8f4e4e 100644 --- a/lldb/tools/lldb-dap/DAPLog.cpp +++ b/lldb/tools/lldb-dap/DAPLog.cpp @@ -17,23 +17,18 @@ using namespace llvm; namespace lldb_dap { -void Log::Emit(StringRef message) { - std::lock_guard<Log::Mutex> lock(m_mutex); - std::chrono::duration<double> now{ - std::chrono::system_clock::now().time_since_epoch()}; - m_stream << formatv("{0:f9} ", now.count()).str() << m_prefix << message - << "\n"; - m_stream.flush(); -} +void Log::Emit(StringRef message) { Emit(message, "", 0); } -void Log::Emit(StringRef file, size_t line, StringRef message) { +void Log::Emit(StringRef message, StringRef file, size_t line) { std::lock_guard<Log::Mutex> lock(m_mutex); std::chrono::duration<double> now{ std::chrono::system_clock::now().time_since_epoch()}; - m_stream << formatv("{0:f9} {1}:{2} ", now.count(), sys::path::filename(file), - line) - .str() - << m_prefix << message << "\n"; + m_stream << formatv("{0:f9}", now.count()) << " "; + if (!file.empty()) + m_stream << sys::path::filename(file) << ":" << line << " "; + if (!m_prefix.empty()) + m_stream << m_prefix; + m_stream << message << "\n"; m_stream.flush(); } diff --git a/lldb/tools/lldb-dap/DAPLog.h b/lldb/tools/lldb-dap/DAPLog.h index c2188c6b73278..2170e6621d691 100644 --- a/lldb/tools/lldb-dap/DAPLog.h +++ b/lldb/tools/lldb-dap/DAPLog.h @@ -20,7 +20,7 @@ #define DAP_LOG(log, ...) \ do { \ ::lldb_dap::Log &log_private = (log); \ - log_private.Emit(__FILE__, __LINE__, ::llvm::formatv(__VA_ARGS__).str()); \ + log_private.Emit(::llvm::formatv(__VA_ARGS__).str(), __FILE__, __LINE__); \ } while (0) // Write message to log, if error is set. In the log message refer to the error @@ -31,8 +31,8 @@ ::llvm::Error error_private = (error); \ if (error_private) \ log_private.Emit( \ - __FILE__, __LINE__, \ - ::lldb_dap::FormatError(::std::move(error_private), __VA_ARGS__)); \ + ::lldb_dap::FormatError(::std::move(error_private), __VA_ARGS__), \ + __FILE__, __LINE__); \ } while (0) namespace lldb_dap { @@ -61,7 +61,7 @@ class Log final { /// Emit writes a message to the underlying stream, including the file and /// line the message originated from. - void Emit(llvm::StringRef file, size_t line, llvm::StringRef message); + void Emit(llvm::StringRef message, llvm::StringRef file, size_t line); private: std::string m_prefix; diff --git a/lldb/unittests/DAP/DAPLogTest.cpp b/lldb/unittests/DAP/DAPLogTest.cpp index c101b9ab7a59c..59ffc27a6a764 100644 --- a/lldb/unittests/DAP/DAPLogTest.cpp +++ b/lldb/unittests/DAP/DAPLogTest.cpp @@ -38,12 +38,12 @@ TEST(DAPLog, Emit) { EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "my_prefix: foobar\n")); - log.Emit("file.cpp", 42, "Hello from a file/line."); + log.Emit("Hello from a file/line.", "file.cpp", 42); EXPECT_THAT( last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "file.cpp:42 Hello from a file/line.\n")); - inner_log.Emit("file.cpp", 42, "Hello from a file/line."); + inner_log.Emit("Hello from a file/line.", "file.cpp", 42); EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "file.cpp:42 my_prefix: Hello from a file/line.\n")); >From b83c816d383adc7956f3b1ff13b88cca7edb0f84 Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Fri, 5 Dec 2025 13:48:10 -0800 Subject: [PATCH 4/5] Addressing reviewer feedback. --- lldb/tools/lldb-dap/DAPLog.cpp | 1 - lldb/tools/lldb-dap/tool/lldb-dap.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lldb/tools/lldb-dap/DAPLog.cpp b/lldb/tools/lldb-dap/DAPLog.cpp index 455fe93a442b5..47d7f783009b4 100644 --- a/lldb/tools/lldb-dap/DAPLog.cpp +++ b/lldb/tools/lldb-dap/DAPLog.cpp @@ -22,7 +22,6 @@ void Log::Emit(StringRef message) { Emit(message, "", 0); } void Log::Emit(StringRef message, StringRef file, size_t line) { std::lock_guard<Log::Mutex> lock(m_mutex); - std::lock_guard<std::mutex> lock(m_mutex); const llvm::sys::TimePoint<> time = std::chrono::system_clock::now(); m_stream << formatv("[{0:%H:%M:%S.%L}]", time) << " "; if (!file.empty()) diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index e9b0c4993309d..6d4eaf18de7ea 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -409,7 +409,7 @@ validateConnection(llvm::StringRef conn) { } static llvm::Error serveConnection( - const Socket::SocketProtocol &protocol, const std::string &name, Log &log, + const Socket::SocketProtocol &protocol, llvm::StringRef name, Log &log, const ReplMode default_repl_mode, const std::vector<std::string> &pre_init_commands, bool no_lldbinit, std::optional<std::chrono::seconds> connection_timeout_seconds) { >From 581a49f81b897695f8750832664180d29bd96b5a Mon Sep 17 00:00:00 2001 From: John Harrison <[email protected]> Date: Fri, 5 Dec 2025 13:52:45 -0800 Subject: [PATCH 5/5] Applying clang-format. --- lldb/tools/lldb-dap/DAPLog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/DAPLog.cpp b/lldb/tools/lldb-dap/DAPLog.cpp index 47d7f783009b4..f3c804008e4b5 100644 --- a/lldb/tools/lldb-dap/DAPLog.cpp +++ b/lldb/tools/lldb-dap/DAPLog.cpp @@ -8,8 +8,8 @@ #include "DAPLog.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/Path.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include <chrono> #include <mutex> _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
