[Lldb-commits] [lldb] 8d57635 - [lldb-dap] Improving logging support and formatting. (#170731)

via lldb-commits lldb-commits at lists.llvm.org
Fri Dec 5 15:43:43 PST 2025


Author: John Harrison
Date: 2025-12-05T15:43:40-08:00
New Revision: 8d57635c59cbe535258dc3b7c69daf6b68b9e2a2

URL: https://github.com/llvm/llvm-project/commit/8d57635c59cbe535258dc3b7c69daf6b68b9e2a2
DIFF: https://github.com/llvm/llvm-project/commit/8d57635c59cbe535258dc3b7c69daf6b68b9e2a2.diff

LOG: [lldb-dap] Improving logging support and formatting. (#170731)

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"}
```

Added: 
    lldb/unittests/DAP/DAPLogTest.cpp

Modified: 
    lldb/tools/lldb-dap/DAP.cpp
    lldb/tools/lldb-dap/DAP.h
    lldb/tools/lldb-dap/DAPLog.cpp
    lldb/tools/lldb-dap/DAPLog.h
    lldb/tools/lldb-dap/DAPSessionManager.cpp
    lldb/tools/lldb-dap/EventHelper.cpp
    lldb/tools/lldb-dap/EventHelper.h
    lldb/tools/lldb-dap/Transport.cpp
    lldb/tools/lldb-dap/Transport.h
    lldb/tools/lldb-dap/tool/lldb-dap.cpp
    lldb/unittests/DAP/CMakeLists.txt
    lldb/unittests/DAP/TestBase.cpp
    lldb/unittests/DAP/TestBase.h

Removed: 
    


################################################################################
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..01139221ea37f 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -33,11 +33,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 +86,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 +192,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 34d26128efd3f..f3c804008e4b5 100644
--- a/lldb/tools/lldb-dap/DAPLog.cpp
+++ b/lldb/tools/lldb-dap/DAPLog.cpp
@@ -9,21 +9,26 @@
 #include "DAPLog.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Chrono.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) { Emit(message, "", 0); }
 
-void Log::WriteMessage(StringRef message) {
-  std::lock_guard<std::mutex> lock(m_mutex);
+void Log::Emit(StringRef message, StringRef file, size_t line) {
+  std::lock_guard<Log::Mutex> lock(m_mutex);
   const llvm::sys::TimePoint<> time = std::chrono::system_clock::now();
-  m_stream << formatv("[{0:%H:%M:%S.%L}] ", time) << message << '\n';
+  m_stream << formatv("[{0:%H:%M:%S.%L}]", time) << " ";
+  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 484001a9b1628..2170e6621d691 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(::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
 // 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(                                               \
-          ::lldb_dap::FormatError(::std::move(error_private), __VA_ARGS__));   \
-    } else                                                                     \
-      ::llvm::consumeError(::std::move(error_private));                        \
+    if (error_private)                                                         \
+      log_private.Emit(                                                        \
+          ::lldb_dap::FormatError(::std::move(error_private), __VA_ARGS__),    \
+          __FILE__, __LINE__);                                                 \
   } 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::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 message, llvm::StringRef file, size_t line);
 
 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..01d4547e2d228 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) {
+static 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/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..6d4eaf18de7ea 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, 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) {
@@ -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,19 @@ 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);
-
     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([=, &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_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 +483,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 +504,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 +641,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 : llvm::nulls(), mutex);
 
   // Initialize LLDB first before we do anything.
   lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling();
@@ -666,7 +667,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 +701,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 +738,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 +759,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..2756e77fbcbaa
--- /dev/null
+++ b/lldb/unittests/DAP/DAPLogTest.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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);
+  if (index == llvm::StringRef::npos)
+    return str;
+  return str.substr(index + 1);
+}
+
+#define TIMESTAMP_PATTERN "\\[[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}\\] "
+
+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:");
+
+  log.Emit("Hi");
+  EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "Hi\n"));
+
+  inner_log.Emit("foobar");
+  EXPECT_THAT(last_line(outs),
+              MatchesRegex(TIMESTAMP_PATTERN "my_prefix: foobar\n"));
+
+  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("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"));
+
+  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 f4dde9559e9d3..e57963e37f68a 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,
       /*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;


        


More information about the lldb-commits mailing list