Skip to content

Commit b601c67

Browse files
committed
[lldb] [client] Support for multiprocess extension
Add a minimal support for the multiprocess extension in gdb-remote client. It accepts PIDs as part of thread-ids, and rejects PIDs that do not match the current inferior. Differential Revision: https://reviews.llvm.org/D99603
1 parent 2a1f871 commit b601c67

File tree

5 files changed

+153
-54
lines changed

5 files changed

+153
-54
lines changed

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

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
8989
m_supports_jGetSharedCacheInfo(eLazyBoolCalculate),
9090
m_supports_QPassSignals(eLazyBoolCalculate),
9191
m_supports_error_string_reply(eLazyBoolCalculate),
92+
m_supports_multiprocess(eLazyBoolCalculate),
9293
m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true),
9394
m_supports_qUserName(true), m_supports_qGroupName(true),
9495
m_supports_qThreadStopInfo(true), m_supports_z0(true),
@@ -292,6 +293,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
292293
m_prepare_for_reg_writing_reply = eLazyBoolCalculate;
293294
m_attach_or_wait_reply = eLazyBoolCalculate;
294295
m_avoid_g_packets = eLazyBoolCalculate;
296+
m_supports_multiprocess = eLazyBoolCalculate;
295297
m_supports_qXfer_auxv_read = eLazyBoolCalculate;
296298
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
297299
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
@@ -342,11 +344,13 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
342344
m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
343345
m_supports_qXfer_features_read = eLazyBoolNo;
344346
m_supports_qXfer_memory_map_read = eLazyBoolNo;
347+
m_supports_multiprocess = eLazyBoolNo;
345348
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
346349
// not, we assume no limit
347350

348351
// build the qSupported packet
349-
std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc"};
352+
std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc",
353+
"multiprocess+"};
350354
StreamString packet;
351355
packet.PutCString("qSupported");
352356
for (uint32_t i = 0; i < features.size(); ++i) {
@@ -433,6 +437,11 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
433437
else
434438
m_supports_QPassSignals = eLazyBoolNo;
435439

440+
if (::strstr(response_cstr, "multiprocess+"))
441+
m_supports_multiprocess = eLazyBoolYes;
442+
else
443+
m_supports_multiprocess = eLazyBoolNo;
444+
436445
const char *packet_size_str = ::strstr(response_cstr, "PacketSize=");
437446
if (packet_size_str) {
438447
StringExtractorGDBRemote packet_response(packet_size_str +
@@ -741,12 +750,14 @@ lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) {
741750
// If we don't get a response for $qC, check if $qfThreadID gives us a
742751
// result.
743752
if (m_curr_pid == LLDB_INVALID_PROCESS_ID) {
744-
std::vector<lldb::tid_t> thread_ids;
745753
bool sequence_mutex_unavailable;
746-
size_t size;
747-
size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable);
748-
if (size && !sequence_mutex_unavailable) {
749-
m_curr_pid = thread_ids.front();
754+
auto ids = GetCurrentProcessAndThreadIDs(sequence_mutex_unavailable);
755+
if (!ids.empty() && !sequence_mutex_unavailable) {
756+
// If server returned an explicit PID, use that.
757+
m_curr_pid = ids.front().first;
758+
// Otherwise, use the TID of the first thread (Linux hack).
759+
if (m_curr_pid == LLDB_INVALID_PROCESS_ID)
760+
m_curr_pid = ids.front().second;
750761
m_curr_pid_is_valid = eLazyBoolYes;
751762
return m_curr_pid;
752763
}
@@ -1125,8 +1136,23 @@ bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) {
11251136
if (!response.IsNormalResponse())
11261137
return false;
11271138

1128-
if (response.GetChar() == 'Q' && response.GetChar() == 'C')
1129-
tid = response.GetHexMaxU64(true, -1);
1139+
if (response.GetChar() == 'Q' && response.GetChar() == 'C') {
1140+
auto pid_tid = response.GetPidTid(0);
1141+
if (!pid_tid)
1142+
return false;
1143+
1144+
lldb::pid_t pid = pid_tid->first;
1145+
// invalid
1146+
if (pid == StringExtractorGDBRemote::AllProcesses)
1147+
return false;
1148+
1149+
// if we get pid as well, update m_curr_pid
1150+
if (pid != 0) {
1151+
m_curr_pid = pid;
1152+
m_curr_pid_is_valid = eLazyBoolYes;
1153+
}
1154+
tid = pid_tid->second;
1155+
}
11301156

11311157
return true;
11321158
}
@@ -2766,9 +2792,10 @@ uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket(
27662792
return UINT8_MAX;
27672793
}
27682794

2769-
size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
2770-
std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) {
2771-
thread_ids.clear();
2795+
std::vector<std::pair<lldb::pid_t, lldb::tid_t>>
2796+
GDBRemoteCommunicationClient::GetCurrentProcessAndThreadIDs(
2797+
bool &sequence_mutex_unavailable) {
2798+
std::vector<std::pair<lldb::pid_t, lldb::tid_t>> ids;
27722799

27732800
Lock lock(*this, false);
27742801
if (lock) {
@@ -2786,11 +2813,11 @@ size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
27862813
break;
27872814
if (ch == 'm') {
27882815
do {
2789-
tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID);
2816+
auto pid_tid = response.GetPidTid(LLDB_INVALID_PROCESS_ID);
2817+
if (!pid_tid)
2818+
return {};
27902819

2791-
if (tid != LLDB_INVALID_THREAD_ID) {
2792-
thread_ids.push_back(tid);
2793-
}
2820+
ids.push_back(pid_tid.getValue());
27942821
ch = response.GetChar(); // Skip the command separator
27952822
} while (ch == ','); // Make sure we got a comma separator
27962823
}
@@ -2803,10 +2830,10 @@ size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
28032830
* be as simple as 'S05'. There is no packet which can give us pid and/or
28042831
* tid.
28052832
* Assume pid=tid=1 in such cases.
2806-
*/
2833+
*/
28072834
if ((response.IsUnsupportedResponse() || response.IsNormalResponse()) &&
2808-
thread_ids.size() == 0 && IsConnected()) {
2809-
thread_ids.push_back(1);
2835+
ids.size() == 0 && IsConnected()) {
2836+
ids.emplace_back(1, 1);
28102837
}
28112838
} else {
28122839
Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
@@ -2815,6 +2842,28 @@ size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
28152842
"packet 'qfThreadInfo'");
28162843
sequence_mutex_unavailable = true;
28172844
}
2845+
2846+
return ids;
2847+
}
2848+
2849+
size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
2850+
std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) {
2851+
lldb::pid_t pid = GetCurrentProcessID();
2852+
thread_ids.clear();
2853+
2854+
auto ids = GetCurrentProcessAndThreadIDs(sequence_mutex_unavailable);
2855+
if (ids.empty() || sequence_mutex_unavailable)
2856+
return 0;
2857+
2858+
for (auto id : ids) {
2859+
// skip threads that do not belong to the current process
2860+
if (id.first != LLDB_INVALID_PROCESS_ID && id.first != pid)
2861+
continue;
2862+
if (id.second != LLDB_INVALID_THREAD_ID &&
2863+
id.second != StringExtractorGDBRemote::AllThreads)
2864+
thread_ids.push_back(id.second);
2865+
}
2866+
28182867
return thread_ids.size();
28192868
}
28202869

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
366366
return m_supports_alloc_dealloc_memory;
367367
}
368368

