Skip to content

Commit a342279

Browse files
committed
[lldb] [llgs] Support resuming one process with PID!=current via vCont
Extend vCont function to support resuming a process with an arbitrary PID, that could be different than the one selected via Hc (or no process at all may be selected). Resuming more than one process simultaneously is not supported yet. Remove the ReadTid() method that was only used by Handle_vCont(), and furthermore it was wrongly using m_current_process rather than m_continue_process. Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.llvm.org/D127862
1 parent 3266b11 commit a342279

File tree

4 files changed

+176
-76
lines changed

4 files changed

+176
-76
lines changed

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

Lines changed: 47 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,12 +1674,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vCont(
16741674
return SendIllFormedResponse(packet, "Missing action from vCont package");
16751675
}
16761676

1677-
// Check if this is all continue (no options or ";c").
1678-
if (::strcmp(packet.Peek(), ";c") == 0) {
1679-
// Move past the ';', then do a simple 'c'.
1680-
packet.SetFilePos(packet.GetFilePos() + 1);
1681-
return Handle_c(packet);
1682-
} else if (::strcmp(packet.Peek(), ";s") == 0) {
1677+
if (::strcmp(packet.Peek(), ";s") == 0) {
16831678
// Move past the ';', then do a simple 's'.
16841679
packet.SetFilePos(packet.GetFilePos() + 1);
16851680
return Handle_s(packet);
@@ -1688,13 +1683,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vCont(
16881683
return SendOKResponse();
16891684
}
16901685

1691-
// Ensure we have a native process.
1692-
if (!m_continue_process) {
1693-
LLDB_LOG(log, "no debugged process");
1694-
return SendErrorResponse(0x36);
1695-
}
1696-
1697-
ResumeActionList thread_actions;
1686+
std::unordered_map<lldb::pid_t, ResumeActionList> thread_actions;
16981687

16991688
while (packet.GetBytesLeft() && *packet.Peek() == ';') {
17001689
// Skip the semi-colon.
@@ -1737,32 +1726,62 @@ GDBRemoteCommunicationServerLLGS::Handle_vCont(
17371726
break;
17381727
}
17391728

1729+
lldb::pid_t pid = StringExtractorGDBRemote::AllProcesses;
1730+
lldb::tid_t tid = StringExtractorGDBRemote::AllThreads;
1731+
17401732
// Parse out optional :{thread-id} value.
17411733
if (packet.GetBytesLeft() && (*packet.Peek() == ':')) {
17421734
// Consume the separator.
17431735
packet.GetChar();
17441736

1745-
llvm::Expected<lldb::tid_t> tid_ret =
1746-
ReadTid(packet, /*allow_all=*/true, m_continue_process->GetID());
1747-
if (!tid_ret)
1748-
return SendErrorResponse(tid_ret.takeError());
1737+
auto pid_tid = packet.GetPidTid(StringExtractorGDBRemote::AllProcesses);
1738+
if (!pid_tid)
1739+
return SendIllFormedResponse(packet, "Malformed thread-id");
17491740

1750-
thread_action.tid = tid_ret.get();
1751-
if (thread_action.tid == StringExtractorGDBRemote::AllThreads)
1752-
thread_action.tid = LLDB_INVALID_THREAD_ID;
1741+
pid = pid_tid->first;
1742+
tid = pid_tid->second;
17531743
}
17541744

1755-
thread_actions.Append(thread_action);
1756-
}
1745+
if (pid == StringExtractorGDBRemote::AllProcesses) {
1746+
if (m_debugged_processes.size() > 1)
1747+
return SendIllFormedResponse(
1748+
packet, "Resuming multiple processes not supported yet");
1749+
if (!m_continue_process) {
1750+
LLDB_LOG(log, "no debugged process");
1751+
return SendErrorResponse(0x36);
1752+
}
1753+
pid = m_continue_process->GetID();
1754+
}
17571755

1758-
Status error = m_continue_process->Resume(thread_actions);
1759-
if (error.Fail()) {
1760-
LLDB_LOG(log, "vCont failed for process {0}: {1}",
1761-
m_continue_process->GetID(), error);
1762-
return SendErrorResponse(GDBRemoteServerError::eErrorResume);
1756+
if (tid == StringExtractorGDBRemote::AllThreads)
1757+
tid = LLDB_INVALID_THREAD_ID;
1758+
1759+
thread_action.tid = tid;
1760+
1761+
thread_actions[pid].Append(thread_action);
17631762
}
17641763

1765-
LLDB_LOG(log, "continued process {0}", m_continue_process->GetID());
1764+
assert(thread_actions.size() >= 1);
1765+
if (thread_actions.size() > 1)
1766+
return SendIllFormedResponse(
1767+
packet, "Resuming multiple processes not supported yet");
1768+
1769+
for (std::pair<lldb::pid_t, ResumeActionList> x : thread_actions) {
1770+
auto process_it = m_debugged_processes.find(x.first);
1771+
if (process_it == m_debugged_processes.end()) {
1772+
LLDB_LOG(log, "vCont failed for process {0}: process not debugged",
1773+
x.first);
1774+
return SendErrorResponse(GDBRemoteServerError::eErrorResume);
1775+
}
1776+
1777+
Status error = process_it->second->Resume(x.second);
1778+
if (error.Fail()) {
1779+
LLDB_LOG(log, "vCont failed for process {0}: {1}", x.first, error);
1780+
return SendErrorResponse(GDBRemoteServerError::eErrorResume);
1781+
}
1782+
1783+
LLDB_LOG(log, "continued process {0}", x.first);
1784+
}
17661785

17671786
return SendContinueSuccessResponse();
17681787
}
@@ -4011,38 +4030,6 @@ std::string GDBRemoteCommunicationServerLLGS::XMLEncodeAttributeValue(
40114030
return result;
40124031
}
40134032

4014-
llvm::Expected<lldb::tid_t> GDBRemoteCommunicationServerLLGS::ReadTid(
4015-
StringExtractorGDBRemote &packet, bool allow_all, lldb::pid_t default_pid) {
4016-
assert(m_current_process);
4017-
assert(m_current_process->GetID() != LLDB_INVALID_PROCESS_ID);
4018-
4019-
auto pid_tid = packet.GetPidTid(default_pid);
4020-
if (!pid_tid)
4021-
return llvm::make_error<StringError>(inconvertibleErrorCode(),
4022-
"Malformed thread-id");
4023-
4024-
lldb::pid_t pid = pid_tid->first;
4025-
lldb::tid_t tid = pid_tid->second;
4026-
4027-
if (!allow_all && pid == StringExtractorGDBRemote::AllProcesses)
4028-
return llvm::make_error<StringError>(
4029-
inconvertibleErrorCode(),
4030-
llvm::formatv("PID value {0} not allowed", pid == 0 ? 0 : -1));
4031-
4032-
if (!allow_all && tid == StringExtractorGDBRemote::AllThreads)
4033-
return llvm::make_error<StringError>(
4034-
inconvertibleErrorCode(),
4035-
llvm::formatv("TID value {0} not allowed", tid == 0 ? 0 : -1));
4036-
4037-
if (pid != StringExtractorGDBRemote::AllProcesses) {
4038-
if (pid != m_current_process->GetID())
4039-
return llvm::make_error<StringError>(
4040-
inconvertibleErrorCode(), llvm::formatv("PID {0} not debugged", pid));
4041-
}
4042-
4043-
return tid;
4044-
}
4045-
40464033
std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
40474034
const llvm::ArrayRef<llvm::StringRef> client_features) {
40484035
std::vector<std::string> ret =

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,6 @@ class GDBRemoteCommunicationServerLLGS
293293

294294
void StopSTDIOForwarding();
295295

296-
// Read thread-id from packet. If the thread-id is correct, returns it.
297-
// Otherwise, returns the error.
298-
//
299-
// If allow_all is true, then the pid/tid value of -1 ('all') will be allowed.
300-
// In any case, the function assumes that exactly one inferior is being
301-
// debugged and rejects pid values that do no match that inferior.
302-
llvm::Expected<lldb::tid_t> ReadTid(StringExtractorGDBRemote &packet,
303-
bool allow_all, lldb::pid_t default_pid);
304-
305296
// Call SetEnabledExtensions() with appropriate flags on the process.
306297
void SetEnabledExtensions(NativeProcessProtocol &process);
307298

lldb/source/Utility/StringExtractorGDBRemote.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ llvm::Optional<std::pair<lldb::pid_t, lldb::tid_t>>
639639
StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) {
640640
llvm::StringRef view = llvm::StringRef(m_packet).substr(m_index);
641641
size_t initial_length = view.size();
642-
lldb::pid_t pid = default_pid;
642+
lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
643643
lldb::tid_t tid;
644644

645645
if (view.consume_front("p")) {
@@ -675,5 +675,5 @@ StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) {
675675
// update m_index
676676
m_index += initial_length - view.size();
677677

678-
return {{pid, tid}};
678+
return {{pid != LLDB_INVALID_PROCESS_ID ? pid : default_pid, tid}};
679679
}

lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ def test_vkill_parent(self):
354354
def test_vkill_both(self):
355355
self.vkill_test(kill_parent=True, kill_child=True)
356356

357-
def resume_one_test(self, run_order):
357+
def resume_one_test(self, run_order, use_vCont=False):
358358
self.build()
359359
self.prep_debug_monitor_and_inferior(inferior_args=["fork", "trap"])
360360
self.add_qSupported_packets(["multiprocess+",
@@ -395,11 +395,19 @@ def resume_one_test(self, run_order):
395395
else:
396396
assert False, "unexpected x={}".format(x)
397397

398+
if use_vCont:
399+
self.test_sequence.add_log_lines([
400+
# continue the selected process
401+
"read packet: $vCont;c:p{}.{}#00".format(*pidtid),
402+
], True)
403+
else:
404+
self.test_sequence.add_log_lines([
405+
# continue the selected process
406+
"read packet: $Hcp{}.{}#00".format(*pidtid),
407+
"send packet: $OK#00",
408+
"read packet: $c#00",
409+
], True)
398410
self.test_sequence.add_log_lines([
399-
# continue the selected process
400-
"read packet: $Hcp{}.{}#00".format(*pidtid),
401-
"send packet: $OK#00",
402-
"read packet: $c#00",
403411
{"direction": "send", "regex": expect},
404412
], True)
405413
# if at least one process remained, check both PIDs
@@ -431,3 +439,117 @@ def test_c_child_then_parent(self):
431439
@add_test_categories(["fork"])
432440
def test_c_interspersed(self):
433441
self.resume_one_test(run_order=["parent", "child", "parent", "child"])
442+
443+
@add_test_categories(["fork"])
444+
def test_vCont_parent(self):
445+
self.resume_one_test(run_order=["parent", "parent"], use_vCont=True)
446+
447+
@add_test_categories(["fork"])
448+
def test_vCont_child(self):
449+
self.resume_one_test(run_order=["child", "child"], use_vCont=True)
450+
451+
@add_test_categories(["fork"])
452+
def test_vCont_parent_then_child(self):
453+
self.resume_one_test(run_order=["parent", "parent", "child", "child"],
454+
use_vCont=True)
455+
456+
@add_test_categories(["fork"])
457+
def test_vCont_child_then_parent(self):
458+
self.resume_one_test(run_order=["child", "child", "parent", "parent"],
459+
use_vCont=True)
460+
461+
@add_test_categories(["fork"])
462+
def test_vCont_interspersed(self):
463+
self.resume_one_test(run_order=["parent", "child", "parent", "child"],
464+
use_vCont=True)
465+
466+
@add_test_categories(["fork"])
467+
def test_vCont_two_processes(self):
468+
self.build()
469+
self.prep_debug_monitor_and_inferior(inferior_args=["fork", "trap"])
470+
self.add_qSupported_packets(["multiprocess+",
471+
"fork-events+"])
472+
ret = self.expect_gdbremote_sequence()
473+
self.assertIn("fork-events+", ret["qSupported_response"])
474+
self.reset_test_sequence()
475+
476+
# continue and expect fork
477+
self.test_sequence.add_log_lines([
478+
"read packet: $c#00",
479+
{"direction": "send", "regex": self.fork_regex.format("fork"),
480+
"capture": self.fork_capture},
481+
], True)
482+
ret = self.expect_gdbremote_sequence()
483+
parent_pid = ret["parent_pid"]
484+
parent_tid = ret["parent_tid"]
485+
child_pid = ret["child_pid"]
486+
child_tid = ret["child_tid"]
487+
self.reset_test_sequence()
488+
489+
self.test_sequence.add_log_lines([
490+
# try to resume both processes
491+
"read packet: $vCont;c:p{}.{};c:p{}.{}#00".format(
492+
parent_pid, parent_tid, child_pid, child_tid),
493+
"send packet: $E03#00",
494+
], True)
495+
self.expect_gdbremote_sequence()
496+
497+
@add_test_categories(["fork"])
498+
def test_vCont_all_processes_explicit(self):
499+
self.build()
500+
self.prep_debug_monitor_and_inferior(inferior_args=["fork", "trap"])
501+
self.add_qSupported_packets(["multiprocess+",
502+
"fork-events+"])
503+
ret = self.expect_gdbremote_sequence()
504+
self.assertIn("fork-events+", ret["qSupported_response"])
505+
self.reset_test_sequence()
506+
507+
# continue and expect fork
508+
self.test_sequence.add_log_lines([
509+
"read packet: $c#00",
510+
{"direction": "send", "regex": self.fork_regex.format("fork"),
511+
"capture": self.fork_capture},
512+
], True)
513+
ret = self.expect_gdbremote_sequence()
514+
parent_pid = ret["parent_pid"]
515+
parent_tid = ret["parent_tid"]
516+
child_pid = ret["child_pid"]
517+
child_tid = ret["child_tid"]
518+
self.reset_test_sequence()
519+
520+
self.test_sequence.add_log_lines([
521+
# try to resume all processes implicitly
522+
"read packet: $vCont;c:p-1.-1#00",
523+
"send packet: $E03#00",
524+
], True)
525+
self.expect_gdbremote_sequence()
526+
527+
@add_test_categories(["fork"])
528+
def test_vCont_all_processes_implicit(self):
529+
self.build()
530+
self.prep_debug_monitor_and_inferior(inferior_args=["fork", "trap"])
531+
self.add_qSupported_packets(["multiprocess+",
532+
"fork-events+"])
533+
ret = self.expect_gdbremote_sequence()
534+
self.assertIn("fork-events+", ret["qSupported_response"])
535+
self.reset_test_sequence()
536+
537+
# continue and expect fork
538+
self.test_sequence.add_log_lines([
539+
"read packet: $c#00",
540+
{"direction": "send", "regex": self.fork_regex.format("fork"),
541+
"capture": self.fork_capture},
542+
], True)
543+
ret = self.expect_gdbremote_sequence()
544+
parent_pid = ret["parent_pid"]
545+
parent_tid = ret["parent_tid"]
546+
child_pid = ret["child_pid"]
547+
child_tid = ret["child_tid"]
548+
self.reset_test_sequence()
549+
550+
self.test_sequence.add_log_lines([
551+
# try to resume all processes implicitly
552+
"read packet: $vCont;c#00",
553+
"send packet: $E03#00",
554+
], True)
555+
self.expect_gdbremote_sequence()

0 commit comments

Comments
 (0)