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

Reply via email to