clayborg updated this revision to Diff 332383.
clayborg added a comment.
Removed all callback related code and now only use events.
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;
+ const char *message = lldb::SBDebugger::GetProgressFromEvent(
+ event, progress_id, completed, total);
+ 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"
@@ -1893,9 +1895,9 @@
filename = first_section_sp->GetObjectFile()->GetFileSpec().GetPath();
Host::SystemLog(Host::eSystemLogError,
- "error: unable to find section %d for a symbol in %s, corrupt file?\n",
- n_sect,
- filename.c_str());
+ "error: unable to find section %d for a symbol in "
+ "%s, corrupt file?\n",
+ n_sect, filename.c_str());
}
}
if (m_section_infos[n_sect].vm_range.Contains(file_addr)) {
@@ -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.
@@ -1879,7 +1881,7 @@
unified_section_list.AddSection(symtab_section_sp);
}
}
- }
+ }
}
std::shared_ptr<ObjectFileELF> ObjectFileELF::GetGnuDebugDataObjectFile() {
@@ -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,56 @@
+//===-- 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)
+ : m_title(title), m_id(++g_id), m_completed(0), m_total(total) {
+ assert(total > 0);
+ 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);
+ }
+}
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,9 +661,15 @@
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>()),
+ Broadcaster(nullptr, GetStaticBroadcasterClass().AsCString()),
m_input_file_sp(std::make_shared<NativeFile>(stdin, false)),
m_output_stream_sp(std::make_shared<StreamFile>(stdout, false)),
m_error_stream_sp(std::make_shared<StreamFile>(stderr, false)),
@@ -1137,6 +1143,59 @@
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;
+}
+
+void Debugger::ReportProgress(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total) {
+
+ 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)
+ (*pos)->ReportProgressPrivate(progress_id, message, completed, total);
+ }
+}
+
+void Debugger::ReportProgressPrivate(uint64_t progress_id,
+ const std::string &message,
+ uint64_t completed, uint64_t total) {
+ // Only deliver progress events if we have any progress listeners.
+ if (!EventTypeHasListeners(eBroadcastBitProgress))
+ return;
+ EventSP event_sp(
+ new Event(eBroadcastBitProgress,
+ new ProgressEventData(progress_id, message, completed, total)));
+ BroadcastEvent(event_sp);
+}
+
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,39 @@
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) {
+ 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();
+ // 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 &), event,
+ progress_id, completed, total);
+ 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.get(), false);
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
void SBDebugger::Initialize() {
LLDB_RECORD_STATIC_METHOD_NO_ARGS(void, SBDebugger, Initialize);
SBError ignored = SBDebugger::InitializeWithErrorHandling();
@@ -808,23 +842,25 @@
// The version of CreateTarget that takes an ArchSpec won't accept an
// empty ArchSpec, so when the arch hasn't been specified, we need to
// call the target triple version.
- error = m_opaque_sp->GetTargetList().CreateTarget(*m_opaque_sp, filename,
- arch_cstr, eLoadDependentsYes, nullptr, target_sp);
+ error = m_opaque_sp->GetTargetList().CreateTarget(
+ *m_opaque_sp, filename, arch_cstr, eLoadDependentsYes, nullptr,
+ target_sp);
} else {
PlatformSP platform_sp = m_opaque_sp->GetPlatformList()
.GetSelectedPlatform();
- ArchSpec arch = Platform::GetAugmentedArchSpec(platform_sp.get(),
- arch_cstr);
+ ArchSpec arch =
+ Platform::GetAugmentedArchSpec(platform_sp.get(), arch_cstr);
if (arch.IsValid())
- error = m_opaque_sp->GetTargetList().CreateTarget(*m_opaque_sp, filename,
- arch, eLoadDependentsYes, platform_sp, target_sp);
+ error = m_opaque_sp->GetTargetList().CreateTarget(
+ *m_opaque_sp, filename, arch, eLoadDependentsYes, platform_sp,
+ target_sp);
else
error.SetErrorStringWithFormat("invalid arch_cstr: %s", arch_cstr);
}
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 &));
+ 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,107 @@
+//===-- 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 there should be no progress displayed and just show a
+ /// spinning progress indicator.
+ Progress(std::string title, uint64_t total = UINT64_MAX);
+
+ /// 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, llvm::None for non deterministic progress.
+ const uint64_t m_total;
+ /// 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
@@ -69,10 +69,48 @@
class Debugger : public std::enable_shared_from_this<Debugger>,
public UserID,
- public Properties {
+ public Properties,
+ public Broadcaster {
friend class SourceManager; // For GetSourceFileCache.
public:
+ /// Broadcaster event bits definitions.
+ enum {
+ eBroadcastBitProgress = (1 << 0),
+ };
+
+ static ConstString GetStaticBroadcasterClass();
+
+ class ProgressEventData : public EventData {
+
+ public:
+ ProgressEventData(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total)
+ : m_id(progress_id), m_message(message), m_completed(completed),
+ m_total(total) {}
+
+ 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; }
+
+ private:
+ const uint64_t m_id;
+ std::string m_message;
+ uint64_t m_completed;
+ const uint64_t m_total;
+ ProgressEventData(const ProgressEventData &) = delete;
+ const ProgressEventData &operator=(const ProgressEventData &) = delete;
+ };
+
~Debugger() override;
static lldb::DebuggerSP
@@ -346,6 +384,14 @@
protected:
friend class CommandInterpreter;
friend class REPL;
+ friend class Progress;
+
+ // Called by the lldb_private::Progress class only.
+ static void ReportProgress(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total);
+
+ void ReportProgressPrivate(uint64_t progress_id, const std::string &message,
+ uint64_t completed, uint64_t total);
bool StartEventHandlerThread();
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,35 @@
~SBDebugger();
+ static const char *GetBroadcasterClass();
+
+ lldb::SBBroadcaster GetBroadcaster();
+
+ /// Get progress data from a SBEvent that whose type is eBroadcastBitProgress.
+ ///
+ /// \param [in] event
+ /// The event to extract the progress information from.
+ ///
+ /// \param [in] progress_id
+ /// The unique integer identifier for the progress to report.
+ ///
+ /// \param [in] completed
+ /// The amount of work completed. It \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 [in] total
+ /// The total amount of work units that need to be completed. If this value
+ /// is UINT64_MAX, then the progress should be displayed as a spinning
+ /// progress with no deterministic value.
+ ///
+ /// \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);
+
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