Index: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestRecognizeBreakpoint.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestRecognizeBreakpoint.py +++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestRecognizeBreakpoint.py @@ -0,0 +1,139 @@ +from __future__ import print_function +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from gdbclientutils import * + + +class TestRecognizeBreakpoint(GDBRemoteTestBase): + """ This tests the case where the gdb-remote server doesn't support any + of the thread-info packets, and just tells which thread got the stop + signal with: + T05thread:01; + There was a bug in lldb that we would set the stop reason from this + packet too early - before we had updated the thread list. So when we + later updated the thread list, we would throw away this info. Normally + we would be able to reconstruct it from the thread info, but not if the + stub doesn't support it """ + + def test(self): + class MyResponder(MockGDBServerResponder): + def __init__(self): + MockGDBServerResponder.__init__(self) + self.thread_info_count = 0 + self.after_cont = False + self.current_thread = 0 + + def cont(self): + # Simulate process stopping due to a breakpoint: + self.after_cont = True + return "T05thread:01;" + + def vCont(self, packet): + self.after_cont = True + return "T05thread:01;" + + def haltReason(self): + return "T02thread:01;" + + def threadStopInfo(self, num): + return "" + + def QThreadSuffixSupported(self): + return "" + + def QListThreadsInStopReply(self): + return "" + + def setBreakpoint(self, packet): + return "OK" + + def qfThreadInfo(self): + return "m1" + + def qsThreadInfo(self): + if (self.thread_info_count % 2) == 0: + str = "m2" + else: + str = "l" + self.thread_info_count += 1 + return str + + def readRegisters(self): + if self.after_cont and self.current_thread == 1: + return "c01e990080ffffff" + else: + return "badcfe10325476980" + + def readRegister(self, regno): + return "" + + def qXferRead(self, obj, annex, offset, length): + if annex == "target.xml": + return """ + + i386:x86-64 + + + + """, False + else: + return None, False + + def selectThread(self, op, thread): + if op != 'g': + return '' + + self.current_thread = thread + return "OK" + + def other (self, packet): + if packet == "vCont?": + return "vCont;c;C;s;S" + return '' + + python_os_plugin_path = os.path.join(self.getSourceDir(), + 'operating_system_2.py') + command ="settings set target.process.python-os-plugin-path '{}'".format( + python_os_plugin_path) + self.runCmd(command) + + self.server.responder = MyResponder() + target = self.dbg.CreateTarget("") + process = self.connect(target) + + bkpt = target.BreakpointCreateByAddress(0xffffff8000991ec0) + self.assertEqual(bkpt.GetNumLocations(), 1, "Fake breakpoint was resolved.") + + # Get the initial stop, and we should have two threads. + num_threads = len(process.threads) + self.assertEqual(num_threads, 2, "Got two threads") + + thread_0 = process.threads[0] + self.assertEqual(thread_0.GetStopReason(), 1, "Thread_0 stopped for no reason") + self.assertEqual(thread_0.GetName(), "one", "Thread_0 is called one") + + thread_1 = process.threads[1] + self.assertEqual(thread_1.GetStopReason(), 5, "Thread_0 stopped for SIGSTOP") + self.assertEqual(thread_1.GetName(), "two", "Thread_0 is called two") + + # Now continue and we will fake hitting a breakpoint. + process.Continue() + + self.assertEqual(process.GetState(),lldb.eStateStopped, "Process is stopped") + num_threads = len(process.threads) + + num_threads = len(process.threads) + self.assertEqual(num_threads, 2, "Got two threads") + + thread_0 = process.threads[0] + self.assertEqual(thread_0.GetStopReason(), 1, "Thread_0 stopped for no reason") + self.assertEqual(thread_0.GetName(), "one", "Thread_0 is called one") + + thread_1 = process.threads[1] + self.assertEqual(thread_1.GetStopReason(), 3, "Thread_0 stopped for SIGTRAP") + self.assertEqual(thread_1.GetName(), "three", "Thread_0 is called three") + + self.assertTrue(thread_1.IsValid(), "Thread_1 is valid") + self.assertEqual(thread_1.GetStopReason(), lldb.eStopReasonBreakpoint, "Stopped at breakpoint") + Index: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py +++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py @@ -103,6 +103,8 @@ return self.interrupt() if packet == "c": return self.cont() + if packet.startswith("vCont;c"): + return self.vCont(packet) if packet[0] == "g": return self.readRegisters() if packet[0] == "G": @@ -168,6 +170,9 @@ def cont(self): raise self.UnexpectedPacketException() + def vCont(self, packet): + raise self.UnexpectedPacketException() + def readRegisters(self): return "00000000" * self.registerCount Index: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/operating_system_2.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/gdb_remote_client/operating_system_2.py +++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/operating_system_2.py @@ -0,0 +1,62 @@ +import lldb +import struct + + +class OperatingSystemPlugIn(object): + """Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class + This version stops once with threads 0x111 and 0x222, then stops a second time with threads + 0x111 and 0x333.""" + + def __init__(self, process): + '''Initialization needs a valid.SBProcess object. + + This plug-in will get created after a live process is valid and has stopped for the first time. + ''' + self.process = None + self.registers = None + self.threads = None + self.times_called = 0 + if isinstance(process, lldb.SBProcess) and process.IsValid(): + self.process = process + self.threads = None # Will be an dictionary containing info for each thread + + def get_target(self): + return self.process.target + + def get_thread_info(self): + self.times_called += 1 + + if self.times_called == 1: + self.threads = [{ + 'tid': 0x111, + 'name': 'one', + 'queue': 'queue1', + 'state': 'stopped', + 'stop_reason': 'none', + 'core': 1 + }, { + 'tid': 0x222, + 'name': 'two', + 'queue': 'queue2', + 'state': 'stopped', + 'stop_reason': 'none', + 'core': 0 + }] + else: + self.threads = [{ + 'tid': 0x111, + 'name': 'one', + 'queue': 'queue1', + 'state': 'stopped', + 'stop_reason': 'none', + 'core': 1 + }, { + 'tid': 0x333, + 'name': 'three', + 'queue': 'queue3', + 'state': 'stopped', + 'stop_reason': 'none', + 'core': 0 + }] + return self.threads + Index: source/Host/common/Editline.cpp =================================================================== --- source/Host/common/Editline.cpp +++ source/Host/common/Editline.cpp @@ -1380,6 +1380,7 @@ stream->Write(s, len); stream->Flush(); if (m_editor_status == EditorStatus::Editing) { + SaveEditedLine(); DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -2423,6 +2423,15 @@ // Scope for the lock { + // Check to see if SetThreadStopInfo() filled in m_thread_ids? + if (m_thread_ids.empty()) { + // No, we need to fetch the thread list manually + UpdateThreadIDList(); + } + // We might set some stop info's so make sure the thread list is up to + // date before we do that or we might overwrite what was computed here. + UpdateThreadListIfNeeded(); + // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // Get the number of stop packets on the stack @@ -2437,13 +2446,7 @@ // Clear the thread stop stack m_stop_packet_stack.clear(); } - - // Check to see if SetThreadStopInfo() filled in m_thread_ids? - if (m_thread_ids.empty()) { - // No, we need to fetch the thread list manually - UpdateThreadIDList(); - } - + // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { m_thread_list.SetSelectedThreadByID(m_initial_tid); Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -3019,8 +3019,16 @@ } } - if (!m_os_up) + if (!m_os_up) { LoadOperatingSystemPlugin(false); + if (m_os_up) { + // Somebody might have gotten threads before now, but we need to force the + // update after we've loaded the OperatingSystem plugin or it won't get a + // chance to process the threads. + m_thread_list.Clear(); + UpdateThreadListIfNeeded(); + } + } // Figure out which one is the executable, and set that in our target: const ModuleList &target_modules = GetTarget().GetImages(); std::lock_guard guard(target_modules.GetMutex());