clayborg updated this revision to Diff 332523.
clayborg added a comment.
Don't store a debugger pointer in the lldb_private::Progress class, store a
debugger ID as an optional value. Then use this value in the debugger report
progress function to do the right thing.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D97739/new/
https://reviews.llvm.org/D97739
Files:
lldb/include/lldb/API/SBBroadcaster.h
lldb/include/lldb/API/SBDebugger.h
lldb/include/lldb/Core/Debugger.h
lldb/include/lldb/Core/Progress.h
lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
lldb/source/API/SBDebugger.cpp
lldb/source/Core/CMakeLists.txt
lldb/source/Core/Debugger.cpp
lldb/source/Core/Module.cpp
lldb/source/Core/Progress.cpp
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py
lldb/tools/lldb-vscode/VSCode.cpp
lldb/tools/lldb-vscode/VSCode.h
lldb/tools/lldb-vscode/lldb-vscode.cpp
Index: lldb/tools/lldb-vscode/lldb-vscode.cpp
===================================================================
--- lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -349,6 +349,34 @@
g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
}
+void ProgressEventThreadFunction() {
+ lldb::SBListener listener("lldb-vscode.progress.listener");
+ g_vsc.debugger.GetBroadcaster().AddListener(
+ listener, lldb::SBDebugger::eBroadcastBitProgress);
+ g_vsc.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
+ lldb::SBEvent event;
+ bool done = false;
+ while (!done) {
+ if (listener.WaitForEvent(1, event)) {
+ const auto event_mask = event.GetType();
+ if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
+ if (event_mask & eBroadcastBitStopProgressThread) {
+ done = true;
+ }
+ } else {
+ uint64_t progress_id = 0;
+ uint64_t completed = 0;
+ uint64_t total = 0;
+ bool is_debugger_specific = false;
+ const char *message = lldb::SBDebugger::GetProgressFromEvent(
+ event, progress_id, completed, total, is_debugger_specific);
+ if (message)
+ g_vsc.SendProgressEvent(progress_id, message, completed, total);
+ }
+ }
+ }
+}
+
// All events from the debugger, target, process, thread and frames are
// received in this function that runs in its own thread. We are using a
// "FILE *" to output packets back to VS Code and they have mutexes in them
@@ -806,6 +834,10 @@
g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
g_vsc.event_thread.join();
}
+ if (g_vsc.progress_event_thread.joinable()) {
+ g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
+ g_vsc.progress_event_thread.join();
+ }
}
void request_exceptionInfo(const llvm::json::Object &request) {
@@ -1357,6 +1389,8 @@
// }
void request_initialize(const llvm::json::Object &request) {
g_vsc.debugger = lldb::SBDebugger::Create(true /*source_init_files*/);
+ g_vsc.progress_event_thread = std::thread(ProgressEventThreadFunction);
+
// Create an empty target right away since we might get breakpoint requests
// before we are given an executable to launch in a "launch" request, or a
// executable when attaching to a process by process ID in a "attach"
@@ -1453,6 +1487,8 @@
body.try_emplace("supportsDelayedStackTraceLoading", true);
// The debug adapter supports the 'loadedSources' request.
body.try_emplace("supportsLoadedSourcesRequest", false);
+ // The debug adapter supports sending progress reporting events.
+ body.try_emplace("supportsProgressReporting", true);
response.try_emplace("body", std::move(body));
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -68,7 +68,10 @@
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
enum class OutputType { Console, Stdout, Stderr, Telemetry };
-enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 };
+enum VSCodeBroadcasterBits {
+ eBroadcastBitStopEventThread = 1u << 0,
+ eBroadcastBitStopProgressThread = 1u << 1
+};
typedef void (*RequestCallback)(const llvm::json::Object &command);
@@ -91,6 +94,7 @@
int64_t num_locals;
int64_t num_globals;
std::thread event_thread;
+ std::thread progress_event_thread;
std::unique_ptr<std::ofstream> log;
llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref;
llvm::DenseMap<int64_t, SourceReference> source_map;
@@ -132,6 +136,9 @@
void SendOutput(OutputType o, const llvm::StringRef output);
+ void SendProgressEvent(uint64_t progress_id, const char *message,
+ uint64_t completed, uint64_t total);
+
void __attribute__((format(printf, 3, 4)))
SendFormattedOutput(OutputType o, const char *format, ...);
Index: lldb/tools/lldb-vscode/VSCode.cpp
===================================================================
--- lldb/tools/lldb-vscode/VSCode.cpp
+++ lldb/tools/lldb-vscode/VSCode.cpp
@@ -6,8 +6,10 @@
//
//===----------------------------------------------------------------------===//
+#include <chrono>
#include <fstream>
#include <mutex>
+#include <sstream>
#include <stdarg.h>
#include "LLDBUtils.h"
@@ -225,6 +227,146 @@
SendJSON(llvm::json::Value(std::move(event)));
}
+// interface ProgressStartEvent extends Event {
+// event: 'progressStart';
+//
+// body: {
+// /**
+// * An ID that must be used in subsequent 'progressUpdate' and
+// 'progressEnd'
+// * events to make them refer to the same progress reporting.
+// * IDs must be unique within a debug session.
+// */
+// progressId: string;
+//
+// /**
+// * Mandatory (short) title of the progress reporting. Shown in the UI to
+// * describe the long running operation.
+// */
+// title: string;
+//
+// /**
+// * The request ID that this progress report is related to. If specified a
+// * debug adapter is expected to emit
+// * progress events for the long running request until the request has
+// been
+// * either completed or cancelled.
+// * If the request ID is omitted, the progress report is assumed to be
+// * related to some general activity of the debug adapter.
+// */
+// requestId?: number;
+//
+// /**
+// * If true, the request that reports progress may be canceled with a
+// * 'cancel' request.
+// * So this property basically controls whether the client should use UX
+// that
+// * supports cancellation.
+// * Clients that don't support cancellation are allowed to ignore the
+// * setting.
+// */
+// cancellable?: boolean;
+//
+// /**
+// * Optional, more detailed progress message.
+// */
+// message?: string;
+//
+// /**
+// * Optional progress percentage to display (value range: 0 to 100). If
+// * omitted no percentage will be shown.
+// */
+// percentage?: number;
+// };
+// }
+//
+// interface ProgressUpdateEvent extends Event {
+// event: 'progressUpdate';
+//
+// body: {
+// /**
+// * The ID that was introduced in the initial 'progressStart' event.
+// */
+// progressId: string;
+//
+// /**
+// * Optional, more detailed progress message. If omitted, the previous
+// * message (if any) is used.
+// */
+// message?: string;
+//
+// /**
+// * Optional progress percentage to display (value range: 0 to 100). If
+// * omitted no percentage will be shown.
+// */
+// percentage?: number;
+// };
+// }
+//
+// interface ProgressEndEvent extends Event {
+// event: 'progressEnd';
+//
+// body: {
+// /**
+// * The ID that was introduced in the initial 'ProgressStartEvent'.
+// */
+// progressId: string;
+//
+// /**
+// * Optional, more detailed progress message. If omitted, the previous
+// * message (if any) is used.
+// */
+// message?: string;
+// };
+// }
+
+void VSCode::SendProgressEvent(uint64_t progress_id, const char *message,
+ uint64_t completed, uint64_t total) {
+ enum ProgressEventType {
+ progressInvalid,
+ progressStart,
+ progressUpdate,
+ progressEnd
+ };
+ const char *event_name = nullptr;
+ ProgressEventType event_type = progressInvalid;
+ if (completed == 0) {
+ event_type = progressStart;
+ event_name = "progressStart";
+ } else if (completed == total) {
+ event_type = progressEnd;
+ event_name = "progressEnd";
+ } else if (completed < total) {
+ event_type = progressUpdate;
+ event_name = "progressUpdate";
+ }
+ if (event_type == progressInvalid)
+ return;
+
+ llvm::json::Object event(CreateEventObject(event_name));
+ llvm::json::Object body;
+ std::string progress_id_str;
+ llvm::raw_string_ostream progress_id_strm(progress_id_str);
+ progress_id_strm << progress_id;
+ progress_id_strm.flush();
+ body.try_emplace("progressId", progress_id_str);
+ if (event_type == progressStart) {
+ EmplaceSafeString(body, "title", message);
+ body.try_emplace("cancellable", false);
+ }
+ auto now = std::chrono::duration<double>(
+ std::chrono::system_clock::now().time_since_epoch());
+ std::string timestamp(llvm::formatv("{0:f9}", now.count()));
+ EmplaceSafeString(body, "timestamp", timestamp);
+
+ if (0 < total && total < UINT64_MAX) {
+ uint32_t percentage = (uint32_t)(((float)completed / (float)total) * 100.0);
+ body.try_emplace("percentage", percentage);
+ }
+ event.try_emplace("body", std::move(body));
+ SendJSON(llvm::json::Value(std::move(event)));
+}
+
void __attribute__((format(printf, 3, 4)))
VSCode::SendFormattedOutput(OutputType o, const char *format, ...) {
char buffer[1024];
Index: lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py
===================================================================
--- lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py
+++ lldb/test/API/tools/lldb-vscode/launch/TestVSCode_launch.py
@@ -440,7 +440,7 @@
'''
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
-
+
terminateCommands = ['expr 4+2']
self.launch(program=program,
terminateCommands=terminateCommands)
@@ -450,3 +450,57 @@
self.vscode.request_disconnect(terminateDebuggee=True)
output = self.collect_console(duration=1.0)
self.verify_commands('terminateCommands', output, terminateCommands)
+
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_progress_events(self):
+ '''
+ Tests the progress events to ensure we are receiving them.
+ '''
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+ # Set a breakpoint at 'main'. This will cause all of the symbol tables
+ # for all modules in LLDB to be parsed and we should get a progress
+ # event for each shared library.
+ breakpoint_ids = self.set_function_breakpoints(['main'])
+ self.continue_to_breakpoints(breakpoint_ids)
+ # Make sure we at least got some progress events
+ self.assertTrue(len(self.vscode.progress_events) > 0)
+ # Track all 'progressStart' events by saving all 'progressId' values.
+ progressStart_ids = set()
+ # Track all 'progressEnd' events by saving all 'progressId' values.
+ progressEnd_ids = set()
+ # We will watch for events whose title starts with
+ # 'Parsing symbol table for ' and we will save the remainder of the
+ # line which will contain the shared library basename. Since we set a
+ # breakpoint by name for 'main', we will expect to see progress events
+ # for all shared libraries that say that the symbol table is being
+ # parsed.
+ symtab_progress_shlibs = set()
+ # Get a list of modules in the current target so we can verify that
+ # we do in fact get a progress event for each shared library.
+ target_shlibs = self.vscode.get_modules()
+
+ # Iterate over all progress events and save all start and end IDs, and
+ # remember any shared libraries that got symbol table parsing progress
+ # events.
+ for progress_event in self.vscode.progress_events:
+ event_type = progress_event['event']
+ if event_type == 'progressStart':
+ progressStart_ids.add(progress_event['body']['progressId'])
+ title = progress_event['body']['title']
+ if title.startswith('Parsing symbol table for '):
+ symtab_progress_shlibs.add(title[25:])
+ if event_type == 'progressEnd':
+ progressEnd_ids.add(progress_event['body']['progressId'])
+ # Make sure for each 'progressStart' event, we got a matching
+ # 'progressEnd' event.
+ self.assertTrue(progressStart_ids == progressEnd_ids,
+ ('Make sure we got a "progressEnd" for each '
+ '"progressStart" event that we have.'))
+ # Verify we got a symbol table parsing progress event for each shared
+ # library in our target.
+ for target_shlib_basename in target_shlibs.keys():
+ self.assertTrue(target_shlib_basename in symtab_progress_shlibs,
+ 'Make sure we got a symbol table progress event for "%s"' % (target_shlib_basename))
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -16,6 +16,7 @@
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Progress.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/Value.h"
@@ -74,6 +75,7 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
#include <algorithm>
#include <map>
@@ -467,22 +469,32 @@
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) {
+ StreamString module_desc;
+ GetObjectFile()->GetModule()->GetDescription(module_desc.AsRawOstream(),
+ lldb::eDescriptionLevelBrief);
DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc;
LoadSectionData(eSectionTypeDWARFAppleNames, apple_names);
LoadSectionData(eSectionTypeDWARFAppleNamespaces, apple_namespaces);
LoadSectionData(eSectionTypeDWARFAppleTypes, apple_types);
LoadSectionData(eSectionTypeDWARFAppleObjC, apple_objc);
- m_index = AppleDWARFIndex::Create(
- *GetObjectFile()->GetModule(), apple_names, apple_namespaces,
- apple_types, apple_objc, m_context.getOrLoadStrData());
+ if (apple_names.GetByteSize() > 0 || apple_namespaces.GetByteSize() > 0 ||
+ apple_types.GetByteSize() > 0 || apple_objc.GetByteSize() > 0) {
+ Progress progress(llvm::formatv("Loading Apple DWARF index for {0}",
+ module_desc.GetData()));
+ m_index = AppleDWARFIndex::Create(
+ *GetObjectFile()->GetModule(), apple_names, apple_namespaces,
+ apple_types, apple_objc, m_context.getOrLoadStrData());
- if (m_index)
- return;
+ if (m_index)
+ return;
+ }
DWARFDataExtractor debug_names;
LoadSectionData(eSectionTypeDWARFDebugNames, debug_names);
if (debug_names.GetByteSize() > 0) {
+ Progress progress(
+ llvm::formatv("Loading DWARF5 index for {0}", module_desc.GetData()));
llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or =
DebugNamesDWARFIndex::Create(*GetObjectFile()->GetModule(),
debug_names,
Index: lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
@@ -13,9 +13,11 @@
#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
#include "lldb/Core/Module.h"
+#include "lldb/Core/Progress.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/Timer.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ThreadPool.h"
using namespace lldb_private;
@@ -56,6 +58,17 @@
if (units_to_index.empty())
return;
+ StreamString module_desc;
+ m_module.GetDescription(module_desc.AsRawOstream(),
+ lldb::eDescriptionLevelBrief);
+
+ // Include 2 passes per unit to index for extracting DIEs from the unit and
+ // indexing the unit, and then 8 extra entries for finalizing each index set.
+ const uint64_t total_progress = units_to_index.size() * 2 + 8;
+ Progress progress(
+ llvm::formatv("Manually indexing DWARF for {0}", module_desc.GetData()),
+ total_progress);
+
std::vector<IndexSet> sets(units_to_index.size());
// Keep memory down by clearing DIEs for any units if indexing
@@ -64,10 +77,12 @@
units_to_index.size());
auto parser_fn = [&](size_t cu_idx) {
IndexUnit(*units_to_index[cu_idx], dwp_dwarf, sets[cu_idx]);
+ progress.Increment();
};
- auto extract_fn = [&units_to_index, &clear_cu_dies](size_t cu_idx) {
+ auto extract_fn = [&](size_t cu_idx) {
clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped();
+ progress.Increment();
};
// Share one thread pool across operations to avoid the overhead of
@@ -92,11 +107,12 @@
pool.async(parser_fn, i);
pool.wait();
- auto finalize_fn = [this, &sets](NameToDIE(IndexSet::*index)) {
+ auto finalize_fn = [this, &sets, &progress](NameToDIE(IndexSet::*index)) {
NameToDIE &result = m_set.*index;
for (auto &set : sets)
result.Append(set.*index);
result.Finalize();
+ progress.Increment();
};
pool.async(finalize_fn, &IndexSet::function_basenames);
Index: lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -17,6 +17,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Progress.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Host/Host.h"
@@ -43,6 +44,7 @@
#include "lldb/Host/SafeMachO.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "ObjectFileMachO.h"
@@ -2167,6 +2169,9 @@
if (!module_sp)
return 0;
+ Progress progress(llvm::formatv("Parsing symbol table for {0}",
+ m_file.GetFilename().AsCString("<Unknown>")));
+
struct symtab_command symtab_load_command = {0, 0, 0, 0, 0, 0};
struct linkedit_data_command function_starts_load_command = {0, 0, 0, 0};
struct dyld_info_command dyld_info = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Index: lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -16,6 +16,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Progress.h"
#include "lldb/Core/Section.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/LZMA.h"
@@ -37,6 +38,7 @@
#include "llvm/Object/Decompressor.h"
#include "llvm/Support/ARMBuildAttributes.h"
#include "llvm/Support/CRC.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/MipsABIFlags.h"
@@ -1861,7 +1863,7 @@
// unified section list.
if (GetType() != eTypeDebugInfo)
unified_section_list = *m_sections_up;
-
+
// If there's a .gnu_debugdata section, we'll try to read the .symtab that's
// embedded in there and replace the one in the original object file (if any).
// If there's none in the orignal object file, we add it to it.
@@ -1923,7 +1925,7 @@
ArchSpec spec = m_gnu_debug_data_object_file->GetArchitecture();
if (spec && m_gnu_debug_data_object_file->SetModulesArchitecture(spec))
return m_gnu_debug_data_object_file;
-
+
return nullptr;
}
@@ -2707,6 +2709,9 @@
if (!module_sp)
return nullptr;
+ Progress progress(llvm::formatv("Parsing symbol table for {0}",
+ m_file.GetFilename().AsCString("<Unknown>")));
+
// We always want to use the main object file so we (hopefully) only have one
// cached copy of our symtab, dynamic sections, etc.
ObjectFile *module_obj_file = module_sp->GetObjectFile();
Index: lldb/source/Core/Progress.cpp
===================================================================
--- /dev/null
+++ lldb/source/Core/Progress.cpp
@@ -0,0 +1,60 @@
+//===-- Progress.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/Core/Progress.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+std::atomic<uint64_t> Progress::g_id(0);
+
+Progress::Progress(std::string title, uint64_t total,
+ lldb_private::Debugger *debugger)
+ : m_title(title), m_id(++g_id), m_completed(0), m_total(total) {
+ assert(total > 0);
+ if (debugger)
+ m_debugger_id = debugger->GetID();
+ std::lock_guard<std::mutex> guard(m_mutex);
+ ReportProgress();
+}
+
+Progress::~Progress() {
+ // Make sure to always report progress completed when this object is
+ // destructed so it indicates the progress dialog/activity should go away.
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (!m_completed) {
+ m_completed = m_total;
+ ReportProgress();
+ }
+}
+
+void Progress::Increment(uint64_t amount) {
+ if (amount > 0) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ // Watch out for unsigned overflow and make sure we don't increment too
+ // much and exceed m_total.
+ if (amount > (m_total - m_completed))
+ m_completed = m_total;
+ else
+ m_completed += amount;
+ ReportProgress();
+ }
+}
+
+void Progress::ReportProgress() {
+ if (!m_complete) {
+ // Make sure we only send one notification that indicates the progress is
+ // complete.
+ m_complete = m_completed == m_total;
+ Debugger::ReportProgress(m_id, m_title, m_completed, m_total,
+ m_debugger_id);
+ }
+}
Index: lldb/source/Core/Module.cpp
===================================================================
--- lldb/source/Core/Module.cpp
+++ lldb/source/Core/Module.cpp
@@ -1072,8 +1072,6 @@
void Module::GetDescription(llvm::raw_ostream &s,
lldb::DescriptionLevel level) {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
-
if (level >= eDescriptionLevelFull) {
if (m_arch.IsValid())
s << llvm::formatv("({0}) ", m_arch.GetArchitectureName());
Index: lldb/source/Core/Debugger.cpp
===================================================================
--- lldb/source/Core/Debugger.cpp
+++ lldb/source/Core/Debugger.cpp
@@ -661,6 +661,11 @@
return target_sp;
}
+ConstString Debugger::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.debugger");
+ return class_name;
+}
+
Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
: UserID(g_unique_id++),
Properties(std::make_shared<OptionValueProperties>()),
@@ -677,6 +682,8 @@
m_io_handler_stack(), m_instance_name(), m_loaded_plugins(),
m_event_handler_thread(), m_io_handler_thread(),
m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
+ m_broadcaster(m_broadcaster_manager_sp,
+ GetStaticBroadcasterClass().AsCString()),
m_forward_listener_sp(), m_clear_once() {
m_instance_name.SetString(llvm::formatv("debugger_{0}", GetID()).str());
if (log_callback)
@@ -1137,6 +1144,74 @@
std::make_shared<StreamCallback>(log_callback, baton);
}
+ConstString Debugger::ProgressEventData::GetFlavorString() {
+ static ConstString g_flavor("Debugger::ProgressEventData");
+ return g_flavor;
+}
+
+ConstString Debugger::ProgressEventData::GetFlavor() const {
+ return Debugger::ProgressEventData::GetFlavorString();
+}
+
+void Debugger::ProgressEventData::Dump(Stream *s) const {
+ s->Printf(" id = %" PRIu64 ", message = \"%s\"", m_id, m_message.c_str());
+ if (m_completed == 0 || m_completed == m_total)
+ s->Printf(", type = %s", m_completed == 0 ? "start" : "end");
+ else
+ s->PutCString(", type = update");
+ // If m_total is UINT64_MAX, there is no progress to report, just "start"
+ // and "end". If it isn't we will show the completed and total amounts.
+ if (m_total != UINT64_MAX)
+ s->Printf(", progress = %" PRIu64 " of %" PRIu64, m_completed, m_total);
+}
+
+const Debugger::ProgressEventData *
+Debugger::ProgressEventData::GetEventDataFromEvent(const Event *event_ptr) {
+ if (event_ptr)
+ if (const EventData *event_data = event_ptr->GetData())
+ if (event_data->GetFlavor() == ProgressEventData::GetFlavorString())
+ return static_cast<const ProgressEventData *>(event_ptr->GetData());
+ return nullptr;
+}
+
+static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id,
+ const std::string &message,
+ uint64_t completed, uint64_t total,
+ bool is_debugger_specific) {
+ // Only deliver progress events if we have any progress listeners.
+ const uint32_t event_type = Debugger::eBroadcastBitProgress;
+ if (!debugger.GetBroadcaster().EventTypeHasListeners(event_type))
+ return;
+ EventSP event_sp(new Event(event_type, new Debugger::ProgressEventData(
+ progress_id, message, completed,
+ total, is_debugger_specific)));
+ debugger.GetBroadcaster().BroadcastEvent(event_sp);
+}
+
+void Debugger::ReportProgress(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total,
+ llvm::Optional<lldb::user_id_t> debugger_id) {
+ // Check if this progress is for a specific debugger.
+ if (debugger_id.hasValue()) {
+ // It is debugger specific, grab it and deliver the event if the debugger
+ // still exists.
+ DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id);
+ if (debugger_sp)
+ PrivateReportProgress(*debugger_sp, progress_id, message, completed,
+ total, /*is_debugger_specific*/ true);
+ return;
+ }
+ // The progress event is not debugger specific, iterate over all debuggers
+ // and deliver a progress event to each one.
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos)
+ PrivateReportProgress(*(*pos), progress_id, message, completed, total,
+ /*is_debugger_specific*/ false);
+ }
+}
+
bool Debugger::EnableLog(llvm::StringRef channel,
llvm::ArrayRef<const char *> categories,
llvm::StringRef log_file, uint32_t log_options,
Index: lldb/source/Core/CMakeLists.txt
===================================================================
--- lldb/source/Core/CMakeLists.txt
+++ lldb/source/Core/CMakeLists.txt
@@ -43,6 +43,7 @@
ModuleList.cpp
Opcode.cpp
PluginManager.cpp
+ Progress.cpp
RichManglingContext.cpp
SearchFilter.cpp
Section.cpp
Index: lldb/source/API/SBDebugger.cpp
===================================================================
--- lldb/source/API/SBDebugger.cpp
+++ lldb/source/API/SBDebugger.cpp
@@ -38,6 +38,7 @@
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Progress.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/DataFormatters/DataVisualization.h"
@@ -149,6 +150,41 @@
return LLDB_RECORD_RESULT(*this);
}
+const char *SBDebugger::GetBroadcasterClass() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBDebugger,
+ GetBroadcasterClass);
+
+ return Debugger::GetStaticBroadcasterClass().AsCString();
+}
+
+const char *SBDebugger::GetProgressFromEvent(const lldb::SBEvent &event,
+ uint64_t &progress_id,
+ uint64_t &completed,
+ uint64_t &total,
+ bool &is_debugger_specific) {
+ const Debugger::ProgressEventData *progress_data =
+ Debugger::ProgressEventData::GetEventDataFromEvent(event.get());
+ if (progress_data == nullptr)
+ return nullptr;
+ progress_id = progress_data->GetID();
+ completed = progress_data->GetCompleted();
+ total = progress_data->GetTotal();
+ is_debugger_specific = progress_data->IsDebuggerSpecific();
+ // We must record the static method _after_ the out paramters have been
+ // filled in.
+ LLDB_RECORD_STATIC_METHOD(
+ const char *, SBDebugger, GetProgressFromEvent,
+ (const lldb::SBEvent &, uint64_t &, uint64_t &, uint64_t &, bool &),
+ event, progress_id, completed, total, is_debugger_specific);
+ return LLDB_RECORD_RESULT(progress_data->GetMessage().c_str())
+}
+
+SBBroadcaster SBDebugger::GetBroadcaster() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBroadcaster, SBDebugger, GetBroadcaster);
+ SBBroadcaster broadcaster(&m_opaque_sp->GetBroadcaster(), false);
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
void SBDebugger::Initialize() {
LLDB_RECORD_STATIC_METHOD_NO_ARGS(void, SBDebugger, Initialize);
SBError ignored = SBDebugger::InitializeWithErrorHandling();
@@ -824,7 +860,7 @@
if (error.Success())
sb_target.SetSP(target_sp);
}
-
+
LLDB_LOGF(log,
"SBDebugger(%p)::CreateTargetWithFileAndArch (filename=\"%s\", "
"arch=%s) => SBTarget(%p)",
@@ -1711,6 +1747,12 @@
LLDB_REGISTER_METHOD(void, SBDebugger, Clear, ());
LLDB_REGISTER_STATIC_METHOD(lldb::SBDebugger, SBDebugger, Create, ());
LLDB_REGISTER_STATIC_METHOD(lldb::SBDebugger, SBDebugger, Create, (bool));
+ LLDB_REGISTER_STATIC_METHOD(
+ const char *, SBDebugger, GetProgressFromEvent,
+ (const lldb::SBEvent &, uint64_t &, uint64_t &, uint64_t &, bool &));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, GetBroadcasterClass,
+ ());
+ LLDB_REGISTER_METHOD(SBBroadcaster, SBDebugger, GetBroadcaster, ());
LLDB_REGISTER_STATIC_METHOD(void, SBDebugger, Destroy, (lldb::SBDebugger &));
LLDB_REGISTER_STATIC_METHOD(void, SBDebugger, MemoryPressureDetected, ());
LLDB_REGISTER_METHOD_CONST(bool, SBDebugger, IsValid, ());
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
@@ -113,6 +113,7 @@
self.initialize_body = None
self.thread_stop_reasons = {}
self.breakpoint_events = []
+ self.progress_events = []
self.sequence = 1
self.threads = None
self.recv_thread.start()
@@ -225,6 +226,13 @@
self.breakpoint_events.append(packet)
# no need to add 'breakpoint' event packets to our packets list
return keepGoing
+ elif event.startswith('progress'):
+ # Progress events come in as 'progressStart', 'progressUpdate',
+ # and 'progressEnd' events. Keep these around in case test
+ # cases want to verify them.
+ self.progress_events.append(packet)
+ # No need to add 'progress' event packets to our packets list.
+ return keepGoing
elif packet_type == 'response':
if packet['command'] == 'disconnect':
Index: lldb/include/lldb/Core/Progress.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Core/Progress.h
@@ -0,0 +1,114 @@
+//===-- Progress.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_CORE_PROGRESS_H
+#define LLDB_CORE_PROGRESS_H
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-types.h"
+#include <atomic>
+#include <mutex>
+
+namespace lldb_private {
+
+/// A Progress indicator helper class.
+///
+/// Any potentially long running sections of code in LLDB should report
+/// progress so that clients are aware of delays that might appear during
+/// debugging. Delays commonly include indexing debug information, parsing
+/// symbol tables for object files, downloading symbols from remote
+/// repositories, and many more things.
+///
+/// The Progress class helps make sure that progress is correctly reported
+/// and will always send an initial progress update, updates when
+/// Progress::Increment() is called, and also will make sure that a progress
+/// completed update is reported even if the user doesn't explicitly cause one
+/// to be sent.
+///
+/// The progress is reported via a callback whose type is ProgressCallback:
+///
+/// typedef void (*ProgressCallback)(uint64_t progress_id,
+/// const char *message,
+/// uint64_t completed,
+/// uint64_t total,
+/// void *baton);
+///
+/// This callback will always initially be called with "completed" set to zero
+/// and "total" set to the total amount specified in the contructor. This is
+/// considered the progress start event. As Progress::Increment() is called,
+/// the callback will be called as long as the Progress::m_completed has not
+/// yet exceeded the Progress::m_total. When the callback is called with
+/// Progress::m_completed == Progress::m_total, that is considered a progress
+/// completed event. If Progress::m_completed is non-zero and less than
+/// Progress::m_total, then this is considered a progress update event.
+///
+/// This callback will be called in the destructor if Progress::m_completed is
+/// not equal to Progress::m_total with the "completed" set to
+/// Progress::m_total. This ensures we always send a progress completed update
+/// even if the user does not.
+
+class Progress {
+public:
+ /// Construct a progress object that will report information.
+ ///
+ /// The constructor will create a unique progress reporting object and
+ /// immediately send out a progress update by calling the installed callback
+ /// with completed set to zero out of the specified total.
+ ///
+ /// @param [in] title The title of this progress activity.
+ ///
+ /// @param [in] total The total units of work to be done if specified, if
+ /// set to UINT64_MAX then an indeterminate progress indicator should be
+ /// displayed.
+ ///
+ /// @param [in] debugger An optional debugger pointer to specify that this
+ /// progress is to be reported only to specific debuggers.
+ Progress(std::string title, uint64_t total = UINT64_MAX,
+ lldb_private::Debugger *debugger = nullptr);
+
+ /// Destroy the progress object.
+ ///
+ /// If the progress has not yet sent a completion update, the destructor
+ /// will send out a notification where the completed == m_total. This ensures
+ /// that we always send out a progress complete notification.
+ ~Progress();
+
+ /// Increment the progress and send a notification to the intalled callback.
+ ///
+ /// If incrementing ends up exceeding m_total, m_completed will be updated
+ /// to match m_total and no subsequent progress notifications will be sent.
+ /// If no total was specified in the constructor, this function will not do
+ /// anything nor send any progress updates.
+ ///
+ /// @param [in] amount The amount to increment m_completed by.
+ void Increment(uint64_t amount = 1);
+
+private:
+ void ReportProgress();
+ static std::atomic<uint64_t> g_id;
+ /// The title of the progress activity.
+ std::string m_title;
+ std::mutex m_mutex;
+ /// A unique integer identifier for progress reporting.
+ const uint64_t m_id;
+ /// How much work ([0...m_total]) that has been completed.
+ uint64_t m_completed;
+ /// Total amount of work, UINT64_MAX for non deterministic progress.
+ const uint64_t m_total;
+ /// The optional debugger ID to report progress to. If this has no value then
+ /// all debuggers will receive this event.
+ llvm::Optional<lldb::user_id_t> m_debugger_id;
+ /// Set to true when progress has been reported where m_completed == m_total
+ /// to ensure that we don't send progress updates after progress has
+ /// completed.
+ bool m_complete = false;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_CORE_PROGRESS_H
Index: lldb/include/lldb/Core/Debugger.h
===================================================================
--- lldb/include/lldb/Core/Debugger.h
+++ lldb/include/lldb/Core/Debugger.h
@@ -73,6 +73,50 @@
friend class SourceManager; // For GetSourceFileCache.
public:
+ /// Broadcaster event bits definitions.
+ enum {
+ eBroadcastBitProgress = (1 << 0),
+ };
+
+ static ConstString GetStaticBroadcasterClass();
+
+ /// Get the public broadcaster for this debugger.
+ Broadcaster &GetBroadcaster() { return m_broadcaster; }
+ const Broadcaster &GetBroadcaster() const { return m_broadcaster; }
+
+ class ProgressEventData : public EventData {
+
+ public:
+ ProgressEventData(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total,
+ bool debugger_specific)
+ : m_message(message), m_id(progress_id), m_completed(completed),
+ m_total(total), m_debugger_specific(debugger_specific) {}
+
+ static ConstString GetFlavorString();
+
+ ConstString GetFlavor() const override;
+
+ void Dump(Stream *s) const override;
+
+ static const ProgressEventData *
+ GetEventDataFromEvent(const Event *event_ptr);
+ uint64_t GetID() const { return m_id; }
+ uint64_t GetCompleted() const { return m_completed; }
+ uint64_t GetTotal() const { return m_total; }
+ const std::string &GetMessage() const { return m_message; }
+ bool IsDebuggerSpecific() const { return m_debugger_specific; }
+
+ private:
+ std::string m_message;
+ const uint64_t m_id;
+ uint64_t m_completed;
+ const uint64_t m_total;
+ const bool m_debugger_specific;
+ ProgressEventData(const ProgressEventData &) = delete;
+ const ProgressEventData &operator=(const ProgressEventData &) = delete;
+ };
+
~Debugger() override;
static lldb::DebuggerSP
@@ -346,6 +390,40 @@
protected:
friend class CommandInterpreter;
friend class REPL;
+ friend class Progress;
+
+ /// Report progress events.
+ ///
+ /// Progress events will be delivered to any debuggers that have listeners
+ /// for the eBroadcastBitProgress. This function is called by the
+ /// lldb_private::Progress class to deliver the events to any debuggers that
+ /// qualify.
+ ///
+ /// \param [in] progress_id
+ /// The unique integer identifier for the progress to report.
+ ///
+ /// \param[in] message
+ /// The title of the progress dialog to display in the UI.
+ ///
+ /// \param [in] completed
+ /// The amount of work completed. If \a completed is zero, then this event
+ /// is a progress started event. If \a completed is equal to \a total, then
+ /// this event is a progress end event. Otherwise completed indicates the
+ /// current progress compare to the total value.
+ ///
+ /// \param [in] total
+ /// The total amount of work units that need to be completed. If this value
+ /// is UINT64_MAX, then an indeterminate progress indicator should be
+ /// displayed.
+ ///
+ /// \param [in] debugger_id
+ /// If this optional parameter has a value, it indicates the unique
+ /// debugger identifier that this progress should be delivered to. If this
+ /// optional parameter does not have a value, the the progress will be
+ /// delivered to all debuggers.
+ static void ReportProgress(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total,
+ llvm::Optional<lldb::user_id_t> debugger_id);
bool StartEventHandlerThread();
@@ -432,7 +510,8 @@
LoadedPluginsList m_loaded_plugins;
HostThread m_event_handler_thread;
HostThread m_io_handler_thread;
- Broadcaster m_sync_broadcaster;
+ Broadcaster m_sync_broadcaster; ///< Private debugger synchronization.
+ Broadcaster m_broadcaster; ///< Public Debugger event broadcaster.
lldb::ListenerSP m_forward_listener_sp;
llvm::once_flag m_clear_once;
lldb::TargetSP m_dummy_target_sp;
Index: lldb/include/lldb/API/SBDebugger.h
===================================================================
--- lldb/include/lldb/API/SBDebugger.h
+++ lldb/include/lldb/API/SBDebugger.h
@@ -33,6 +33,8 @@
class LLDB_API SBDebugger {
public:
+ FLAGS_ANONYMOUS_ENUM(){eBroadcastBitProgress = (1 << 0)};
+
SBDebugger();
SBDebugger(const lldb::SBDebugger &rhs);
@@ -41,6 +43,42 @@
~SBDebugger();
+ static const char *GetBroadcasterClass();
+
+ lldb::SBBroadcaster GetBroadcaster();
+
+ /// Get progress data from a SBEvent whose type is eBroadcastBitProgress.
+ ///
+ /// \param [in] event
+ /// The event to extract the progress information from.
+ ///
+ /// \param [out] progress_id
+ /// The unique integer identifier for the progress to report.
+ ///
+ /// \param [out] completed
+ /// The amount of work completed. If \a completed is zero, then this event
+ /// is a progress started event. If \a completed is equal to \a total, then
+ /// this event is a progress end event. Otherwise completed indicates the
+ /// current progress update.
+ ///
+ /// \param [out] total
+ /// The total amount of work units that need to be completed. If this value
+ /// is UINT64_MAX, then an indeterminate progress indicator should be
+ /// displayed.
+ ///
+ /// \param [out] is_debugger_specific
+ /// Set to true if this progress is specific to this debugger only. Many
+ /// progress events are not specific to a debugger instance, like any
+ /// progress events for loading information in modules since LLDB has a
+ /// global module cache that all debuggers use.
+ ///
+ /// \return The message for the progress. If the returned value is NULL, then
+ /// \a event was not a eBroadcastBitProgress event.
+ static const char *GetProgressFromEvent(const lldb::SBEvent &event,
+ uint64_t &progress_id,
+ uint64_t &completed, uint64_t &total,
+ bool &is_debugger_specific);
+
lldb::SBDebugger &operator=(const lldb::SBDebugger &rhs);
static void Initialize();
Index: lldb/include/lldb/API/SBBroadcaster.h
===================================================================
--- lldb/include/lldb/API/SBBroadcaster.h
+++ lldb/include/lldb/API/SBBroadcaster.h
@@ -63,6 +63,7 @@
protected:
friend class SBCommandInterpreter;
friend class SBCommunication;
+ friend class SBDebugger;
friend class SBEvent;
friend class SBListener;
friend class SBProcess;
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits