1*be691f3bSpatrick#!/usr/bin/env python 2061da546Spatrick 3061da546Spatrickimport argparse 4061da546Spatrickimport datetime 5061da546Spatrickimport re 6061da546Spatrickimport subprocess 7061da546Spatrickimport sys 8061da546Spatrickimport time 9061da546Spatrick 10061da546Spatrickparser = argparse.ArgumentParser( 11061da546Spatrick description="Run an exhaustive test of the LLDB disassembler for a specific architecture.") 12061da546Spatrick 13061da546Spatrickparser.add_argument( 14061da546Spatrick '--arch', 15061da546Spatrick required=True, 16061da546Spatrick action='store', 17061da546Spatrick help='The architecture whose disassembler is to be tested') 18061da546Spatrickparser.add_argument( 19061da546Spatrick '--bytes', 20061da546Spatrick required=True, 21061da546Spatrick action='store', 22061da546Spatrick type=int, 23061da546Spatrick help='The byte width of instructions for that architecture') 24061da546Spatrickparser.add_argument( 25061da546Spatrick '--random', 26061da546Spatrick required=False, 27061da546Spatrick action='store_true', 28061da546Spatrick help='Enables non-sequential testing') 29061da546Spatrickparser.add_argument( 30061da546Spatrick '--start', 31061da546Spatrick required=False, 32061da546Spatrick action='store', 33061da546Spatrick type=int, 34061da546Spatrick help='The first instruction value to test') 35061da546Spatrickparser.add_argument( 36061da546Spatrick '--skip', 37061da546Spatrick required=False, 38061da546Spatrick action='store', 39061da546Spatrick type=int, 40061da546Spatrick help='The interval between instructions to test') 41061da546Spatrickparser.add_argument( 42061da546Spatrick '--log', 43061da546Spatrick required=False, 44061da546Spatrick action='store', 45061da546Spatrick help='A log file to write the most recent instruction being tested') 46061da546Spatrickparser.add_argument( 47061da546Spatrick '--time', 48061da546Spatrick required=False, 49061da546Spatrick action='store_true', 50061da546Spatrick help='Every 100,000 instructions, print an ETA to standard out') 51061da546Spatrickparser.add_argument( 52061da546Spatrick '--lldb', 53061da546Spatrick required=False, 54061da546Spatrick action='store', 55061da546Spatrick help='The path to LLDB.framework, if LLDB should be overridden') 56061da546Spatrick 57061da546Spatrickarguments = sys.argv[1:] 58061da546Spatrick 59061da546Spatrickarg_ns = parser.parse_args(arguments) 60061da546Spatrick 61061da546Spatrick 62061da546Spatrickdef AddLLDBToSysPathOnMacOSX(): 63061da546Spatrick def GetLLDBFrameworkPath(): 64061da546Spatrick lldb_path = subprocess.check_output(["xcrun", "-find", "lldb"]) 65061da546Spatrick re_result = re.match("(.*)/Developer/usr/bin/lldb", lldb_path) 66061da546Spatrick if re_result is None: 67061da546Spatrick return None 68061da546Spatrick xcode_contents_path = re_result.group(1) 69061da546Spatrick return xcode_contents_path + "/SharedFrameworks/LLDB.framework" 70061da546Spatrick 71061da546Spatrick lldb_framework_path = GetLLDBFrameworkPath() 72061da546Spatrick 73061da546Spatrick if lldb_framework_path is None: 74061da546Spatrick print("Couldn't find LLDB.framework") 75061da546Spatrick sys.exit(-1) 76061da546Spatrick 77061da546Spatrick sys.path.append(lldb_framework_path + "/Resources/Python") 78061da546Spatrick 79061da546Spatrickif arg_ns.lldb is None: 80061da546Spatrick AddLLDBToSysPathOnMacOSX() 81061da546Spatrickelse: 82061da546Spatrick sys.path.append(arg_ns.lldb + "/Resources/Python") 83061da546Spatrick 84061da546Spatrickimport lldb 85061da546Spatrick 86061da546Spatrickdebugger = lldb.SBDebugger.Create() 87061da546Spatrick 88061da546Spatrickif debugger.IsValid() == False: 89061da546Spatrick print("Couldn't create an SBDebugger") 90061da546Spatrick sys.exit(-1) 91061da546Spatrick 92061da546Spatricktarget = debugger.CreateTargetWithFileAndArch(None, arg_ns.arch) 93061da546Spatrick 94061da546Spatrickif target.IsValid() == False: 95061da546Spatrick print("Couldn't create an SBTarget for architecture " + arg_ns.arch) 96061da546Spatrick sys.exit(-1) 97061da546Spatrick 98061da546Spatrick 99061da546Spatrickdef ResetLogFile(log_file): 100061da546Spatrick if log_file != sys.stdout: 101061da546Spatrick log_file.seek(0) 102061da546Spatrick 103061da546Spatrick 104061da546Spatrickdef PrintByteArray(log_file, byte_array): 105061da546Spatrick for byte in byte_array: 106061da546Spatrick print(hex(byte) + " ", end=' ', file=log_file) 107061da546Spatrick print(file=log_file) 108061da546Spatrick 109061da546Spatrick 110061da546Spatrickclass SequentialInstructionProvider: 111061da546Spatrick 112061da546Spatrick def __init__(self, byte_width, log_file, start=0, skip=1): 113061da546Spatrick self.m_byte_width = byte_width 114061da546Spatrick self.m_log_file = log_file 115061da546Spatrick self.m_start = start 116061da546Spatrick self.m_skip = skip 117061da546Spatrick self.m_value = start 118061da546Spatrick self.m_last = (1 << (byte_width * 8)) - 1 119061da546Spatrick 120061da546Spatrick def PrintCurrentState(self, ret): 121061da546Spatrick ResetLogFile(self.m_log_file) 122061da546Spatrick print(self.m_value, file=self.m_log_file) 123061da546Spatrick PrintByteArray(self.m_log_file, ret) 124061da546Spatrick 125061da546Spatrick def GetNextInstruction(self): 126061da546Spatrick if self.m_value > self.m_last: 127061da546Spatrick return None 128061da546Spatrick ret = bytearray(self.m_byte_width) 129061da546Spatrick for i in range(self.m_byte_width): 130061da546Spatrick ret[self.m_byte_width - (i + 1)] = (self.m_value >> (i * 8)) & 255 131061da546Spatrick self.PrintCurrentState(ret) 132061da546Spatrick self.m_value += self.m_skip 133061da546Spatrick return ret 134061da546Spatrick 135061da546Spatrick def GetNumInstructions(self): 136061da546Spatrick return (self.m_last - self.m_start) / self.m_skip 137061da546Spatrick 138061da546Spatrick def __iter__(self): 139061da546Spatrick return self 140061da546Spatrick 141061da546Spatrick def next(self): 142061da546Spatrick ret = self.GetNextInstruction() 143061da546Spatrick if ret is None: 144061da546Spatrick raise StopIteration 145061da546Spatrick return ret 146061da546Spatrick 147061da546Spatrick 148061da546Spatrickclass RandomInstructionProvider: 149061da546Spatrick 150061da546Spatrick def __init__(self, byte_width, log_file): 151061da546Spatrick self.m_byte_width = byte_width 152061da546Spatrick self.m_log_file = log_file 153061da546Spatrick self.m_random_file = open("/dev/random", 'r') 154061da546Spatrick 155061da546Spatrick def PrintCurrentState(self, ret): 156061da546Spatrick ResetLogFile(self.m_log_file) 157061da546Spatrick PrintByteArray(self.m_log_file, ret) 158061da546Spatrick 159061da546Spatrick def GetNextInstruction(self): 160061da546Spatrick ret = bytearray(self.m_byte_width) 161061da546Spatrick for i in range(self.m_byte_width): 162061da546Spatrick ret[i] = self.m_random_file.read(1) 163061da546Spatrick self.PrintCurrentState(ret) 164061da546Spatrick return ret 165061da546Spatrick 166061da546Spatrick def __iter__(self): 167061da546Spatrick return self 168061da546Spatrick 169061da546Spatrick def next(self): 170061da546Spatrick ret = self.GetNextInstruction() 171061da546Spatrick if ret is None: 172061da546Spatrick raise StopIteration 173061da546Spatrick return ret 174061da546Spatrick 175061da546Spatricklog_file = None 176061da546Spatrick 177061da546Spatrick 178061da546Spatrickdef GetProviderWithArguments(args): 179061da546Spatrick global log_file 180061da546Spatrick if args.log is not None: 181061da546Spatrick log_file = open(args.log, 'w') 182061da546Spatrick else: 183061da546Spatrick log_file = sys.stdout 184061da546Spatrick instruction_provider = None 185061da546Spatrick if args.random: 186061da546Spatrick instruction_provider = RandomInstructionProvider(args.bytes, log_file) 187061da546Spatrick else: 188061da546Spatrick start = 0 189061da546Spatrick skip = 1 190061da546Spatrick if args.start is not None: 191061da546Spatrick start = args.start 192061da546Spatrick if args.skip is not None: 193061da546Spatrick skip = args.skip 194061da546Spatrick instruction_provider = SequentialInstructionProvider( 195061da546Spatrick args.bytes, log_file, start, skip) 196061da546Spatrick return instruction_provider 197061da546Spatrick 198061da546Spatrickinstruction_provider = GetProviderWithArguments(arg_ns) 199061da546Spatrick 200061da546Spatrickfake_address = lldb.SBAddress() 201061da546Spatrick 202061da546Spatrickactually_time = arg_ns.time and not arg_ns.random 203061da546Spatrick 204061da546Spatrickif actually_time: 205061da546Spatrick num_instructions_logged = 0 206061da546Spatrick total_num_instructions = instruction_provider.GetNumInstructions() 207061da546Spatrick start_time = time.time() 208061da546Spatrick 209061da546Spatrickfor inst_bytes in instruction_provider: 210061da546Spatrick if actually_time: 211061da546Spatrick if (num_instructions_logged != 0) and ( 212061da546Spatrick num_instructions_logged % 100000 == 0): 213061da546Spatrick curr_time = time.time() 214061da546Spatrick elapsed_time = curr_time - start_time 215061da546Spatrick remaining_time = float( 216061da546Spatrick total_num_instructions - num_instructions_logged) * ( 217061da546Spatrick float(elapsed_time) / float(num_instructions_logged)) 218061da546Spatrick print(str(datetime.timedelta(seconds=remaining_time))) 219061da546Spatrick num_instructions_logged = num_instructions_logged + 1 220061da546Spatrick inst_list = target.GetInstructions(fake_address, inst_bytes) 221061da546Spatrick if not inst_list.IsValid(): 222061da546Spatrick print("Invalid instruction list", file=log_file) 223061da546Spatrick continue 224061da546Spatrick inst = inst_list.GetInstructionAtIndex(0) 225061da546Spatrick if not inst.IsValid(): 226061da546Spatrick print("Invalid instruction", file=log_file) 227061da546Spatrick continue 228061da546Spatrick instr_output_stream = lldb.SBStream() 229061da546Spatrick inst.GetDescription(instr_output_stream) 230061da546Spatrick print(instr_output_stream.GetData(), file=log_file) 231