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());