xref: /llvm-project/lldb/test/API/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py (revision 80fcecb13c388ff087a27a4b0e7ca3dd8c98eaa4)
199451b44SJordan Rupprechtimport json
299451b44SJordan Rupprechtimport re
399451b44SJordan Rupprecht
499451b44SJordan Rupprechtimport gdbremote_testcase
599451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
699451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
799451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
899451b44SJordan Rupprecht
999451b44SJordan Rupprecht
102238dcc3SJonas Devlieghereclass TestGdbRemoteThreadsInStopReply(gdbremote_testcase.GdbRemoteTestCaseBase):
1199451b44SJordan Rupprecht    ENABLE_THREADS_IN_STOP_REPLY_ENTRIES = [
1299451b44SJordan Rupprecht        "read packet: $QListThreadsInStopReply#21",
1399451b44SJordan Rupprecht        "send packet: $OK#00",
1499451b44SJordan Rupprecht    ]
1599451b44SJordan Rupprecht
165a4fe166SPavel Labath    def gather_stop_reply_fields(self, thread_count, field_names):
175a4fe166SPavel Labath        context, threads = self.launch_with_threads(thread_count)
185a4fe166SPavel Labath        key_vals_text = context.get("stop_reply_kv")
195a4fe166SPavel Labath        self.assertIsNotNone(key_vals_text)
2099451b44SJordan Rupprecht
215a4fe166SPavel Labath        self.reset_test_sequence()
2299451b44SJordan Rupprecht        self.add_register_info_collection_packets()
2399451b44SJordan Rupprecht        self.add_process_info_collection_packets()
2499451b44SJordan Rupprecht
2599451b44SJordan Rupprecht        context = self.expect_gdbremote_sequence()
2699451b44SJordan Rupprecht        self.assertIsNotNone(context)
2799451b44SJordan Rupprecht        hw_info = self.parse_hw_info(context)
2899451b44SJordan Rupprecht
2999451b44SJordan Rupprecht        # Parse the stop reply contents.
3099451b44SJordan Rupprecht        kv_dict = self.parse_key_val_dict(key_vals_text)
3199451b44SJordan Rupprecht
322238dcc3SJonas Devlieghere        result = dict()
3399451b44SJordan Rupprecht        result["pc_register"] = hw_info["pc_register"]
3499451b44SJordan Rupprecht        result["little_endian"] = hw_info["little_endian"]
3599451b44SJordan Rupprecht        for key_field in field_names:
3699451b44SJordan Rupprecht            result[key_field] = kv_dict.get(key_field)
3799451b44SJordan Rupprecht
3899451b44SJordan Rupprecht        return result
3999451b44SJordan Rupprecht
405a4fe166SPavel Labath    def gather_stop_reply_threads(self, thread_count):
4199451b44SJordan Rupprecht        # Pull out threads from stop response.
4299451b44SJordan Rupprecht        stop_reply_threads_text = self.gather_stop_reply_fields(
432238dcc3SJonas Devlieghere            thread_count, ["threads"]
442238dcc3SJonas Devlieghere        )["threads"]
4599451b44SJordan Rupprecht        if stop_reply_threads_text:
462238dcc3SJonas Devlieghere            return [
472238dcc3SJonas Devlieghere                int(thread_id, 16) for thread_id in stop_reply_threads_text.split(",")
482238dcc3SJonas Devlieghere            ]
4999451b44SJordan Rupprecht        else:
5099451b44SJordan Rupprecht            return []
5199451b44SJordan Rupprecht
525a4fe166SPavel Labath    def gather_stop_reply_pcs(self, thread_count):
535a4fe166SPavel Labath        results = self.gather_stop_reply_fields(thread_count, ["threads", "thread-pcs"])
5499451b44SJordan Rupprecht        if not results:
5599451b44SJordan Rupprecht            return []
5699451b44SJordan Rupprecht
5799451b44SJordan Rupprecht        threads_text = results["threads"]
5899451b44SJordan Rupprecht        pcs_text = results["thread-pcs"]
5999451b44SJordan Rupprecht        thread_ids = threads_text.split(",")
6099451b44SJordan Rupprecht        pcs = pcs_text.split(",")
61*80fcecb1SJonas Devlieghere        self.assertEqual(len(thread_ids), len(pcs))
6299451b44SJordan Rupprecht
6399451b44SJordan Rupprecht        thread_pcs = dict()
6499451b44SJordan Rupprecht        for i in range(0, len(pcs)):
6599451b44SJordan Rupprecht            thread_pcs[int(thread_ids[i], 16)] = pcs[i]
6699451b44SJordan Rupprecht
6799451b44SJordan Rupprecht        result = dict()
6899451b44SJordan Rupprecht        result["thread_pcs"] = thread_pcs
6999451b44SJordan Rupprecht        result["pc_register"] = results["pc_register"]
7099451b44SJordan Rupprecht        result["little_endian"] = results["little_endian"]
7199451b44SJordan Rupprecht        return result
7299451b44SJordan Rupprecht
7399451b44SJordan Rupprecht    def switch_endian(self, egg):
7499451b44SJordan Rupprecht        return "".join(reversed(re.findall("..", egg)))
7599451b44SJordan Rupprecht
7699451b44SJordan Rupprecht    def parse_hw_info(self, context):
7799451b44SJordan Rupprecht        self.assertIsNotNone(context)
7899451b44SJordan Rupprecht        process_info = self.parse_process_info_response(context)
7999451b44SJordan Rupprecht        endian = process_info.get("endian")
8099451b44SJordan Rupprecht        reg_info = self.parse_register_info_packets(context)
8199451b44SJordan Rupprecht        (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_info)
8299451b44SJordan Rupprecht
8399451b44SJordan Rupprecht        hw_info = dict()
8499451b44SJordan Rupprecht        hw_info["pc_register"] = pc_lldb_reg_index
852238dcc3SJonas Devlieghere        hw_info["little_endian"] = endian == "little"
8699451b44SJordan Rupprecht        return hw_info
8799451b44SJordan Rupprecht
8899451b44SJordan Rupprecht    def gather_threads_info_pcs(self, pc_register, little_endian):
8999451b44SJordan Rupprecht        self.reset_test_sequence()
9099451b44SJordan Rupprecht        self.test_sequence.add_log_lines(
9199451b44SJordan Rupprecht            [
9299451b44SJordan Rupprecht                "read packet: $jThreadsInfo#c1",
9399451b44SJordan Rupprecht                {
9499451b44SJordan Rupprecht                    "direction": "send",
9599451b44SJordan Rupprecht                    "regex": r"^\$(.*)#[0-9a-fA-F]{2}$",
962238dcc3SJonas Devlieghere                    "capture": {1: "threads_info"},
972238dcc3SJonas Devlieghere                },
9899451b44SJordan Rupprecht            ],
992238dcc3SJonas Devlieghere            True,
1002238dcc3SJonas Devlieghere        )
10199451b44SJordan Rupprecht
10299451b44SJordan Rupprecht        context = self.expect_gdbremote_sequence()
10399451b44SJordan Rupprecht        self.assertIsNotNone(context)
10499451b44SJordan Rupprecht        threads_info = context.get("threads_info")
10599451b44SJordan Rupprecht        register = str(pc_register)
10699451b44SJordan Rupprecht        # The jThreadsInfo response is not valid JSON data, so we have to
10799451b44SJordan Rupprecht        # clean it up first.
108e609fe68SMuhammad Omair Javaid        jthreads_info = json.loads(re.sub(r"}]", "}", threads_info))
10999451b44SJordan Rupprecht        thread_pcs = dict()
11099451b44SJordan Rupprecht        for thread_info in jthreads_info:
11199451b44SJordan Rupprecht            tid = thread_info["tid"]
11299451b44SJordan Rupprecht            pc = thread_info["registers"][register]
11399451b44SJordan Rupprecht            thread_pcs[tid] = self.switch_endian(pc) if little_endian else pc
11499451b44SJordan Rupprecht
11599451b44SJordan Rupprecht        return thread_pcs
11699451b44SJordan Rupprecht
1170a8a2453SPavel Labath    def test_QListThreadsInStopReply_supported(self):
1180a8a2453SPavel Labath        self.build()
1190a8a2453SPavel Labath        self.set_inferior_startup_launch()
12099451b44SJordan Rupprecht        procs = self.prep_debug_monitor_and_inferior()
12199451b44SJordan Rupprecht        self.test_sequence.add_log_lines(
1222238dcc3SJonas Devlieghere            self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True
1232238dcc3SJonas Devlieghere        )
12499451b44SJordan Rupprecht
12599451b44SJordan Rupprecht        context = self.expect_gdbremote_sequence()
12699451b44SJordan Rupprecht        self.assertIsNotNone(context)
12799451b44SJordan Rupprecht
12899451b44SJordan Rupprecht    @skipIfNetBSD
1291046b726SPavel Labath    @expectedFailureAll(oslist=["windows"])  # Extra threads present
1300a8a2453SPavel Labath    def test_stop_reply_reports_multiple_threads(self):
13199451b44SJordan Rupprecht        self.build()
13299451b44SJordan Rupprecht        self.set_inferior_startup_launch()
1330a8a2453SPavel Labath        # Gather threads from stop notification when QThreadsInStopReply is
13499451b44SJordan Rupprecht        # enabled.
1355a4fe166SPavel Labath        self.test_sequence.add_log_lines(
1362238dcc3SJonas Devlieghere            self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True
1372238dcc3SJonas Devlieghere        )
1385a4fe166SPavel Labath        stop_reply_threads = self.gather_stop_reply_threads(5)
1390a8a2453SPavel Labath        self.assertEqual(len(stop_reply_threads), 5)
14099451b44SJordan Rupprecht
1410a8a2453SPavel Labath    @skipIfNetBSD
1420a8a2453SPavel Labath    def test_no_QListThreadsInStopReply_supplies_no_threads(self):
14399451b44SJordan Rupprecht        self.build()
14499451b44SJordan Rupprecht        self.set_inferior_startup_launch()
1450a8a2453SPavel Labath        # Gather threads from stop notification when QThreadsInStopReply is not
1460a8a2453SPavel Labath        # enabled.
1475a4fe166SPavel Labath        stop_reply_threads = self.gather_stop_reply_threads(5)
1480a8a2453SPavel Labath        self.assertEqual(len(stop_reply_threads), 0)
14999451b44SJordan Rupprecht
15099451b44SJordan Rupprecht    @skipIfNetBSD
1510a8a2453SPavel Labath    def test_stop_reply_reports_correct_threads(self):
15299451b44SJordan Rupprecht        self.build()
15399451b44SJordan Rupprecht        self.set_inferior_startup_launch()
15499451b44SJordan Rupprecht        # Gather threads from stop notification when QThreadsInStopReply is
15599451b44SJordan Rupprecht        # enabled.
1560a8a2453SPavel Labath        thread_count = 5
1575a4fe166SPavel Labath        self.test_sequence.add_log_lines(
1582238dcc3SJonas Devlieghere            self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True
1592238dcc3SJonas Devlieghere        )
1605a4fe166SPavel Labath        stop_reply_threads = self.gather_stop_reply_threads(thread_count)
16199451b44SJordan Rupprecht
16299451b44SJordan Rupprecht        # Gather threads from q{f,s}ThreadInfo.
16399451b44SJordan Rupprecht        self.reset_test_sequence()
16499451b44SJordan Rupprecht        self.add_threadinfo_collection_packets()
16599451b44SJordan Rupprecht
16699451b44SJordan Rupprecht        context = self.expect_gdbremote_sequence()
16799451b44SJordan Rupprecht        self.assertIsNotNone(context)
16899451b44SJordan Rupprecht
16999451b44SJordan Rupprecht        threads = self.parse_threadinfo_packets(context)
17099451b44SJordan Rupprecht        self.assertIsNotNone(threads)
1711046b726SPavel Labath        self.assertGreaterEqual(len(threads), thread_count)
17299451b44SJordan Rupprecht
17399451b44SJordan Rupprecht        # Ensure each thread in q{f,s}ThreadInfo appears in stop reply threads
17499451b44SJordan Rupprecht        for tid in threads:
1750a8a2453SPavel Labath            self.assertIn(tid, stop_reply_threads)
17699451b44SJordan Rupprecht
17799451b44SJordan Rupprecht    @skipIfNetBSD
178bd56c8d5SDavid Spickett    @skipIfWindows  # Flaky on Windows
1790a8a2453SPavel Labath    def test_stop_reply_contains_thread_pcs(self):
18099451b44SJordan Rupprecht        self.build()
18199451b44SJordan Rupprecht        self.set_inferior_startup_launch()
1820a8a2453SPavel Labath        thread_count = 5
1835a4fe166SPavel Labath        self.test_sequence.add_log_lines(
1842238dcc3SJonas Devlieghere            self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True
1852238dcc3SJonas Devlieghere        )
1865a4fe166SPavel Labath        results = self.gather_stop_reply_pcs(thread_count)
18799451b44SJordan Rupprecht        stop_reply_pcs = results["thread_pcs"]
18899451b44SJordan Rupprecht        pc_register = results["pc_register"]
18999451b44SJordan Rupprecht        little_endian = results["little_endian"]
1901046b726SPavel Labath        self.assertGreaterEqual(len(stop_reply_pcs), thread_count)
19199451b44SJordan Rupprecht
1922238dcc3SJonas Devlieghere        threads_info_pcs = self.gather_threads_info_pcs(pc_register, little_endian)
19399451b44SJordan Rupprecht
1941046b726SPavel Labath        self.assertEqual(len(threads_info_pcs), len(stop_reply_pcs))
19599451b44SJordan Rupprecht        for thread_id in stop_reply_pcs:
1960a8a2453SPavel Labath            self.assertIn(thread_id, threads_info_pcs)
1972238dcc3SJonas Devlieghere            self.assertEqual(
1982238dcc3SJonas Devlieghere                int(stop_reply_pcs[thread_id], 16), int(threads_info_pcs[thread_id], 16)
1992238dcc3SJonas Devlieghere            )
200