[Lldb-commits] [PATCH] D101933: If an interrupt fails, don't try to fetch any more packets from the server

2021-05-06 Thread Jim Ingham via Phabricator via lldb-commits
jingham added a comment.

I moved the eStateExited test as Greg suggested on commit.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D101933/new/

https://reviews.llvm.org/D101933

___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [PATCH] D101933: If an interrupt fails, don't try to fetch any more packets from the server

2021-05-06 Thread Jim Ingham via Phabricator via lldb-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG72ba78c29e92: When SendContinuePacketAndWaitForResponse 
returns eStateInvalid, don't fetch… (authored by jingham).

Changed prior to commit:
  https://reviews.llvm.org/D101933?vs=343131&id=343499#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D101933/new/

https://reviews.llvm.org/D101933

Files:
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py

Index: lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py
===
--- /dev/null
+++ lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py
@@ -0,0 +1,72 @@
+from __future__ import print_function
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from gdbclientutils import *
+
+
+class TestHaltFails(GDBRemoteTestBase):
+
+class MyResponder(MockGDBServerResponder):
+
+def setBreakpoint(self, packet):
+return "OK"
+
+def interrupt(self):
+# Simulate process waiting longer than the interrupt
+# timeout to stop, then sending the reply.
+time.sleep(14)
+return "T02reason:signal"
+
+def cont(self):
+# No response, wait for the client to interrupt us.
+return None
+
+def wait_for_and_check_event(self, wait_time, value):
+event = lldb.SBEvent()
+got_event = self.dbg.GetListener().WaitForEvent(wait_time, event)
+self.assertTrue(got_event, "Failed to get event after wait")
+self.assertTrue(lldb.SBProcess.EventIsProcessEvent(event), "Event was not a process event")
+event_type = lldb.SBProcess.GetStateFromEvent(event)
+self.assertEqual(event_type, value)
+
+def get_to_running(self):
+self.server.responder = self.MyResponder()
+self.target = self.createTarget("a.yaml")
+process = self.connect(self.target)
+self.dbg.SetAsync(True)
+
+# There should be a stopped event, consume that:
+self.wait_for_and_check_event(2, lldb.eStateStopped)
+process.Continue()
+
+# There should be a running event, consume that:
+self.wait_for_and_check_event(2, lldb.eStateRunning)
+return process
+
+@skipIfReproducer # FIXME: Unexpected packet during (passive) replay
+def test_destroy_while_running(self):
+process = self.get_to_running()
+process.Destroy()
+
+# Again pretend that after failing to be interrupted, we delivered the stop
+# and make sure we still exit properly.
+self.wait_for_and_check_event(14, lldb.eStateExited)
+
+@skipIfReproducer # FIXME: Unexpected packet during (passive) replay
+def test_async_interrupt(self):
+"""
+Test that explicitly calling AsyncInterrupt, which then fails, leads
+to an "eStateExited" state.
+"""
+process = self.get_to_running()
+# Now do the interrupt:
+process.SendAsyncInterrupt()
+
+# That should have caused the Halt to time out and we should
+# be in eStateExited:
+self.wait_for_and_check_event(15, lldb.eStateExited)
+
+
+
+
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3689,12 +3689,25 @@
 __FUNCTION__, arg, process->GetID());
 
   EventSP event_sp;
+
+  // We need to ignore any packets that come in after we have
+  // have decided the process has exited.  There are some
+  // situations, for instance when we try to interrupt a running
+  // process and the interrupt fails, where another packet might
+  // get delivered after we've decided to give up on the process.
+  // But once we've decided we are done with the process we will
+  // not be in a state to do anything useful with new packets.
+  // So it is safer to simply ignore any remaining packets by
+  // explicitly checking for eStateExited before reentering the
+  // fetch loop.
+  
   bool done = false;
