Skip to content

[LLDB][Progress-On-Dap] Have indeterminate progress actually send events. #140162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 19, 2025

Conversation

Jlalond
Copy link
Contributor

@Jlalond Jlalond commented May 15, 2025

Recently, I got a report how a user 'hung', and come to find out it's actually because DAP is checking percentage, and on non-deterministic events, we will effectively never send a progress event to DAP. Here we short circuit and don't view percentages for DAP messages and solely depend on the DAP 250ms throttle, which is probably still too aggressive, but better than no updates.

@llvmbot
Copy link
Member

llvmbot commented May 15, 2025

@llvm/pr-subscribers-lldb

Author: Jacob Lalonde (Jlalond)

Changes

Recently, I got a report how a user 'hung', and come to find out it's actually because DAP is checking percentage, and on non-deterministic events, we will effectively never send a progress event to DAP. Here we short circuit and don't view percentages for DAP messages and solely depend on the DAP 250ms throttle, which is probably still too aggressive, but better than no updates.


Full diff: https://github.com/llvm/llvm-project/pull/140162.diff

3 Files Affected:

  • (modified) lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py (+1-1)
  • (modified) lldb/tools/lldb-dap/ProgressEvent.cpp (+7-4)
  • (modified) lldb/tools/lldb-dap/ProgressEvent.h (+1-1)
diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
index fee63655de0da..c87d2afe36821 100755
--- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
+++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
@@ -81,7 +81,7 @@ def test(self):
 
         self.verify_progress_events(
             expected_title="Progress tester: Initial Indeterminate Detail",
-            expected_message="Step 1",
+            expected_message="Step 2",
             only_verify_first_update=True,
         )
 
diff --git a/lldb/tools/lldb-dap/ProgressEvent.cpp b/lldb/tools/lldb-dap/ProgressEvent.cpp
index 6a4978c055e51..b6b62efb5f33c 100644
--- a/lldb/tools/lldb-dap/ProgressEvent.cpp
+++ b/lldb/tools/lldb-dap/ProgressEvent.cpp
@@ -77,16 +77,19 @@ ProgressEvent::Create(uint64_t progress_id, std::optional<StringRef> message,
   if (event.GetEventType() == progressStart && event.GetEventName().empty())
     return std::nullopt;
 
-  if (prev_event && prev_event->EqualsForIDE(event))
+  if (prev_event && prev_event->EqualsForIDE(event, total))
     return std::nullopt;
 
   return event;
 }
 
-bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {
+bool ProgressEvent::EqualsForIDE(const ProgressEvent &other, uint64_t total) const {
   return m_progress_id == other.m_progress_id &&
-         m_event_type == other.m_event_type &&
-         m_percentage == other.m_percentage;
+         m_event_type == other.m_event_type && 
+         // If we check the percentage of a non-deterministic event
+         // we will basically never send the event, because N+1/Uint64_max
+         // will always be an infinitesimally small change.
+         (total != UINT64_MAX && m_percentage == other.m_percentage);
 }
 
 ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }
diff --git a/lldb/tools/lldb-dap/ProgressEvent.h b/lldb/tools/lldb-dap/ProgressEvent.h
index d1b9b9dd887cd..ab3487c1dbc3d 100644
--- a/lldb/tools/lldb-dap/ProgressEvent.h
+++ b/lldb/tools/lldb-dap/ProgressEvent.h
@@ -54,7 +54,7 @@ class ProgressEvent {
   /// \return
   ///       \b true if two event messages would result in the same event for the
   ///       IDE, e.g. same rounded percentage.
-  bool EqualsForIDE(const ProgressEvent &other) const;
+  bool EqualsForIDE(const ProgressEvent &other, uint64_t total) const;
 
   llvm::StringRef GetEventName() const;
 

m_event_type == other.m_event_type &&
m_percentage == other.m_percentage;
m_event_type == other.m_event_type &&
// If we check the percentage of a non-deterministic event
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JDevlieghere & @ashgti, thoughts on also making it 500 ms between non-deterministic progress events, or do we think 250 is the best tradeoff of performance to responsiveness?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't m_percentage a nullopt when the total is UINT64_MAX? Or is there a logic issue in lines 35-65?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct, but then we'd be comparing if null_opt == null_opt, and no further updates would be sent. I overlooked this when adding the total, we should short circuit and ignore the percentage if percentage is a null opt.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But won't they have different event types if its changed? For a non-determinitic progress event, it should only go from m_event_type == progressStart to m_event_type == progressEnd, right? If we have 2 starts or 2 ends that feels like the logic got mixed up somewhere else, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, let me apologize for being overly brief in my explanation. You're correct @ashgti, but now imagine all the updates.

Let's say we're indexing dwarf, you'll get the start, the first update and then the end.

As an extreme hypothetical, if this takes 30 minutes you will have 0 progress actually sent to the progress bar in VSCode resulting in what appears to be a hang of LLDB.

So, for non-deterministic events we want to update for every time window. DAP limits to 250 ms, which I think is fine. I might need to change the equals check now that I spell this out but I hope this explains the intent!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that should be fine though, right? Unless the message or progress % changes then we shouldn't need to send an update (see https://microsoft.github.io/debug-adapter-protocol/specification#Events_ProgressUpdate). If this is to update the message, should we add m_message to the Equals check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add m_message to the Equals check?

Correct, I was using my progress tester which I realized every event has it's own unique message. But it is optional. Let me fix that...

Copy link

github-actions bot commented May 15, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@Jlalond Jlalond force-pushed the dap-progress-non-deterministic-updates branch 3 times, most recently from ad616dc to 4522493 Compare May 19, 2025 01:00
@Jlalond Jlalond force-pushed the dap-progress-non-deterministic-updates branch from 4522493 to dabda3b Compare May 19, 2025 01:02
Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if @ashgti is happy.

@Jlalond Jlalond force-pushed the dap-progress-non-deterministic-updates branch from d4006df to 5d0bd7d Compare May 19, 2025 22:08
Copy link
Contributor

@ashgti ashgti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@Jlalond Jlalond merged commit f27cfea into llvm:main May 19, 2025
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants