Skip to content
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

SBDebugger: Add new APIs AddDestroyCallback and RemoveDestroyCallback #89868

Merged
merged 21 commits into from
May 20, 2024

Conversation

royitaqi
Copy link
Contributor

@royitaqi royitaqi commented Apr 24, 2024

Motivation

Individual callers of SBDebugger::SetDestroyCallback() might think that they have registered their callback and expect it to be called when the debugger is destroyed. In reality, only the last caller survives, and all previous callers are forgotten, which might be a surprise to them. Worse, if this is called in a race condition, which callback survives is less predictable, which may case confusing behavior elsewhere.

This PR

Allows multiple destroy callbacks to be registered and all called when the debugger is destroyed.

EDIT: Adds two new APIs: AddDestroyCallback() and ClearDestroyCallback(). SetDestroyCallback() will first clear then add the given callback. Tests are added for the new APIs.

Tests

bin/llvm-lit -sv ../external/llvm-project/lldb/test/API/python_api/debugger/TestDebuggerAPI.py

(out-dated, see comments below) Semantic change to SetDestroyCallback()

Currently, the method overwrites the old callback with the new one. With this PR, it will NOT overwrite. Instead, it will hold on to both. Both callbacks get called during destroy.

Risk: Although the documentation of SetDestroyCallback() (see C++ and python) doesn't really specify the behavior, there is a risk: if existing call sites rely on the "overwrite" behavior, they will be surprised because now the old callback will get called. But as the above said, the current behavior of "overwrite" itself might be unintended, so I don't anticipate users to rely on this behavior. In short, this risk might be less of a problem if we correct it sooner rather than later (which is what this PR is trying to do).

(out-dated, see comments below) Implementation

The implementation holds a std::vector<std::pair<callback, baton>>. When SetDestroyCallback() is called, callbacks and batons are appended to the std::vector. When destroy event happen, the (callback, baton) pairs are invoked FIFO. Finally, the std::vector is cleared.

(out-dated, see comments below) Alternatives considered

Instead of changing SetDestroyCallback(), a new method AddDestroyCallback() can be added, which use the same std::vector<std::pair<>> implementation. Together with ClearDestroyCallback() (see below), they will replace and deprecate SetDestroyCallback(). Meanwhile, in order to be backward compatible, SetDestroyCallback() need to be updated to clear the std::vector and then add the new callback. Pros: The end state is semantically more correct. Cons: More steps to take; potentially maintaining an "incorrect" behavior (of "overwrite").

A new method ClearDestroyCallback() can be added. Might be unnecessary at this point, because workflows which need to set then clear callbacks may exist but shouldn't be too common at least for now. Such method can be added later when needed.

The std::vector may bring slight performance drawback if its implementation doesn't handle small size efficiently. However, even if that's the case, this path should be very cold (only used during init and destroy). Such performance drawback should be negligible.

A different implementation was also considered. Instead of using std::vector, the current m_destroy_callback field can be kept unchanged. When SetDestroyCallback() is called, a lambda function can be stored into m_destroy_callback. This lambda function will first call the old callback, then the new one. This way, std::vector is avoided. However, this implementation is more complex, thus less readable, with not much perf to gain.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the lldb label Apr 24, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Apr 24, 2024

@llvm/pr-subscribers-lldb

Author: None (royitaqi)

Changes

Motivation

Individual callers of SBDebugger::SetDestroyCallback() might think that they have registered their callback and expect it to be called when the debugger is destroyed. In reality, only the last caller survives, and all previous callers are forgotten, which might be a surprise to them. Worse, if this is called in a race condition, which callback survives is less predictable, which may case confusing behavior elsewhere.

This PR

Allows multiple destroy callbacks to be registered and all called when the debugger is destroyed.

Semantic change to SetDestroyCallback()

Currently, the method overwrites the old callback with the new one. With this PR, it will NOT overwrite. Instead, it will hold on to both. Both callbacks get called during destroy.

Risk: Although the documentation of SetDestroyCallback() (see C++ and python) doesn't really specify the behavior, there is a risk: if existing call sites rely on the "overwrite" behavior, they will be surprised because now the old callback will get called. But as the above said, the current behavior of "overwrite" itself might be unintended, so I don't anticipate users to rely on this behavior. In short, this risk might be less of a problem if we correct it sooner rather than later (which is what this PR is trying to do).

