diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py --- a/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py +++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py @@ -9,10 +9,12 @@ fork_regex = ("[$]T05thread:p([0-9a-f]+)[.]([0-9a-f]+);.*" "{}:p([0-9a-f]+)[.]([0-9a-f]+).*") + fork_regex_nonstop = ("%Stop:T05thread:p([0-9a-f]+)[.]([0-9a-f]+);.*" + "{}:p([0-9a-f]+)[.]([0-9a-f]+).*") fork_capture = {1: "parent_pid", 2: "parent_tid", 3: "child_pid", 4: "child_tid"} - def start_fork_test(self, args, variant="fork"): + def start_fork_test(self, args, variant="fork", nonstop=False): self.build() self.prep_debug_monitor_and_inferior(inferior_args=args) self.add_qSupported_packets(["multiprocess+", @@ -22,11 +24,24 @@ self.reset_test_sequence() # continue and expect fork - self.test_sequence.add_log_lines([ - "read packet: $c#00", - {"direction": "send", "regex": self.fork_regex.format(variant), - "capture": self.fork_capture}, - ], True) + if nonstop: + self.test_sequence.add_log_lines([ + "read packet: $QNonStop:1#00", + "send packet: $OK#00", + "read packet: $c#00", + "send packet: $OK#00", + {"direction": "send", + "regex": self.fork_regex_nonstop.format(variant), + "capture": self.fork_capture}, + "read packet: $vStopped#00", + "send packet: $OK#00", + ], True) + else: + self.test_sequence.add_log_lines([ + "read packet: $c#00", + {"direction": "send", "regex": self.fork_regex.format(variant), + "capture": self.fork_capture}, + ], True) ret = self.expect_gdbremote_sequence() self.reset_test_sequence() @@ -45,9 +60,9 @@ ], True) self.expect_gdbremote_sequence() - def fork_and_detach_test(self, variant): + def fork_and_detach_test(self, variant, nonstop=False): parent_pid, parent_tid, child_pid, child_tid = ( - self.start_fork_test([variant], variant)) + self.start_fork_test([variant], variant, nonstop=nonstop)) # detach the forked child self.test_sequence.add_log_lines([ @@ -77,6 +92,20 @@ ], True) self.expect_gdbremote_sequence() + @add_test_categories(["fork"]) + def test_fork_nonstop(self): + parent_pid, _ = self.fork_and_detach_test("fork", nonstop=True) + + # resume the parent + self.test_sequence.add_log_lines([ + "read packet: $c#00", + "send packet: $OK#00", + "send packet: %Stop:W00;process:{}#00".format(parent_pid), + "read packet: $vStopped#00", + "send packet: $OK#00", + ], True) + self.expect_gdbremote_sequence() + @add_test_categories(["fork"]) def test_vfork(self): parent_pid, parent_tid = self.fork_and_detach_test("vfork") @@ -93,9 +122,32 @@ ], True) self.expect_gdbremote_sequence() - def fork_and_follow_test(self, variant): + @add_test_categories(["fork"]) + def test_vfork_nonstop(self): + parent_pid, parent_tid = self.fork_and_detach_test("vfork", + nonstop=True) + + # resume the parent + self.test_sequence.add_log_lines([ + "read packet: $c#00", + "send packet: $OK#00", + {"direction": "send", + "regex": r"%Stop:T05thread:p{}[.]{}.*vforkdone.*".format( + parent_pid, parent_tid), + }, + "read packet: $vStopped#00", + "send packet: $OK#00", + "read packet: $c#00", + "send packet: $OK#00", + "send packet: %Stop:W00;process:{}#00".format(parent_pid), + "read packet: $vStopped#00", + "send packet: $OK#00", + ], True) + self.expect_gdbremote_sequence() + + def fork_and_follow_test(self, variant, nonstop=False): parent_pid, parent_tid, child_pid, child_tid = ( - self.start_fork_test([variant], variant)) + self.start_fork_test([variant], variant, nonstop=nonstop)) # switch to the forked child self.test_sequence.add_log_lines([ @@ -113,18 +165,37 @@ "send packet: $OK#00", # then resume the child "read packet: $c#00", - "send packet: $W00;process:{}#00".format(child_pid), ], True) + + if nonstop: + self.test_sequence.add_log_lines([ + "send packet: $OK#00", + "send packet: %Stop:W00;process:{}#00".format(child_pid), + "read packet: $vStopped#00", + "send packet: $OK#00", + ], True) + else: + self.test_sequence.add_log_lines([ + "send packet: $W00;process:{}#00".format(child_pid), + ], True) self.expect_gdbremote_sequence() @add_test_categories(["fork"]) def test_fork_follow(self): self.fork_and_follow_test("fork") + @add_test_categories(["fork"]) + def test_fork_follow_nonstop(self): + self.fork_and_follow_test("fork", nonstop=True) + @add_test_categories(["fork"]) def test_vfork_follow(self): self.fork_and_follow_test("vfork") + @add_test_categories(["fork"]) + def test_vfork_follow_nonstop(self): + self.fork_and_follow_test("vfork", nonstop=True) + @add_test_categories(["fork"]) def test_select_wrong_pid(self): self.build() @@ -191,10 +262,9 @@ ], True) self.expect_gdbremote_sequence() - @add_test_categories(["fork"]) - def test_detach_all(self): + def detach_all_test(self, nonstop=False): parent_pid, parent_tid, child_pid, child_tid = ( - self.start_fork_test(["fork"])) + self.start_fork_test(["fork"], nonstop=nonstop)) self.test_sequence.add_log_lines([ # double-check our PIDs @@ -213,6 +283,14 @@ ], True) self.expect_gdbremote_sequence() + @add_test_categories(["fork"]) + def test_detach_all(self): + self.detach_all_test() + + @add_test_categories(["fork"]) + def test_detach_all_nonstop(self): + self.detach_all_test(nonstop=True) + @add_test_categories(["fork"]) def test_kill_all(self): parent_pid, _, child_pid, _ = self.start_fork_test(["fork"]) @@ -230,10 +308,51 @@ self.assertEqual(set([ret["pid1"], ret["pid2"]]), set([parent_pid, child_pid])) - def vkill_test(self, kill_parent=False, kill_child=False): + @add_test_categories(["fork"]) + def test_kill_all_nonstop(self): + parent_pid, _, child_pid, _ = self.start_fork_test(["fork"], + nonstop=True) + + exit_regex = "X09;process:([0-9a-f]+)" + # Depending on a potential race, the second kill may make it into + # the async queue before we issue vStopped or after. In the former + # case, we should expect the exit status in reply to vStopped. + # In the latter, we should expect an OK response (queue empty), + # followed by another async notification. + vstop_regex = "[$](OK|{})#.*".format(exit_regex) + self.test_sequence.add_log_lines([ + # kill all processes + "read packet: $k#00", + "send packet: $OK#00", + {"direction": "send", "regex": "%Stop:{}#.*".format(exit_regex), + "capture": {1: "pid1"}}, + "read packet: $vStopped#00", + {"direction": "send", "regex": vstop_regex, + "capture": {1: "vstop_reply", 2: "pid2"}}, + ], True) + ret = self.expect_gdbremote_sequence() + pid1 = ret["pid1"] + if ret["vstop_reply"] == "OK": + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + {"direction": "send", "regex": "%Stop:{}#.*".format(exit_regex), + "capture": {1: "pid2"}}, + ], True) + ret = self.expect_gdbremote_sequence() + pid2 = ret["pid2"] + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: $vStopped#00", + "send packet: $OK#00", + ], True) + self.expect_gdbremote_sequence() + self.assertEqual(set([ret["pid1"], ret["pid2"]]), + set([parent_pid, child_pid])) + + def vkill_test(self, kill_parent=False, kill_child=False, nonstop=False): assert kill_parent or kill_child parent_pid, parent_tid, child_pid, child_tid = ( - self.start_fork_test(["fork"])) + self.start_fork_test(["fork"], nonstop=nonstop)) if kill_parent: self.test_sequence.add_log_lines([ @@ -269,6 +388,10 @@ def test_vkill_both(self): self.vkill_test(kill_parent=True, kill_child=True) + @add_test_categories(["fork"]) + def test_vkill_both_nonstop(self): + self.vkill_test(kill_parent=True, kill_child=True, nonstop=True) + def resume_one_test(self, run_order, use_vCont=False): parent_pid, parent_tid, child_pid, child_tid = ( self.start_fork_test(["fork", "trap"]))