[lldb] Remove the Diagnostics callback mechanism#206132
Conversation
The Diagnostics framework had a callback registry (AddCallback / RemoveCallback) so subsystems could contribute files to a diagnostics directory, intended to also run during crash handling. That crash-time path never materialized, and the sole registered callback was the Debugger copying its file-backed logs. If you had no logging enabled, the directory would be empty, confusing the users. Remove the registry and the callback loop in Diagnostics::Create (which now just writes the in-memory log), and expose the log copying as Debugger::CopyLogFilesToDirectory, which "diagnostics dump" calls directly. The dump command now copies the invoking debugger's logs rather than every debugger's, which is the more useful behavior.
|
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) ChangesThe Diagnostics framework had a callback registry (AddCallback / RemoveCallback) so subsystems could contribute files to a diagnostics directory, intended to also run during crash handling. That crash-time path never materialized, and the sole registered callback was the Debugger copying its file-backed logs. If you had no logging enabled, the directory would be empty, confusing the users. Remove the registry and the callback loop in Diagnostics::Create (which now just writes the in-memory log), and expose the log copying as Debugger::CopyLogFilesToDirectory, which "diagnostics dump" calls directly. The dump command now copies the invoking debugger's logs rather than every debugger's, which is the more useful behavior I want to double down on. Full diff: https://github.com/llvm/llvm-project/pull/206132.diff 5 Files Affected:
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 179ef0f38d940..b7d66f5795d43 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -266,6 +266,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton);
+ /// Copy this debugger's file-backed log files into the given directory, for
+ /// inclusion in a diagnostics bundle. Best-effort; failures are skipped.
+ void CopyLogFilesToDirectory(const FileSpec &dir);
+
Status SetPropertyValue(const ExecutionContext *exe_ctx,
VarSetOperationType op, llvm::StringRef property_path,
llvm::StringRef value) override;
@@ -805,7 +809,6 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
lldb::ListenerSP m_forward_listener_sp;
llvm::once_flag m_clear_once;
lldb::TargetSP m_dummy_target_sp;
- Diagnostics::CallbackID m_diagnostics_callback_id;
/// Bookkeeping for command line progress events.
/// @{
diff --git a/lldb/include/lldb/Utility/Diagnostics.h b/lldb/include/lldb/Utility/Diagnostics.h
index c2e4b44350a37..e32ffa5233f11 100644
--- a/lldb/include/lldb/Utility/Diagnostics.h
+++ b/lldb/include/lldb/Utility/Diagnostics.h
@@ -11,29 +11,25 @@
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Log.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Error.h"
-#include <functional>
-#include <mutex>
#include <optional>
-#include <vector>
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.
+/// Diagnostics maintain an always-on, in-memory log of recent diagnostic
+/// messages that can be written out to help investigate bugs and troubleshoot
+/// issues.
class Diagnostics {
public:
Diagnostics();
~Diagnostics();
- /// Gather diagnostics in the given directory.
+ /// Write the in-memory diagnostic log into the given directory.
llvm::Error Create(const FileSpec &dir);
- /// Gather diagnostics and print a message to the given output stream.
+ /// Write the diagnostic log into a directory and print a message to the given
+ /// output stream.
/// @{
bool Dump(llvm::raw_ostream &stream);
bool Dump(llvm::raw_ostream &stream, const FileSpec &dir);
@@ -41,12 +37,6 @@ class Diagnostics {
void Report(llvm::StringRef message);
- using Callback = std::function<llvm::Error(const FileSpec &)>;
- using CallbackID = uint64_t;
-
- CallbackID AddCallback(Callback callback);
- void RemoveCallback(CallbackID id);
-
static Diagnostics &Instance();
static bool Enabled();
@@ -62,23 +52,6 @@ class Diagnostics {
llvm::Error DumpDiangosticsLog(const FileSpec &dir) const;
RotatingLogHandler m_log_handler;
-
- struct CallbackEntry {
- CallbackEntry(CallbackID id, Callback callback)
- : id(id), callback(std::move(callback)) {}
- CallbackID id;
- Callback callback;
- };
-
- /// Monotonically increasing callback identifier. Unique per Diagnostic
- /// instance.
- CallbackID m_callback_id;
-
- /// List of callback entries.
- llvm::SmallVector<CallbackEntry, 4> m_callbacks;
-
- /// Mutex to protect callback list and callback identifier.
- std::mutex m_callbacks_mutex;
};
} // namespace lldb_private
diff --git a/lldb/source/Commands/CommandObjectDiagnostics.cpp b/lldb/source/Commands/CommandObjectDiagnostics.cpp
index b565e16e76b53..3e942c98c8180 100644
--- a/lldb/source/Commands/CommandObjectDiagnostics.cpp
+++ b/lldb/source/Commands/CommandObjectDiagnostics.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "CommandObjectDiagnostics.h"
+#include "lldb/Core/Debugger.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
@@ -93,6 +94,10 @@ class CommandObjectDiagnosticsDump : public CommandObjectParsed {
return;
}
+ // Copy this debugger's file-backed logs into the directory. This used to be
+ // done by a Diagnostics callback registered by the Debugger.
+ GetDebugger().CopyLogFilesToDirectory(*directory);
+
result.GetOutputStream() << "diagnostics written to " << *directory << '\n';
result.SetStatus(eReturnStatusSuccessFinishResult);
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 9a75c5eb407c5..b2e6fe66dc29e 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1103,22 +1103,6 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
if (!GetOutputFileSP()->GetIsTerminalWithColors())
disable_color();
- if (Diagnostics::Enabled()) {
- m_diagnostics_callback_id = Diagnostics::Instance().AddCallback(
- [this](const FileSpec &dir) -> llvm::Error {
- for (auto &entry : m_stream_handlers) {
- llvm::StringRef log_path = entry.first();
- llvm::StringRef file_name = llvm::sys::path::filename(log_path);
- FileSpec destination = dir.CopyByAppendingPathComponent(file_name);
- std::error_code ec =
- llvm::sys::fs::copy_file(log_path, destination.GetPath());
- if (ec)
- return llvm::errorCodeToError(ec);
- }
- return llvm::Error::success();
- });
- }
-
#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
// Enabling use of ANSI color codes because LLDB is using them to highlight
// text.
@@ -1164,9 +1148,6 @@ void Debugger::Clear() {
GetInputFile().Close();
m_command_interpreter_up->Clear();
-
- if (Diagnostics::Enabled())
- Diagnostics::Instance().RemoveCallback(m_diagnostics_callback_id);
});
}
@@ -1692,6 +1673,16 @@ void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
std::make_shared<CallbackLogHandler>(log_callback, baton);
}
+void Debugger::CopyLogFilesToDirectory(const FileSpec &dir) {
+ for (auto &entry : m_stream_handlers) {
+ llvm::StringRef log_path = entry.first();
+ llvm::StringRef file_name = llvm::sys::path::filename(log_path);
+ FileSpec destination = dir.CopyByAppendingPathComponent(file_name);
+ // Best-effort: skip logs that can't be copied rather than aborting.
+ llvm::sys::fs::copy_file(log_path, destination.GetPath());
+ }
+}
+
void Debugger::SetDestroyCallback(
lldb_private::DebuggerDestroyCallback destroy_callback, void *baton) {
std::lock_guard<std::mutex> guard(m_destroy_callback_mutex);
diff --git a/lldb/source/Utility/Diagnostics.cpp b/lldb/source/Utility/Diagnostics.cpp
index b2a08165dd6ca..175011783ead2 100644
--- a/lldb/source/Utility/Diagnostics.cpp
+++ b/lldb/source/Utility/Diagnostics.cpp
@@ -43,19 +43,6 @@ Diagnostics::Diagnostics() : m_log_handler(g_num_log_messages) {}
Diagnostics::~Diagnostics() {}
-Diagnostics::CallbackID Diagnostics::AddCallback(Callback callback) {
- std::lock_guard<std::mutex> guard(m_callbacks_mutex);
- CallbackID id = m_callback_id++;
- m_callbacks.emplace_back(id, callback);
- return id;
-}
-
-void Diagnostics::RemoveCallback(CallbackID id) {
- std::lock_guard<std::mutex> guard(m_callbacks_mutex);
- llvm::erase_if(m_callbacks,
- [id](const CallbackEntry &e) { return e.id == id; });
-}
-
bool Diagnostics::Dump(raw_ostream &stream) {
Expected<FileSpec> diagnostics_dir = CreateUniqueDirectory();
if (!diagnostics_dir) {
@@ -92,11 +79,6 @@ Error Diagnostics::Create(const FileSpec &dir) {
if (Error err = DumpDiangosticsLog(dir))
return err;
- for (CallbackEntry e : m_callbacks) {
- if (Error err = e.callback(dir))
- return err;
- }
-
return Error::success();
}
|
bulbazord
left a comment
There was a problem hiding this comment.
Looks ok to me. We can revive this system or add another one if needed.
The Diagnostics framework had a callback registry (AddCallback / RemoveCallback) so subsystems could contribute files to a diagnostics directory, intended to also run during crash handling. That crash-time path never materialized, and the sole registered callback was the Debugger copying its file-backed logs. If you had no logging enabled, the directory would be empty, confusing the users. Remove the registry and the callback loop in Diagnostics::Create (which now just writes the in-memory log), and expose the log copying as Debugger::CopyLogFilesToDirectory, which "diagnostics dump" calls directly. The dump command now copies the invoking debugger's logs rather than every debugger's, which is the more useful behavior I want to double down on.
The Diagnostics framework had a callback registry (AddCallback / RemoveCallback) so subsystems could contribute files to a diagnostics directory, intended to also run during crash handling. That crash-time path never materialized, and the sole registered callback was the Debugger copying its file-backed logs. If you had no logging enabled, the directory would be empty, confusing the users. Remove the registry and the callback loop in Diagnostics::Create (which now just writes the in-memory log), and expose the log copying as Debugger::CopyLogFilesToDirectory, which "diagnostics dump" calls directly. The dump command now copies the invoking debugger's logs rather than every debugger's, which is the more useful behavior I want to double down on.
* [lldb] Add a non-Darwin Host::OpenURL and a Host::URLEncode helper (llvm#206129) Host::OpenURL was only defined for Darwin (in Host.mm). Add a portable implementation in the common Host.cpp: on Unix it launches xdg-open; on Windows it returns "unsupported" for now. xdg-open is run without a shell (run_in_shell=false) so query-string metacharacters in the URL are never interpreted by the shell. Also add Host::URLEncode, an RFC 3986 percent-encoder for assembling tracker URLs. These are the building blocks for an upcoming "diagnostics report" command that opens a pre-filled bug URL, and the encoder is shared with a downstream tap-to-radar reporter. (cherry picked from commit dbd4528) * [lldb] Remove the Diagnostics callback mechanism (llvm#206132) The Diagnostics framework had a callback registry (AddCallback / RemoveCallback) so subsystems could contribute files to a diagnostics directory, intended to also run during crash handling. That crash-time path never materialized, and the sole registered callback was the Debugger copying its file-backed logs. If you had no logging enabled, the directory would be empty, confusing the users. Remove the registry and the callback loop in Diagnostics::Create (which now just writes the in-memory log), and expose the log copying as Debugger::CopyLogFilesToDirectory, which "diagnostics dump" calls directly. The dump command now copies the invoking debugger's logs rather than every debugger's, which is the more useful behavior I want to double down on. (cherry picked from commit 9f34f1c) * [lldb] Move Diagnostics from Utility to Core (NFC) (llvm#206152) Nothing in the Utility or Host layers uses Diagnostics. Its only callers are Debugger (the always-on log feeder), SBDebugger, and the SystemInitializerCommon lifecycle. Those all live in Core or above. The header depends only on Utility primitives (FileSpec, Log, Error), and lldbInitialization already links lldbCore, so the move adds no new link dependency anywhere. Relocating it to Core lets Diagnostics reach Debugger, Target, CommandInterpreter, and Host, which simplifies an upcoming change that collect a richer diagnostics bundle (statistics, command snapshots, invocation, etc) and allows us to implement that directly in the Diagnostics class. (cherry picked from commit 2ceab13) * [lldb] Collect a diagnostics bundle on the Diagnostics class (llvm#206189) Add Diagnostics::Collect, which gathers the state a triager needs into a directory, best-effort (one failed section never sinks the rest): the always-on log plus the debugger's file logs, statistics.json from DebuggerStats, and a snapshot of the commands run first when triaging (target list, image list, thread list, backtraces, image lookup, frame variable). It returns a Diagnostics::Report with the LLDB version, host, and how LLDB was invoked, plus an Attachments holding the bundle directory and the files written into it. Each file is recorded as it is written, so a file that could not be created is simply absent from the list. The report is expected to grow more fields over time. `diagnostics dump` now calls Collect and prints the report as JSON to the terminal instead of only reporting where the directory was written. Here's what this all looks like: ``` (lldb) diagnostics dump { "attachments": { "directory": "/var/folders/6b/3sb80ks56rz5vwlhsdvpsxmh0000gn/T/diagnostics-4b5e9f", "files": [ "diagnostics.log", "statistics.json", "commands.txt" ] }, "invocation": "./build/bin/count", "os": "arm64-apple-macosx platform=host os=27.0 build=26A374", "version": "lldb version 23.0.0git (git@github.com:llvm/llvm-project.git revision 85fa95f)\n clang revision 85fa95f\n llvm revision 85fa95f" } ``` This is in preparation for a future PR which adds the ability to take all this information and pre-fill a bug report with it. (cherry picked from commit a873660) * [lldb] Add a BugReporter plugin type and "diagnostics report" (llvm#206578) Introduce a BugReporter plugin kind that files an assembled Diagnostics::Report through a pluggable destination, plus a "diagnostics report" command (aliased "bugreport") that collects the bundle and files it through the first registered reporter. CreateBugReporterInstance() returns the first registered reporter, so a reporter registered earlier wins and a downstream tree can take over by registering ahead of the built-ins. BugReporterNone is the always-registered, last-in-order fallback. Its File() returns an error pointing at LLDB_BUG_REPORT_URL, so the command surfaces "no tracker configured" through the normal error path instead of special-casing it. "diagnostics report" writes the bundle, prints a review warning, and files it unless --no-open is given. The upcoming GitHub reporter, gated by a CMake option, is the first real destination. (cherry picked from commit 8cf09c5) * [lldb] Add a GitHub bug reporter (llvm#206607) Add a BugReporter plugin that files a diagnostics bundle as an llvm/llvm-project GitHub issue. File() renders a short Markdown body from the Diagnostics::Report (version, host, invocation, and a pointer to the bundle directory to attach), truncates it under a GET-safe URL length on a UTF-8 character boundary, and opens a pre-filled issues/new page with Host::OpenURL. It is gated by LLDB_ENABLE_GITHUB_BUG_REPORTER (default on) and registers ahead of the no-op fallback, so it is the default destination for "diagnostics report" while a downstream tree can still register its own reporter ahead of it. (cherry picked from commit 1556da0) * [lldb] Add an artifact provider mechanism to Diagnostics (llvm#210417) Add a hook that lets a subsystem contribute a file to the diagnostics bundle, without breaking layering and making Core depend on it. A plugin registers an ArtifactProvider callback and a file name via AddArtifactProvider. Providers run when a bundle is collected, and each one that yields content is written into the bundle and recorded in the report's attachments. This restores, in a simpler form, the collection point that the removed Diagnostics callback mechanism offered. The motivating consumer is the downstream Swift health check, which registers a provider to write its "swift-healthcheck.log" into the bundle. Unlike the old callback, a provider does not open or write its own file: it just returns the contents as a string and Diagnostics handles the rest. (cherry picked from commit 2120490) * [lldb] Contribute swift-healthcheck to the diagnostics bundle The Swift language plugin added swift-healthcheck.log to the diagnostics directory through the Diagnostics callback registry, which the bundle series removed. Port it to the artifact provider mechanism (llvm#210417): LogChannelSwift returns the health log's contents on demand and Diagnostics writes the file, so it lands in the bundle's attachments and the plugin no longer touches the filesystem itself.
The Diagnostics framework had a callback registry (AddCallback / RemoveCallback) so subsystems could contribute files to a diagnostics directory, intended to also run during crash handling. That crash-time path never materialized, and the sole registered callback was the Debugger copying its file-backed logs. If you had no logging enabled, the directory would be empty, confusing the users.
Remove the registry and the callback loop in Diagnostics::Create (which now just writes the in-memory log), and expose the log copying as Debugger::CopyLogFilesToDirectory, which "diagnostics dump" calls directly. The dump command now copies the invoking debugger's logs rather than every debugger's, which is the more useful behavior I want to double down on.