Implementation

The implementation holds a std::vector&lt;std::pair&lt;callback, baton&gt;&gt;. When SetDestroyCallback() is called, callbacks and batons are appended to the std::vector. When destroy event happen, the (callback, baton) pairs are invoked FIFO. Finally, the std::vector is cleared.

Other considerations

Instead of changing SetDestroyCallback(), a new method AddDestroyCallback() can be added, which use the same std::vector&lt;std::pair&lt;&gt;&gt; implementation. Together with ClearDestroyCallback() (see below), they will replace and deprecate SetDestroyCallback(). Meanwhile, in order to be backward compatible, SetDestroyCallback() need to be updated to clear the std::vector and then add the new callback. Pros: The end state is semantically more correct. Cons: More steps to take; potentially maintaining an "incorrect" behavior (of "overwrite").

A new method ClearDestroyCallback() can be added. Might be unnecessary at this point, because workflows which need to set then clear callbacks may exist but shouldn't be too common at least for now. Such method can be added later when needed.

The std::vector may bring slight performance drawback if its implementation doesn't handle small size efficiently. However, even if that's the case, this path should be very cold (only used during init and destroy). Such performance drawback should be negligible.

A different implementation was also considered. Instead of using std::vector, the current m_destroy_callback field can be kept unchanged. When SetDestroyCallback() is called, a lambda function can be stored into m_destroy_callback. This lambda function will first call the old callback, then the new one. This way, std::vector is avoided. However, this implementation is more complex, thus less readable, with not much perf to gain.


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

2 Files Affected:

  • (modified) lldb/include/lldb/Core/Debugger.h (+2-2)
  • (modified) lldb/source/Core/Debugger.cpp (+5-5)
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 418c2403d020f4..20884f954ec7db 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -731,8 +731,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   lldb::TargetSP m_dummy_target_sp;
   Diagnostics::CallbackID m_diagnostics_callback_id;
 
