diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -94,6 +94,8 @@ static void PrintStackTraceOnError(); + static void PrintDiagnosticsOnError(); + static void Terminate(); // Deprecated, use the one that takes a source_init_files bool. diff --git a/lldb/include/lldb/Utility/Diagnostics.h b/lldb/include/lldb/Utility/Diagnostics.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Utility/Diagnostics.h @@ -0,0 +1,56 @@ +//===-- Diagnostics.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_UTILITY_DIAGNOSTICS_H +#define LLDB_UTILITY_DIAGNOSTICS_H + +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Error.h" + +#include +#include +#include + +namespace lldb_private { + +/// Diagnostics are a collection of files to help investigate bugs and +/// troubleshoot issues. Any part of the debugger can register itself with the +/// help of a callback to emit one or more files into the diagnostic directory. +class Diagnostics { +public: + Diagnostics(); + ~Diagnostics(); + + /// Gather diagnostics in the given directory. + llvm::Error Create(const FileSpec &dir); + + /// Gather diagnostics and print a message to the given output stream. + bool Dump(llvm::raw_ostream &stream); + + using Callback = std::function; + + void AddCallback(Callback callback); + + static Diagnostics &Instance(); + static void Initialize(); + static void Terminate(); + +private: + static llvm::Optional &InstanceImpl(); + + llvm::SmallVector m_callbacks; + std::mutex m_callbacks_mutex; +}; + +} // namespace lldb_private + +#endif diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -51,6 +51,7 @@ #include "lldb/Target/Process.h" #include "lldb/Target/TargetList.h" #include "lldb/Utility/Args.h" +#include "lldb/Utility/Diagnostics.h" #include "lldb/Utility/State.h" #include "lldb/Version/Version.h" @@ -218,6 +219,16 @@ llvm::sys::PrintStackTraceOnErrorSignal(executable); } +static void DumpDiagnostics(void *cookie) { + Diagnostics::Instance().Dump(llvm::errs()); +} + +void SBDebugger::PrintDiagnosticsOnError() { + LLDB_INSTRUMENT(); + + llvm::sys::AddSignalHandler(&DumpDiagnostics, nullptr); +} + void SBDebugger::Terminate() { LLDB_INSTRUMENT(); diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -44,6 +44,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadList.h" #include "lldb/Utility/AnsiTerminal.h" +#include "lldb/Utility/Diagnostics.h" #include "lldb/Utility/Event.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Listener.h" diff --git a/lldb/source/Initialization/SystemInitializerCommon.cpp b/lldb/source/Initialization/SystemInitializerCommon.cpp --- a/lldb/source/Initialization/SystemInitializerCommon.cpp +++ b/lldb/source/Initialization/SystemInitializerCommon.cpp @@ -12,6 +12,8 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/Socket.h" +#include "lldb/Target/Statistics.h" +#include "lldb/Utility/Diagnostics.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Timer.h" #include "lldb/Version/Version.h" @@ -63,6 +65,7 @@ InitializeLldbChannel(); + Diagnostics::Initialize(); FileSystem::Initialize(); HostInfo::Initialize(m_shlib_dir_helper); @@ -95,4 +98,5 @@ HostInfo::Terminate(); Log::DisableAllLogChannels(); FileSystem::Terminate(); + Diagnostics::Terminate(); } diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -35,6 +35,7 @@ DataBufferLLVM.cpp DataEncoder.cpp DataExtractor.cpp + Diagnostics.cpp Environment.cpp Event.cpp FileSpec.cpp diff --git a/lldb/source/Utility/Diagnostics.cpp b/lldb/source/Utility/Diagnostics.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Utility/Diagnostics.cpp @@ -0,0 +1,74 @@ +//===-- Diagnostics.cpp ---------------------------------------------------===// +// +// 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 "lldb/Utility/Diagnostics.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb_private; +using namespace lldb; +using namespace llvm; + +void Diagnostics::Initialize() { + lldbassert(!InstanceImpl() && "Already initialized."); + InstanceImpl().emplace(); +} + +void Diagnostics::Terminate() { + lldbassert(InstanceImpl() && "Already terminated."); + InstanceImpl().reset(); +} + +Optional &Diagnostics::InstanceImpl() { + static Optional g_diagnostics; + return g_diagnostics; +} + +Diagnostics &Diagnostics::Instance() { return *InstanceImpl(); } + +Diagnostics::Diagnostics() {} + +Diagnostics::~Diagnostics() {} + +void Diagnostics::AddCallback(Callback callback) { + std::lock_guard guard(m_callbacks_mutex); + m_callbacks.push_back(callback); +} + +bool Diagnostics::Dump(raw_ostream &stream) { + SmallString<128> diagnostics_dir; + std::error_code ec = + sys::fs::createUniqueDirectory("diagnostics", diagnostics_dir); + if (ec) { + stream << "unable to create diagnostic dir: " + << toString(errorCodeToError(ec)) << '\n'; + return false; + } + + stream << "LLDB diagnostics written to " << diagnostics_dir << "\n"; + stream << "Please include the directory content when filing a bug report\n"; + + Error error = Create(FileSpec(diagnostics_dir.str())); + if (error) { + stream << toString(std::move(error)) << '\n'; + return false; + } + + return true; +} + +Error Diagnostics::Create(const FileSpec &dir) { + for (Callback c : m_callbacks) { + if (Error err = c(dir)) + return err; + } + return Error::success(); +} diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -788,6 +788,10 @@ << '\n'; return 1; } + + // Setup LLDB signal handlers once the debugger has been initialized. + SBDebugger::PrintDiagnosticsOnError(); + SBHostOS::ThreadCreated(""); signal(SIGINT, sigint_handler);