diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 394dac3662345..041d1fd864733 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -425,6 +425,26 @@ class Debugger : public std::enable_shared_from_this, llvm::Optional debugger_id = llvm::None, std::once_flag *once = nullptr); + /// Report info events. + /// + /// Unlike warning and error events, info events are not broadcast but are + /// logged for diagnostic purposes. + /// + /// \param[in] message + /// The info message to be reported. + /// + /// \param [in] debugger_id + /// If this optional parameter has a value, it indicates this diagnostic is + /// associated with a unique debugger instance. + /// + /// \param [in] once + /// If a pointer is passed to a std::once_flag, then it will be used to + /// ensure the given info is only logged once. + static void + ReportInfo(std::string message, + llvm::Optional debugger_id = llvm::None, + std::once_flag *once = nullptr); + static void ReportSymbolChange(const ModuleSpec &module_spec); protected: diff --git a/lldb/include/lldb/Core/DebuggerEvents.h b/lldb/include/lldb/Core/DebuggerEvents.h index e1203dcf38c8c..f2e23a94e610f 100644 --- a/lldb/include/lldb/Core/DebuggerEvents.h +++ b/lldb/include/lldb/Core/DebuggerEvents.h @@ -52,6 +52,7 @@ class ProgressEventData : public EventData { class DiagnosticEventData : public EventData { public: enum class Type { + Info, Warning, Error, }; diff --git a/lldb/include/lldb/Utility/Diagnostics.h b/lldb/include/lldb/Utility/Diagnostics.h index 86c97c7ff074d..d4041d5ea8bcb 100644 --- a/lldb/include/lldb/Utility/Diagnostics.h +++ b/lldb/include/lldb/Utility/Diagnostics.h @@ -39,11 +39,15 @@ class Diagnostics { bool Dump(llvm::raw_ostream &stream, const FileSpec &dir); /// @} + void Report(llvm::StringRef message); + using Callback = std::function; void AddCallback(Callback callback); static Diagnostics &Instance(); + + static bool Enabled(); static void Initialize(); static void Terminate(); @@ -53,6 +57,10 @@ class Diagnostics { private: static llvm::Optional &InstanceImpl(); + llvm::Error DumpDiangosticsLog(const FileSpec &dir) const; + + RotatingLogHandler m_log_handler; + llvm::SmallVector m_callbacks; std::mutex m_callbacks_mutex; }; diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index a4c453c6f422c..77d943787df9c 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1311,6 +1311,9 @@ static void PrivateReportDiagnostic(Debugger &debugger, bool debugger_specific) { uint32_t event_type = 0; switch (type) { + case DiagnosticEventData::Type::Info: + assert(false && "DiagnosticEventData::Type::Info should not be broadcast"); + return; case DiagnosticEventData::Type::Warning: event_type = Debugger::eBroadcastBitWarning; break; @@ -1339,6 +1342,15 @@ void Debugger::ReportDiagnosticImpl(DiagnosticEventData::Type type, llvm::Optional debugger_id, std::once_flag *once) { auto ReportDiagnosticLambda = [&]() { + // The diagnostic subsystem is optional but we still want to broadcast + // events when it's disabled. + if (Diagnostics::Enabled()) + Diagnostics::Instance().Report(message); + + // We don't broadcast info events. + if (type == DiagnosticEventData::Type::Info) + return; + // Check if this diagnostic is for a specific debugger. if (debugger_id) { // It is debugger specific, grab it and deliver the event if the debugger @@ -1377,6 +1389,13 @@ void Debugger::ReportError(std::string message, debugger_id, once); } +void Debugger::ReportInfo(std::string message, + llvm::Optional debugger_id, + std::once_flag *once) { + ReportDiagnosticImpl(DiagnosticEventData::Type::Info, std::move(message), + debugger_id, once); +} + void Debugger::ReportSymbolChange(const ModuleSpec &module_spec) { if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); diff --git a/lldb/source/Core/DebuggerEvents.cpp b/lldb/source/Core/DebuggerEvents.cpp index 0cc9ee9ad96f5..6b33af9e0ee1f 100644 --- a/lldb/source/Core/DebuggerEvents.cpp +++ b/lldb/source/Core/DebuggerEvents.cpp @@ -51,6 +51,8 @@ ProgressEventData::GetEventDataFromEvent(const Event *event_ptr) { llvm::StringRef DiagnosticEventData::GetPrefix() const { switch (m_type) { + case Type::Info: + return "info"; case Type::Warning: return "warning"; case Type::Error: diff --git a/lldb/source/Utility/Diagnostics.cpp b/lldb/source/Utility/Diagnostics.cpp index 6bd8fcf28b58b..a6e51e25d51fb 100644 --- a/lldb/source/Utility/Diagnostics.cpp +++ b/lldb/source/Utility/Diagnostics.cpp @@ -17,6 +17,8 @@ using namespace lldb_private; using namespace lldb; using namespace llvm; +static constexpr size_t g_num_log_messages = 100; + void Diagnostics::Initialize() { lldbassert(!InstanceImpl() && "Already initialized."); InstanceImpl().emplace(); @@ -27,6 +29,8 @@ void Diagnostics::Terminate() { InstanceImpl().reset(); } +bool Diagnostics::Enabled() { return InstanceImpl().operator bool(); } + Optional &Diagnostics::InstanceImpl() { static Optional g_diagnostics; return g_diagnostics; @@ -34,7 +38,7 @@ Optional &Diagnostics::InstanceImpl() { Diagnostics &Diagnostics::Instance() { return *InstanceImpl(); } -Diagnostics::Diagnostics() {} +Diagnostics::Diagnostics() : m_log_handler(g_num_log_messages) {} Diagnostics::~Diagnostics() {} @@ -58,8 +62,7 @@ bool Diagnostics::Dump(raw_ostream &stream, const FileSpec &dir) { stream << "LLDB diagnostics will be written to " << dir.GetPath() << "\n"; stream << "Please include the directory content when filing a bug report\n"; - Error error = Create(dir); - if (error) { + if (Error error = Create(dir)) { stream << toString(std::move(error)) << '\n'; return false; } @@ -77,9 +80,27 @@ llvm::Expected Diagnostics::CreateUniqueDirectory() { } Error Diagnostics::Create(const FileSpec &dir) { + if (Error err = DumpDiangosticsLog(dir)) + return err; + for (Callback c : m_callbacks) { if (Error err = c(dir)) return err; } + return Error::success(); } + +llvm::Error Diagnostics::DumpDiangosticsLog(const FileSpec &dir) const { + FileSpec log_file = dir.CopyByAppendingPathComponent("diagnostics.log"); + std::error_code ec; + llvm::raw_fd_ostream stream(log_file.GetPath(), ec, llvm::sys::fs::OF_None); + if (ec) + return errorCodeToError(ec); + m_log_handler.Dump(stream); + return Error::success(); +} + +void Diagnostics::Report(llvm::StringRef message) { + m_log_handler.Emit(message); +} diff --git a/lldb/test/Shell/Diagnostics/TestDiagnosticsLog.test b/lldb/test/Shell/Diagnostics/TestDiagnosticsLog.test new file mode 100644 index 0000000000000..3be56e747fd24 --- /dev/null +++ b/lldb/test/Shell/Diagnostics/TestDiagnosticsLog.test @@ -0,0 +1,33 @@ +# RUN: yaml2obj %s -o %t +# RUN: mkdir -p %t.diags +# RUN: %lldb -c %t -o 'diagnostics dump -d %t.diags' 2> %t.stderr + +# RUN: cat %t.stderr | FileCheck %s --check-prefix ERROR +# RUN: cat %t.diags/diagnostics.log | FileCheck %s --check-prefix ERROR + +# ERROR: unable to retrieve process ID from minidump file, setting process ID to 1 + +--- !minidump +Streams: + - Type: ThreadList + Threads: + - Thread Id: 0x00003E81 + Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000006020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000010A234EBFC7F000010A234EBFC7F00000000000000000000F09C34EBFC7F0000C0A91ABCE97F00000000000000000000A0163FBCE97F00004602000000000000921C40000000000030A434EBFC7F000000000000000000000000000000000000C61D4000000000007F0300000000000000000000000000000000000000000000801F0000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF25252525252525252525252525252525000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + Stack: + Start of Memory Range: 0x00007FFCEB34A000 + Content: '' + - Type: ModuleList + Modules: + - Base of Image: 0x0000000000400000 + Size of Image: 0x00017000 + Module Name: 'a.out' + CodeView Record: '' + - Type: SystemInfo + Processor Arch: AMD64 + Platform ID: Linux + CSD Version: 'Linux 3.13' + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 +...