-  lldb_private::DebuggerDestroyCallback m_destroy_callback = nullptr;
-  void *m_destroy_callback_baton = nullptr;
+  std::vector<std::pair<lldb_private::DebuggerDestroyCallback, void *>>
+ 	    m_destroy_callback_and_baton;
 
   uint32_t m_interrupt_requested = 0; ///< Tracks interrupt requests
   std::mutex m_interrupt_mutex;
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 19b3cf3bbf46b1..0ebdf2b0a0f970 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -743,10 +743,11 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
 }
 
 void Debugger::HandleDestroyCallback() {
-  if (m_destroy_callback) {
-    m_destroy_callback(GetID(), m_destroy_callback_baton);
-    m_destroy_callback = nullptr;
+  const lldb::user_id_t user_id = GetID();
+  for (const auto &callback_and_baton : m_destroy_callback_and_baton) {
+    callback_and_baton.first(user_id, callback_and_baton.second);
   }
+  m_destroy_callback_and_baton.clear();
 }
 
 void Debugger::Destroy(DebuggerSP &debugger_sp) {
@@ -1427,8 +1428,7 @@ void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
 
 void Debugger::SetDestroyCallback(
     lldb_private::DebuggerDestroyCallback destroy_callback, void *baton) {
-  m_destroy_callback = destroy_callback;
-  m_destroy_callback_baton = baton;
+  m_destroy_callback_and_baton.emplace_back(destroy_callback, baton);
 }
 
 static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id,

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.

Can you elaborate a bit more about why you want to change the behavior? Your motivation touches on the fact that it might be surprising or racy. While I think having the ability to register multiple callbacks makes sense, I'm not sure I agree with either.

  • The method is called SetDestroyCallback which makes it pretty clear that it's a setter, which is generally expected to change the value, rather than add to it. If the method had been called AddDestroyCallback on the other hand, I would expect it to add the callback rather than overwrite it.
  • I don't think there's anything racy about a setter. Even if it were, nothing in this PR addresses the racy-ness.

The setter also makes it possible to remove a callback by overwriting it with a callback that does nothing. With the current approach that's no longer possible. I'm not sure if that's important, but something to consider.

If the goal is to be able to specify multiple callbacks, a potential solution could be to store the callbacks in a list as you do in this PR, but add a new method AddDestroyCallback which appends to the list. We could keep the existing behavior for SetDestroyCallback by clearing the list and adding it as the first entry. That would also solve the problem of not being able to remove existing callbacks.

Regardless of the direction, this definitely needs a corresponding test and updated documentation.

@jimingham
Copy link
Collaborator

I would also find it surprising if "SetDestroyCallback" did "AddDestroyCallback". That's not what the name says it does. I have no problem with supporting multiple Destroy callbacks. But I agree with Jonas, that needs to be a separate API with an appropriate name. And also tests...

@royitaqi
Copy link
Contributor Author

royitaqi commented Apr 24, 2024

Thank you, @JDevlieghere and @jimingham for the input.

IIUR, you are basically advocating for the following alternative approach (that was mentioned in the "Alternatives considered" section in the above):

Instead of changing SetDestroyCallback(), a new method AddDestroyCallback() can be added, which use the same std::vector<std::pair<>> implementation. Together with ClearDestroyCallback() (see below), they will replace and deprecate SetDestroyCallback(). Meanwhile, in order to be backward compatible, SetDestroyCallback() need to be updated to clear the std::vector and then add the new callback. Pros: The end state is semantically more correct. Cons: More steps to take; potentially maintaining an "incorrect" behavior (of "overwrite").

A new method ClearDestroyCallback() can be added. Might be unnecessary at this point, because workflows which need to set then clear callbacks may exist but shouldn't be too common at least for now. Such method can be added later when needed.

I agree that adding new methods have more clear naming and don't change the semantics of the eixsting method. I will update this PR (with test). Or, should I create a new PR? - Noob I am.

@jimingham
Copy link
Collaborator

I don't have a strong preference for new PR vrs. reuse this one.

@royitaqi
Copy link
Contributor Author

royitaqi commented Apr 24, 2024

Hi @JDevlieghere and @jimingham,

I have updated the PR to add AddDestroyCallback(), ClearDestroyCallback(), and tests for these. Also updated SetDestroyCallback() to work with the new field std::vector<std::pair<callback, baton>>.

Hope it looks better now. LMK if anything else needs to be added/changed.

--

BTW, noob as I am, is there anything I need to do to change the PR's state "Changes requested"? E.g. In some other diff review tools, there is a thing to flip the state into "Ready for review". I clicked on the "Re-request review" icon. Maybe that's it.

@royitaqi
Copy link
Contributor Author

royitaqi commented Apr 24, 2024

Tests

Manual test

username-mac ~/public_llvm/build % bin/lldb
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> lldb.debugger.AddDestroyCallback(lambda user_id: print('foo %s' % user_id))
0   <-- token
>>> lldb.debugger.AddDestroyCallback(lambda user_id: print('bar %s' % user_id))
1   <-- token
>>> ^D
now exiting InteractiveConsole...
(lldb) ^D
foo 1
bar 1
username-mac ~/public_llvm/build %

Run the added unit tests

bin/llvm-lit -sv ../external/llvm-project/lldb/test/API/python_api/debugger/TestDebuggerAPI.py

@jimingham
Copy link
Collaborator

jimingham commented Apr 25, 2024 via email

@royitaqi royitaqi requested a review from jimingham April 25, 2024 20:41
@royitaqi
Copy link
Contributor Author

Hi @jimingham ,

I have updated the code. Now we have Add and Remove (with tokens as you suggested). Set clears everything then add the given one (also returns a token).

Thread-safety is done through a std::lock_guard<std::recursive_mutex>. This is consistent with some of the other functions/fields.

Didn't understand one of your comment about "one destroy callback can add/remove others" and how that relates to the code. See my reply in the above.

Another thing I am not sure is, in python API test, how do I make concurrent calls to these APIs. Do you know if this should be tested (e.g. if other existing functions has similar tests)? If the answer is "yes", do you happen to know what existing test I can see as examples?

Thanks for your reviews so far. Hope this PR is getting better as we converse.

Best,
Roy

@royitaqi
Copy link
Contributor Author

royitaqi commented May 7, 2024

Gentle ping to make progress on this PR. :)

@JDevlieghere , could you see my rely to your comment about why I think signed int makes sense for typedef int destroy_callback_token_t? TL;DR: I think signed is better. I have no strong opinion. If you still think it should be unsigned, I can update the PR.

@JDevlieghere and @bulbazord , about the necessity of thread safety, could you see if my proposal is agreeable? TL;DR: currently the class isn't thread safe; let's skip thread safety in this PR; we can add it to the whole class in one go when, in the future, the class is decided to need thread safety.

@jimingham
Copy link
Collaborator

jimingham commented May 7, 2024 via email

@royitaqi
Copy link
Contributor Author

royitaqi commented May 7, 2024

@jimingham

Adding debuggers, or querying them is controlled by the debugger list mutex. Manipulating the I/O handlers is governed by the io handler stack mutex, there's a lock around the interrupt requests. Debugger doesn't seem like a class that is not locking the lists of things it controls as a general rule.

This is fair. I somehow missed them the last time I was searching for mutex in the same code.

--

With the above, I'm on the fence. Since I have less knowledge and context, at this point I will depend on @jimingham & @JDevlieghere you guys aligning with each other and pointing me in the right direction. LMK. Either ways it should be an easy change in the PR (since we already have thread-safety implemented).

@royitaqi
Copy link
Contributor Author

royitaqi commented May 10, 2024

Gentle ping. :)

