1*be691f3bSpatrick#!/usr/bin/env python 2061da546Spatrick 3061da546Spatrick#---------------------------------------------------------------------- 4061da546Spatrick# Be sure to add the python path that points to the LLDB shared library. 5061da546Spatrick# On MacOSX csh, tcsh: 6061da546Spatrick# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 7061da546Spatrick# On MacOSX sh, bash: 8061da546Spatrick# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 9061da546Spatrick#---------------------------------------------------------------------- 10061da546Spatrick 11061da546Spatrickimport optparse 12061da546Spatrickimport os 13061da546Spatrickimport platform 14061da546Spatrickimport re 15061da546Spatrickimport resource 16061da546Spatrickimport sys 17061da546Spatrickimport subprocess 18061da546Spatrickimport time 19061da546Spatrickimport types 20061da546Spatrick 21061da546Spatrick#---------------------------------------------------------------------- 22061da546Spatrick# Code that auto imports LLDB 23061da546Spatrick#---------------------------------------------------------------------- 24061da546Spatricktry: 25061da546Spatrick # Just try for LLDB in case PYTHONPATH is already correctly setup 26061da546Spatrick import lldb 27061da546Spatrickexcept ImportError: 28061da546Spatrick lldb_python_dirs = list() 29061da546Spatrick # lldb is not in the PYTHONPATH, try some defaults for the current platform 30061da546Spatrick platform_system = platform.system() 31061da546Spatrick if platform_system == 'Darwin': 32061da546Spatrick # On Darwin, try the currently selected Xcode directory 33061da546Spatrick xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) 34061da546Spatrick if xcode_dir: 35061da546Spatrick lldb_python_dirs.append( 36061da546Spatrick os.path.realpath( 37061da546Spatrick xcode_dir + 38061da546Spatrick '/../SharedFrameworks/LLDB.framework/Resources/Python')) 39061da546Spatrick lldb_python_dirs.append( 40061da546Spatrick xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 41061da546Spatrick lldb_python_dirs.append( 42061da546Spatrick '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 43061da546Spatrick success = False 44061da546Spatrick for lldb_python_dir in lldb_python_dirs: 45061da546Spatrick if os.path.exists(lldb_python_dir): 46061da546Spatrick if not (sys.path.__contains__(lldb_python_dir)): 47061da546Spatrick sys.path.append(lldb_python_dir) 48061da546Spatrick try: 49061da546Spatrick import lldb 50061da546Spatrick except ImportError: 51061da546Spatrick pass 52061da546Spatrick else: 53061da546Spatrick print('imported lldb from: "%s"' % (lldb_python_dir)) 54061da546Spatrick success = True 55061da546Spatrick break 56061da546Spatrick if not success: 57061da546Spatrick print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") 58061da546Spatrick sys.exit(1) 59061da546Spatrick 60061da546Spatrick 61061da546Spatrickclass Timer: 62061da546Spatrick 63061da546Spatrick def __enter__(self): 64061da546Spatrick self.start = time.clock() 65061da546Spatrick return self 66061da546Spatrick 67061da546Spatrick def __exit__(self, *args): 68061da546Spatrick self.end = time.clock() 69061da546Spatrick self.interval = self.end - self.start 70061da546Spatrick 71061da546Spatrick 72061da546Spatrickclass Action(object): 73061da546Spatrick """Class that encapsulates actions to take when a thread stops for a reason.""" 74061da546Spatrick 75061da546Spatrick def __init__(self, callback=None, callback_owner=None): 76061da546Spatrick self.callback = callback 77061da546Spatrick self.callback_owner = callback_owner 78061da546Spatrick 79061da546Spatrick def ThreadStopped(self, thread): 80061da546Spatrick assert False, "performance.Action.ThreadStopped(self, thread) must be overridden in a subclass" 81061da546Spatrick 82061da546Spatrick 83061da546Spatrickclass PlanCompleteAction (Action): 84061da546Spatrick 85061da546Spatrick def __init__(self, callback=None, callback_owner=None): 86061da546Spatrick Action.__init__(self, callback, callback_owner) 87061da546Spatrick 88061da546Spatrick def ThreadStopped(self, thread): 89061da546Spatrick if thread.GetStopReason() == lldb.eStopReasonPlanComplete: 90061da546Spatrick if self.callback: 91061da546Spatrick if self.callback_owner: 92061da546Spatrick self.callback(self.callback_owner, thread) 93061da546Spatrick else: 94061da546Spatrick self.callback(thread) 95061da546Spatrick return True 96061da546Spatrick return False 97061da546Spatrick 98061da546Spatrick 99061da546Spatrickclass BreakpointAction (Action): 100061da546Spatrick 101061da546Spatrick def __init__( 102061da546Spatrick self, 103061da546Spatrick callback=None, 104061da546Spatrick callback_owner=None, 105061da546Spatrick name=None, 106061da546Spatrick module=None, 107061da546Spatrick file=None, 108061da546Spatrick line=None, 109061da546Spatrick breakpoint=None): 110061da546Spatrick Action.__init__(self, callback, callback_owner) 111061da546Spatrick self.modules = lldb.SBFileSpecList() 112061da546Spatrick self.files = lldb.SBFileSpecList() 113061da546Spatrick self.breakpoints = list() 114061da546Spatrick # "module" can be a list or a string 115061da546Spatrick if breakpoint: 116061da546Spatrick self.breakpoints.append(breakpoint) 117061da546Spatrick else: 118061da546Spatrick if module: 119061da546Spatrick if isinstance(module, types.ListType): 120061da546Spatrick for module_path in module: 121061da546Spatrick self.modules.Append( 122061da546Spatrick lldb.SBFileSpec(module_path, False)) 123061da546Spatrick elif isinstance(module, types.StringTypes): 124061da546Spatrick self.modules.Append(lldb.SBFileSpec(module, False)) 125061da546Spatrick if name: 126061da546Spatrick # "file" can be a list or a string 127061da546Spatrick if file: 128061da546Spatrick if isinstance(file, types.ListType): 129061da546Spatrick self.files = lldb.SBFileSpecList() 130061da546Spatrick for f in file: 131061da546Spatrick self.files.Append(lldb.SBFileSpec(f, False)) 132061da546Spatrick elif isinstance(file, types.StringTypes): 133061da546Spatrick self.files.Append(lldb.SBFileSpec(file, False)) 134061da546Spatrick self.breakpoints.append( 135061da546Spatrick self.target.BreakpointCreateByName( 136061da546Spatrick name, self.modules, self.files)) 137061da546Spatrick elif file and line: 138061da546Spatrick self.breakpoints.append( 139061da546Spatrick self.target.BreakpointCreateByLocation( 140061da546Spatrick file, line)) 141061da546Spatrick 142061da546Spatrick def ThreadStopped(self, thread): 143061da546Spatrick if thread.GetStopReason() == lldb.eStopReasonBreakpoint: 144061da546Spatrick for bp in self.breakpoints: 145061da546Spatrick if bp.GetID() == thread.GetStopReasonDataAtIndex(0): 146061da546Spatrick if self.callback: 147061da546Spatrick if self.callback_owner: 148061da546Spatrick self.callback(self.callback_owner, thread) 149061da546Spatrick else: 150061da546Spatrick self.callback(thread) 151061da546Spatrick return True 152061da546Spatrick return False 153061da546Spatrick 154061da546Spatrick 155061da546Spatrickclass TestCase: 156061da546Spatrick """Class that aids in running performance tests.""" 157061da546Spatrick 158061da546Spatrick def __init__(self): 159061da546Spatrick self.verbose = False 160061da546Spatrick self.debugger = lldb.SBDebugger.Create() 161061da546Spatrick self.target = None 162061da546Spatrick self.process = None 163061da546Spatrick self.thread = None 164061da546Spatrick self.launch_info = None 165061da546Spatrick self.done = False 166061da546Spatrick self.listener = self.debugger.GetListener() 167061da546Spatrick self.user_actions = list() 168061da546Spatrick self.builtin_actions = list() 169061da546Spatrick self.bp_id_to_dict = dict() 170061da546Spatrick 171061da546Spatrick def Setup(self, args): 172061da546Spatrick self.launch_info = lldb.SBLaunchInfo(args) 173061da546Spatrick 174061da546Spatrick def Run(self, args): 175061da546Spatrick assert False, "performance.TestCase.Run(self, args) must be subclassed" 176061da546Spatrick 177061da546Spatrick def Launch(self): 178061da546Spatrick if self.target: 179061da546Spatrick error = lldb.SBError() 180061da546Spatrick self.process = self.target.Launch(self.launch_info, error) 181061da546Spatrick if not error.Success(): 182061da546Spatrick print("error: %s" % error.GetCString()) 183061da546Spatrick if self.process: 184061da546Spatrick self.process.GetBroadcaster().AddListener(self.listener, 185061da546Spatrick lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitInterrupt) 186061da546Spatrick return True 187061da546Spatrick return False 188061da546Spatrick 189061da546Spatrick def WaitForNextProcessEvent(self): 190061da546Spatrick event = None 191061da546Spatrick if self.process: 192061da546Spatrick while event is None: 193061da546Spatrick process_event = lldb.SBEvent() 194061da546Spatrick if self.listener.WaitForEvent(lldb.UINT32_MAX, process_event): 195061da546Spatrick state = lldb.SBProcess.GetStateFromEvent(process_event) 196061da546Spatrick if self.verbose: 197061da546Spatrick print("event = %s" % (lldb.SBDebugger.StateAsCString(state))) 198061da546Spatrick if lldb.SBProcess.GetRestartedFromEvent(process_event): 199061da546Spatrick continue 200061da546Spatrick if state == lldb.eStateInvalid or state == lldb.eStateDetached or state == lldb.eStateCrashed or state == lldb.eStateUnloaded or state == lldb.eStateExited: 201061da546Spatrick event = process_event 202061da546Spatrick self.done = True 203061da546Spatrick elif state == lldb.eStateConnected or state == lldb.eStateAttaching or state == lldb.eStateLaunching or state == lldb.eStateRunning or state == lldb.eStateStepping or state == lldb.eStateSuspended: 204061da546Spatrick continue 205061da546Spatrick elif state == lldb.eStateStopped: 206061da546Spatrick event = process_event 207061da546Spatrick call_test_step = True 208061da546Spatrick fatal = False 209061da546Spatrick selected_thread = False 210061da546Spatrick for thread in self.process: 211061da546Spatrick frame = thread.GetFrameAtIndex(0) 212061da546Spatrick select_thread = False 213061da546Spatrick 214061da546Spatrick stop_reason = thread.GetStopReason() 215061da546Spatrick if self.verbose: 216061da546Spatrick print("tid = %#x pc = %#x " % (thread.GetThreadID(), frame.GetPC()), end=' ') 217061da546Spatrick if stop_reason == lldb.eStopReasonNone: 218061da546Spatrick if self.verbose: 219061da546Spatrick print("none") 220061da546Spatrick elif stop_reason == lldb.eStopReasonTrace: 221061da546Spatrick select_thread = True 222061da546Spatrick if self.verbose: 223061da546Spatrick print("trace") 224061da546Spatrick elif stop_reason == lldb.eStopReasonPlanComplete: 225061da546Spatrick select_thread = True 226061da546Spatrick if self.verbose: 227061da546Spatrick print("plan complete") 228061da546Spatrick elif stop_reason == lldb.eStopReasonThreadExiting: 229061da546Spatrick if self.verbose: 230061da546Spatrick print("thread exiting") 231061da546Spatrick elif stop_reason == lldb.eStopReasonExec: 232061da546Spatrick if self.verbose: 233061da546Spatrick print("exec") 234061da546Spatrick elif stop_reason == lldb.eStopReasonInvalid: 235061da546Spatrick if self.verbose: 236061da546Spatrick print("invalid") 237061da546Spatrick elif stop_reason == lldb.eStopReasonException: 238061da546Spatrick select_thread = True 239061da546Spatrick if self.verbose: 240061da546Spatrick print("exception") 241061da546Spatrick fatal = True 242061da546Spatrick elif stop_reason == lldb.eStopReasonBreakpoint: 243061da546Spatrick select_thread = True 244061da546Spatrick bp_id = thread.GetStopReasonDataAtIndex(0) 245061da546Spatrick bp_loc_id = thread.GetStopReasonDataAtIndex(1) 246061da546Spatrick if self.verbose: 247061da546Spatrick print("breakpoint id = %d.%d" % (bp_id, bp_loc_id)) 248061da546Spatrick elif stop_reason == lldb.eStopReasonWatchpoint: 249061da546Spatrick select_thread = True 250061da546Spatrick if self.verbose: 251061da546Spatrick print("watchpoint id = %d" % (thread.GetStopReasonDataAtIndex(0))) 252061da546Spatrick elif stop_reason == lldb.eStopReasonSignal: 253061da546Spatrick select_thread = True 254061da546Spatrick if self.verbose: 255061da546Spatrick print("signal %d" % (thread.GetStopReasonDataAtIndex(0))) 256*be691f3bSpatrick elif stop_reason == lldb.eStopReasonFork: 257*be691f3bSpatrick if self.verbose: 258*be691f3bSpatrick print("fork pid = %d" % (thread.GetStopReasonDataAtIndex(0))) 259*be691f3bSpatrick elif stop_reason == lldb.eStopReasonVFork: 260*be691f3bSpatrick if self.verbose: 261*be691f3bSpatrick print("vfork pid = %d" % (thread.GetStopReasonDataAtIndex(0))) 262*be691f3bSpatrick elif stop_reason == lldb.eStopReasonVForkDone: 263*be691f3bSpatrick if self.verbose: 264*be691f3bSpatrick print("vfork done") 265061da546Spatrick 266061da546Spatrick if select_thread and not selected_thread: 267061da546Spatrick self.thread = thread 268061da546Spatrick selected_thread = self.process.SetSelectedThread( 269061da546Spatrick thread) 270061da546Spatrick 271061da546Spatrick for action in self.user_actions: 272061da546Spatrick action.ThreadStopped(thread) 273061da546Spatrick 274061da546Spatrick if fatal: 275061da546Spatrick # if self.verbose: 276061da546Spatrick # Xcode.RunCommand(self.debugger,"bt all",true) 277061da546Spatrick sys.exit(1) 278061da546Spatrick return event 279061da546Spatrick 280061da546Spatrick 281061da546Spatrickclass Measurement: 282061da546Spatrick '''A class that encapsulates a measurement''' 283061da546Spatrick 284061da546Spatrick def __init__(self): 285061da546Spatrick object.__init__(self) 286061da546Spatrick 287061da546Spatrick def Measure(self): 288061da546Spatrick assert False, "performance.Measurement.Measure() must be subclassed" 289061da546Spatrick 290061da546Spatrick 291061da546Spatrickclass MemoryMeasurement(Measurement): 292061da546Spatrick '''A class that can measure memory statistics for a process.''' 293061da546Spatrick 294061da546Spatrick def __init__(self, pid): 295061da546Spatrick Measurement.__init__(self) 296061da546Spatrick self.pid = pid 297061da546Spatrick self.stats = [ 298061da546Spatrick "rprvt", 299061da546Spatrick "rshrd", 300061da546Spatrick "rsize", 301061da546Spatrick "vsize", 302061da546Spatrick "vprvt", 303061da546Spatrick "kprvt", 304061da546Spatrick "kshrd", 305061da546Spatrick "faults", 306061da546Spatrick "cow", 307061da546Spatrick "pageins"] 308061da546Spatrick self.command = "top -l 1 -pid %u -stats %s" % ( 309061da546Spatrick self.pid, ",".join(self.stats)) 310061da546Spatrick self.value = dict() 311061da546Spatrick 312061da546Spatrick def Measure(self): 313061da546Spatrick output = subprocess.getoutput(self.command).split("\n")[-1] 314061da546Spatrick values = re.split('[-+\s]+', output) 315061da546Spatrick for (idx, stat) in enumerate(values): 316061da546Spatrick multiplier = 1 317061da546Spatrick if stat: 318061da546Spatrick if stat[-1] == 'K': 319061da546Spatrick multiplier = 1024 320061da546Spatrick stat = stat[:-1] 321061da546Spatrick elif stat[-1] == 'M': 322061da546Spatrick multiplier = 1024 * 1024 323061da546Spatrick stat = stat[:-1] 324061da546Spatrick elif stat[-1] == 'G': 325061da546Spatrick multiplier = 1024 * 1024 * 1024 326061da546Spatrick elif stat[-1] == 'T': 327061da546Spatrick multiplier = 1024 * 1024 * 1024 * 1024 328061da546Spatrick stat = stat[:-1] 329061da546Spatrick self.value[self.stats[idx]] = int(stat) * multiplier 330061da546Spatrick 331061da546Spatrick def __str__(self): 332061da546Spatrick '''Dump the MemoryMeasurement current value''' 333061da546Spatrick s = '' 334061da546Spatrick for key in self.value.keys(): 335061da546Spatrick if s: 336061da546Spatrick s += "\n" 337061da546Spatrick s += "%8s = %s" % (key, self.value[key]) 338061da546Spatrick return s 339061da546Spatrick 340061da546Spatrick 341061da546Spatrickclass TesterTestCase(TestCase): 342061da546Spatrick 343061da546Spatrick def __init__(self): 344061da546Spatrick TestCase.__init__(self) 345061da546Spatrick self.verbose = True 346061da546Spatrick self.num_steps = 5 347061da546Spatrick 348061da546Spatrick def BreakpointHit(self, thread): 349061da546Spatrick bp_id = thread.GetStopReasonDataAtIndex(0) 350061da546Spatrick loc_id = thread.GetStopReasonDataAtIndex(1) 351061da546Spatrick print("Breakpoint %i.%i hit: %s" % (bp_id, loc_id, thread.process.target.FindBreakpointByID(bp_id))) 352061da546Spatrick thread.StepOver() 353061da546Spatrick 354061da546Spatrick def PlanComplete(self, thread): 355061da546Spatrick if self.num_steps > 0: 356061da546Spatrick thread.StepOver() 357061da546Spatrick self.num_steps = self.num_steps - 1 358061da546Spatrick else: 359061da546Spatrick thread.process.Kill() 360061da546Spatrick 361061da546Spatrick def Run(self, args): 362061da546Spatrick self.Setup(args) 363061da546Spatrick with Timer() as total_time: 364061da546Spatrick self.target = self.debugger.CreateTarget(args[0]) 365061da546Spatrick if self.target: 366061da546Spatrick with Timer() as breakpoint_timer: 367061da546Spatrick bp = self.target.BreakpointCreateByName("main") 368061da546Spatrick print( 369061da546Spatrick 'Breakpoint time = %.03f sec.' % 370061da546Spatrick breakpoint_timer.interval) 371061da546Spatrick 372061da546Spatrick self.user_actions.append( 373061da546Spatrick BreakpointAction( 374061da546Spatrick breakpoint=bp, 375061da546Spatrick callback=TesterTestCase.BreakpointHit, 376061da546Spatrick callback_owner=self)) 377061da546Spatrick self.user_actions.append( 378061da546Spatrick PlanCompleteAction( 379061da546Spatrick callback=TesterTestCase.PlanComplete, 380061da546Spatrick callback_owner=self)) 381061da546Spatrick 382061da546Spatrick if self.Launch(): 383061da546Spatrick while not self.done: 384061da546Spatrick self.WaitForNextProcessEvent() 385061da546Spatrick else: 386061da546Spatrick print("error: failed to launch process") 387061da546Spatrick else: 388061da546Spatrick print("error: failed to create target with '%s'" % (args[0])) 389061da546Spatrick print('Total time = %.03f sec.' % total_time.interval) 390061da546Spatrick 391061da546Spatrick 392061da546Spatrickif __name__ == '__main__': 393061da546Spatrick lldb.SBDebugger.Initialize() 394061da546Spatrick test = TesterTestCase() 395061da546Spatrick test.Run(sys.argv[1:]) 396061da546Spatrick mem = MemoryMeasurement(os.getpid()) 397061da546Spatrick mem.Measure() 398061da546Spatrick print(str(mem)) 399061da546Spatrick lldb.SBDebugger.Terminate() 400061da546Spatrick # print "sleeeping for 100 seconds" 401061da546Spatrick # time.sleep(100) 402