-  while (!done) {
+  while (!done && process->GetPrivateState() != eStateExited) {
 LLDB_LOGF(log,
   "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
   ") listener.WaitForEvent (NULL, event_sp)...",
   __FUNCTION__, arg, process->GetID());
+
 if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
   const uint32_t event_type = event_sp->GetType();
   if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) {
@@ -3793,6 +3806,7 @@
 } else {
  

[Lldb-commits] [PATCH] D101933: If an interrupt fails, don't try to fetch any more packets from the server

2021-05-05 Thread Greg Clayton via Phabricator via lldb-commits
clayborg accepted this revision.
clayborg added inline comments.
This revision is now accepted and ready to land.



Comment at: 
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp:3698-3701
+// If the process has exited, don't try to fetch more events from the
+// server for it.  We wouldn't be in a correct state to handle them.
+if (process->GetPrivateState() == eStateExited)
+  break;

You could put this up into the while condition?

```
while (!done && process->GetPrivateState() != eStateExited)
```



Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D101933/new/

https://reviews.llvm.org/D101933

___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [PATCH] D101933: If an interrupt fails, don't try to fetch any more packets from the server

2021-05-05 Thread Jim Ingham via Phabricator via lldb-commits
jingham created this revision.
jingham added reviewers: clayborg, JDevlieghere, labath.
jingham requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

When a gdb-remote plugin process doesn't respond to an interrupt, it returns 
from
SendContinuePacketAndWaitForResponse with eStateInvalid.  The AsyncThread
code responds to this return by setting the state to eStateExited.  But it 
doesn't
immediately exit the ProcessGDBRemote::AsyncThread's packet fetch loop.

  

This looks like just an oversight in the AsyncThread function. It's important 
not to
go back to wait for another packet.  If it arrives while you are tearing down 
the
process, the internal-state-thread might try to handle it when the process in 
not
in a good state.

We weren't going to do anything useful with the extra packet anyway, we've 
already
declared the process exited.  So rather than put more checks into all the 
shutdown paths 
to make sure this extra packet doesn't cause problems, just don't fetch it.

  

The main part of the patch is setting "done = true" when we get the 
eStateInvalid.
I also added a check at the beginning of the while(done) loop to prevent 
another error
from getting us to fetch packets for an exited process.

  

I also added a test case to ensure that if an Interrupt fails, we call the 
process
exited.  I can't test exactly the error I'm fixing, there's no good way to know
that the stop reply for the failed interrupt wasn't fetched.  But at least this
asserts that the overall behavior is correct.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D101933

Files:
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py

Index: lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py
===
--- /dev/null
+++ lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py
@@ -0,0 +1,72 @@
+from __future__ import print_function
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from gdbclientutils import *
+
+
+class TestHaltFails(GDBRemoteTestBase):
+
+class MyResponder(MockGDBServerResponder):
+
+def setBreakpoint(self, packet):
+return "OK"
+
+def interrupt(self):
+# Simulate process waiting longer than the interrupt
+# timeout to stop, then sending the reply.
+time.sleep(14)
+return "T02reason:signal"
+
+def cont(self):
+# No response, wait for the client to interrupt us.
+return None
+
+def wait_for_and_check_event(self, wait_time, value):
+event = lldb.SBEvent()
+got_event = self.dbg.GetListener().WaitForEvent(wait_time, event)
+self.assertTrue(got_event, "Failed to get event after wait")
+self.assertTrue(lldb.SBProcess.EventIsProcessEvent(event), "Event was not a process event")
+event_type = lldb.SBProcess.GetStateFromEvent(event)
+self.assertEqual(event_type, value)
+
+def get_to_running(self):
+self.server.responder = self.MyResponder()
+self.target = self.createTarget("a.yaml")
+process = self.connect(self.target)
+self.dbg.SetAsync(True)
+
+# There should be a stopped event, consume that:
+self.wait_for_and_check_event(2, lldb.eStateStopped)
+process.Continue()
+
+# There should be a running event, consume that:
+self.wait_for_and_check_event(2, lldb.eStateRunning)
+return process
+
+@skipIfReproducer # FIXME: Unexpected packet during (passive) replay
+def test_destroy_while_running(self):
+process = self.get_to_running()
+process.Destroy()
+
+# Again pretend that after failing to be interrupted, we delivered the stop
+# and make sure we still exit properly.
+self.wait_for_and_check_event(14, lldb.eStateExited)
+
+@skipIfReproducer # FIXME: Unexpected packet during (passive) replay
+def test_async_interrupt(self):
+"""
+Test that explicitly calling AsyncInterrupt, which then fails, leads
+to an "eStateExited" state.
+"""
+process = self.get_to_running()
+# Now do the interrupt:
+process.SendAsyncInterrupt()
+
+# That should have caused the Halt to time out and we should
+# be in eStateExited:
+self.wait_for_and_check_event(15, lldb.eStateExited)
+
+
+
+
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3695,6 +3695,11 @@
   "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64