@jimingham and @JDevlieghere I'm hoping that you guys can align on whether or not this PR should contain thread-safety.

My thoughts (from my last comment):

With the above, I'm on the fence. Since I have less knowledge and context, at this point I will depend on @jimingham & @JDevlieghere you guys aligning with each other and pointing me in the right direction. LMK. Either ways it should be an easy change in the PR (since we already have thread-safety implemented).

--

Note: in below, mistakenly closed and reopened this PR.

@royitaqi royitaqi closed this May 10, 2024
@royitaqi royitaqi reopened this May 10, 2024
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.

We should make this thread safe. It can only help and this isn't an API which gets called all of the time, so performance isn't an issue. A few inline comments

lldb/source/API/SBDebugger.cpp Outdated Show resolved Hide resolved
lldb/source/API/SBDebugger.cpp Outdated Show resolved Hide resolved
lldb/include/lldb/Core/Debugger.h Outdated Show resolved Hide resolved
lldb/include/lldb/API/SBDebugger.h Outdated Show resolved Hide resolved
lldb/include/lldb/Core/Debugger.h Outdated Show resolved Hide resolved
lldb/test/API/python_api/debugger/TestDebuggerAPI.py Outdated Show resolved Hide resolved
lldb/source/Core/Debugger.cpp Outdated Show resolved Hide resolved
lldb/source/Core/Debugger.cpp Outdated Show resolved Hide resolved
// Using `token` to erase, because elements may have been added/removed, and
// that will cause error "invalid iterator access!" if `iter` is used
// instead.
m_destroy_callback_and_baton.erase(token);
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason we remove after calling the callback?

Copy link
Contributor Author

@royitaqi royitaqi May 13, 2024

Choose a reason for hiding this comment

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

The callbacks, when executed by this loop, can add/remove other callbacks. I.e. the content in the container can change during the loop.

To handle this, the loop always take the first element, call it, then remove it, so that the next "first element" will be a new one.

If we don't remove, I wonder how we can make sure that the callbacks which are added during the loop are executed.

Copy link
Contributor

Choose a reason for hiding this comment

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

The biggest concern is that, this implementation completely changes the semantics -- now, client using this API has to re-register the callbacks after destroying debugger which is weird and not expected because there is no removeCallback() called from client. For example, the following user case won't work anymore:

void MyDestroyCallback() {
...
}

SBDebugger::AddDestroyCallbac(MyDestroyCallback);
SBDebugger::Create();
...
SBDebugger::Destroy();

// Recreate another debugger
SBDebugger::Create();
SBDebugger::Destroy();   // Now the MyDestroyCallback won't be called even user did not call RemoveDestroyCallback() which is not expected

There are several ways to handle this issue without clearing m_destroy_callback_and_baton. One simply way is making a copy of m_destroy_callback_and_baton, and calling callbacks from the copy (by checking if it still exists in original m_destroy_callback_and_baton). And at the end, checking there is no new entries in m_destroy_callback_and_baton, otherwise, getting the delta of the local copy and original copy, and redo the process in a loop.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The callbacks shouldn't be removed when called, that is for sure

Copy link
Contributor

Choose a reason for hiding this comment

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

I was more concerned if AddDestroyCallback() is a static API but since it is an instance method, I am less concerned now.

