Skip to content

When SendContinuePacketAndWaitForResponse returns eStateInvalid, don'… #2927

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3665,12 +3665,25 @@ thread_result_t ProcessGDBRemote::AsyncThread(void *arg) {
__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)) {
Expand Down Expand Up @@ -3769,6 +3782,7 @@ thread_result_t ProcessGDBRemote::AsyncThread(void *arg) {
} else {
process->SetExitStatus(-1, "lost connection");
}
done = true;
break;
}

Expand Down
72 changes: 72 additions & 0 deletions lldb/test/API/functionalities/gdb_remote_client/TestHaltFails.py
Original file line number Diff line number Diff line change
@@ -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)