Skip to content

Commit 7af0bfe

Browse files
authored
[lldb-dap] Ensure logging statements are written as a single chunk. (#131916)
I noticed this while debugging some unit tests that the logs occasionally would intersperse two log statements. Previously, it was possible for a log statement to have two messages interspersed since the timestamp and log statement were two different writes to the log stream. I created a Log helper to ensure we have a lock while attempting to write to the logs.
1 parent 25613b3 commit 7af0bfe

File tree

10 files changed

+83
-37
lines changed

10 files changed

+83
-37
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_lldb_tool(lldb-dap
2323
Breakpoint.cpp
2424
BreakpointBase.cpp
2525
DAP.cpp
26+
DAPLog.cpp
2627
EventHelper.cpp
2728
ExceptionBreakpoint.cpp
2829
FifoFiles.cpp

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ const char DEV_NULL[] = "/dev/null";
6868

6969
namespace lldb_dap {
7070

71-
DAP::DAP(llvm::StringRef path, std::ofstream *log,
72-
const ReplMode default_repl_mode,
71+
DAP::DAP(llvm::StringRef path, Log *log, const ReplMode default_repl_mode,
7372
std::vector<std::string> pre_init_commands, Transport &transport)
7473
: debug_adapter_path(path), log(log), transport(transport),
7574
broadcaster("lldb-dap"), exception_breakpoints(),

lldb/tools/lldb-dap/DAP.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
144144

145145
struct DAP {
146146
llvm::StringRef debug_adapter_path;
147-
std::ofstream *log;
147+
Log *log;
148148
Transport &transport;
149149
lldb::SBFile in;
150150
OutputRedirector out;
@@ -211,15 +211,14 @@ struct DAP {
211211
/// \param[in] path
212212
/// Path to the lldb-dap binary.
213213
/// \param[in] log
214-
/// Log file stream, if configured.
214+
/// Log stream, if configured.
215215
/// \param[in] default_repl_mode
216216
/// Default repl mode behavior, as configured by the binary.
217217
/// \param[in] pre_init_commands
218218
/// LLDB commands to execute as soon as the debugger instance is allocaed.
219219
/// \param[in] transport
220220
/// Transport for this debug session.
221-
DAP(llvm::StringRef path, std::ofstream *log,
222-
const ReplMode default_repl_mode,
221+
DAP(llvm::StringRef path, Log *log, const ReplMode default_repl_mode,
223222
std::vector<std::string> pre_init_commands, Transport &transport);
224223

225224
~DAP();

lldb/tools/lldb-dap/DAPForward.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct SourceBreakpoint;
1919
struct Watchpoint;
2020
struct InstructionBreakpoint;
2121
struct DAP;
22+
class Log;
2223
class BaseRequestHandler;
2324
class ResponseHandler;
2425
} // namespace lldb_dap

lldb/tools/lldb-dap/DAPLog.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===-- DAPLog.cpp --------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DAPLog.h"
10+
#include "llvm/ADT/StringRef.h"
11+
#include "llvm/Support/raw_ostream.h"
12+
#include <chrono>
13+
#include <mutex>
14+
#include <system_error>
15+
16+
using namespace llvm;
17+
18+
namespace lldb_dap {
19+
20+
Log::Log(StringRef filename, std::error_code &EC) : m_stream(filename, EC) {}
21+
22+
void Log::WriteMessage(StringRef message) {
23+
std::lock_guard<std::mutex> lock(m_mutex);
24+
std::chrono::duration<double> now{
25+
std::chrono::system_clock::now().time_since_epoch()};
26+
m_stream << formatv("{0:f9} ", now.count()).str() << message << "\n";
27+
m_stream.flush();
28+
}
29+
30+
} // namespace lldb_dap

lldb/tools/lldb-dap/DAPLog.h

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,44 +9,53 @@
99
#ifndef LLDB_TOOLS_LLDB_DAP_DAPLOG_H
1010
#define LLDB_TOOLS_LLDB_DAP_DAPLOG_H
1111

12+
#include "llvm/ADT/StringRef.h"
1213
#include "llvm/Support/Error.h"
1314
#include "llvm/Support/FormatAdapters.h"
1415
#include "llvm/Support/FormatVariadic.h"
15-
#include <chrono>
16-
#include <fstream>
16+
#include "llvm/Support/raw_ostream.h"
17+
#include <mutex>
1718
#include <string>
19+
#include <system_error>
1820

1921
// Write a message to log, if logging is enabled.
2022
#define DAP_LOG(log, ...) \
2123
do { \
22-
::std::ofstream *log_private = (log); \
24+
::lldb_dap::Log *log_private = (log); \
2325
if (log_private) { \
24-
::std::chrono::duration<double> now{ \
25-
::std::chrono::system_clock::now().time_since_epoch()}; \
26-
*log_private << ::llvm::formatv("{0:f9} ", now.count()).str() \
27-
<< ::llvm::formatv(__VA_ARGS__).str() << std::endl; \
26+
log_private->WriteMessage(::llvm::formatv(__VA_ARGS__).str()); \
2827
} \
2928
} while (0)
3029

3130
// Write message to log, if error is set. In the log message refer to the error
3231
// with {0}. Error is cleared regardless of whether logging is enabled.
3332
#define DAP_LOG_ERROR(log, error, ...) \
3433
do { \
35-
::std::ofstream *log_private = (log); \
34+
::lldb_dap::Log *log_private = (log); \
3635
::llvm::Error error_private = (error); \
3736
if (log_private && error_private) { \
38-
::std::chrono::duration<double> now{ \
39-
std::chrono::system_clock::now().time_since_epoch()}; \
40-
*log_private << ::llvm::formatv("{0:f9} ", now.count()).str() \
41-
<< ::lldb_dap::FormatError(::std::move(error_private), \
42-
__VA_ARGS__) \
43-
<< std::endl; \
37+
log_private->WriteMessage( \
38+
::lldb_dap::FormatError(::std::move(error_private), __VA_ARGS__)); \
4439
} else \
4540
::llvm::consumeError(::std::move(error_private)); \
4641
} while (0)
4742

4843
namespace lldb_dap {
4944

45+
/// Log manages the lldb-dap log file, used with the corresponding `DAP_LOG` and
46+
/// `DAP_LOG_ERROR` helpers.
47+
class Log final {
48+
public:
49+
/// Creates a log file with the given filename.
50+
Log(llvm::StringRef filename, std::error_code &EC);
51+
52+
void WriteMessage(llvm::StringRef message);
53+
54+
private:
55+
std::mutex m_mutex;
56+
llvm::raw_fd_ostream m_stream;
57+
};
58+
5059
template <typename... Args>
5160
inline auto FormatError(llvm::Error error, const char *format, Args &&...args) {
5261
return llvm::formatv(format, llvm::toString(std::move(error)),

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "EventHelper.h"
1010
#include "DAP.h"
11+
#include "DAPLog.h"
1112
#include "JSONUtils.h"
1213
#include "LLDBUtils.h"
1314
#include "lldb/API/SBFileSpec.h"
@@ -178,15 +179,13 @@ void SendThreadStoppedEvent(DAP &dap) {
178179
SendThreadExitedEvent(dap, tid);
179180
}
180181
} else {
181-
if (dap.log)
182-
*dap.log << "error: SendThreadStoppedEvent() when process"
183-
" isn't stopped ("
184-
<< lldb::SBDebugger::StateAsCString(state) << ')' << std::endl;
182+
DAP_LOG(
183+
dap.log,
184+
"error: SendThreadStoppedEvent() when process isn't stopped ({0})",
185+
lldb::SBDebugger::StateAsCString(state));
185186
}
186187
} else {
187-
if (dap.log)
188-
*dap.log << "error: SendThreadStoppedEvent() invalid process"
189-
<< std::endl;
188+
DAP_LOG(dap.log, "error: SendThreadStoppedEvent() invalid process");
190189
}
191190
dap.RunStopCommands();
192191
}

lldb/tools/lldb-dap/Transport.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ static constexpr StringLiteral kHeaderSeparator = "\r\n\r\n";
6363

6464
namespace lldb_dap {
6565

66-
Transport::Transport(StringRef client_name, std::ofstream *log,
67-
IOObjectSP input, IOObjectSP output)
66+
Transport::Transport(StringRef client_name, Log *log, IOObjectSP input,
67+
IOObjectSP output)
6868
: m_client_name(client_name), m_log(log), m_input(std::move(input)),
6969
m_output(std::move(output)) {}
7070

lldb/tools/lldb-dap/Transport.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
#ifndef LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
1515
#define LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
1616

17+
#include "DAPForward.h"
1718
#include "Protocol/ProtocolBase.h"
1819
#include "lldb/lldb-forward.h"
1920
#include "llvm/ADT/StringRef.h"
2021
#include "llvm/Support/Error.h"
21-
#include <fstream>
2222
#include <optional>
2323

2424
namespace lldb_dap {
@@ -27,8 +27,8 @@ namespace lldb_dap {
2727
/// with the client.
2828
class Transport {
2929
public:
30-
Transport(llvm::StringRef client_name, std::ofstream *log,
31-
lldb::IOObjectSP input, lldb::IOObjectSP output);
30+
Transport(llvm::StringRef client_name, Log *log, lldb::IOObjectSP input,
31+
lldb::IOObjectSP output);
3232
~Transport() = default;
3333

3434
/// Transport is not copyable.
@@ -51,7 +51,7 @@ class Transport {
5151

5252
private:
5353
llvm::StringRef m_client_name;
54-
std::ofstream *m_log;
54+
Log *m_log;
5555
lldb::IOObjectSP m_input;
5656
lldb::IOObjectSP m_output;
5757
};

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include <memory>
4949
#include <mutex>
5050
#include <string>
51+
#include <system_error>
5152
#include <thread>
5253
#include <utility>
5354
#include <vector>
@@ -281,7 +282,7 @@ validateConnection(llvm::StringRef conn) {
281282

282283
static llvm::Error
283284
serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
284-
std::ofstream *log, llvm::StringRef program_path,
285+
Log *log, llvm::StringRef program_path,
285286
const ReplMode default_repl_mode,
286287
const std::vector<std::string> &pre_init_commands) {
287288
Status status;
@@ -484,10 +485,17 @@ int main(int argc, char *argv[]) {
484485
}
485486
#endif
486487

487-
std::unique_ptr<std::ofstream> log = nullptr;
488+
std::unique_ptr<Log> log = nullptr;
488489
const char *log_file_path = getenv("LLDBDAP_LOG");
489-
if (log_file_path)
490-
log = std::make_unique<std::ofstream>(log_file_path);
490+
if (log_file_path) {
491+
std::error_code EC;
492+
log = std::make_unique<Log>(log_file_path, EC);
493+
if (EC) {
494+
llvm::logAllUnhandledErrors(llvm::errorCodeToError(EC), llvm::errs(),
495+
"Failed to create log file: ");
496+
return EXIT_FAILURE;
497+
}
498+
}
491499

492500
// Initialize LLDB first before we do anything.
493501
lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling();

0 commit comments

Comments
 (0)