1#!/usr/bin/env python 2 3import argparse 4import datetime 5import re 6import subprocess 7import sys 8import time 9 10parser = argparse.ArgumentParser( 11 description="Run an exhaustive test of the LLDB disassembler for a specific architecture.") 12 13parser.add_argument( 14 '--arch', 15 required=True, 16 action='store', 17 help='The architecture whose disassembler is to be tested') 18parser.add_argument( 19 '--bytes', 20 required=True, 21 action='store', 22 type=int, 23 help='The byte width of instructions for that architecture') 24parser.add_argument( 25 '--random', 26 required=False, 27 action='store_true', 28 help='Enables non-sequential testing') 29parser.add_argument( 30 '--start', 31 required=False, 32 action='store', 33 type=int, 34 help='The first instruction value to test') 35parser.add_argument( 36 '--skip', 37 required=False, 38 action='store', 39 type=int, 40 help='The interval between instructions to test') 41parser.add_argument( 42 '--log', 43 required=False, 44 action='store', 45 help='A log file to write the most recent instruction being tested') 46parser.add_argument( 47 '--time', 48 required=False, 49 action='store_true', 50 help='Every 100,000 instructions, print an ETA to standard out') 51parser.add_argument( 52 '--lldb', 53 required=False, 54 action='store', 55 help='The path to LLDB.framework, if LLDB should be overridden') 56 57arguments = sys.argv[1:] 58 59arg_ns = parser.parse_args(arguments) 60 61 62def AddLLDBToSysPathOnMacOSX(): 63 def GetLLDBFrameworkPath(): 64 lldb_path = subprocess.check_output(["xcrun", "-find", "lldb"]) 65 re_result = re.match("(.*)/Developer/usr/bin/lldb", lldb_path) 66 if re_result is None: 67 return None 68 xcode_contents_path = re_result.group(1) 69 return xcode_contents_path + "/SharedFrameworks/LLDB.framework" 70 71 lldb_framework_path = GetLLDBFrameworkPath() 72 73 if lldb_framework_path is None: 74 print("Couldn't find LLDB.framework") 75 sys.exit(-1) 76 77 sys.path.append(lldb_framework_path + "/Resources/Python") 78 79if arg_ns.lldb is None: 80 AddLLDBToSysPathOnMacOSX() 81else: 82 sys.path.append(arg_ns.lldb + "/Resources/Python") 83 84import lldb 85 86debugger = lldb.SBDebugger.Create() 87 88if debugger.IsValid() == False: 89 print("Couldn't create an SBDebugger") 90 sys.exit(-1) 91 92target = debugger.CreateTargetWithFileAndArch(None, arg_ns.arch) 93 94if target.IsValid() == False: 95 print("Couldn't create an SBTarget for architecture " + arg_ns.arch) 96 sys.exit(-1) 97 98 99def ResetLogFile(log_file): 100 if log_file != sys.stdout: 101 log_file.seek(0) 102 103 104def PrintByteArray(log_file, byte_array): 105 for byte in byte_array: 106 print(hex(byte) + " ", end=' ', file=log_file) 107 print(file=log_file) 108 109 110class SequentialInstructionProvider: 111 112 def __init__(self, byte_width, log_file, start=0, skip=1): 113 self.m_byte_width = byte_width 114 self.m_log_file = log_file 115 self.m_start = start 116 self.m_skip = skip 117 self.m_value = start 118 self.m_last = (1 << (byte_width * 8)) - 1 119 120 def PrintCurrentState(self, ret): 121 ResetLogFile(self.m_log_file) 122 print(self.m_value, file=self.m_log_file) 123 PrintByteArray(self.m_log_file, ret) 124 125 def GetNextInstruction(self): 126 if self.m_value > self.m_last: 127 return None 128 ret = bytearray(self.m_byte_width) 129 for i in range(self.m_byte_width): 130 ret[self.m_byte_width - (i + 1)] = (self.m_value >> (i * 8)) & 255 131 self.PrintCurrentState(ret) 132 self.m_value += self.m_skip 133 return ret 134 135 def GetNumInstructions(self): 136 return (self.m_last - self.m_start) / self.m_skip 137 138 def __iter__(self): 139 return self 140 141 def next(self): 142 ret = self.GetNextInstruction() 143 if ret is None: 144 raise StopIteration 145 return ret 146 147 148class RandomInstructionProvider: 149 150 def __init__(self, byte_width, log_file): 151 self.m_byte_width = byte_width 152 self.m_log_file = log_file 153 self.m_random_file = open("/dev/random", 'r') 154 155 def PrintCurrentState(self, ret): 156 ResetLogFile(self.m_log_file) 157 PrintByteArray(self.m_log_file, ret) 158 159 def GetNextInstruction(self): 160 ret = bytearray(self.m_byte_width) 161 for i in range(self.m_byte_width): 162 ret[i] = self.m_random_file.read(1) 163 self.PrintCurrentState(ret) 164 return ret 165 166 def __iter__(self): 167 return self 168 169 def next(self): 170 ret = self.GetNextInstruction() 171 if ret is None: 172 raise StopIteration 173 return ret 174 175log_file = None 176 177 178def GetProviderWithArguments(args): 179 global log_file 180 if args.log is not None: 181 log_file = open(args.log, 'w') 182 else: 183 log_file = sys.stdout 184 instruction_provider = None 185 if args.random: 186 instruction_provider = RandomInstructionProvider(args.bytes, log_file) 187 else: 188 start = 0 189 skip = 1 190 if args.start is not None: 191 start = args.start 192 if args.skip is not None: 193 skip = args.skip 194 instruction_provider = SequentialInstructionProvider( 195 args.bytes, log_file, start, skip) 196 return instruction_provider 197 198instruction_provider = GetProviderWithArguments(arg_ns) 199 200fake_address = lldb.SBAddress() 201 202actually_time = arg_ns.time and not arg_ns.random 203 204if actually_time: 205 num_instructions_logged = 0 206 total_num_instructions = instruction_provider.GetNumInstructions() 207 start_time = time.time() 208 209for inst_bytes in instruction_provider: 210 if actually_time: 211 if (num_instructions_logged != 0) and ( 212 num_instructions_logged % 100000 == 0): 213 curr_time = time.time() 214 elapsed_time = curr_time - start_time 215 remaining_time = float( 216 total_num_instructions - num_instructions_logged) * ( 217 float(elapsed_time) / float(num_instructions_logged)) 218 print(str(datetime.timedelta(seconds=remaining_time))) 219 num_instructions_logged = num_instructions_logged + 1 220 inst_list = target.GetInstructions(fake_address, inst_bytes) 221 if not inst_list.IsValid(): 222 print("Invalid instruction list", file=log_file) 223 continue 224 inst = inst_list.GetInstructionAtIndex(0) 225 if not inst.IsValid(): 226 print("Invalid instruction", file=log_file) 227 continue 228 instr_output_stream = lldb.SBStream() 229 inst.GetDescription(instr_output_stream) 230 print(instr_output_stream.GetData(), file=log_file) 231