From 84ea6b6f78df789db6724ef8d774cf04d94d2313 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 28 Oct 2022 14:26:11 -0700 Subject: [PATCH] [lldb] Add diagnostics Around this time last year, I said on the mailing list [1] that I wanted to to transform the reproducers into something that resembles a sysdiagnose on Apple platforms: a collection of files containing a variety of information to help diagnose bugs or troubleshoot issues. This patch adds that framework. Based on lessons learned from the reproducers, I've intentionally tried to keep it small and simple. Different parts of LLDB can register callbacks (this is necessary for layering purposes) that will get called when the diagnostics should be generated. [1] https://lists.llvm.org/pipermail/lldb-dev/2021-September/017045.html Differential revision: https://reviews.llvm.org/D134991 --- lldb/include/lldb/API/SBDebugger.h | 2 + lldb/include/lldb/Utility/Diagnostics.h | 56 ++++++++++++++ lldb/source/API/SBDebugger.cpp | 11 +++ lldb/source/Core/Debugger.cpp | 1 + .../SystemInitializerCommon.cpp | 4 + lldb/source/Utility/CMakeLists.txt | 1 + lldb/source/Utility/Diagnostics.cpp | 74 +++++++++++++++++++ lldb/tools/driver/Driver.cpp | 4 + 8 files changed, 153 insertions(+) create mode 100644 lldb/include/lldb/Utility/Diagnostics.h create mode 100644 lldb/source/Utility/Diagnostics.cpp diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h index d3de61caeb811..950e8e29ef79c 100644 --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -94,6 +94,8 @@ class LLDB_API SBDebugger { 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 index 0000000000000..4a748bd73fd50 --- /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 index c35c60476ffbd..ae66ff951b34a 100644 --- 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 @@ void SBDebugger::PrintStackTraceOnError() { 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 index e12979234bb96..a4c453c6f422c 100644 --- 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 index e0b7a4c1c195e..40e54cd7f0e09 100644 --- 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 @@ llvm::Error SystemInitializerCommon::Initialize() { InitializeLldbChannel(); + Diagnostics::Initialize(); FileSystem::Initialize(); HostInfo::Initialize(m_shlib_dir_helper); @@ -95,4 +98,5 @@ void SystemInitializerCommon::Terminate() { HostInfo::Terminate(); Log::DisableAllLogChannels(); FileSystem::Terminate(); + Diagnostics::Terminate(); } diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index 144704ef80354..81145055ca6c6 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -35,6 +35,7 @@ add_lldb_library(lldbUtility 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 index 0000000000000..14e75c1f2dccb --- /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 index def2e0550c137..682384e241538 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -788,6 +788,10 @@ int main(int argc, char const *argv[]) { << '\n'; return 1; } + + // Setup LLDB signal handlers once the debugger has been initialized. + SBDebugger::PrintDiagnosticsOnError(); + SBHostOS::ThreadCreated(""); signal(SIGINT, sigint_handler);