diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -120,6 +120,7 @@ def setUp(self): super(GdbRemoteTestCaseBase, self).setUp() + self.captures = {} self.setUpBaseLogging() self.debug_monitor_extra_args = [] @@ -529,6 +530,7 @@ self.add_set_environment_packets(name, value) if self._inferior_startup == self._STARTUP_LAUNCH: self.add_verified_launch_packets(launch_args) + self.expect_gdbremote_sequence() return {"inferior": inferior, "server": server} @@ -540,6 +542,18 @@ self.assertEqual(server.get_normal_packet(), b"OK") server.send_ack() + def assertPacketResponse(self, packet, response_pattern): + self._server.send_packet(packet) + response = self._server.get_normal_packet() + if isinstance(response_pattern, re.Pattern): + match = response_pattern.match(response) + self.assertIsNotNone(match, "{!r} matches {!r}".format(response, + response_pattern)) + self.captures.update(match.groupdict()) + else: + self.assertEqual(response, response_pattern) + self.captures[0] = response + def add_verified_launch_packets(self, launch_args): self.test_sequence.add_log_lines( ["read packet: %s" % build_gdbremote_A_packet(launch_args), @@ -1570,8 +1584,17 @@ self.assertEqual(step_count, expected_step_count) def maybe_strict_output_regex(self, regex): - return '.*' + regex + \ - '.*' if lldbplatformutil.hasChattyStderr(self) else '^' + regex + '$' + if lldbplatformutil.hasChattyStderr(self): + prefix = b"^" + suffix = b"$" + else: + prefix = suffix = b".*" + + if isinstance(regex, str): + prefix = prefix.decode() + suffix = suffix.decode() + + return prefix + regex + suffix def install_and_create_launch_args(self): exe_path = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py --- a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py +++ b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py @@ -25,77 +25,44 @@ mydir = TestBase.compute_mydir(__file__) def test_thread_suffix_supported(self): - server = self.connect_to_debug_monitor() - self.assertIsNotNone(server) - + self.connect_to_debug_monitor() self.do_handshake() - self.test_sequence.add_log_lines( - ["lldb-server < 26> read packet: $QThreadSuffixSupported#e4", - "lldb-server < 6> send packet: $OK#9a"], - True) - - self.expect_gdbremote_sequence() - + self.assertPacketResponse(b"QThreadSuffixSupported", b"OK") def test_list_threads_in_stop_reply_supported(self): - server = self.connect_to_debug_monitor() - self.assertIsNotNone(server) - + self.connect_to_debug_monitor() self.do_handshake() - self.test_sequence.add_log_lines( - ["lldb-server < 27> read packet: $QListThreadsInStopReply#21", - "lldb-server < 6> send packet: $OK#9a"], - True) - self.expect_gdbremote_sequence() + self.assertPacketResponse(b"QListThreadsInStopReply", b"OK") def test_c_packet_works(self): self.build() - procs = self.prep_debug_monitor_and_inferior() - self.test_sequence.add_log_lines( - ["read packet: $c#63", - "send packet: $W00#00"], - True) - - self.expect_gdbremote_sequence() + self.prep_debug_monitor_and_inferior() + self.assertPacketResponse(b"c", b"W00") @skipIfWindows # No pty support to test any inferior output def test_inferior_print_exit(self): self.build() - procs = self.prep_debug_monitor_and_inferior( - inferior_args=["hello, world"]) - self.test_sequence.add_log_lines( - ["read packet: $vCont;c#a8", - {"type": "output_match", "regex": self.maybe_strict_output_regex(r"hello, world\r\n")}, - "send packet: $W00#00"], - True) + self.prep_debug_monitor_and_inferior(inferior_args=["hello, world"]) - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) + self.assertPacketResponse(b"vCont;c", b"W00") + self.assertRegex(self._server.consume_accumulated_output(), + self.maybe_strict_output_regex(br"hello, world\r\n")) def test_first_launch_stop_reply_thread_matches_first_qC(self): self.build() - procs = self.prep_debug_monitor_and_inferior() - self.test_sequence.add_log_lines(["read packet: $qC#00", - {"direction": "send", - "regex": r"^\$QC([0-9a-fA-F]+)#", - "capture": {1: "thread_id_QC"}}, - "read packet: $?#00", - {"direction": "send", - "regex": r"^\$T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+)", - "capture": {1: "thread_id_?"}}], - True) - context = self.expect_gdbremote_sequence() - self.assertEqual(context.get("thread_id_QC"), context.get("thread_id_?")) + self.prep_debug_monitor_and_inferior() + + self.assertPacketResponse(b"qC", re.compile(b"QC(?P[0-9a-fA-F]+)")) + self.assertPacketResponse(b"?", + re.compile(b"T[0-9a-fA-F]{2}thread:(?P[0-9a-fA-F]+)")) + self.assertEqual(self.captures["QC"], self.captures["q"]) def test_attach_commandline_continue_app_exits(self): self.build() self.set_inferior_startup_attach() procs = self.prep_debug_monitor_and_inferior() - self.test_sequence.add_log_lines( - ["read packet: $vCont;c#a8", - "send packet: $W00#00"], - True) - self.expect_gdbremote_sequence() + + self.assertPacketResponse(b"c", b"W00") # Wait a moment for completed and now-detached inferior process to # clear. @@ -115,19 +82,12 @@ def test_qRegisterInfo_returns_one_valid_result(self): self.build() self.prep_debug_monitor_and_inferior() - self.test_sequence.add_log_lines( - ["read packet: $qRegisterInfo0#00", - {"direction": "send", "regex": r"^\$(.+);#[0-9A-Fa-f]{2}", "capture": {1: "reginfo_0"}}], - True) - # Run the stream - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) + self.assertPacketResponse(b"qRegisterInfo0", + re.compile(br"(?P.+)")) - reg_info_packet = context.get("reginfo_0") - self.assertIsNotNone(reg_info_packet) self.assert_valid_reg_info( - lldbgdbserverutils.parse_reg_info_response(reg_info_packet)) + lldbgdbserverutils.parse_reg_info_response(self.captures["reginfo_0"].decode())) def test_qRegisterInfo_returns_all_valid_results(self): self.build() @@ -260,14 +220,9 @@ procs = self.prep_debug_monitor_and_inferior() self.add_threadinfo_collection_packets() - self.test_sequence.add_log_lines( - ["read packet: $qC#00", - {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}} - ], True) - - # Run the packet stream. context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) + + self.assertPacketResponse(b"qC", re.compile(b"QC(?P[0-9a-fA-F]+)")) # Gather threadinfo entries. threads = self.parse_threadinfo_packets(context) @@ -277,7 +232,7 @@ self.assertEqual(len(threads), 1) # We should have a valid thread_id from $QC. - QC_thread_id_hex = context.get("thread_id") + QC_thread_id_hex = self.captures["QC"] self.assertIsNotNone(QC_thread_id_hex) QC_thread_id = int(QC_thread_id_hex, 16) @@ -322,24 +277,15 @@ if not "set" in reg_info: continue - # Clear existing packet expectations. - self.reset_test_sequence() - - # Run the register query - self.test_sequence.add_log_lines( - ["read packet: $p{0:x}#00".format(reg_index), - {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}], - True) - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) + self.assertPacketResponse(b"p%x"%reg_index, + re.compile(b"^[0-9a-fA-F]+$")) # Verify the response length. - p_response = context.get("p_response") - self.assertIsNotNone(p_response) + p_response = self.captures[0] # Skip erraneous (unsupported) registers. # TODO: remove this once we make unsupported registers disappear. - if p_response.startswith("E") and len(p_response) == 3: + if p_response[0:1] == b"E" and len(p_response) == 3: continue if "dynamic_size_dwarf_expr_bytes" in reg_info: @@ -364,27 +310,15 @@ threads = self.wait_for_thread_count(3) self.assertEqual(len(threads), 3) - pid_str = "" + pid_str = b"" if pass_pid: - pid_str = "p{0:x}.".format(procs["inferior"].pid) + pid_str = b"p%x."%procs["inferior"].pid # verify we can $H to each thead, and $qC matches the thread we set. for thread in threads: # Change to each thread, verify current thread id. - self.reset_test_sequence() - self.test_sequence.add_log_lines( - ["read packet: $Hg{0}{1:x}#00".format(pid_str, thread), # Set current thread. - "send packet: $OK#00", - "read packet: $qC#00", - {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}}], - True) - - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) - - # Verify the thread id. - self.assertIsNotNone(context.get("thread_id")) - self.assertEqual(int(context.get("thread_id"), 16), thread) + self.assertPacketResponse(b"Hg%s%x"%(pid_str, thread), b"OK") + self.assertPacketResponse(b"qC", b"QC%x"%thread) @expectedFailureAll(oslist=["windows"]) # expect 4 threads def test_Hg_switches_to_3_threads_launch(self): @@ -416,17 +350,12 @@ self.assertEqual(len(threads), 2) if pass_pid == -1: - pid_str = "p-1." + pid_str = b"p-1." else: - pid_str = "p{0:x}.".format(pass_pid) + pid_str = b"p%x."%pass_pid thread = threads[1] - self.test_sequence.add_log_lines( - ["read packet: $Hg{0}{1:x}#00".format(pid_str, thread), # Set current thread. - "send packet: $Eff#00"], - True) - - self.expect_gdbremote_sequence() + self.assertPacketResponse(b"Hg%s%x"%(pid_str, thread), b"Eff") @expectedFailureAll(oslist=["windows"]) @add_test_categories(["llgs"]) @@ -463,14 +392,14 @@ # Give time between thread creation/segfaulting for the handler to work. # inferior_args.append("sleep:1") inferior_args.append("thread:new") - inferior_args.append("sleep:10") # Launch/attach. (In our case, this should only ever be launched since # we need inferior stdout/stderr). - procs = self.prep_debug_monitor_and_inferior( - inferior_args=inferior_args) - self.test_sequence.add_log_lines(["read packet: $c#63"], True) - context = self.expect_gdbremote_sequence() + self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + + stop_regex = re.compile(b"T%02xthread:(?P[0-9a-fA-F]+);"%segfault_signo) + # Run until SIGSEGV comes in. + self.assertPacketResponse(b"c", stop_regex) # Let the inferior process have a few moments to start up the thread when launched. # context = self.run_process_then_stop(run_seconds=1) @@ -482,70 +411,39 @@ signaled_tids = {} print_thread_ids = {} + sigusr_signo = lldbutil.get_signal_number('SIGUSR1') # Switch to each thread, deliver a signal, and verify signal delivery for i in range(NUM_THREADS - 1): - # Run until SIGSEGV comes in. - self.reset_test_sequence() - self.test_sequence.add_log_lines([{"direction": "send", - "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", - "capture": {1: "signo", - 2: "thread_id"}}], - True) - - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) - signo = context.get("signo") - self.assertEqual(int(signo, 16), segfault_signo) - # Ensure we haven't seen this tid yet. - thread_id = int(context.get("thread_id"), 16) + thread_id = int(self.captures["tid"], 16) self.assertNotIn(thread_id, signaled_tids) signaled_tids[thread_id] = 1 # Send SIGUSR1 to the thread that signaled the SIGSEGV. - self.reset_test_sequence() - self.test_sequence.add_log_lines( - [ - # Set the continue thread. - # Set current thread. - "read packet: $Hc{0:x}#00".format(thread_id), - "send packet: $OK#00", - - # Continue sending the signal number to the continue thread. - # The commented out packet is a way to do this same operation without using - # a $Hc (but this test is testing $Hc, so we'll stick with the former). - "read packet: $C{0:x}#00".format(lldbutil.get_signal_number('SIGUSR1')), - # "read packet: $vCont;C{0:x}:{1:x};c#00".format(lldbutil.get_signal_number('SIGUSR1'), thread_id), - - # FIXME: Linux does not report the thread stop on the delivered signal (SIGUSR1 here). MacOSX debugserver does. - # But MacOSX debugserver isn't guaranteeing the thread the signal handler runs on, so currently its an XFAIL. - # Need to rectify behavior here. The linux behavior is more intuitive to me since we're essentially swapping out - # an about-to-be-delivered signal (for which we already sent a stop packet) to a different signal. - # {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, - # "read packet: $c#63", - {"type": "output_match", "regex": r"^received SIGUSR1 on thread id: ([0-9a-fA-F]+)\r\nthread ([0-9a-fA-F]+): past SIGSEGV\r\n", "capture": {1: "print_thread_id", 2: "post_handle_thread_id"}}, - ], - True) - - # Run the sequence. - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) - - # Ensure the stop signal is the signal we delivered. - # stop_signo = context.get("stop_signo") - # self.assertIsNotNone(stop_signo) - # self.assertEquals(int(stop_signo,16), lldbutil.get_signal_number('SIGUSR1')) - - # Ensure the stop thread is the thread to which we delivered the signal. - # stop_thread_id = context.get("stop_thread_id") - # self.assertIsNotNone(stop_thread_id) - # self.assertEquals(int(stop_thread_id,16), thread_id) + # Set the continue thread. + self.assertPacketResponse(b"Hc%x"%thread_id, b"OK") + + # Continue sending the signal number to the continue thread. + # FIXME: Linux does not report the thread stop on the delivered + # signal (SIGUSR1 here). MacOSX debugserver does. But MacOSX + # debugserver isn't guaranteeing the thread the signal handler runs + # on, so currently its an XFAIL. Need to rectify behavior here. + # The linux behavior is more intuitive to me since we're essentially + # swapping out an about-to-be-delivered signal (for which we already + # sent a stop packet) to a different signal. + self.assertPacketResponse(b"C%x"%sigusr_signo, + stop_regex if i < NUM_THREADS-2 else b"W00") + + output = self._server.consume_accumulated_output() + match = re.match(rb"^received SIGUSR1 on thread id: (?P[0-9a-fA-F]+)\r\nthread (?P=ptid): past SIGSEGV\r\n", output) + self.assertIsNotNone(match, "{!r} did not match".format(output)) # Ensure we haven't seen this thread id yet. The inferior's # self-obtained thread ids are not guaranteed to match the stub # tids (at least on MacOSX). - print_thread_id = context.get("print_thread_id") - self.assertIsNotNone(print_thread_id) + # The regex ensures the post signal-handle thread id matches the + # thread that initially raised the SIGSEGV. + print_thread_id = match["ptid"] print_thread_id = int(print_thread_id, 16) self.assertNotIn(print_thread_id, print_thread_ids) @@ -553,13 +451,6 @@ # ensure we don't hit it again. print_thread_ids[print_thread_id] = 1 - # Ensure post signal-handle thread id matches the thread that - # initially raised the SIGSEGV. - post_handle_thread_id = context.get("post_handle_thread_id") - self.assertIsNotNone(post_handle_thread_id) - post_handle_thread_id = int(post_handle_thread_id, 16) - self.assertEqual(post_handle_thread_id, print_thread_id) - @expectedFailureDarwin @skipIfWindows # no SIGSEGV support @expectedFailureAll(oslist=["freebsd"], bugnumber="llvm.org/pr48419") @@ -633,16 +524,8 @@ def test_qMemoryRegionInfo_is_supported(self): self.build() - self.set_inferior_startup_launch() - # Start up the inferior. procs = self.prep_debug_monitor_and_inferior() - - # Ask if it supports $qMemoryRegionInfo. - self.test_sequence.add_log_lines( - ["read packet: $qMemoryRegionInfo#00", - "send packet: $OK#00" - ], True) - self.expect_gdbremote_sequence() + self.assertPacketResponse(b"qMemoryRegionInfo", b"OK") @skipIfWindows # No pty support to test any inferior output def test_qMemoryRegionInfo_reports_code_address_as_executable(self): @@ -1076,10 +959,9 @@ @skipIfWindows def test_P_and_p_thread_suffix_work(self): self.build() - self.set_inferior_startup_launch() # Startup the inferior with three threads. - procs = self.prep_debug_monitor_and_inferior( + self.prep_debug_monitor_and_inferior( inferior_args=["thread:new", "thread:new"]) self.add_thread_suffix_request_packets() self.add_register_info_collection_packets() @@ -1121,41 +1003,23 @@ # If we don't have a next value yet, start it with the initial read # value + 1 if not next_value: - # Read pre-existing register value. - self.reset_test_sequence() - self.test_sequence.add_log_lines( - ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), - {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, - ], True) - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) + self.assertPacketResponse(b"p%x;thread:%x"%(reg_index, thread), + re.compile(b"^[0-9a-fA-F]+$")) # Set the next value to use for writing as the increment plus # current value. - p_response = context.get("p_response") - self.assertIsNotNone(p_response) next_value = lldbgdbserverutils.unpack_register_hex_unsigned( - endian, p_response) + endian, self.captures[0]) # Set new value using P and thread suffix. - self.reset_test_sequence() - self.test_sequence.add_log_lines( - [ - "read packet: $P{0:x}={1};thread:{2:x}#00".format( - reg_index, - lldbgdbserverutils.pack_register_hex( - endian, - next_value, - byte_size=reg_byte_size), - thread), - "send packet: $OK#00", - ], - True) - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) + value_str = lldbgdbserverutils.pack_register_hex( + endian, + next_value, + byte_size=reg_byte_size).encode() + self.assertPacketResponse(b"P%x=%s;thread:%x"%(reg_index, value_str, thread), b"OK") # Save the value we set. - expected_reg_values.append(next_value) + expected_reg_values.append(value_str) # Increment value for next thread to use (we want them all # different so we can verify they wrote to each thread correctly @@ -1166,21 +1030,6 @@ # the register we wrote. thread_index = 0 for thread in threads: - # Read pre-existing register value. - self.reset_test_sequence() - self.test_sequence.add_log_lines( - ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), - {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, - ], True) - context = self.expect_gdbremote_sequence() - self.assertIsNotNone(context) - - # Get the register value. - p_response = context.get("p_response") - self.assertIsNotNone(p_response) - read_value = lldbgdbserverutils.unpack_register_hex_unsigned( - endian, p_response) - - # Make sure we read back what we wrote. - self.assertEqual(read_value, expected_reg_values[thread_index]) + self.assertPacketResponse(b"p%x;thread:%x"%(reg_index, thread), + expected_reg_values[thread_index]) thread_index += 1 diff --git a/lldb/test/API/tools/lldb-server/main.cpp b/lldb/test/API/tools/lldb-server/main.cpp --- a/lldb/test/API/tools/lldb-server/main.cpp +++ b/lldb/test/API/tools/lldb-server/main.cpp @@ -177,6 +177,7 @@ std::lock_guard lock(g_print_mutex); printf("thread %" PRIx64 ": past SIGSEGV\n", get_thread_id()); } + return nullptr; } int sleep_seconds_remaining = 60;