Skip to content

Commit 8d3255a

Browse files
jiminghamadrian-prantl
authored andcommitted
Make sure the thread list is updated before you set the stop reason
on a thread. When talking to some older gdb-remote stubs, We were getting a stop reason from the stop reply packet and setting it on the relevant thread before we updated the full stop list. That would get discarded when the full list was updated. Also, if you already have a thread list when you go to see if there is an Operating System plugin, and you do indeed load a new OS plugin, you have to re-fetch the thread list or it will only show the raw threads. Differential Revision: https://reviews.llvm.org/D62887 llvm-svn: 364666 (cherry picked from commit 8864b43)
1 parent 3fef517 commit 8d3255a

File tree

5 files changed

+225
-8
lines changed

5 files changed

+225
-8
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
from __future__ import print_function
2+
import lldb
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test.decorators import *
5+
from gdbclientutils import *
6+
7+
8+
class TestRecognizeBreakpoint(GDBRemoteTestBase):
9+
""" This tests the case where the gdb-remote server doesn't support any
10+
of the thread-info packets, and just tells which thread got the stop
11+
signal with:
12+
T05thread:01;
13+
There was a bug in lldb that we would set the stop reason from this
14+
packet too early - before we had updated the thread list. So when we
15+
later updated the thread list, we would throw away this info. Normally
16+
we would be able to reconstruct it from the thread info, but not if the
17+
stub doesn't support it """
18+
19+
def test(self):
20+
class MyResponder(MockGDBServerResponder):
21+
def __init__(self):
22+
MockGDBServerResponder.__init__(self)
23+
self.thread_info_count = 0
24+
self.after_cont = False
25+
self.current_thread = 0
26+
27+
def cont(self):
28+
# Simulate process stopping due to a breakpoint:
29+
self.after_cont = True
30+
return "T05thread:01;"
31+
32+
def vCont(self, packet):
33+
self.after_cont = True
34+
return "T05thread:01;"
35+
36+
def haltReason(self):
37+
return "T02thread:01;"
38+
39+
def threadStopInfo(self, num):
40+
return ""
41+
42+
def QThreadSuffixSupported(self):
43+
return ""
44+
45+
def QListThreadsInStopReply(self):
46+
return ""
47+
48+
def setBreakpoint(self, packet):
49+
return "OK"
50+
51+
def qfThreadInfo(self):
52+
return "m1"
53+
54+
def qsThreadInfo(self):
55+
if (self.thread_info_count % 2) == 0:
56+
str = "m2"
57+
else:
58+
str = "l"
59+
self.thread_info_count += 1
60+
return str
61+
62+
def readRegisters(self):
63+
if self.after_cont and self.current_thread == 1:
64+
return "c01e990080ffffff"
65+
else:
66+
return "badcfe10325476980"
67+
68+
def readRegister(self, regno):
69+
return ""
70+
71+
def qXferRead(self, obj, annex, offset, length):
72+
if annex == "target.xml":
73+
return """<?xml version="1.0"?>
74+
<target version="1.0">
75+
<architecture>i386:x86-64</architecture>
76+
<feature name="org.gnu.gdb.i386.core">
77+
<reg name="rip" bitsize="64" regnum="0" type="code_ptr" group="general"/>
78+
</feature>
79+
</target>""", False
80+
else:
81+
return None, False
82+
83+
def selectThread(self, op, thread):
84+
if op != 'g':
85+
return ''
86+
87+
self.current_thread = thread
88+
return "OK"
89+
90+
def other (self, packet):
91+
if packet == "vCont?":
92+
return "vCont;c;C;s;S"
93+
return ''
94+
95+
python_os_plugin_path = os.path.join(self.getSourceDir(),
96+
'operating_system_2.py')
97+
command ="settings set target.process.python-os-plugin-path '{}'".format(
98+
python_os_plugin_path)
99+
self.runCmd(command)
100+
101+
self.server.responder = MyResponder()
102+
target = self.dbg.CreateTarget("")
103+
process = self.connect(target)
104+
105+
bkpt = target.BreakpointCreateByAddress(0xffffff8000991ec0)
106+
self.assertEqual(bkpt.GetNumLocations(), 1, "Fake breakpoint was resolved.")
107+
108+
# Get the initial stop, and we should have two threads.
109+
num_threads = len(process.threads)
110+
self.assertEqual(num_threads, 2, "Got two threads")
111+
112+
thread_0 = process.threads[0]
113+
self.assertEqual(thread_0.GetStopReason(), 1, "Thread_0 stopped for no reason")
114+
self.assertEqual(thread_0.GetName(), "one", "Thread_0 is called one")
115+
116+
thread_1 = process.threads[1]
117+
self.assertEqual(thread_1.GetStopReason(), 5, "Thread_0 stopped for SIGSTOP")
118+
self.assertEqual(thread_1.GetName(), "two", "Thread_0 is called two")
119+
120+
# Now continue and we will fake hitting a breakpoint.
121+
process.Continue()
122+
123+
self.assertEqual(process.GetState(),lldb.eStateStopped, "Process is stopped")
124+
num_threads = len(process.threads)
125+
126+
num_threads = len(process.threads)
127+
self.assertEqual(num_threads, 2, "Got two threads")
128+
129+
thread_0 = process.threads[0]
130+
self.assertEqual(thread_0.GetStopReason(), 1, "Thread_0 stopped for no reason")
131+
self.assertEqual(thread_0.GetName(), "one", "Thread_0 is called one")
132+
133+
thread_1 = process.threads[1]
134+
self.assertEqual(thread_1.GetStopReason(), 3, "Thread_0 stopped for SIGTRAP")
135+
self.assertEqual(thread_1.GetName(), "three", "Thread_0 is called three")
136+
137+
self.assertTrue(thread_1.IsValid(), "Thread_1 is valid")
138+
self.assertEqual(thread_1.GetStopReason(), lldb.eStopReasonBreakpoint, "Stopped at breakpoint")
139+

