xref: /llvm-project/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py (revision a52be0cc114cc58a35bee65c517adaeb66ee6d89)
1"""
2Test lldb-dap "port" configuration to "attach" request
3"""
4
5
6import dap_server
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10from lldbsuite.test import lldbplatformutil
11from lldbgdbserverutils import Pipe
12import lldbdap_testcase
13import os
14import shutil
15import subprocess
16import tempfile
17import threading
18import sys
19import socket
20
21
22class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
23    default_timeout = 20
24
25    def set_and_hit_breakpoint(self, continueToExit=True):
26        source = "main.c"
27        main_source_path = os.path.join(os.getcwd(), source)
28        breakpoint1_line = line_number(main_source_path, "// breakpoint 1")
29        lines = [breakpoint1_line]
30        # Set breakpoint in the thread function so we can step the threads
31        breakpoint_ids = self.set_source_breakpoints(main_source_path, lines)
32        self.assertEqual(
33            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
34        )
35        self.continue_to_breakpoints(breakpoint_ids)
36        if continueToExit:
37            self.continue_to_exit()
38
39    def get_debug_server_command_line_args(self):
40        args = []
41        if lldbplatformutil.getPlatform() == "linux":
42            args = ["gdbserver"]
43        elif lldbplatformutil.getPlatform() == "macosx":
44            args = ["--listen"]
45        if lldb.remote_platform:
46            args += ["*:0"]
47        else:
48            args += ["localhost:0"]
49        return args
50
51    def get_debug_server_pipe(self):
52        pipe = Pipe(self.getBuildDir())
53        self.addTearDownHook(lambda: pipe.close())
54        pipe.finish_connection(self.default_timeout)
55        return pipe
56
57    @skipIfWindows
58    @skipIfNetBSD
59    def test_by_port(self):
60        """
61        Tests attaching to a process by port.
62        """
63        self.build_and_create_debug_adaptor()
64        program = self.getBuildArtifact("a.out")
65
66        debug_server_tool = self.getBuiltinDebugServerTool()
67
68        pipe = self.get_debug_server_pipe()
69        args = self.get_debug_server_command_line_args()
70        args += [program]
71        args += ["--named-pipe", pipe.name]
72
73        self.process = self.spawnSubprocess(
74            debug_server_tool, args, install_remote=False
75        )
76
77        # Read the port number from the debug server pipe.
78        port = pipe.read(10, self.default_timeout)
79        # Trim null byte, convert to int
80        port = int(port[:-1])
81        self.assertIsNotNone(
82            port, " Failed to read the port number from debug server pipe"
83        )
84
85        self.attach(program=program, gdbRemotePort=port, sourceInitFile=True)
86        self.set_and_hit_breakpoint(continueToExit=True)
87        self.process.terminate()
88
89    @skipIfWindows
90    @skipIfNetBSD
91    def test_by_port_and_pid(self):
92        """
93        Tests attaching to a process by process ID and port number.
94        """
95        self.build_and_create_debug_adaptor()
96        program = self.getBuildArtifact("a.out")
97
98        # It is not necessary to launch "lldb-server" to obtain the actual port and pid for attaching.
99        # However, when providing the port number and pid directly, "lldb-dap" throws an error message, which is expected.
100        # So, used random pid and port numbers here.
101
102        pid = 1354
103        port = 1234
104
105        response = self.attach(
106            program=program,
107            pid=pid,
108            gdbRemotePort=port,
109            sourceInitFile=True,
110            expectFailure=True,
111        )
112        if not (response and response["success"]):
113            self.assertFalse(
114                response["success"], "The user can't specify both pid and port"
115            )
116
117    @skipIfWindows
118    @skipIfNetBSD
119    def test_by_invalid_port(self):
120        """
121        Tests attaching to a process by invalid port number 0.
122        """
123        self.build_and_create_debug_adaptor()
124        program = self.getBuildArtifact("a.out")
125
126        port = 0
127        response = self.attach(
128            program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True
129        )
130        if not (response and response["success"]):
131            self.assertFalse(
132                response["success"],
133                "The user can't attach with invalid port (%s)" % port,
134            )
135
136    @skipIfWindows
137    @skipIfNetBSD
138    def test_by_illegal_port(self):
139        """
140        Tests attaching to a process by illegal/greater port number 65536
141        """
142        self.build_and_create_debug_adaptor()
143        program = self.getBuildArtifact("a.out")
144
145        port = 65536
146        args = [program]
147        debug_server_tool = self.getBuiltinDebugServerTool()
148        self.process = self.spawnSubprocess(
149            debug_server_tool, args, install_remote=False
150        )
151
152        response = self.attach(
153            program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True
154        )
155        if not (response and response["success"]):
156            self.assertFalse(
157                response["success"],
158                "The user can't attach with illegal port (%s)" % port,
159            )
160        self.process.terminate()
161