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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to