lldb/source/API/SBDebugger.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@jeffreytan81 jeffreytan81 left a comment

Choose a reason for hiding this comment

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

Added another inline comment.

@@ -62,12 +62,15 @@ typedef void *thread_arg_t; // Host thread argument type
typedef void *thread_result_t; // Host thread result type
typedef void *(*thread_func_t)(void *); // Host thread function type
typedef int pipe_t; // Host pipe type
typedef int destroy_callback_token_t; // Debugger destroy callback token type
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we are adding this to the public API, we should remove the "destroy" from this so we could re-use this for other callback tokens. So change this to:

typedef int callback_token_t;

And remove the comment since making a comment of // Callback token type doesn't really tell you anything more than the name already does. The reason being we don't want to have to add a ton of callback token types for every callback we might want to register in the future (process_callback_token_t, thread_callback_token_t, ...). We can just use one for all of them in the future.

lldb/include/lldb/lldb-types.h Outdated Show resolved Hide resolved
lldb/include/lldb/Core/Debugger.h Outdated Show resolved Hide resolved
lldb/include/lldb/Core/Debugger.h Outdated Show resolved Hide resolved
lldb/source/Core/Debugger.cpp Outdated Show resolved Hide resolved
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.

Thanks for all of the changes. Looks good.

Copy link

⚠️ Python code formatter, darker found issues in your code. ⚠️

You can test this locally with the following command:
darker --check --diff -r 7ecdf620330d8e044a48b6f59f8eddd2f88f01d4...25a51ed4e87042411b03ba4e57cb4a595679c37b lldb/test/API/python_api/debugger/TestDebuggerAPI.py
View the diff from darker here.
--- TestDebuggerAPI.py	2024-05-17 00:49:05.000000 +0000
+++ TestDebuggerAPI.py	2024-05-20 22:39:20.840610 +0000
@@ -167,69 +167,72 @@
         called = []
 
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('foo', dbg_id)]
+            called += [("foo", dbg_id)]
 
         def bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('bar', dbg_id)]
+            called += [("bar", dbg_id)]
 
         token_foo = self.dbg.AddDestroyCallback(foo)
         token_bar = self.dbg.AddDestroyCallback(bar)
         self.dbg.Destroy(self.dbg)
 
         # Should call both `foo()` and `bar()`.
-        self.assertEqual(called, [
-            ('foo', original_dbg_id),
-            ('bar', original_dbg_id),
-        ])
+        self.assertEqual(
+            called,
+            [
+                ("foo", original_dbg_id),
+                ("bar", original_dbg_id),
+            ],
+        )
 
     def test_RemoveDestroyCallback(self):
         original_dbg_id = self.dbg.GetID()
         called = []
 
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('foo', dbg_id)]
+            called += [("foo", dbg_id)]
 
         def bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('bar', dbg_id)]
+            called += [("bar", dbg_id)]
 
         token_foo = self.dbg.AddDestroyCallback(foo)
         token_bar = self.dbg.AddDestroyCallback(bar)
         ret = self.dbg.RemoveDestroyCallback(token_foo)
         self.dbg.Destroy(self.dbg)
 
         # `Remove` should be successful
         self.assertTrue(ret)
         # Should only call `bar()`
-        self.assertEqual(called, [('bar', original_dbg_id)])
+        self.assertEqual(called, [("bar", original_dbg_id)])
 
     def test_RemoveDestroyCallback_invalid_token(self):
         original_dbg_id = self.dbg.GetID()
         magic_token_that_should_not_exist = 32413
         called = []
 
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('foo', dbg_id)]
+            called += [("foo", dbg_id)]
 
         token_foo = self.dbg.AddDestroyCallback(foo)
         ret = self.dbg.RemoveDestroyCallback(magic_token_that_should_not_exist)
         self.dbg.Destroy(self.dbg)
 
         # `Remove` should be unsuccessful
         self.assertFalse(ret)
         # Should call `foo()`