369+
std::vector<std::pair<lldb::pid_t, lldb::tid_t>>
370+
GetCurrentProcessAndThreadIDs(bool &sequence_mutex_unavailable);
371+
369372
size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids,
370373
bool &sequence_mutex_unavailable);
371374

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

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,22 +1487,22 @@ void ProcessGDBRemote::ClearThreadIDList() {
14871487
m_thread_pcs.clear();
14881488
}
14891489
1490-
size_t
1491-
ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) {
1490+
size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(
1491+
llvm::StringRef value) {
14921492
m_thread_ids.clear();
1493-
size_t comma_pos;
1494-
lldb::tid_t tid;
1495-
while ((comma_pos = value.find(',')) != std::string::npos) {
1496-
value[comma_pos] = '\0';
1497-
// thread in big endian hex
1498-
tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16);
1499-
if (tid != LLDB_INVALID_THREAD_ID)
1500-
m_thread_ids.push_back(tid);
1501-
value.erase(0, comma_pos + 1);
1502-
}
1503-
tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16);
1504-
if (tid != LLDB_INVALID_THREAD_ID)
1505-
m_thread_ids.push_back(tid);
1493+
lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
1494+
StringExtractorGDBRemote thread_ids{value};
1495+
1496+
do {
1497+
auto pid_tid = thread_ids.GetPidTid(pid);
1498+
if (pid_tid && pid_tid->first == pid) {
1499+
lldb::tid_t tid = pid_tid->second;
1500+
if (tid != LLDB_INVALID_THREAD_ID &&
1501+
tid != StringExtractorGDBRemote::AllProcesses)
1502+
m_thread_ids.push_back(tid);
1503+
}
1504+
} while (thread_ids.GetChar() == ',');
1505+
15061506
return m_thread_ids.size();
15071507
}
15081508
@@ -1519,7 +1519,7 @@ ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) {
15191519
value.erase(0, comma_pos + 1);
15201520
}
15211521
pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16);
1522-
if (pc != LLDB_INVALID_THREAD_ID)
1522+
if (pc != LLDB_INVALID_ADDRESS)
15231523
m_thread_pcs.push_back(pc);
15241524
return m_thread_pcs.size();
15251525
}
@@ -2141,6 +2141,7 @@ ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) {
21412141
}
21422142

