1# Copyright (C) 2021-2023 Free Software Foundation, Inc. 2 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 3 of the License, or 6# (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16import xml.etree.ElementTree as ET 17import gdb 18 19# Make use of gdb.RemoteTargetConnection.send_packet to fetch the 20# thread list from the remote target. 21# 22# Sending existing serial protocol packets like this is not a good 23# idea, there should be better ways to get this information using an 24# official API, this is just being used as a test case. 25# 26# Really, the send_packet API would be used to send target 27# specific packets to the target, but these are, by definition, target 28# specific, so hard to test in a general testsuite. 29def get_thread_list_str(): 30 start_pos = 0 31 thread_desc = "" 32 conn = gdb.selected_inferior().connection 33 if not isinstance(conn, gdb.RemoteTargetConnection): 34 raise gdb.GdbError("connection is the wrong type") 35 while True: 36 str = conn.send_packet("qXfer:threads:read::%d,200" % start_pos).decode("ascii") 37 start_pos += 200 38 c = str[0] 39 str = str[1:] 40 thread_desc += str 41 if c == "l": 42 break 43 return thread_desc 44 45 46# Use gdb.RemoteTargetConnection.send_packet to manually fetch the 47# thread list, then extract the thread list using the gdb.Inferior and 48# gdb.InferiorThread API. Compare the two results to ensure we 49# managed to successfully read the thread list from the remote. 50def run_send_packet_test(): 51 # Find the IDs of all current threads. 52 all_threads = {} 53 for inf in gdb.inferiors(): 54 for thr in inf.threads(): 55 id = "p%x.%x" % (thr.ptid[0], thr.ptid[1]) 56 all_threads[id] = False 57 58 # Now fetch the thread list from the remote, and parse the XML. 59 str = get_thread_list_str() 60 threads_xml = ET.fromstring(str) 61 62 # Look over all threads in the XML list and check we expected to 63 # find them, mark the ones we do find. 64 for thr in threads_xml: 65 id = thr.get("id") 66 if not id in all_threads: 67 raise "found unexpected thread in remote thread list" 68 else: 69 all_threads[id] = True 70 71 # Check that all the threads were found in the XML list. 72 for id in all_threads: 73 if not all_threads[id]: 74 raise "thread missingt from remote thread list" 75 76 # Test complete. 77 print("Send packet test passed") 78 79 80# Convert a bytes object to a string. This follows the same rules as 81# the 'maint packet' command so that the output from the two sources 82# can be compared. 83def bytes_to_string(byte_array): 84 res = "" 85 for b in byte_array: 86 b = int(b) 87 if b >= 32 and b <= 126: 88 res = res + ("%c" % b) 89 else: 90 res = res + ("\\x%02x" % b) 91 return res 92 93 94# A very simple test for sending the packet that reads the auxv data. 95# We convert the result to a string and expect to find some 96# hex-encoded bytes in the output. This test will only work on 97# targets that actually supply auxv data. 98def run_auxv_send_packet_test(expected_result): 99 inf = gdb.selected_inferior() 100 conn = inf.connection 101 assert isinstance(conn, gdb.RemoteTargetConnection) 102 res = conn.send_packet("qXfer:auxv:read::0,1000") 103 assert isinstance(res, bytes) 104 string = bytes_to_string(res) 105 assert string.count("\\x") > 0 106 assert string == expected_result 107 print("auxv send packet test passed") 108 109 110# Check that the value of 'global_var' is EXPECTED_VAL. 111def check_global_var(expected_val): 112 val = int(gdb.parse_and_eval("global_var")) 113 val = val & 0xFFFFFFFF 114 if val != expected_val: 115 raise gdb.GdbError("global_var is 0x%x, expected 0x%x" % (val, expected_val)) 116 117 118# Return a bytes object representing an 'X' packet header with 119# address ADDR. 120def xpacket_header(addr): 121 return ("X%x,4:" % addr).encode("ascii") 122 123 124# Set the 'X' packet to the remote target to set a global variable. 125# Checks that we can send byte values. 126def run_set_global_var_test(): 127 inf = gdb.selected_inferior() 128 conn = inf.connection 129 assert isinstance(conn, gdb.RemoteTargetConnection) 130 addr = gdb.parse_and_eval("&global_var") 131 res = conn.send_packet("X%x,4:\x01\x01\x01\x01" % addr) 132 assert isinstance(res, bytes) 133 check_global_var(0x01010101) 134 res = conn.send_packet(xpacket_header(addr) + b"\x02\x02\x02\x02") 135 assert isinstance(res, bytes) 136 check_global_var(0x02020202) 137 138 # This first attempt will not work as we're passing a Unicode string 139 # containing non-ascii characters. 140 saw_error = False 141 try: 142 res = conn.send_packet("X%x,4:\xff\xff\xff\xff" % addr) 143 except UnicodeError: 144 saw_error = True 145 except: 146 assert False 147 148 assert saw_error 149 check_global_var(0x02020202) 150 # Now we pass a bytes object, which will work. 151 res = conn.send_packet(xpacket_header(addr) + b"\xff\xff\xff\xff") 152 check_global_var(0xFFFFFFFF) 153 154 print("set global_var test passed") 155 156 157# Just to indicate the file was sourced correctly. 158print("Sourcing complete.") 159