xref: /llvm-project/lldb/packages/Python/lldbsuite/test/tools/lldb-server/fork_testbase.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1import gdbremote_testcase
2
3
4class GdbRemoteForkTestBase(gdbremote_testcase.GdbRemoteTestCaseBase):
5    fork_regex = (
6        "[$]T[0-9a-fA-F]{{2}}thread:p([0-9a-f]+)[.]([0-9a-f]+);.*"
7        "{}:p([0-9a-f]+)[.]([0-9a-f]+).*"
8    )
9    fork_regex_nonstop = (
10        "%Stop:T[0-9a-fA-F]{{2}}"
11        "thread:p([0-9a-f]+)[.]([0-9a-f]+);.*"
12        "{}:p([0-9a-f]+)[.]([0-9a-f]+).*"
13    )
14    fork_capture = {1: "parent_pid", 2: "parent_tid", 3: "child_pid", 4: "child_tid"}
15    stop_regex_base = "T[0-9a-fA-F]{{2}}thread:p{}.{};.*reason:signal.*"
16    stop_regex = "^[$]" + stop_regex_base
17
18    def start_fork_test(self, args, variant="fork", nonstop=False):
19        self.build()
20        self.prep_debug_monitor_and_inferior(inferior_args=args)
21        self.add_qSupported_packets(["multiprocess+", "{}-events+".format(variant)])
22        ret = self.expect_gdbremote_sequence()
23        self.assertIn("{}-events+".format(variant), ret["qSupported_response"])
24        self.reset_test_sequence()
25
26        # continue and expect fork
27        if nonstop:
28            self.test_sequence.add_log_lines(
29                [
30                    "read packet: $QNonStop:1#00",
31                    "send packet: $OK#00",
32                    "read packet: $c#00",
33                    "send packet: $OK#00",
34                    {
35                        "direction": "send",
36                        "regex": self.fork_regex_nonstop.format(variant),
37                        "capture": self.fork_capture,
38                    },
39                    "read packet: $vStopped#00",
40                    "send packet: $OK#00",
41                ],
42                True,
43            )
44        else:
45            self.test_sequence.add_log_lines(
46                [
47                    "read packet: $c#00",
48                    {
49                        "direction": "send",
50                        "regex": self.fork_regex.format(variant),
51                        "capture": self.fork_capture,
52                    },
53                ],
54                True,
55            )
56        ret = self.expect_gdbremote_sequence()
57        self.reset_test_sequence()
58
59        return tuple(
60            ret[x] for x in ("parent_pid", "parent_tid", "child_pid", "child_tid")
61        )
62
63    def fork_and_detach_test(self, variant, nonstop=False):
64        parent_pid, parent_tid, child_pid, child_tid = self.start_fork_test(
65            [variant], variant, nonstop=nonstop
66        )
67
68        # detach the forked child
69        self.test_sequence.add_log_lines(
70            [
71                "read packet: $D;{}#00".format(child_pid),
72                "send packet: $OK#00",
73                # verify that the current process is correct
74                "read packet: $qC#00",
75                "send packet: $QCp{}.{}#00".format(parent_pid, parent_tid),
76                # verify that the correct processes are detached/available
77                "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
78                "send packet: $Eff#00",
79                "read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
80                "send packet: $OK#00",
81            ],
82            True,
83        )
84        self.expect_gdbremote_sequence()
85        self.reset_test_sequence()
86        return parent_pid, parent_tid
87
88    def fork_and_follow_test(self, variant, nonstop=False):
89        parent_pid, parent_tid, child_pid, child_tid = self.start_fork_test(
90            [variant], variant, nonstop=nonstop
91        )
92
93        # switch to the forked child
94        self.test_sequence.add_log_lines(
95            [
96                "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
97                "send packet: $OK#00",
98                "read packet: $Hcp{}.{}#00".format(child_pid, child_tid),
99                "send packet: $OK#00",
100                # detach the parent
101                "read packet: $D;{}#00".format(parent_pid),
102                "send packet: $OK#00",
103                # verify that the correct processes are detached/available
104                "read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
105                "send packet: $Eff#00",
106                "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
107                "send packet: $OK#00",
108                # then resume the child
109                "read packet: $c#00",
110            ],
111            True,
112        )
113
114        if nonstop:
115            self.test_sequence.add_log_lines(
116                [
117                    "send packet: $OK#00",
118                    "send packet: %Stop:W00;process:{}#00".format(child_pid),
119                    "read packet: $vStopped#00",
120                    "send packet: $OK#00",
121                ],
122                True,
123            )
124        else:
125            self.test_sequence.add_log_lines(
126                [
127                    "send packet: $W00;process:{}#00".format(child_pid),
128                ],
129                True,
130            )
131        self.expect_gdbremote_sequence()
132
133    def detach_all_test(self, nonstop=False):
134        parent_pid, parent_tid, child_pid, child_tid = self.start_fork_test(
135            ["fork"], nonstop=nonstop
136        )
137
138        self.test_sequence.add_log_lines(
139            [
140                # double-check our PIDs
141                "read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
142                "send packet: $OK#00",
143                "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
144                "send packet: $OK#00",
145                # detach all processes
146                "read packet: $D#00",
147                "send packet: $OK#00",
148                # verify that both PIDs are invalid now
149                "read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
150                "send packet: $Eff#00",
151                "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
152                "send packet: $Eff#00",
153            ],
154            True,
155        )
156        self.expect_gdbremote_sequence()
157
158    def vkill_test(self, kill_parent=False, kill_child=False, nonstop=False):
159        assert kill_parent or kill_child
160        parent_pid, parent_tid, child_pid, child_tid = self.start_fork_test(
161            ["fork"], nonstop=nonstop
162        )
163
164        if kill_parent:
165            self.test_sequence.add_log_lines(
166                [
167                    # kill the process
168                    "read packet: $vKill;{}#00".format(parent_pid),
169                    "send packet: $OK#00",
170                ],
171                True,
172            )
173        if kill_child:
174            self.test_sequence.add_log_lines(
175                [
176                    # kill the process
177                    "read packet: $vKill;{}#00".format(child_pid),
178                    "send packet: $OK#00",
179                ],
180                True,
181            )
182        self.test_sequence.add_log_lines(
183            [
184                # check child PID/TID
185                "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
186                "send packet: ${}#00".format("Eff" if kill_child else "OK"),
187                # check parent PID/TID
188                "read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
189                "send packet: ${}#00".format("Eff" if kill_parent else "OK"),
190            ],
191            True,
192        )
193        self.expect_gdbremote_sequence()
194
195    def resume_one_test(self, run_order, use_vCont=False, nonstop=False):
196        parent_pid, parent_tid, child_pid, child_tid = self.start_fork_test(
197            ["fork", "stop"], nonstop=nonstop
198        )
199
200        parent_expect = [
201            self.stop_regex_base.format(parent_pid, parent_tid),
202            "W00;process:{}#.*".format(parent_pid),
203        ]
204        child_expect = [
205            self.stop_regex_base.format(child_pid, child_tid),
206            "W00;process:{}#.*".format(child_pid),
207        ]
208
209        for x in run_order:
210            if x == "parent":
211                pidtid = (parent_pid, parent_tid)
212                expect = parent_expect.pop(0)
213            elif x == "child":
214                pidtid = (child_pid, child_tid)
215                expect = child_expect.pop(0)
216            else:
217                assert False, "unexpected x={}".format(x)
218
219            if use_vCont:
220                self.test_sequence.add_log_lines(
221                    [
222                        # continue the selected process
223                        "read packet: $vCont;c:p{}.{}#00".format(*pidtid),
224                    ],
225                    True,
226                )
227            else:
228                self.test_sequence.add_log_lines(
229                    [
230                        # continue the selected process
231                        "read packet: $Hcp{}.{}#00".format(*pidtid),
232                        "send packet: $OK#00",
233                        "read packet: $c#00",
234                    ],
235                    True,
236                )
237            if nonstop:
238                self.test_sequence.add_log_lines(
239                    [
240                        "send packet: $OK#00",
241                        {"direction": "send", "regex": "%Stop:" + expect},
242                        "read packet: $vStopped#00",
243                        "send packet: $OK#00",
244                    ],
245                    True,
246                )
247            else:
248                self.test_sequence.add_log_lines(
249                    [
250                        {"direction": "send", "regex": "[$]" + expect},
251                    ],
252                    True,
253                )
254            # if at least one process remained, check both PIDs
255            if parent_expect or child_expect:
256                self.test_sequence.add_log_lines(
257                    [
258                        "read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
259                        "send packet: ${}#00".format("OK" if parent_expect else "Eff"),
260                        "read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
261                        "send packet: ${}#00".format("OK" if child_expect else "Eff"),
262                    ],
263                    True,
264                )
265        self.expect_gdbremote_sequence()
266