Files
llvm/lldb/source/API/SystemInitializerFull.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

151 lines
5.1 KiB
C++
Raw Normal View History

//===-- SystemInitializerFull.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 "SystemInitializerFull.h"
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
[lldb] Implement coalescing of disjoint progress events (#84854) This implements coalescing of progress events using a timeout, as discussed in the RFC on Discourse [1]. This PR consists of two commits which, depending on the feedback, I may split up into two PRs. For now, I think it's easier to review this as a whole. 1. The first commit introduces a new generic `Alarm` class. The class lets you to schedule a function (callback) to be executed after a given timeout expires. You can cancel and reset a callback before its corresponding timeout expires. It achieves this with the help of a worker thread that sleeps until the next timeout expires. The only guarantee it provides is that your function is called no sooner than the requested timeout. Because the callback is called directly from the worker thread, a long running callback could potentially block the worker thread. I intentionally kept the implementation as simple as possible while addressing the needs for the `ProgressManager` use case. If we want to rely on this somewhere else, we can reassess whether we need to address those limitations. 2. The second commit uses the Alarm class to coalesce progress events. To recap the Discourse discussion, when multiple progress events with the same title execute in close succession, they get broadcast as one to `eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of the in-flight progress events and when the refcount hits zero, the Alarm class is used to schedule broadcasting the event. If a new progress event comes in before the alarm fires, the alarm is reset (and the process repeats when the new progress event ends). If no new event comes in before the timeout expires, the progress event is broadcast. [1] https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
#include "lldb/Core/Progress.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/Host.h"
#include "lldb/Initialization/SystemInitializerCommon.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Target/ProcessTrace.h"
#include "lldb/Utility/Timer.h"
#include "lldb/Version/Version.h"
[lldb] Prevent that LLDB randomly crashes in CommandLineParser::addOption by initializing LLVM's command line parser Since quite a while Apple's LLDB fork (that contains the Swift debugging support) is randomly crashing in `CommandLineParser::addOption` with an error such as `CommandLine Error: Option 'h' registered more than once!` The backtrace of the crashing thread is shown below. There are also usually many other threads also performing similar clang::FrontendActions which are all trying to generate (usually outdated) Clang modules which are used by Swift for various reasons. ``` [ 6] LLDB`CommandLineParser::addOption(llvm::cl::Option*, llvm::cl::SubCommand*) + 856 [ 7] LLDB`CommandLineParser::addOption(llvm::cl::Option*, llvm::cl::SubCommand*) + 733 [ 8] LLDB`CommandLineParser::addOption(llvm::cl::Option*, bool) + 184 [ 9] LLDB`llvm::cl::ParseCommandLineOptions(...) [inlined] ::CommandLineParser::ParseCommandLineOptions(... + 1279 [ 9] LLDB`llvm::cl::ParseCommandLineOptions(...) + 497 [ 10] LLDB`setCommandLineOpts(clang::CodeGenOptions const&) + 416 [ 11] LLDB`EmitAssemblyHelper::EmitAssemblyWithNewPassManager(...) + 98 [ 12] LLDB`clang::EmitBackendOutput(...) + 4580 [ 13] LLDB`PCHContainerGenerator::HandleTranslationUnit(clang::ASTContext&) + 871 [ 14] LLDB`clang::MultiplexConsumer::HandleTranslationUnit(clang::ASTContext&) + 43 [ 15] LLDB`clang::ParseAST(clang::Sema&, bool, bool) + 579 [ 16] LLDB`clang::FrontendAction::Execute() + 74 [ 17] LLDB`clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 1808 ``` The underlying reason for the crash is that the CommandLine code in LLVM isn't thread-safe and will never be thread-safe with its current architecture. The way LLVM's CommandLine logic works is that all parts of the LLVM can provide command line arguments by defining `cl::opt` global variables and their constructors (which are invoked during static initialisation) register the variable in LLVM's CommandLineParser (which is also just a global variable). At some later point after static initialization we actually try to parse command line arguments and we ask the CommandLineParser to parse our `argv`. The CommandLineParser then lazily constructs it's internal parsing state in a non-thread-safe way (this is where the crash happens), parses the provided command line and then goes back to the respective `cl::opt` global variables and sets their values according to the parse result. As all of this is based on global state, this whole mechanism isn't thread-safe so the only time to ever use it is when we know we only have one active thread dealing with LLVM logic. That's why nearly all callers of `llvm::cl::ParseCommandLineOptions` are at the top of the `main` function of the some LLVM-based tool. One of the few exceptions to this rule is in the `setCommandLineOpts` function in `BackendUtil.cpp` which is in our backtrace: ``` static void setCommandLineOpts(const CodeGenOptions &CodeGenOpts) { SmallVector<const char *, 16> BackendArgs; BackendArgs.push_back("clang"); // Fake program name. if (!CodeGenOpts.DebugPass.empty()) { BackendArgs.push_back("-debug-pass"); BackendArgs.push_back(CodeGenOpts.DebugPass.c_str()); } if (!CodeGenOpts.LimitFloatPrecision.empty()) { BackendArgs.push_back("-limit-float-precision"); BackendArgs.push_back(CodeGenOpts.LimitFloatPrecision.c_str()); } BackendArgs.push_back(nullptr); llvm::cl::ParseCommandLineOptions(BackendArgs.size() - 1, BackendArgs.data()); } ``` This is trying to set `cl::opt` variables in the LLVM backend to their right value as the passed via CodeGenOptions by invoking the CommandLine parser. As this is just in some generic Clang CodeGen code (where we allow having multiple threads) this is code is clearly wrong. If we're unlucky it either overwrites the value of the global variables or it causes the CommandLine parser to crash. So the next question is why is this only crashing in LLDB? The main reason seems to be that easiest way to crash this code is to concurrently enter the initial CommandLineParser construction where it tries to collect all the registered `cl::opt` options and checks for sanity: ``` // If it's a DefaultOption, check to make sure it isn't already there. if (O->isDefaultOption() && SC->OptionsMap.find(O->ArgStr) != SC->OptionsMap.end()) return; // Add argument to the argument map! if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr << "' registered more than once!\n"; HadErrors = true; } ``` The `OptionsMap` here is global variable and if we end up in this code with two threads at once then two threads at the same time can register an option (such as 'h') when they pass the first `if` and then we fail with the sanity check in the second `if`. After this sanity check and initial setup code the only remaining work is just parsing the provided CommandLine which isn't thread-safe but at least doesn't crash in all my attempts at breaking it (as it's usually just reading from the already generated parser state but not further modifying it). The exception to this is probably that once people actually specify the options in the code snippet above we might run into some new interesting ways to crash everything. To go back to why it's only affecting LLDB: Nearly all LLVM tools I could find (even if they are using threads) seem to call the CommandLine parser at the start so they all execute the initial parser setup at a point where there is only one thread. So once the code above is executed they are mostly safe from the sanity check crashes. We even have some shady code for the gtest `main` in `TestMain.cpp` which is why this also doesn't affect unit tests. The only exception to this rule is ... *drum roll* ... LLDB! it's not using that CommandLine library for parsing options so it also never ends up calling it in `main`. So when we end up in the `FrontendAction` code from the backtrace we are already very deep in some LLDB logic and usually already have several threads. In a situation where Swift decides to compile a large amount of Clang modules in parallel we then end up entering this code via several threads. If several threads reach this code at the same time we end up in the situation where the sanity-checking code of CommandLine crashes. I have a very reliable way of demonstrating the whole thing in D99650 (just run the unit test several times, it usually crashes after 3-4 attempts). We have several ways to fix this: 1. Make the whole CommandLine mechanism in LLVM thread-safe. 2. Get rid of `setCommandLineOpts` in `BackendUtil.cpp` and other callers of the command line parsing in generic Clang code. 3. Initialise the CommandLine library in a safe point in LLDB. Option 1 is just a lot of work and I'm not even sure where to start. The whole mechanism is based on global variables and global state and this seems like a humongous task. Option 2 is probably the best thing we can do in the near future. There are only two callers of the command line parser in generic Clang code. The one in `BackendUtils.cpp` looks like it can be replaced with some reasonable refactoring (as it only deals with two specific options). There is another one in `ExecuteCompilerInvocation` which deals with forwarding the generic `-mllvm` options to the backend which seems like it will just end up requiring us to do Option 1. Option 3 is what this patch is doing. We just parse some dummy command line invocation in a point of the LLDB execution where we only have one thread that is dealing with LLVM/Clang stuff. This way we are at least prevent the frequent crashes for users as parsing the dummy command line invocation will set up the initial parser state safely. Fixes rdar://70989856 Reviewed By: mib, JDevlieghere Differential Revision: https://reviews.llvm.org/D99652
2021-04-01 19:50:08 +02:00
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#include "llvm/ExecutionEngine/MCJIT.h"
#pragma clang diagnostic pop
#include <string>
#define LLDB_PLUGIN(p) LLDB_PLUGIN_DECLARE(p)
#include "Plugins/Plugins.def"
2021-02-15 21:51:32 +01:00
#if LLDB_ENABLE_PYTHON
#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
constexpr lldb_private::HostInfo::SharedLibraryDirectoryHelper
*g_shlib_dir_helper =
lldb_private::ScriptInterpreterPython::SharedLibraryDirectoryHelper;
#else
constexpr lldb_private::HostInfo::SharedLibraryDirectoryHelper
*g_shlib_dir_helper = nullptr;
2021-02-15 21:51:32 +01:00
#endif
using namespace lldb_private;
2021-02-15 21:51:32 +01:00
SystemInitializerFull::SystemInitializerFull()
: SystemInitializerCommon(g_shlib_dir_helper) {}
SystemInitializerFull::~SystemInitializerFull() = default;
llvm::Error SystemInitializerFull::Initialize() {
llvm::Error error = SystemInitializerCommon::Initialize();
if (error)
return error;
// Initialize LLVM and Clang
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
[lldb] Implement coalescing of disjoint progress events (#84854) This implements coalescing of progress events using a timeout, as discussed in the RFC on Discourse [1]. This PR consists of two commits which, depending on the feedback, I may split up into two PRs. For now, I think it's easier to review this as a whole. 1. The first commit introduces a new generic `Alarm` class. The class lets you to schedule a function (callback) to be executed after a given timeout expires. You can cancel and reset a callback before its corresponding timeout expires. It achieves this with the help of a worker thread that sleeps until the next timeout expires. The only guarantee it provides is that your function is called no sooner than the requested timeout. Because the callback is called directly from the worker thread, a long running callback could potentially block the worker thread. I intentionally kept the implementation as simple as possible while addressing the needs for the `ProgressManager` use case. If we want to rely on this somewhere else, we can reassess whether we need to address those limitations. 2. The second commit uses the Alarm class to coalesce progress events. To recap the Discourse discussion, when multiple progress events with the same title execute in close succession, they get broadcast as one to `eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of the in-flight progress events and when the refcount hits zero, the Alarm class is used to schedule broadcasting the event. If a new progress event comes in before the alarm fires, the alarm is reset (and the process repeats when the new progress event ends). If no new event comes in before the timeout expires, the progress event is broadcast. [1] https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
[lldb] Prevent that LLDB randomly crashes in CommandLineParser::addOption by initializing LLVM's command line parser Since quite a while Apple's LLDB fork (that contains the Swift debugging support) is randomly crashing in `CommandLineParser::addOption` with an error such as `CommandLine Error: Option 'h' registered more than once!` The backtrace of the crashing thread is shown below. There are also usually many other threads also performing similar clang::FrontendActions which are all trying to generate (usually outdated) Clang modules which are used by Swift for various reasons. ``` [ 6] LLDB`CommandLineParser::addOption(llvm::cl::Option*, llvm::cl::SubCommand*) + 856 [ 7] LLDB`CommandLineParser::addOption(llvm::cl::Option*, llvm::cl::SubCommand*) + 733 [ 8] LLDB`CommandLineParser::addOption(llvm::cl::Option*, bool) + 184 [ 9] LLDB`llvm::cl::ParseCommandLineOptions(...) [inlined] ::CommandLineParser::ParseCommandLineOptions(... + 1279 [ 9] LLDB`llvm::cl::ParseCommandLineOptions(...) + 497 [ 10] LLDB`setCommandLineOpts(clang::CodeGenOptions const&) + 416 [ 11] LLDB`EmitAssemblyHelper::EmitAssemblyWithNewPassManager(...) + 98 [ 12] LLDB`clang::EmitBackendOutput(...) + 4580 [ 13] LLDB`PCHContainerGenerator::HandleTranslationUnit(clang::ASTContext&) + 871 [ 14] LLDB`clang::MultiplexConsumer::HandleTranslationUnit(clang::ASTContext&) + 43 [ 15] LLDB`clang::ParseAST(clang::Sema&, bool, bool) + 579 [ 16] LLDB`clang::FrontendAction::Execute() + 74 [ 17] LLDB`clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 1808 ``` The underlying reason for the crash is that the CommandLine code in LLVM isn't thread-safe and will never be thread-safe with its current architecture. The way LLVM's CommandLine logic works is that all parts of the LLVM can provide command line arguments by defining `cl::opt` global variables and their constructors (which are invoked during static initialisation) register the variable in LLVM's CommandLineParser (which is also just a global variable). At some later point after static initialization we actually try to parse command line arguments and we ask the CommandLineParser to parse our `argv`. The CommandLineParser then lazily constructs it's internal parsing state in a non-thread-safe way (this is where the crash happens), parses the provided command line and then goes back to the respective `cl::opt` global variables and sets their values according to the parse result. As all of this is based on global state, this whole mechanism isn't thread-safe so the only time to ever use it is when we know we only have one active thread dealing with LLVM logic. That's why nearly all callers of `llvm::cl::ParseCommandLineOptions` are at the top of the `main` function of the some LLVM-based tool. One of the few exceptions to this rule is in the `setCommandLineOpts` function in `BackendUtil.cpp` which is in our backtrace: ``` static void setCommandLineOpts(const CodeGenOptions &CodeGenOpts) { SmallVector<const char *, 16> BackendArgs; BackendArgs.push_back("clang"); // Fake program name. if (!CodeGenOpts.DebugPass.empty()) { BackendArgs.push_back("-debug-pass"); BackendArgs.push_back(CodeGenOpts.DebugPass.c_str()); } if (!CodeGenOpts.LimitFloatPrecision.empty()) { BackendArgs.push_back("-limit-float-precision"); BackendArgs.push_back(CodeGenOpts.LimitFloatPrecision.c_str()); } BackendArgs.push_back(nullptr); llvm::cl::ParseCommandLineOptions(BackendArgs.size() - 1, BackendArgs.data()); } ``` This is trying to set `cl::opt` variables in the LLVM backend to their right value as the passed via CodeGenOptions by invoking the CommandLine parser. As this is just in some generic Clang CodeGen code (where we allow having multiple threads) this is code is clearly wrong. If we're unlucky it either overwrites the value of the global variables or it causes the CommandLine parser to crash. So the next question is why is this only crashing in LLDB? The main reason seems to be that easiest way to crash this code is to concurrently enter the initial CommandLineParser construction where it tries to collect all the registered `cl::opt` options and checks for sanity: ``` // If it's a DefaultOption, check to make sure it isn't already there. if (O->isDefaultOption() && SC->OptionsMap.find(O->ArgStr) != SC->OptionsMap.end()) return; // Add argument to the argument map! if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr << "' registered more than once!\n"; HadErrors = true; } ``` The `OptionsMap` here is global variable and if we end up in this code with two threads at once then two threads at the same time can register an option (such as 'h') when they pass the first `if` and then we fail with the sanity check in the second `if`. After this sanity check and initial setup code the only remaining work is just parsing the provided CommandLine which isn't thread-safe but at least doesn't crash in all my attempts at breaking it (as it's usually just reading from the already generated parser state but not further modifying it). The exception to this is probably that once people actually specify the options in the code snippet above we might run into some new interesting ways to crash everything. To go back to why it's only affecting LLDB: Nearly all LLVM tools I could find (even if they are using threads) seem to call the CommandLine parser at the start so they all execute the initial parser setup at a point where there is only one thread. So once the code above is executed they are mostly safe from the sanity check crashes. We even have some shady code for the gtest `main` in `TestMain.cpp` which is why this also doesn't affect unit tests. The only exception to this rule is ... *drum roll* ... LLDB! it's not using that CommandLine library for parsing options so it also never ends up calling it in `main`. So when we end up in the `FrontendAction` code from the backtrace we are already very deep in some LLDB logic and usually already have several threads. In a situation where Swift decides to compile a large amount of Clang modules in parallel we then end up entering this code via several threads. If several threads reach this code at the same time we end up in the situation where the sanity-checking code of CommandLine crashes. I have a very reliable way of demonstrating the whole thing in D99650 (just run the unit test several times, it usually crashes after 3-4 attempts). We have several ways to fix this: 1. Make the whole CommandLine mechanism in LLVM thread-safe. 2. Get rid of `setCommandLineOpts` in `BackendUtil.cpp` and other callers of the command line parsing in generic Clang code. 3. Initialise the CommandLine library in a safe point in LLDB. Option 1 is just a lot of work and I'm not even sure where to start. The whole mechanism is based on global variables and global state and this seems like a humongous task. Option 2 is probably the best thing we can do in the near future. There are only two callers of the command line parser in generic Clang code. The one in `BackendUtils.cpp` looks like it can be replaced with some reasonable refactoring (as it only deals with two specific options). There is another one in `ExecuteCompilerInvocation` which deals with forwarding the generic `-mllvm` options to the backend which seems like it will just end up requiring us to do Option 1. Option 3 is what this patch is doing. We just parse some dummy command line invocation in a point of the LLDB execution where we only have one thread that is dealing with LLVM/Clang stuff. This way we are at least prevent the frequent crashes for users as parsing the dummy command line invocation will set up the initial parser state safely. Fixes rdar://70989856 Reviewed By: mib, JDevlieghere Differential Revision: https://reviews.llvm.org/D99652
2021-04-01 19:50:08 +02:00
// Initialize the command line parser in LLVM. This usually isn't necessary
// as we aren't dealing with command line options here, but otherwise some
// other code in Clang/LLVM might be tempted to call this function from a
// different thread later on which won't work (as the function isn't
// thread-safe).
const char *arg0 = "lldb";
llvm::cl::ParseCommandLineOptions(1, &arg0);
#define LLDB_PLUGIN(p) LLDB_PLUGIN_INITIALIZE(p);
#include "Plugins/Plugins.def"
[lldb] Implement coalescing of disjoint progress events (#84854) This implements coalescing of progress events using a timeout, as discussed in the RFC on Discourse [1]. This PR consists of two commits which, depending on the feedback, I may split up into two PRs. For now, I think it's easier to review this as a whole. 1. The first commit introduces a new generic `Alarm` class. The class lets you to schedule a function (callback) to be executed after a given timeout expires. You can cancel and reset a callback before its corresponding timeout expires. It achieves this with the help of a worker thread that sleeps until the next timeout expires. The only guarantee it provides is that your function is called no sooner than the requested timeout. Because the callback is called directly from the worker thread, a long running callback could potentially block the worker thread. I intentionally kept the implementation as simple as possible while addressing the needs for the `ProgressManager` use case. If we want to rely on this somewhere else, we can reassess whether we need to address those limitations. 2. The second commit uses the Alarm class to coalesce progress events. To recap the Discourse discussion, when multiple progress events with the same title execute in close succession, they get broadcast as one to `eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of the in-flight progress events and when the refcount hits zero, the Alarm class is used to schedule broadcasting the event. If a new progress event comes in before the alarm fires, the alarm is reset (and the process repeats when the new progress event ends). If no new event comes in before the timeout expires, the progress event is broadcast. [1] https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
// Scan for any system or user LLDB plug-ins.
PluginManager::Initialize();
// The process settings need to know about installed plug-ins, so the
// Settings must be initialized AFTER PluginManager::Initialize is called.
Debugger::SettingsInitialize();
// Use the Debugger's LLDBAssert callback.
SetLLDBAssertCallback(Debugger::AssertCallback);
// Use the system log to report errors that would otherwise get dropped.
SetLLDBErrorLog(GetLog(SystemLog::System));
LLDB_LOG(GetLog(SystemLog::System), "{0}", GetVersion());
auto LoadPlugin = [](const lldb::DebuggerSP &debugger_sp,
const FileSpec &spec,
Status &error) -> llvm::sys::DynamicLibrary {
llvm::sys::DynamicLibrary dynlib =
llvm::sys::DynamicLibrary::getPermanentLibrary(spec.GetPath().c_str());
if (dynlib.isValid()) {
typedef bool (*LLDBCommandPluginInit)(lldb::SBDebugger debugger);
lldb::SBDebugger debugger_sb(debugger_sp);
// This calls the bool lldb::PluginInitialize(lldb::SBDebugger debugger)
// function.
// TODO: mangle this differently for your system - on OSX, the first
// underscore needs to be removed and the second one stays
LLDBCommandPluginInit init_func =
(LLDBCommandPluginInit)(uintptr_t)dynlib.getAddressOfSymbol(
"_ZN4lldb16PluginInitializeENS_10SBDebuggerE");
if (init_func) {
if (init_func(debugger_sb))
return dynlib;
else
error = Status::FromErrorString(
"plug-in refused to load "
"(lldb::PluginInitialize(lldb::SBDebugger) "
"returned false)");
} else {
error = Status::FromErrorString(
"plug-in is missing the required initialization: "
"lldb::PluginInitialize(lldb::SBDebugger)");
}
} else {
if (FileSystem::Instance().Exists(spec))
error = Status::FromErrorString(
"this file does not represent a loadable dylib");
else
error = Status::FromErrorString("no such file");
}
return llvm::sys::DynamicLibrary();
};
Debugger::Initialize(LoadPlugin);
return llvm::Error::success();
}
void SystemInitializerFull::Terminate() {
Debugger::Terminate();
Debugger::SettingsTerminate();
[lldb] Implement coalescing of disjoint progress events (#84854) This implements coalescing of progress events using a timeout, as discussed in the RFC on Discourse [1]. This PR consists of two commits which, depending on the feedback, I may split up into two PRs. For now, I think it's easier to review this as a whole. 1. The first commit introduces a new generic `Alarm` class. The class lets you to schedule a function (callback) to be executed after a given timeout expires. You can cancel and reset a callback before its corresponding timeout expires. It achieves this with the help of a worker thread that sleeps until the next timeout expires. The only guarantee it provides is that your function is called no sooner than the requested timeout. Because the callback is called directly from the worker thread, a long running callback could potentially block the worker thread. I intentionally kept the implementation as simple as possible while addressing the needs for the `ProgressManager` use case. If we want to rely on this somewhere else, we can reassess whether we need to address those limitations. 2. The second commit uses the Alarm class to coalesce progress events. To recap the Discourse discussion, when multiple progress events with the same title execute in close succession, they get broadcast as one to `eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of the in-flight progress events and when the refcount hits zero, the Alarm class is used to schedule broadcasting the event. If a new progress event comes in before the alarm fires, the alarm is reset (and the process repeats when the new progress event ends). If no new event comes in before the timeout expires, the progress event is broadcast. [1] https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
// Terminate plug-ins in core LLDB.
ProcessTrace::Terminate();
[lldb] Implement coalescing of disjoint progress events (#84854) This implements coalescing of progress events using a timeout, as discussed in the RFC on Discourse [1]. This PR consists of two commits which, depending on the feedback, I may split up into two PRs. For now, I think it's easier to review this as a whole. 1. The first commit introduces a new generic `Alarm` class. The class lets you to schedule a function (callback) to be executed after a given timeout expires. You can cancel and reset a callback before its corresponding timeout expires. It achieves this with the help of a worker thread that sleeps until the next timeout expires. The only guarantee it provides is that your function is called no sooner than the requested timeout. Because the callback is called directly from the worker thread, a long running callback could potentially block the worker thread. I intentionally kept the implementation as simple as possible while addressing the needs for the `ProgressManager` use case. If we want to rely on this somewhere else, we can reassess whether we need to address those limitations. 2. The second commit uses the Alarm class to coalesce progress events. To recap the Discourse discussion, when multiple progress events with the same title execute in close succession, they get broadcast as one to `eBroadcastBitProgressCategory`. The `ProgressManager` keeps track of the in-flight progress events and when the refcount hits zero, the Alarm class is used to schedule broadcasting the event. If a new progress event comes in before the alarm fires, the alarm is reset (and the process repeats when the new progress event ends). If no new event comes in before the timeout expires, the progress event is broadcast. [1] https://discourse.llvm.org/t/rfc-improve-lldb-progress-reporting/75717/
2024-03-25 14:50:58 -07:00
// Terminate and unload and loaded system or user LLDB plug-ins.
PluginManager::Terminate();
#define LLDB_PLUGIN(p) LLDB_PLUGIN_TERMINATE(p);
#include "Plugins/Plugins.def"
// Now shutdown the common parts, in reverse order.
SystemInitializerCommon::Terminate();
}