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 Rupprechtclass TestGdbRemoteThreadsInStopReply( 1099451b44SJordan Rupprecht gdbremote_testcase.GdbRemoteTestCaseBase): 1199451b44SJordan Rupprecht 1299451b44SJordan Rupprecht ENABLE_THREADS_IN_STOP_REPLY_ENTRIES = [ 1399451b44SJordan Rupprecht "read packet: $QListThreadsInStopReply#21", 1499451b44SJordan Rupprecht "send packet: $OK#00", 1599451b44SJordan Rupprecht ] 1699451b44SJordan Rupprecht 175a4fe166SPavel Labath def gather_stop_reply_fields(self, thread_count, field_names): 185a4fe166SPavel Labath context, threads = self.launch_with_threads(thread_count) 195a4fe166SPavel Labath key_vals_text = context.get("stop_reply_kv") 205a4fe166SPavel Labath self.assertIsNotNone(key_vals_text) 2199451b44SJordan Rupprecht 225a4fe166SPavel Labath self.reset_test_sequence() 2399451b44SJordan Rupprecht self.add_register_info_collection_packets() 2499451b44SJordan Rupprecht self.add_process_info_collection_packets() 2599451b44SJordan Rupprecht 2699451b44SJordan Rupprecht context = self.expect_gdbremote_sequence() 2799451b44SJordan Rupprecht self.assertIsNotNone(context) 2899451b44SJordan Rupprecht hw_info = self.parse_hw_info(context) 2999451b44SJordan Rupprecht 3099451b44SJordan Rupprecht # Parse the stop reply contents. 3199451b44SJordan Rupprecht kv_dict = self.parse_key_val_dict(key_vals_text) 3299451b44SJordan Rupprecht 3399451b44SJordan Rupprecht result = dict(); 3499451b44SJordan Rupprecht result["pc_register"] = hw_info["pc_register"] 3599451b44SJordan Rupprecht result["little_endian"] = hw_info["little_endian"] 3699451b44SJordan Rupprecht for key_field in field_names: 3799451b44SJordan Rupprecht result[key_field] = kv_dict.get(key_field) 3899451b44SJordan Rupprecht 3999451b44SJordan Rupprecht return result 4099451b44SJordan Rupprecht 415a4fe166SPavel Labath def gather_stop_reply_threads(self, thread_count): 4299451b44SJordan Rupprecht # Pull out threads from stop response. 4399451b44SJordan Rupprecht stop_reply_threads_text = self.gather_stop_reply_fields( 445a4fe166SPavel Labath thread_count, ["threads"])["threads"] 4599451b44SJordan Rupprecht if stop_reply_threads_text: 4699451b44SJordan Rupprecht return [int(thread_id, 16) 4799451b44SJordan Rupprecht for thread_id in stop_reply_threads_text.split(",")] 4899451b44SJordan Rupprecht else: 4999451b44SJordan Rupprecht return [] 5099451b44SJordan Rupprecht 515a4fe166SPavel Labath def gather_stop_reply_pcs(self, thread_count): 525a4fe166SPavel Labath results = self.gather_stop_reply_fields(thread_count, ["threads", "thread-pcs"]) 5399451b44SJordan Rupprecht if not results: 5499451b44SJordan Rupprecht return [] 5599451b44SJordan Rupprecht 5699451b44SJordan Rupprecht threads_text = results["threads"] 5799451b44SJordan Rupprecht pcs_text = results["thread-pcs"] 5899451b44SJordan Rupprecht thread_ids = threads_text.split(",") 5999451b44SJordan Rupprecht pcs = pcs_text.split(",") 60b3a0c4d7SRaphael Isemann self.assertEquals(len(thread_ids), len(pcs)) 6199451b44SJordan Rupprecht 6299451b44SJordan Rupprecht thread_pcs = dict() 6399451b44SJordan Rupprecht for i in range(0, len(pcs)): 6499451b44SJordan Rupprecht thread_pcs[int(thread_ids[i], 16)] = pcs[i] 6599451b44SJordan Rupprecht 6699451b44SJordan Rupprecht result = dict() 6799451b44SJordan Rupprecht result["thread_pcs"] = thread_pcs 6899451b44SJordan Rupprecht result["pc_register"] = results["pc_register"] 6999451b44SJordan Rupprecht result["little_endian"] = results["little_endian"] 7099451b44SJordan Rupprecht return result 7199451b44SJordan Rupprecht 7299451b44SJordan Rupprecht def switch_endian(self, egg): 7399451b44SJordan Rupprecht return "".join(reversed(re.findall("..", egg))) 7499451b44SJordan Rupprecht 7599451b44SJordan Rupprecht def parse_hw_info(self, context): 7699451b44SJordan Rupprecht self.assertIsNotNone(context) 7799451b44SJordan Rupprecht process_info = self.parse_process_info_response(context) 7899451b44SJordan Rupprecht endian = process_info.get("endian") 7999451b44SJordan Rupprecht reg_info = self.parse_register_info_packets(context) 8099451b44SJordan Rupprecht (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_info) 8199451b44SJordan Rupprecht 8299451b44SJordan Rupprecht hw_info = dict() 8399451b44SJordan Rupprecht hw_info["pc_register"] = pc_lldb_reg_index 8499451b44SJordan Rupprecht hw_info["little_endian"] = (endian == "little") 8599451b44SJordan Rupprecht return hw_info 8699451b44SJordan Rupprecht 8799451b44SJordan Rupprecht def gather_threads_info_pcs(self, pc_register, little_endian): 8899451b44SJordan Rupprecht self.reset_test_sequence() 8999451b44SJordan Rupprecht self.test_sequence.add_log_lines( 9099451b44SJordan Rupprecht [ 9199451b44SJordan Rupprecht "read packet: $jThreadsInfo#c1", 9299451b44SJordan Rupprecht { 9399451b44SJordan Rupprecht "direction": "send", 9499451b44SJordan Rupprecht "regex": r"^\$(.*)#[0-9a-fA-F]{2}$", 9599451b44SJordan Rupprecht "capture": { 9699451b44SJordan Rupprecht 1: "threads_info"}}, 9799451b44SJordan Rupprecht ], 9899451b44SJordan Rupprecht True) 9999451b44SJordan Rupprecht 10099451b44SJordan Rupprecht context = self.expect_gdbremote_sequence() 10199451b44SJordan Rupprecht self.assertIsNotNone(context) 10299451b44SJordan Rupprecht threads_info = context.get("threads_info") 10399451b44SJordan Rupprecht register = str(pc_register) 10499451b44SJordan Rupprecht # The jThreadsInfo response is not valid JSON data, so we have to 10599451b44SJordan Rupprecht # clean it up first. 106e609fe68SMuhammad Omair Javaid jthreads_info = json.loads(re.sub(r"}]", "}", threads_info)) 10799451b44SJordan Rupprecht thread_pcs = dict() 10899451b44SJordan Rupprecht for thread_info in jthreads_info: 10999451b44SJordan Rupprecht tid = thread_info["tid"] 11099451b44SJordan Rupprecht pc = thread_info["registers"][register] 11199451b44SJordan Rupprecht thread_pcs[tid] = self.switch_endian(pc) if little_endian else pc 11299451b44SJordan Rupprecht 11399451b44SJordan Rupprecht return thread_pcs 11499451b44SJordan Rupprecht 1150a8a2453SPavel Labath 1160a8a2453SPavel Labath def test_QListThreadsInStopReply_supported(self): 1170a8a2453SPavel Labath self.build() 1180a8a2453SPavel Labath self.set_inferior_startup_launch() 11999451b44SJordan Rupprecht procs = self.prep_debug_monitor_and_inferior() 12099451b44SJordan Rupprecht self.test_sequence.add_log_lines( 12199451b44SJordan Rupprecht self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True) 12299451b44SJordan Rupprecht 12399451b44SJordan Rupprecht context = self.expect_gdbremote_sequence() 12499451b44SJordan Rupprecht self.assertIsNotNone(context) 12599451b44SJordan Rupprecht 12699451b44SJordan Rupprecht @skipIfNetBSD 127*1046b726SPavel Labath @expectedFailureAll(oslist=["windows"]) # Extra threads present 1280a8a2453SPavel Labath def test_stop_reply_reports_multiple_threads(self): 12999451b44SJordan Rupprecht self.build() 13099451b44SJordan Rupprecht self.set_inferior_startup_launch() 1310a8a2453SPavel Labath # Gather threads from stop notification when QThreadsInStopReply is 13299451b44SJordan Rupprecht # enabled. 1335a4fe166SPavel Labath self.test_sequence.add_log_lines( 1345a4fe166SPavel Labath self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True) 1355a4fe166SPavel Labath stop_reply_threads = self.gather_stop_reply_threads(5) 1360a8a2453SPavel Labath self.assertEqual(len(stop_reply_threads), 5) 13799451b44SJordan Rupprecht 1380a8a2453SPavel Labath @skipIfNetBSD 1390a8a2453SPavel Labath def test_no_QListThreadsInStopReply_supplies_no_threads(self): 14099451b44SJordan Rupprecht self.build() 14199451b44SJordan Rupprecht self.set_inferior_startup_launch() 1420a8a2453SPavel Labath # Gather threads from stop notification when QThreadsInStopReply is not 1430a8a2453SPavel Labath # enabled. 1445a4fe166SPavel Labath stop_reply_threads = self.gather_stop_reply_threads(5) 1450a8a2453SPavel Labath self.assertEqual(len(stop_reply_threads), 0) 14699451b44SJordan Rupprecht 14799451b44SJordan Rupprecht @skipIfNetBSD 1480a8a2453SPavel Labath def test_stop_reply_reports_correct_threads(self): 14999451b44SJordan Rupprecht self.build() 15099451b44SJordan Rupprecht self.set_inferior_startup_launch() 15199451b44SJordan Rupprecht # Gather threads from stop notification when QThreadsInStopReply is 15299451b44SJordan Rupprecht # enabled. 1530a8a2453SPavel Labath thread_count = 5 1545a4fe166SPavel Labath self.test_sequence.add_log_lines( 1555a4fe166SPavel Labath self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True) 1565a4fe166SPavel Labath stop_reply_threads = self.gather_stop_reply_threads(thread_count) 15799451b44SJordan Rupprecht 15899451b44SJordan Rupprecht # Gather threads from q{f,s}ThreadInfo. 15999451b44SJordan Rupprecht self.reset_test_sequence() 16099451b44SJordan Rupprecht self.add_threadinfo_collection_packets() 16199451b44SJordan Rupprecht 16299451b44SJordan Rupprecht context = self.expect_gdbremote_sequence() 16399451b44SJordan Rupprecht self.assertIsNotNone(context) 16499451b44SJordan Rupprecht 16599451b44SJordan Rupprecht threads = self.parse_threadinfo_packets(context) 16699451b44SJordan Rupprecht self.assertIsNotNone(threads) 167*1046b726SPavel Labath self.assertGreaterEqual(len(threads), thread_count) 16899451b44SJordan Rupprecht 16999451b44SJordan Rupprecht # Ensure each thread in q{f,s}ThreadInfo appears in stop reply threads 17099451b44SJordan Rupprecht for tid in threads: 1710a8a2453SPavel Labath self.assertIn(tid, stop_reply_threads) 17299451b44SJordan Rupprecht 17399451b44SJordan Rupprecht @skipIfNetBSD 1740a8a2453SPavel Labath def test_stop_reply_contains_thread_pcs(self): 17599451b44SJordan Rupprecht self.build() 17699451b44SJordan Rupprecht self.set_inferior_startup_launch() 1770a8a2453SPavel Labath thread_count = 5 1785a4fe166SPavel Labath self.test_sequence.add_log_lines( 1795a4fe166SPavel Labath self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True) 1805a4fe166SPavel Labath results = self.gather_stop_reply_pcs(thread_count) 18199451b44SJordan Rupprecht stop_reply_pcs = results["thread_pcs"] 18299451b44SJordan Rupprecht pc_register = results["pc_register"] 18399451b44SJordan Rupprecht little_endian = results["little_endian"] 184*1046b726SPavel Labath self.assertGreaterEqual(len(stop_reply_pcs), thread_count) 18599451b44SJordan Rupprecht 18699451b44SJordan Rupprecht threads_info_pcs = self.gather_threads_info_pcs(pc_register, 18799451b44SJordan Rupprecht little_endian) 18899451b44SJordan Rupprecht 189*1046b726SPavel Labath self.assertEqual(len(threads_info_pcs), len(stop_reply_pcs)) 19099451b44SJordan Rupprecht for thread_id in stop_reply_pcs: 1910a8a2453SPavel Labath self.assertIn(thread_id, threads_info_pcs) 1920a8a2453SPavel Labath self.assertEqual(int(stop_reply_pcs[thread_id], 16), 1930a8a2453SPavel Labath int(threads_info_pcs[thread_id], 16)) 194