21432143
StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
2144+
lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
21442145
stop_packet.SetFilePos(0);
21452146
const char stop_type = stop_packet.GetChar();
21462147
switch (stop_type) {
@@ -2155,14 +2156,12 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
21552156
if (stop_id == 0) {
21562157
// Our first stop, make sure we have a process ID, and also make sure we
21572158
// know about our registers
2158-
if (GetID() == LLDB_INVALID_PROCESS_ID) {
2159-
lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
2160-
if (pid != LLDB_INVALID_PROCESS_ID)
2161-
SetID(pid);
2162-
}
2159+
if (GetID() == LLDB_INVALID_PROCESS_ID && pid != LLDB_INVALID_PROCESS_ID)
2160+
SetID(pid);
21632161
BuildDynamicRegisterInfo(true);
21642162
}
21652163
// Stop with signal and thread info
2164+
lldb::pid_t stop_pid = LLDB_INVALID_PROCESS_ID;
21662165
lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
21672166
const uint8_t signo = stop_packet.GetHexU8();
21682167
llvm::StringRef key;
@@ -2191,24 +2190,18 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
21912190
value.getAsInteger(16, x);
21922191
exc_data.push_back(x);
21932192
} else if (key.compare("thread") == 0) {
2194-
// thread in big endian hex
2195-
if (value.getAsInteger(16, tid))
2193+
// thread-id
2194+
StringExtractorGDBRemote thread_id{value};
2195+
auto pid_tid = thread_id.GetPidTid(pid);
2196+
if (pid_tid) {
2197+
stop_pid = pid_tid->first;
2198+
tid = pid_tid->second;
2199+
} else
21962200
tid = LLDB_INVALID_THREAD_ID;
21972201
} else if (key.compare("threads") == 0) {
21982202
std::lock_guard<std::recursive_mutex> guard(
21992203
m_thread_list_real.GetMutex());
2200-
2201-
m_thread_ids.clear();
2202-
// A comma separated list of all threads in the current
2203-
// process that includes the thread for this stop reply packet
2204-
lldb::tid_t tid;
2205-
while (!value.empty()) {
2206-
llvm::StringRef tid_str;
2207-
std::tie(tid_str, value) = value.split(',');
2208-
if (tid_str.getAsInteger(16, tid))
2209-
tid = LLDB_INVALID_THREAD_ID;
2210-
m_thread_ids.push_back(tid);
2211-
}
2204+
UpdateThreadIDsFromStopReplyThreadsValue(value);
22122205
} else if (key.compare("thread-pcs") == 0) {
22132206
m_thread_pcs.clear();
22142207
// A comma separated list of all threads in the current
@@ -2321,6 +2314,14 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
23212314
}
23222315
}
23232316

2317+
if (stop_pid != LLDB_INVALID_PROCESS_ID && stop_pid != pid) {
2318+
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
2319+
LLDB_LOG(log,
2320+
"Received stop for incorrect PID = {0} (inferior PID = {1})",
2321+
stop_pid, pid);
2322+
return eStateInvalid;
2323+
}
2324+
23242325
if (tid == LLDB_INVALID_THREAD_ID) {
23252326
// A thread id may be invalid if the response is old style 'S' packet
23262327
// which does not provide the

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ class ProcessGDBRemote : public Process,
335335

336336
size_t UpdateThreadPCsFromStopReplyThreadsValue(std::string &value);
337337

338-
size_t UpdateThreadIDsFromStopReplyThreadsValue(std::string &value);
338+
size_t UpdateThreadIDsFromStopReplyThreadsValue(llvm::StringRef value);
339339

340340
bool HandleNotifyPacket(StringExtractorGDBRemote &packet);
341341

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from __future__ import print_function
2+
import lldb
3+
import unittest
4+
from lldbsuite.test.lldbtest import *
5+
from lldbsuite.test.decorators import *
6+
from gdbclientutils import *
7+
8+
9+
class TestMultiprocess(GDBRemoteTestBase):
10+
def test_qfThreadInfo(self):
11+
class MyResponder(MockGDBServerResponder):
12+
def qfThreadInfo(self):
13+
return "mp400.10200,p400.10204,p401.10300,p400.10208"
14+
15+
self.server.responder = MyResponder()
16+
target = self.dbg.CreateTarget('')
17+
if self.TraceOn():
18+
self.runCmd("log enable gdb-remote packets")
19+
self.addTearDownHook(
20+
lambda: self.runCmd("log disable gdb-remote packets"))
21+
process = self.connect(target)
22+
self.assertEqual(process.id, 0x400)
23+
self.assertEqual(
24+
[process.threads[i].id for i in range(process.num_threads)],
25+
[0x10200, 0x10204, 0x10208])
26+
27+
def test_stop_reason(self):
28+
class MyResponder(MockGDBServerResponder):
29+
def qfThreadInfo(self):
30+
return "mp400.10200,p400.10204"
31+
32+
def cont(self):
33+
return "S02thread:p400.10200;"
34+
35+
self.server.responder = MyResponder()
36+
target = self.dbg.CreateTarget('')
37+
if self.TraceOn():
38+
self.runCmd("log enable gdb-remote packets")
39+
self.addTearDownHook(
40+
lambda: self.runCmd("log disable gdb-remote packets"))
41+
process = self.connect(target)
42+
process.Continue()
43+
self.assertEqual(process.GetThreadByID(0x10200).stop_reason,
44+
lldb.eStopReasonSignal)
45+
self.assertEqual(process.GetThreadByID(0x10204).stop_reason,
46+
lldb.eStopReasonNone)

0 commit comments

Comments
 (0)