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