clayborg updated this revision to Diff 327871.
clayborg added a comment.
Update fixes:
- make progress callback and baton members of a lldb_private::Debugger object
to allow each debugger to have their own callbacks.
- switch to have std::string title and clients use llvm::formatv() to create
the title to avoid printf variadic args in progress constructor
- make the progress class thread safe using a mutex
- verified that lldb-vscode callback is threadsafe
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/SBDebugger.h
lldb/include/lldb/Core/Debugger.h
lldb/include/lldb/Core/Progress.h
lldb/include/lldb/lldb-types.h
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/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
@@ -100,6 +100,12 @@
typedef void (*RequestCallback)(const llvm::json::Object &command);
+static void LLDBProgressCallback(uint64_t progress_id, const char *message,
+ uint64_t completed, uint64_t total,
+ void *baton) {
+ g_vsc.SendProgressEvent(progress_id, message, completed, total);
+}
+
enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
SOCKET AcceptConnection(int portno) {
@@ -1352,6 +1358,8 @@
// }
void request_initialize(const llvm::json::Object &request) {
g_vsc.debugger = lldb::SBDebugger::Create(true /*source_init_files*/);
+ g_vsc.debugger.SetProgressCallback(LLDBProgressCallback, nullptr);
+
// 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"
@@ -1448,6 +1456,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
@@ -132,6 +132,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
@@ -40,7 +40,7 @@
focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
stop_at_entry(false), is_attach(false),
reverse_request_seq(0), waiting_for_run_in_terminal(false) {
- const char *log_file_path = getenv("LLDBVSCODE_LOG");
+ const char *log_file_path = "/tmp/vscode.txt"; // getenv("LLDBVSCODE_LOG");
#if defined(_WIN32)
// Windows opens stdout and stdin in text mode which converts \n to 13,10
// while the value is just 10 on Darwin/Linux. Setting the file mode to binary
@@ -225,6 +225,142 @@
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);
+ }
+
+ 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/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_progress_id(0);
+
+Progress::Progress(std::string title, uint64_t total)
+ : m_title(title), m_completed(0), m_total(total), m_id(++g_progress_id) {
+ 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.c_str(), 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
@@ -1137,6 +1137,26 @@
std::make_shared<StreamCallback>(log_callback, baton);
}
+void Debugger::SetProgressCallback(lldb::ProgressCallback callback,
+ void *baton) {
+ m_progress_callback = callback;
+ m_progress_baton = baton;
+}
+
+void Debugger::ReportProgress(uint64_t progress_id, const char *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) {
+ if ((*pos)->m_progress_callback)
+ (*pos)->m_progress_callback(progress_id, message, completed, total,
+ (*pos)->m_progress_baton);
+ }
+ }
+}
+
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
@@ -44,6 +44,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"
@@ -808,23 +809,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)",
@@ -1651,6 +1654,14 @@
}
}
+void SBDebugger::SetProgressCallback(ProgressCallback callback, void *baton) {
+ LLDB_RECORD_DUMMY(void, SBDebugger, SetProgressCallback,
+ (lldb::ProgressCallback, void *), callback, baton);
+ if (m_opaque_sp) {
+ return m_opaque_sp->SetProgressCallback(callback, baton);
+ }
+}
+
namespace lldb_private {
namespace repro {
Index: lldb/include/lldb/lldb-types.h
===================================================================
--- lldb/include/lldb/lldb-types.h
+++ lldb/include/lldb/lldb-types.h
@@ -73,6 +73,9 @@
void *baton, const char **argv, lldb_private::CommandReturnObject &result);
typedef bool (*ExpressionCancelCallback)(ExpressionEvaluationPhase phase,
void *baton);
+typedef void (*ProgressCallback)(uint64_t progress_id, const char *message,
+ uint64_t completed, uint64_t total,
+ void *baton);
} // namespace lldb
#define LLDB_INVALID_PROCESS ((lldb::process_t)-1)
Index: lldb/include/lldb/Core/Progress.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Core/Progress.h
@@ -0,0 +1,115 @@
+//===-- 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);
+
+ /// Set the progress notification callback.
+ ///
+ /// If the callback is set to a valid non NULL value, the \a callback
+ /// function will be called when ever a Progress object is constructed,
+ /// Increment is called, or the Progress object is destroyed and has not
+ /// already sent a progress complete notification.
+ static void SetProgressCallback(lldb::ProgressCallback callback, void *baton);
+
+private:
+ void ReportProgress();
+ static std::atomic<uint64_t> g_progress_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
@@ -226,6 +226,8 @@
void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton);
+ void SetProgressCallback(lldb::ProgressCallback callback, void *baton);
+
// Properties Functions
enum StopDisassemblyType {
eStopDisassemblyTypeNever = 0,
@@ -346,6 +348,11 @@
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 char *message,
+ uint64_t completed, uint64_t total);
bool StartEventHandlerThread();
@@ -436,6 +443,8 @@
lldb::ListenerSP m_forward_listener_sp;
llvm::once_flag m_clear_once;
lldb::TargetSP m_dummy_target_sp;
+ lldb::ProgressCallback m_progress_callback;
+ void *m_progress_baton;
// Events for m_sync_broadcaster
enum {
Index: lldb/include/lldb/API/SBDebugger.h
===================================================================
--- lldb/include/lldb/API/SBDebugger.h
+++ lldb/include/lldb/API/SBDebugger.h
@@ -223,6 +223,27 @@
void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton);
+ /// Set a progress callback for this debugger object.
+ ///
+ /// Long running operations in LLDB can cause delays when debugging, and
+ /// these delys make it seem like the debug session is deadlocked or stalled.
+ /// Clients can register a callback with debugger objects to get updates on
+ /// such activities so they can display feedback to the user so they stay
+ /// informed on what is happening.
+ ///
+ /// Long running operations include indexing debug information, parsing
+ /// symbol tables, and downloading debug info files.
+ ///
+ /// The callback can be called from multiple threads and clients should
+ /// create a callback that is thread safe.
+ ///
+ /// \param[in] callback A callback that will be called on any thread as
+ /// progress updates are reported.
+ ///
+ /// \param[in] baton A user specified baton that will be sent to the callback
+ /// when progress is reported.
+ void SetProgressCallback(ProgressCallback callback, void *baton);
+
// DEPRECATED
void DispatchInput(void *baton, const void *data, size_t data_len);
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits