| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| import logging | ||
| import os | ||
| import os.path | ||
| import random | ||
|
|
||
| import lldb | ||
| from lldbsuite.test.lldbtest import * | ||
| from lldbsuite.test.gdbclientutils import * | ||
| import lldbgdbserverutils | ||
| from lldbsuite.support import seven | ||
|
|
||
|
|
||
| class GDBProxyTestBase(TestBase): | ||
| """ | ||
| Base class for gdbserver proxy tests. | ||
| This class will setup and start a mock GDB server for the test to use. | ||
| It pases through requests to a regular lldb-server/debugserver and | ||
| forwards replies back to the LLDB under test. | ||
| """ | ||
|
|
||
| """The gdbserver that we implement.""" | ||
| server = None | ||
| """The inner lldb-server/debugserver process that we proxy requests into.""" | ||
| monitor_server = None | ||
| monitor_sock = None | ||
|
|
||
| server_socket_class = TCPServerSocket | ||
|
|
||
| DEFAULT_TIMEOUT = 20 * (10 if ("ASAN_OPTIONS" in os.environ) else 1) | ||
|
|
||
| _verbose_log_handler = None | ||
| _log_formatter = logging.Formatter(fmt="%(asctime)-15s %(levelname)-8s %(message)s") | ||
|
|
||
| def setUpBaseLogging(self): | ||
| self.logger = logging.getLogger(__name__) | ||
|
|
||
| if len(self.logger.handlers) > 0: | ||
| return # We have set up this handler already | ||
|
|
||
| self.logger.propagate = False | ||
| self.logger.setLevel(logging.DEBUG) | ||
|
|
||
| # log all warnings to stderr | ||
| handler = logging.StreamHandler() | ||
| handler.setLevel(logging.WARNING) | ||
| handler.setFormatter(self._log_formatter) | ||
| self.logger.addHandler(handler) | ||
|
|
||
| def setUp(self): | ||
| TestBase.setUp(self) | ||
|
|
||
| self.setUpBaseLogging() | ||
|
|
||
| if self.isVerboseLoggingRequested(): | ||
| # If requested, full logs go to a log file | ||
| log_file_name = self.getLogBasenameForCurrentTest() + "-proxy.log" | ||
| self._verbose_log_handler = logging.FileHandler( | ||
| log_file_name | ||
| ) | ||
| self._verbose_log_handler.setFormatter(self._log_formatter) | ||
| self._verbose_log_handler.setLevel(logging.DEBUG) | ||
| self.logger.addHandler(self._verbose_log_handler) | ||
|
|
||
| lldb_server_exe = lldbgdbserverutils.get_lldb_server_exe() | ||
| if lldb_server_exe is None: | ||
| self.debug_monitor_exe = lldbgdbserverutils.get_debugserver_exe() | ||
| self.assertTrue(self.debug_monitor_exe is not None) | ||
| self.debug_monitor_extra_args = [] | ||
| else: | ||
| self.debug_monitor_exe = lldb_server_exe | ||
| self.debug_monitor_extra_args = ["gdbserver"] | ||
|
|
||
| self.server = MockGDBServer(self.server_socket_class()) | ||
| self.server.responder = self | ||
|
|
||
| def tearDown(self): | ||
| # TestBase.tearDown will kill the process, but we need to kill it early | ||
| # so its client connection closes and we can stop the server before | ||
| # finally calling the base tearDown. | ||
| if self.process() is not None: | ||
| self.process().Kill() | ||
| self.server.stop() | ||
|
|
||
| self.logger.removeHandler(self._verbose_log_handler) | ||
| self._verbose_log_handler = None | ||
|
|
||
| TestBase.tearDown(self) | ||
|
|
||
| def isVerboseLoggingRequested(self): | ||
| # We will report our detailed logs if the user requested that the "gdb-remote" channel is | ||
| # logged. | ||
| return any(("gdb-remote" in channel) for channel in lldbtest_config.channels) | ||
|
|
||
| def connect(self, target): | ||
| """ | ||
| Create a process by connecting to the mock GDB server. | ||
| """ | ||
| self.prep_debug_monitor_and_inferior() | ||
| self.server.start() | ||
|
|
||
| listener = self.dbg.GetListener() | ||
| error = lldb.SBError() | ||
| process = target.ConnectRemote( | ||
| listener, self.server.get_connect_url(), "gdb-remote", error | ||
| ) | ||
| self.assertTrue(error.Success(), error.description) | ||
| self.assertTrue(process, PROCESS_IS_VALID) | ||
| return process | ||
|
|
||
| def get_next_port(self): | ||
| return 12000 + random.randint(0, 3999) | ||
|
|
||
| def prep_debug_monitor_and_inferior(self): | ||
| inferior_exe_path = self.getBuildArtifact("a.out") | ||
| self.connect_to_debug_monitor([inferior_exe_path]) | ||
| self.assertIsNotNone(self.monitor_server) | ||
| self.initial_handshake() | ||
|
|
||
| def initial_handshake(self): | ||
| self.monitor_server.send_packet(seven.bitcast_to_bytes("+")) | ||
| reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet()) | ||
| self.assertEqual(reply, "+") | ||
| self.monitor_server.send_packet(seven.bitcast_to_bytes("QStartNoAckMode")) | ||
| reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet()) | ||
| self.assertEqual(reply, "+") | ||
| reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet()) | ||
| self.assertEqual(reply, "OK") | ||
| self.monitor_server.send_packet(seven.bitcast_to_bytes("+")) | ||
| reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet()) | ||
| self.assertEqual(reply, "+") | ||
|
|
||
| def get_debug_monitor_command_line_args(self, connect_address, launch_args): | ||
| return self.debug_monitor_extra_args + ["--reverse-connect", connect_address] + launch_args | ||
|
|
||
| def launch_debug_monitor(self, launch_args): | ||
| family, type, proto, _, addr = socket.getaddrinfo( | ||
| "localhost", 0, proto=socket.IPPROTO_TCP | ||
| )[0] | ||
| sock = socket.socket(family, type, proto) | ||
| sock.settimeout(self.DEFAULT_TIMEOUT) | ||
| sock.bind(addr) | ||
| sock.listen(1) | ||
| addr = sock.getsockname() | ||
| connect_address = "[{}]:{}".format(*addr) | ||
|
|
||
| commandline_args = self.get_debug_monitor_command_line_args( | ||
| connect_address, launch_args | ||
| ) | ||
|
|
||
| # Start the server. | ||
| self.logger.info(f"Spawning monitor {commandline_args}") | ||
| monitor_process = self.spawnSubprocess( | ||
| self.debug_monitor_exe, commandline_args, install_remote=False | ||
| ) | ||
| self.assertIsNotNone(monitor_process) | ||
|
|
||
| self.monitor_sock = sock.accept()[0] | ||
| self.monitor_sock.settimeout(self.DEFAULT_TIMEOUT) | ||
| return monitor_process | ||
|
|
||
| def connect_to_debug_monitor(self, launch_args): | ||
| monitor_process = self.launch_debug_monitor(launch_args) | ||
| self.monitor_server = lldbgdbserverutils.Server(self.monitor_sock, monitor_process) | ||
|
|
||
| def respond(self, packet): | ||
| """Subclasses can override this to change how packets are handled.""" | ||
| return self.pass_through(packet) | ||
|
|
||
| def pass_through(self, packet): | ||
| self.logger.info(f"Sending packet {packet}") | ||
| self.monitor_server.send_packet(seven.bitcast_to_bytes(packet)) | ||
| reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet()) | ||
| self.logger.info(f"Received reply {reply}") | ||
| return reply |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| C_SOURCES := main.c | ||
|
|
||
| include Makefile.rules |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import lldb | ||
| import time | ||
| import unittest | ||
| from lldbsuite.test.lldbtest import * | ||
| from lldbsuite.test.decorators import * | ||
| from lldbsuite.test.gdbclientutils import * | ||
| from lldbsuite.test.lldbreverse import ReverseTestBase | ||
| from lldbsuite.test import lldbutil | ||
|
|
||
|
|
||
| class TestReverseContinueBreakpoints(ReverseTestBase): | ||
| NO_DEBUG_INFO_TESTCASE = True | ||
|
|
||
| def test_reverse_continue(self): | ||
| self.reverse_continue_internal(async_mode=False) | ||
|
|
||
| def test_reverse_continue_async(self): | ||
| self.reverse_continue_internal(async_mode=True) | ||
|
|
||
| def reverse_continue_internal(self, async_mode): | ||
| target, process, initial_threads = self.setup_recording(async_mode) | ||
|
|
||
| # Reverse-continue. We'll stop at the point where we started recording. | ||
| status = process.Continue(lldb.eRunReverse) | ||
| self.assertSuccess(status) | ||
| self.expect_async_state_changes(async_mode, process, [lldb.eStateRunning, lldb.eStateStopped]) | ||
| self.expect( | ||
| "thread list", | ||
| STOPPED_DUE_TO_HISTORY_BOUNDARY, | ||
| substrs=["stopped", "stop reason = history boundary"], | ||
| ) | ||
|
|
||
| # Continue forward normally until the target exits. | ||
| status = process.Continue() | ||
| self.expect_async_state_changes(async_mode, process, [lldb.eStateRunning, lldb.eStateExited]) | ||
| self.assertSuccess(status) | ||
| self.assertState(process.GetState(), lldb.eStateExited) | ||
| self.assertEqual(process.GetExitStatus(), 0) | ||
|
|
||
| def test_reverse_continue_breakpoint(self): | ||
| self.reverse_continue_breakpoint_internal(async_mode=False) | ||
|
|
||
| def test_reverse_continue_breakpoint_async(self): | ||
| self.reverse_continue_breakpoint_internal(async_mode=True) | ||
|
|
||
| def reverse_continue_breakpoint_internal(self, async_mode): | ||
| target, process, initial_threads = self.setup_recording(async_mode) | ||
|
|
||
| # Reverse-continue to the function "trigger_breakpoint". | ||
| trigger_bkpt = target.BreakpointCreateByName("trigger_breakpoint", None) | ||
| status = process.Continue(lldb.eRunReverse) | ||
| self.assertSuccess(status) | ||
| self.expect_async_state_changes(async_mode, process, [lldb.eStateRunning, lldb.eStateStopped]) | ||
| threads_now = lldbutil.get_threads_stopped_at_breakpoint(process, trigger_bkpt) | ||
| self.assertEqual(threads_now, initial_threads) | ||
|
|
||
| def test_reverse_continue_skip_breakpoint(self): | ||
| self.reverse_continue_skip_breakpoint_internal(async_mode=False) | ||
|
|
||
| def test_reverse_continue_skip_breakpoint_async(self): | ||
| self.reverse_continue_skip_breakpoint_internal(async_mode=True) | ||
|
|
||
| def reverse_continue_skip_breakpoint_internal(self, async_mode): | ||
| target, process, initial_threads = self.setup_recording(async_mode) | ||
|
|
||
| # Reverse-continue over a breakpoint at "trigger_breakpoint" whose | ||
| # condition is false. | ||
| # This tests that we continue in the correct direction after hitting | ||
| # the breakpoint. | ||
| trigger_bkpt = target.BreakpointCreateByName("trigger_breakpoint", None) | ||
| trigger_bkpt.SetCondition("false_condition") | ||
| status = process.Continue(lldb.eRunReverse) | ||
| self.expect_async_state_changes(async_mode, process, [lldb.eStateRunning, lldb.eStateStopped]) | ||
| self.assertSuccess(status) | ||
| self.expect( | ||
| "thread list", | ||
| STOPPED_DUE_TO_HISTORY_BOUNDARY, | ||
| substrs=["stopped", "stop reason = history boundary"], | ||
| ) | ||
|
|
||
| def setup_recording(self, async_mode): | ||
| """ | ||
| Record execution of code between "start_recording" and "stop_recording" breakpoints. | ||
| Returns with the target stopped at "stop_recording", with recording disabled, | ||
| ready to reverse-execute. | ||
| """ | ||
| self.build() | ||
| target = self.dbg.CreateTarget("") | ||
| process = self.connect(target) | ||
|
|
||
| # Record execution from the start of the function "start_recording" | ||
| # to the start of the function "stop_recording". We want to keep the | ||
| # interval that we record as small as possible to minimize the run-time | ||
| # of our single-stepping recorder. | ||
| start_recording_bkpt = target.BreakpointCreateByName("start_recording", None) | ||
| initial_threads = lldbutil.continue_to_breakpoint(process, start_recording_bkpt) | ||
| self.assertEqual(len(initial_threads), 1) | ||
| target.BreakpointDelete(start_recording_bkpt.GetID()) | ||
| self.start_recording() | ||
| stop_recording_bkpt = target.BreakpointCreateByName("stop_recording", None) | ||
| lldbutil.continue_to_breakpoint(process, stop_recording_bkpt) | ||
| target.BreakpointDelete(stop_recording_bkpt.GetID()) | ||
| self.stop_recording() | ||
|
|
||
| self.dbg.SetAsync(async_mode) | ||
| self.expect_async_state_changes(async_mode, process, [lldb.eStateStopped]) | ||
|
|
||
| return target, process, initial_threads | ||
|
|
||
| def expect_async_state_changes(self, async_mode, process, states): | ||
| if not async_mode: | ||
| return | ||
| listener = self.dbg.GetListener() | ||
| lldbutil.expect_state_changes(self, listener, process, states) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import lldb | ||
| import unittest | ||
| from lldbsuite.test.lldbtest import * | ||
| from lldbsuite.test.decorators import * | ||
| from lldbsuite.test import lldbutil | ||
|
|
||
|
|
||
| class TestReverseContinueNotSupported(TestBase): | ||
| NO_DEBUG_INFO_TESTCASE = True | ||
|
|
||
| def test_reverse_continue_not_supported(self): | ||
| self.build() | ||
| exe = self.getBuildArtifact("a.out") | ||
| target = self.dbg.CreateTarget(exe) | ||
| self.assertTrue(target, VALID_TARGET) | ||
|
|
||
| main_bkpt = target.BreakpointCreateByName("main", None) | ||
| self.assertTrue(main_bkpt, VALID_BREAKPOINT) | ||
|
|
||
| process = target.LaunchSimple(None, None, self.get_process_working_directory()) | ||
| self.assertTrue(process, PROCESS_IS_VALID) | ||
|
|
||
| # This will fail gracefully. | ||
| status = process.Continue(lldb.eRunReverse) | ||
| self.assertFailure(status, "target does not support reverse-continue") | ||
|
|
||
| status = process.Continue() | ||
| self.assertSuccess(status) | ||
| self.assertState(process.GetState(), lldb.eStateExited) | ||
| self.assertEqual(process.GetExitStatus(), 0) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| volatile int false_condition = 0; | ||
|
|
||
| static void start_recording() {} | ||
|
|
||
| static void trigger_breakpoint() {} | ||
|
|
||
| static void stop_recording() {} | ||
|
|
||
| int main() { | ||
| start_recording(); | ||
| trigger_breakpoint(); | ||
| stop_recording(); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,48 +1,107 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 | ||
| ; RUN: llc < %s -march=nvptx -mcpu=sm_20 -verify-machineinstrs -trap-unreachable=false \ | ||
| ; RUN: | FileCheck %s --check-prefixes=CHECK,NO-TRAP-UNREACHABLE | ||
| ; RUN: llc < %s -march=nvptx64 -mcpu=sm_20 -verify-machineinstrs -trap-unreachable=false \ | ||
| ; RUN: | FileCheck %s --check-prefixes=CHECK,NO-TRAP-UNREACHABLE | ||
| ; RUN: llc < %s -march=nvptx -mcpu=sm_20 -verify-machineinstrs -trap-unreachable -no-trap-after-noreturn \ | ||
| ; RUN: | FileCheck %s --check-prefixes=CHECK,NO-TRAP-AFTER-NORETURN | ||
| ; RUN: llc < %s -march=nvptx64 -mcpu=sm_20 -verify-machineinstrs -trap-unreachable -no-trap-after-noreturn \ | ||
| ; RUN: | FileCheck %s --check-prefixes=CHECK,NO-TRAP-AFTER-NORETURN | ||
| ; RUN: llc < %s -march=nvptx -mcpu=sm_20 -verify-machineinstrs -trap-unreachable -no-trap-after-noreturn=false \ | ||
| ; RUN: | FileCheck %s --check-prefixes=CHECK,TRAP | ||
| ; RUN: llc < %s -march=nvptx64 -mcpu=sm_20 -verify-machineinstrs -trap-unreachable -no-trap-after-noreturn=false \ | ||
| ; RUN: | FileCheck %s --check-prefixes=CHECK,TRAP | ||
| ; RUN: llc < %s -march=nvptx64 -mcpu=sm_20 -verify-machineinstrs -trap-unreachable -mattr=+ptx83 \ | ||
| ; RUN: | FileCheck %s --check-prefixes=BUG-FIXED | ||
| ; RUN: %if ptxas && !ptxas-12.0 %{ llc < %s -march=nvptx -mcpu=sm_20 -verify-machineinstrs | %ptxas-verify %} | ||
| ; RUN: %if ptxas %{ llc < %s -march=nvptx64 -mcpu=sm_20 -verify-machineinstrs | %ptxas-verify %} | ||
|
|
||
| target triple = "nvptx-unknown-cuda" | ||
|
|
||
| declare void @throw() #0 | ||
| declare void @llvm.trap() #0 | ||
|
|
||
| define void @kernel_func() { | ||
| ; NO-TRAP-UNREACHABLE-LABEL: kernel_func( | ||
| ; NO-TRAP-UNREACHABLE: { | ||
| ; NO-TRAP-UNREACHABLE-EMPTY: | ||
| ; NO-TRAP-UNREACHABLE-EMPTY: | ||
| ; NO-TRAP-UNREACHABLE-NEXT: // %bb.0: | ||
| ; NO-TRAP-UNREACHABLE-NEXT: { // callseq 0, 0 | ||
| ; NO-TRAP-UNREACHABLE-NEXT: call.uni | ||
| ; NO-TRAP-UNREACHABLE-NEXT: throw, | ||
| ; NO-TRAP-UNREACHABLE-NEXT: ( | ||
| ; NO-TRAP-UNREACHABLE-NEXT: ); | ||
| ; NO-TRAP-UNREACHABLE-NEXT: } // callseq 0 | ||
| ; NO-TRAP-UNREACHABLE-NEXT: // begin inline asm | ||
| ; NO-TRAP-UNREACHABLE-NEXT: exit; | ||
| ; NO-TRAP-UNREACHABLE-NEXT: // end inline asm | ||
| ; | ||
| ; NO-TRAP-AFTER-NORETURN-LABEL: kernel_func( | ||
| ; NO-TRAP-AFTER-NORETURN: { | ||
| ; NO-TRAP-AFTER-NORETURN-EMPTY: | ||
| ; NO-TRAP-AFTER-NORETURN-EMPTY: | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: // %bb.0: | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: { // callseq 0, 0 | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: call.uni | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: throw, | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: ( | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: ); | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: } // callseq 0 | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: // begin inline asm | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: exit; | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: // end inline asm | ||
| ; NO-TRAP-AFTER-NORETURN-NEXT: trap; exit; | ||
| ; | ||
| ; TRAP-LABEL: kernel_func( | ||
| ; TRAP: { | ||
| ; TRAP-EMPTY: | ||
| ; TRAP-EMPTY: | ||
| ; TRAP-NEXT: // %bb.0: | ||
| ; TRAP-NEXT: { // callseq 0, 0 | ||
| ; TRAP-NEXT: call.uni | ||
| ; TRAP-NEXT: throw, | ||
| ; TRAP-NEXT: ( | ||
| ; TRAP-NEXT: ); | ||
| ; TRAP-NEXT: } // callseq 0 | ||
| ; TRAP-NEXT: trap; exit; | ||
| ; | ||
| ; BUG-FIXED-LABEL: kernel_func( | ||
| ; BUG-FIXED: { | ||
| ; BUG-FIXED-EMPTY: | ||
| ; BUG-FIXED-EMPTY: | ||
| ; BUG-FIXED-NEXT: // %bb.0: | ||
| ; BUG-FIXED-NEXT: { // callseq 0, 0 | ||
| ; BUG-FIXED-NEXT: call.uni | ||
| ; BUG-FIXED-NEXT: throw, | ||
| ; BUG-FIXED-NEXT: ( | ||
| ; BUG-FIXED-NEXT: ); | ||
| ; BUG-FIXED-NEXT: } // callseq 0 | ||
| ; BUG-FIXED-NEXT: trap; | ||
| call void @throw() | ||
| unreachable | ||
| } | ||
|
|
||
| define void @kernel_func_2() { | ||
| ; CHECK-LABEL: kernel_func_2( | ||
| ; CHECK: { | ||
| ; CHECK-EMPTY: | ||
| ; CHECK-EMPTY: | ||
| ; CHECK-NEXT: // %bb.0: | ||
| ; CHECK-NEXT: trap; exit; | ||
| ; | ||
| ; BUG-FIXED-LABEL: kernel_func_2( | ||
| ; BUG-FIXED: { | ||
| ; BUG-FIXED-EMPTY: | ||
| ; BUG-FIXED-EMPTY: | ||
| ; BUG-FIXED-NEXT: // %bb.0: | ||
| ; BUG-FIXED-NEXT: trap; | ||
| call void @llvm.trap() | ||
| ; Make sure we avoid emitting two trap instructions. | ||
| unreachable | ||
| } | ||
|
|
||
| attributes #0 = { noreturn } | ||
|
|
||
| !nvvm.annotations = !{!1} | ||
| !1 = !{ptr @kernel_func, !"kernel", i32 1} |