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