lldb/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ def respond(self, packet):
103103
return self.interrupt()
104104
if packet == "c":
105105
return self.cont()
106+
if packet.startswith("vCont;c"):
107+
return self.vCont(packet)
106108
if packet[0] == "g":
107109
return self.readRegisters()
108110
if packet[0] == "G":
@@ -168,6 +170,9 @@ def interrupt(self):
168170
def cont(self):
169171
raise self.UnexpectedPacketException()
170172

173+
def vCont(self, packet):
174+
raise self.UnexpectedPacketException()
175+
171176
def readRegisters(self):
172177
return "00000000" * self.registerCount
173178

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import lldb
2+
import struct
3+
4+
5+
class OperatingSystemPlugIn(object):
6+
"""Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class
7+
This version stops once with threads 0x111 and 0x222, then stops a second time with threads
8+
0x111 and 0x333."""
9+
10+
def __init__(self, process):
11+
'''Initialization needs a valid.SBProcess object.
12+
13+
This plug-in will get created after a live process is valid and has stopped for the first time.
14+
'''
15+
self.process = None
16+
self.registers = None
17+
self.threads = None
18+
self.times_called = 0
19+
if isinstance(process, lldb.SBProcess) and process.IsValid():
20+
self.process = process
21+
self.threads = None # Will be an dictionary containing info for each thread
22+
23+
def get_target(self):
24+
return self.process.target
25+
26+
def get_thread_info(self):
27+
self.times_called += 1
28+
29+
if self.times_called == 1:
30+
self.threads = [{
31+
'tid': 0x111,
32+
'name': 'one',
33+
'queue': 'queue1',
34+
'state': 'stopped',
35+
'stop_reason': 'none',
36+
'core': 1
37+
}, {
38+
'tid': 0x222,
39+
'name': 'two',
40+
'queue': 'queue2',
41+
'state': 'stopped',
42+
'stop_reason': 'none',
43+
'core': 0
44+
}]
45+
else:
46+
self.threads = [{
47+
'tid': 0x111,
48+
'name': 'one',
49+
'queue': 'queue1',
50+
'state': 'stopped',
51+
'stop_reason': 'none',
52+
'core': 1
53+
}, {
54+
'tid': 0x333,
55+
'name': 'three',
56+
'queue': 'queue3',
57+
'state': 'stopped',
58+
'stop_reason': 'none',
59+
'core': 0
60+
}]
61+
return self.threads
62+

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,6 +2423,15 @@ void ProcessGDBRemote::RefreshStateAfterStop() {
24232423

24242424
// Scope for the lock
24252425
{
2426+
// Check to see if SetThreadStopInfo() filled in m_thread_ids?
2427+
if (m_thread_ids.empty()) {
2428+
// No, we need to fetch the thread list manually
2429+
UpdateThreadIDList();
2430+
}
2431+
// We might set some stop info's so make sure the thread list is up to
2432+
// date before we do that or we might overwrite what was computed here.
2433+
UpdateThreadListIfNeeded();
2434+
24262435
// Lock the thread stack while we access it
24272436
std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex);
24282437
// Get the number of stop packets on the stack
@@ -2437,13 +2446,7 @@ void ProcessGDBRemote::RefreshStateAfterStop() {
24372446
// Clear the thread stop stack
24382447
m_stop_packet_stack.clear();
24392448
}
2440-
2441-
// Check to see if SetThreadStopInfo() filled in m_thread_ids?
2442-
if (m_thread_ids.empty()) {
2443-
// No, we need to fetch the thread list manually
2444-
UpdateThreadIDList();
2445-
}
2446-
2449+
24472450
// If we have queried for a default thread id
24482451
if (m_initial_tid != LLDB_INVALID_THREAD_ID) {
24492452
m_thread_list.SetSelectedThreadByID(m_initial_tid);

lldb/source/Target/Process.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3019,8 +3019,16 @@ void Process::CompleteAttach() {
30193019
}
30203020
}
30213021

3022-
if (!m_os_up)
3022+
if (!m_os_up) {
30233023
LoadOperatingSystemPlugin(false);
3024+
if (m_os_up) {
3025+
// Somebody might have gotten threads before now, but we need to force the
3026+
// update after we've loaded the OperatingSystem plugin or it won't get a
3027+
// chance to process the threads.
3028+
m_thread_list.Clear();
3029+
UpdateThreadListIfNeeded();
3030+
}
3031+
}
30243032
// Figure out which one is the executable, and set that in our target:
30253033
const ModuleList &target_modules = GetTarget().GetImages();
30263034
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());

0 commit comments

Comments
 (0)