Skip to content

[LLDB-DAP] SBDebugger don't prefix title on progress updates #124648

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 15 commits into from
Mar 5, 2025

Conversation

Jlalond
Copy link
Contributor

@Jlalond Jlalond commented Jan 27, 2025

In my last DAP patch (#123837), we piped the DAP update message into the update event. However, we had the title embedded into the update message. This makes sense for progress Start, but makes the update message look pretty wonky.

image

Instead, we only use the title when it's the first message, removing the duplicate title problem.
image

@llvmbot
Copy link
Member

llvmbot commented Jan 27, 2025

@llvm/pr-subscribers-lldb

Author: Jacob Lalonde (Jlalond)

Changes

In my last DAP patch (#123837), we piped the DAP update message into the update event. However, we had the title embedded into the update message. This makes sense for progress Start, but makes the update message look pretty wonky.

image

Instead, we only use the title when it's the first message, removing the duplicate title problem.
image


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

2 Files Affected:

  • (modified) lldb/include/lldb/Core/DebuggerEvents.h (+10-6)
  • (modified) lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py (+5)
diff --git a/lldb/include/lldb/Core/DebuggerEvents.h b/lldb/include/lldb/Core/DebuggerEvents.h
index 49a4ecf8e537e3..ca06ee835fde38 100644
--- a/lldb/include/lldb/Core/DebuggerEvents.h
+++ b/lldb/include/lldb/Core/DebuggerEvents.h
@@ -44,12 +44,16 @@ class ProgressEventData : public EventData {
   uint64_t GetCompleted() const { return m_completed; }
   uint64_t GetTotal() const { return m_total; }
   std::string GetMessage() const {
-    std::string message = m_title;
-    if (!m_details.empty()) {
-      message.append(": ");
-      message.append(m_details);
-    }
-    return message;
+    // Only put the title in the message of the progress create event.
+    if (m_completed == 0) {
+      std::string message = m_title;
+      if (!m_details.empty()) {
+        message.append(": ");
+        message.append(m_details);
+      }
+      return message;
+    } else
+      return !m_details.empty() ? m_details : std::string();
   }
   const std::string &GetTitle() const { return m_title; }
   const std::string &GetDetails() const { return m_details; }
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 36c0cef9c47143..945c3f7633364c 100755
--- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
+++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
@@ -41,8 +41,13 @@ def test_output(self):
         for event in self.dap_server.progress_events:
             event_type = event["event"]
             if "progressStart" in event_type:
+                title = event["body"]["title"]
+                self.assertIn("Progress tester", title)
                 start_found = True
             if "progressUpdate" in event_type:
+                message = event["body"]["message"]
+                print(f"Progress update: {message}")
+                self.assertNotIn("Progres tester", message)
                 update_found = True
 
         self.assertTrue(start_found)

@Jlalond Jlalond changed the title [LLDB-DAP] Sbdebugger progress no title prefix [LLDB-DAP] SBDebugger don't prefix title on progress updates Jan 27, 2025
}
return message;
// Only put the title in the message of the progress create event.
if (m_completed == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

can you create some using/defines for constants like this 0? otherwise it's hard to read

Comment on lines 47 to 56
// Only put the title in the message of the progress create event.
if (m_completed == 0) {
std::string message = m_title;
if (!m_details.empty()) {
message.append(": ");
message.append(m_details);
}
return message;
} else
return !m_details.empty() ? m_details : std::string();
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can't change the way that progress events get reported. It will work for DAP nicely, but not for anyone else. We are removing the title from any subsequent notications and that will affect everyone.

I would propose adding a new call like SBDebugger::GetProgressFromEvent(...) where we can get just the detail and the title as separate strings. Maybe:

const char *SBDebugger::GetProgressFromEvent(const lldb::SBEvent &event,
                                             uint64_t &progress_id,
                                             uint64_t &completed,
                                             uint64_t &total,
                                             bool &is_debugger_specific,
                                             const char **detail);

And if detail is NULL, we return the combined string of title + ": " + detail and if it is non NULL, the return value is the title only, and the detail gets filled into detail. Or we can make accessors for the title and detail like:

const char *SBDebugger::GetProgressTitleFromEvent(const lldb::SBEvent &event);
const char *SBDebugger::GetProgressDetailFromEvent(const lldb::SBEvent &event);

Any thoughts on which approach?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The current API for const char * SBDebugger::GetProgressFromEvent(...) is causing some memory bloat for any users as each return value, which is the <title> + ":" + <detail> is being constified via ConstString so that we can return a const char *. Currently I am not sure of how many Progress objects update the detail and return millions of updates, but if they did, that will cause use to constify all of the <title> + ":" + <detail> strings, which is bad. So maybe a new API should be something like:

bool SBDebugger::GetProgressFromEvent(
  const lldb::SBEvent &event,
  uint64_t &progress_id,
  uint64_t &completed,
  uint64_t &total,
  bool &is_debugger_specific,
  char *title, size_t max_title_length,
  char *detail, size_t max_detail_length);

Then we can call strncpy on the Progress::m_title and Progress::m_detail as needed and avoid constifying the progress strings and bloating up the const string pool.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Might be good to allow title and detail to be NULL and not filled in if they are NULL.

Copy link
Member

Choose a reason for hiding this comment

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

Can we use SBDebugger::GetProgressDataFromEvent which returns SBStructuredData with all the fields and then construct the message in lldb-dap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we use SBDebugger::GetProgressDataFromEvent which returns SBStructuredData with all the fields and then construct the message in lldb-dap?

This was going to be my alternative proposal. I assumed this wouldn't fly because I didn't know how many consumers need a workflow that doesn't follow the DAP rules. But based on the conversation I had with Greg offline the DAP progress events needs to handle it's own branching. I think the best way is returning a structured data to give us flexibility in the future.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed, that method was added exactly for that reason: so we didn't need to keep adding overloads to GetProgressFromEvent every time we added a new field. FWIW I personally think it's reasonable to construct the string in lldb-dap.

Comment on lines 417 to 427
lldb::SBStructuredData keyValue = data.GetValueForKey(key);
if (!keyValue)
return std::string();

size_t size = keyValue.GetStringValue(nullptr, 0);
std::cout << "Size for " << key << " " << size << std::endl;
std::string stringValue;
stringValue.resize(size);
keyValue.GetStringValue(&stringValue[0], size + 1);
std::cout << "String value after: " << stringValue << std::endl;
return stringValue;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
lldb::SBStructuredData keyValue = data.GetValueForKey(key);
if (!keyValue)
return std::string();
size_t size = keyValue.GetStringValue(nullptr, 0);
std::cout << "Size for " << key << " " << size << std::endl;
std::string stringValue;
stringValue.resize(size);
keyValue.GetStringValue(&stringValue[0], size + 1);
std::cout << "String value after: " << stringValue << std::endl;
return stringValue;
lldb::SBStructuredData keyValue = data.GetValueForKey(key);
if (!keyValue)
return std::string();
const size_t length = keyValue.GetStringValue(nullptr, 0);
std::string str(length + 1, 0);
value.GetStringValue(&str[0], length);
return str;

Not really, you can avoid the resize with the ctor but otherwise that's as good as it gets (without the logging).

Comment on lines 458 to 460
uint64_t progress_id = GetUintFromStructuredData(data, "progress_id");
uint64_t completed = GetUintFromStructuredData(data, "completed");
uint64_t total = GetUintFromStructuredData(data, "total");
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
uint64_t progress_id = GetUintFromStructuredData(data, "progress_id");
uint64_t completed = GetUintFromStructuredData(data, "completed");
uint64_t total = GetUintFromStructuredData(data, "total");
const uint64_t progress_id = GetUintFromStructuredData(data, "progress_id");
const uint64_t completed = GetUintFromStructuredData(data, "completed");
const uint64_t total = GetUintFromStructuredData(data, "total");
const std::string details = GetStringFromStructuredData(data, "details");

Nit: I'd make these const to make it clear that we're not going to change these, as opposed to the message that we're building. I'd also move details up here so that all the message building is co-located.

Comment on lines 473 to 474
if (message.length() > 0 && progress_id > 0 && total >= 0 &&
completed >= 0)
Copy link
Member

Choose a reason for hiding this comment

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

total and completed are unsigned so isn't total >= 0 && completed >= 0 always true?

@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from ca2f7e3 to 19a03d8 Compare February 18, 2025 19:34
@Jlalond
Copy link
Contributor Author

Jlalond commented Feb 18, 2025

@JDevlieghere Cleaned up as you suggested, sorry about the initial rough state but I mostly wanted a sanity check if I was overlooking anything.

Copy link

github-actions bot commented Feb 18, 2025

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

@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from 19a03d8 to fbe04c1 Compare February 18, 2025 19:40
@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch 2 times, most recently from 6e20c5e to f79d7bd Compare February 18, 2025 22:33
Copy link
Collaborator

@clayborg clayborg left a comment

Choose a reason for hiding this comment

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

So this is changing lldb-dap progress reporting so the first reporting sends the title + detail, and subsequent entries sent just the details. How does this look in the progress API that is shown by VS Code? Does the detail just show up somewhere and remove the title?

if (completed == 0) {
const std::string title = GetStringFromStructuredData(data, "title");
message += title;
message += ": ";
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should check if details contains anything before appending ": "

message += ": ";
}

message += details;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if details is empty? We should send just the title again in that case instead of nothing? This really depends on how the progress reporting looks in VS code.

@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from 11fd267 to d96b308 Compare February 19, 2025 21:09
@Jlalond
Copy link
Contributor Author

Jlalond commented Feb 19, 2025

@clayborg

DAP with a detail and a total
image

Indeterminate with an increment detail
image

Then with an indeterminate progress, but no increment detail
image

For number 2 I think I found another bug for indeterminate messages not updating the detail correctly, but I will address this in a future patch.


# Check to see if total is set to -1 to indicate an indeterminate progress
# then default to 10 steps.
if total == -1:
Copy link
Collaborator

Choose a reason for hiding this comment

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

if total is None:

@@ -68,10 +78,23 @@ def __call__(self, debugger, command, exe_ctx, result):
return

total = cmd_options.total
progress = lldb.SBProgress("Progress tester", "Detail", total, debugger)
if total == -1:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Default will be None if not set now, so this should be:

if total is None:

// the title and the detail as a single message.
if (completed == 0 && total == 1) {
const std::string message =
GetStringFromStructuredData(data, "message");
Copy link
Collaborator

Choose a reason for hiding this comment

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

is the "message" value a combined string if title + detail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, message is title and detail

Comment on lines 466 to 468
// If we're only creating and sending on progress event, we send
// the title and the detail as a single message.
if (completed == 0 && total == 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

restructure as:

if (completed == 0) {
  if (total == 1) {
    // This progress is non deterministic and won't get updated until it is completed.
    // Send the "message" which will be the combined title and detail. The only other
    // progress event for thus non deterministic progress will be the completed event
    // So there will be no need to update the detail.
   const std::string message = ...
  } else {
    // This progress is deterministic and will receive updates. On the first event...
 }
}

Comment on lines 482 to 483
// We don't check if message has any contents, because we still want
// to broadcast a status update.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets document what VS code expects a bit more to make it clear. Something like:

// This progress event is either the end of the progress dialog, or an update
// with possible detail. The "detail" string we send to VS Code will be appended to
// the progress dialog's initial text from when it was created.

Comment on lines 473 to 475
// On the first event, send just the title which will be captured by
// DAP Then if details is not empty, send a second event with the
// detail.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets explain this better so any others making modifications will know what is going on. Make it clear that the first string we send to VS code when creating a new progress will be copied into the progress window title, and any subsequent detail strings will get appended to the initial title we provide to VS code.

@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from afe6168 to 8616ea3 Compare February 20, 2025 17:38
Copy link

github-actions bot commented Feb 20, 2025

✅ With the latest revision this PR passed the Python code formatter.

@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch 2 times, most recently from 681352c to 6d4b3aa Compare February 26, 2025 20:22
@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from 6d4b3aa to cee7540 Compare March 3, 2025 23:08
@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from b0731ca to 41b9d7d Compare March 3, 2025 23:17
@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from e762fb3 to 458707c Compare March 3, 2025 23:24
expected_not_in_message=None,
only_verify_first_update=False,
):
self.dap_server.wait_for_event("progressEnd", 5)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If a server is running multiple tests at once, there may be some delays. If you are sending 3 events, one second apart, I would give this timeout a larger value to ensure we don't timeout on busy machines. Something like 10 or 15 seconds at least.

@Jlalond Jlalond force-pushed the sbdebugger-progress-no-title-prefix branch from 19eb994 to c7a5f3f Compare March 4, 2025 22:29
@Jlalond Jlalond merged commit 02f024c into llvm:main Mar 5, 2025
10 checks passed
@Jlalond Jlalond deleted the sbdebugger-progress-no-title-prefix branch March 7, 2025 19:19
jph-13 pushed a commit to jph-13/llvm-project that referenced this pull request Mar 21, 2025
This patch adds a finalize method which destroys the underlying RAII
SBProgress. My primary motivation for this is so I can write better
tests that are non-flaky, but after discussing with @clayborg in my DAP
message improvement patch (llvm#124648) this is probably an essential API
despite that I originally argued it wasn't.
jph-13 pushed a commit to jph-13/llvm-project that referenced this pull request Mar 21, 2025
…4648)

In my last DAP patch (llvm#123837), we piped the DAP update message into the
update event. However, we had the title embedded into the update
message. This makes sense for progress Start, but makes the update
message look pretty wonky.


![image](https://github.com/user-attachments/assets/9f6083d1-fc50-455c-a1a7-a2f9bdaba22e)

Instead, we only use the title when it's the first message, removing the
duplicate title problem.

![image](https://github.com/user-attachments/assets/ee7aefd1-1852-46f7-94bc-84b8faef6dac)
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.

5 participants