-        self.assertEqual(called, [('foo', original_dbg_id)])
+        self.assertEqual(called, [("foo", original_dbg_id)])
 
     def test_HandleDestroyCallback(self):
         """
         Validates:
         1. AddDestroyCallback and RemoveDestroyCallback work during debugger destroy.
@@ -240,45 +243,51 @@
         bar_token = None
 
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('foo called', dbg_id))
+            events.append(("foo called", dbg_id))
 
         def bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('bar called', dbg_id))
+            events.append(("bar called", dbg_id))
 
         def add_foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('add_foo called', dbg_id))
-            events.append(('foo token', self.dbg.AddDestroyCallback(foo)))
+            events.append(("add_foo called", dbg_id))
+            events.append(("foo token", self.dbg.AddDestroyCallback(foo)))
 
         def remove_bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('remove_bar called', dbg_id))
-            events.append(('remove bar ret', self.dbg.RemoveDestroyCallback(bar_token)))
+            events.append(("remove_bar called", dbg_id))
+            events.append(("remove bar ret", self.dbg.RemoveDestroyCallback(bar_token)))
 
         # Setup
-        events.append(('add_foo token', self.dbg.AddDestroyCallback(add_foo)))
+        events.append(("add_foo token", self.dbg.AddDestroyCallback(add_foo)))
         bar_token = self.dbg.AddDestroyCallback(bar)
-        events.append(('bar token', bar_token))
-        events.append(('remove_bar token', self.dbg.AddDestroyCallback(remove_bar)))
+        events.append(("bar token", bar_token))
+        events.append(("remove_bar token", self.dbg.AddDestroyCallback(remove_bar)))
         # Destroy
         self.dbg.Destroy(self.dbg)
 
-        self.assertEqual(events, [
-            # Setup
-            ('add_foo token', 0), # add_foo should be added
-            ('bar token', 1), # bar should be added
-            ('remove_bar token', 2), # remove_bar should be added
-            # Destroy
-            ('add_foo called', original_dbg_id), # add_foo should be called
-            ('foo token', 3), # foo should be added
-            ('bar called', original_dbg_id), # bar should be called
-            ('remove_bar called', original_dbg_id), # remove_bar should be called
-            ('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed
-            ('foo called', original_dbg_id), # foo should be called
-        ])
+        self.assertEqual(
+            events,
+            [
+                # Setup
+                ("add_foo token", 0),  # add_foo should be added
+                ("bar token", 1),  # bar should be added
+                ("remove_bar token", 2),  # remove_bar should be added
+                # Destroy
+                ("add_foo called", original_dbg_id),  # add_foo should be called
+                ("foo token", 3),  # foo should be added
+                ("bar called", original_dbg_id),  # bar should be called
+                ("remove_bar called", original_dbg_id),  # remove_bar should be called
+                (
+                    "remove bar ret",
+                    False,
+                ),  # remove_bar should fail, because it's already invoked and removed
+                ("foo called", original_dbg_id),  # foo should be called
+            ],
+        )

@clayborg clayborg merged commit 9f62775 into llvm:main May 20, 2024
3 of 4 checks passed
Copy link

@royitaqi Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested
by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as
the builds can include changes from many authors. It is not uncommon for your
change to be included in a build that fails due to someone else's changes, or
infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself.
This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

@antmox
Copy link
Contributor

antmox commented May 21, 2024

Hi @royitaqi ,
This commit broke lldb-aarch64-windows buildbot : https://lab.llvm.org/buildbot/#/builders/219/builds/11564
Looks like callback_token_t is not defined for WIN32 in lldb-types.h
Could you please look at this ?

@antmox
Copy link
Contributor

antmox commented May 21, 2024

@royitaqi @clayborg I proposed a fix here : #92870

@royitaqi
Copy link
Contributor Author

Hi @antmox,

I'm sorry that it broke the lldb-aarch64-windows build. My bad. Your fix makes sense, though I do have the same comment as @labath had, i.e. to move that typedef outside platform-specific region. From the latest comments in that PR it seems it's already got moved. If that's the case then I think we are all good.

My apologies that I didn't respond earlier - I was watching my email and the build bot (https://lab.llvm.org/buildbot/#/changes/134099), saw all green and I thought it's went well. Questions for my learning (I'm noob to all these): I realize that the build bot I quoted didn't run the windows build you mentioned (lldb-aarch64-windows). This tells me that there are multiple build bots that I need to watch in the future. Is there an exhaustive list somewhere? Email obviously failed to inform me about the build break (i.e. I searched for "lldb-aarch64-windows" in my email and nothing except your message).

Thanks,
Roy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants