1#!/usr/bin/python 2 3#---------------------------------------------------------------------- 4# Be sure to add the python path that points to the LLDB shared library. 5# On MacOSX csh, tcsh: 6# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 7# On MacOSX sh, bash: 8# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 9#---------------------------------------------------------------------- 10 11import commands 12import optparse 13import os 14import platform 15import re 16import resource 17import sys 18import time 19import types 20 21#---------------------------------------------------------------------- 22# Code that auto imports LLDB 23#---------------------------------------------------------------------- 24try: 25 # Just try for LLDB in case PYTHONPATH is already correctly setup 26 import lldb 27except ImportError: 28 lldb_python_dirs = list() 29 # lldb is not in the PYTHONPATH, try some defaults for the current platform 30 platform_system = platform.system() 31 if platform_system == 'Darwin': 32 # On Darwin, try the currently selected Xcode directory 33 xcode_dir = commands.getoutput("xcode-select --print-path") 34 if xcode_dir: 35 lldb_python_dirs.append( 36 os.path.realpath( 37 xcode_dir + 38 '/../SharedFrameworks/LLDB.framework/Resources/Python')) 39 lldb_python_dirs.append( 40 xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 41 lldb_python_dirs.append( 42 '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 43 success = False 44 for lldb_python_dir in lldb_python_dirs: 45 if os.path.exists(lldb_python_dir): 46 if not (sys.path.__contains__(lldb_python_dir)): 47 sys.path.append(lldb_python_dir) 48 try: 49 import lldb 50 except ImportError: 51 pass 52 else: 53 print 'imported lldb from: "%s"' % (lldb_python_dir) 54 success = True 55 break 56 if not success: 57 print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" 58 sys.exit(1) 59 60 61class Timer: 62 63 def __enter__(self): 64 self.start = time.clock() 65 return self 66 67 def __exit__(self, *args): 68 self.end = time.clock() 69 self.interval = self.end - self.start 70 71 72class Action(object): 73 """Class that encapsulates actions to take when a thread stops for a reason.""" 74 75 def __init__(self, callback=None, callback_owner=None): 76 self.callback = callback 77 self.callback_owner = callback_owner 78 79 def ThreadStopped(self, thread): 80 assert False, "performance.Action.ThreadStopped(self, thread) must be overridden in a subclass" 81 82 83class PlanCompleteAction (Action): 84 85 def __init__(self, callback=None, callback_owner=None): 86 Action.__init__(self, callback, callback_owner) 87 88 def ThreadStopped(self, thread): 89 if thread.GetStopReason() == lldb.eStopReasonPlanComplete: 90 if self.callback: 91 if self.callback_owner: 92 self.callback(self.callback_owner, thread) 93 else: 94 self.callback(thread) 95 return True 96 return False 97 98 99class BreakpointAction (Action): 100 101 def __init__( 102 self, 103 callback=None, 104 callback_owner=None, 105 name=None, 106 module=None, 107 file=None, 108 line=None, 109 breakpoint=None): 110 Action.__init__(self, callback, callback_owner) 111 self.modules = lldb.SBFileSpecList() 112 self.files = lldb.SBFileSpecList() 113 self.breakpoints = list() 114 # "module" can be a list or a string 115 if breakpoint: 116 self.breakpoints.append(breakpoint) 117 else: 118 if module: 119 if isinstance(module, types.ListType): 120 for module_path in module: 121 self.modules.Append( 122 lldb.SBFileSpec(module_path, False)) 123 elif isinstance(module, types.StringTypes): 124 self.modules.Append(lldb.SBFileSpec(module, False)) 125 if name: 126 # "file" can be a list or a string 127 if file: 128 if isinstance(file, types.ListType): 129 self.files = lldb.SBFileSpecList() 130 for f in file: 131 self.files.Append(lldb.SBFileSpec(f, False)) 132 elif isinstance(file, types.StringTypes): 133 self.files.Append(lldb.SBFileSpec(file, False)) 134 self.breakpoints.append( 135 self.target.BreakpointCreateByName( 136 name, self.modules, self.files)) 137 elif file and line: 138 self.breakpoints.append( 139 self.target.BreakpointCreateByLocation( 140 file, line)) 141 142 def ThreadStopped(self, thread): 143 if thread.GetStopReason() == lldb.eStopReasonBreakpoint: 144 for bp in self.breakpoints: 145 if bp.GetID() == thread.GetStopReasonDataAtIndex(0): 146 if self.callback: 147 if self.callback_owner: 148 self.callback(self.callback_owner, thread) 149 else: 150 self.callback(thread) 151 return True 152 return False 153 154 155class TestCase: 156 """Class that aids in running performance tests.""" 157 158 def __init__(self): 159 self.verbose = False 160 self.debugger = lldb.SBDebugger.Create() 161 self.target = None 162 self.process = None 163 self.thread = None 164 self.launch_info = None 165 self.done = False 166 self.listener = self.debugger.GetListener() 167 self.user_actions = list() 168 self.builtin_actions = list() 169 self.bp_id_to_dict = dict() 170 171 def Setup(self, args): 172 self.launch_info = lldb.SBLaunchInfo(args) 173 174 def Run(self, args): 175 assert False, "performance.TestCase.Run(self, args) must be subclassed" 176 177 def Launch(self): 178 if self.target: 179 error = lldb.SBError() 180 self.process = self.target.Launch(self.launch_info, error) 181 if not error.Success(): 182 print "error: %s" % error.GetCString() 183 if self.process: 184 self.process.GetBroadcaster().AddListener(self.listener, 185 lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitInterrupt) 186 return True 187 return False 188 189 def WaitForNextProcessEvent(self): 190 event = None 191 if self.process: 192 while event is None: 193 process_event = lldb.SBEvent() 194 if self.listener.WaitForEvent(lldb.UINT32_MAX, process_event): 195 state = lldb.SBProcess.GetStateFromEvent(process_event) 196 if self.verbose: 197 print "event = %s" % (lldb.SBDebugger.StateAsCString(state)) 198 if lldb.SBProcess.GetRestartedFromEvent(process_event): 199 continue 200 if state == lldb.eStateInvalid or state == lldb.eStateDetached or state == lldb.eStateCrashed or state == lldb.eStateUnloaded or state == lldb.eStateExited: 201 event = process_event 202 self.done = True 203 elif state == lldb.eStateConnected or state == lldb.eStateAttaching or state == lldb.eStateLaunching or state == lldb.eStateRunning or state == lldb.eStateStepping or state == lldb.eStateSuspended: 204 continue 205 elif state == lldb.eStateStopped: 206 event = process_event 207 call_test_step = True 208 fatal = False 209 selected_thread = False 210 for thread in self.process: 211 frame = thread.GetFrameAtIndex(0) 212 select_thread = False 213 214 stop_reason = thread.GetStopReason() 215 if self.verbose: 216 print "tid = %#x pc = %#x " % (thread.GetThreadID(), frame.GetPC()), 217 if stop_reason == lldb.eStopReasonNone: 218 if self.verbose: 219 print "none" 220 elif stop_reason == lldb.eStopReasonTrace: 221 select_thread = True 222 if self.verbose: 223 print "trace" 224 elif stop_reason == lldb.eStopReasonPlanComplete: 225 select_thread = True 226 if self.verbose: 227 print "plan complete" 228 elif stop_reason == lldb.eStopReasonThreadExiting: 229 if self.verbose: 230 print "thread exiting" 231 elif stop_reason == lldb.eStopReasonExec: 232 if self.verbose: 233 print "exec" 234 elif stop_reason == lldb.eStopReasonInvalid: 235 if self.verbose: 236 print "invalid" 237 elif stop_reason == lldb.eStopReasonException: 238 select_thread = True 239 if self.verbose: 240 print "exception" 241 fatal = True 242 elif stop_reason == lldb.eStopReasonBreakpoint: 243 select_thread = True 244 bp_id = thread.GetStopReasonDataAtIndex(0) 245 bp_loc_id = thread.GetStopReasonDataAtIndex(1) 246 if self.verbose: 247 print "breakpoint id = %d.%d" % (bp_id, bp_loc_id) 248 elif stop_reason == lldb.eStopReasonWatchpoint: 249 select_thread = True 250 if self.verbose: 251 print "watchpoint id = %d" % (thread.GetStopReasonDataAtIndex(0)) 252 elif stop_reason == lldb.eStopReasonSignal: 253 select_thread = True 254 if self.verbose: 255 print "signal %d" % (thread.GetStopReasonDataAtIndex(0)) 256 257 if select_thread and not selected_thread: 258 self.thread = thread 259 selected_thread = self.process.SetSelectedThread( 260 thread) 261 262 for action in self.user_actions: 263 action.ThreadStopped(thread) 264 265 if fatal: 266 # if self.verbose: 267 # Xcode.RunCommand(self.debugger,"bt all",true) 268 sys.exit(1) 269 return event 270 271 272class Measurement: 273 '''A class that encapsulates a measurement''' 274 275 def __init__(self): 276 object.__init__(self) 277 278 def Measure(self): 279 assert False, "performance.Measurement.Measure() must be subclassed" 280 281 282class MemoryMeasurement(Measurement): 283 '''A class that can measure memory statistics for a process.''' 284 285 def __init__(self, pid): 286 Measurement.__init__(self) 287 self.pid = pid 288 self.stats = [ 289 "rprvt", 290 "rshrd", 291 "rsize", 292 "vsize", 293 "vprvt", 294 "kprvt", 295 "kshrd", 296 "faults", 297 "cow", 298 "pageins"] 299 self.command = "top -l 1 -pid %u -stats %s" % ( 300 self.pid, ",".join(self.stats)) 301 self.value = dict() 302 303 def Measure(self): 304 output = commands.getoutput(self.command).split("\n")[-1] 305 values = re.split('[-+\s]+', output) 306 for (idx, stat) in enumerate(values): 307 multiplier = 1 308 if stat: 309 if stat[-1] == 'K': 310 multiplier = 1024 311 stat = stat[:-1] 312 elif stat[-1] == 'M': 313 multiplier = 1024 * 1024 314 stat = stat[:-1] 315 elif stat[-1] == 'G': 316 multiplier = 1024 * 1024 * 1024 317 elif stat[-1] == 'T': 318 multiplier = 1024 * 1024 * 1024 * 1024 319 stat = stat[:-1] 320 self.value[self.stats[idx]] = int(stat) * multiplier 321 322 def __str__(self): 323 '''Dump the MemoryMeasurement current value''' 324 s = '' 325 for key in self.value.keys(): 326 if s: 327 s += "\n" 328 s += "%8s = %s" % (key, self.value[key]) 329 return s 330 331 332class TesterTestCase(TestCase): 333 334 def __init__(self): 335 TestCase.__init__(self) 336 self.verbose = True 337 self.num_steps = 5 338 339 def BreakpointHit(self, thread): 340 bp_id = thread.GetStopReasonDataAtIndex(0) 341 loc_id = thread.GetStopReasonDataAtIndex(1) 342 print "Breakpoint %i.%i hit: %s" % (bp_id, loc_id, thread.process.target.FindBreakpointByID(bp_id)) 343 thread.StepOver() 344 345 def PlanComplete(self, thread): 346 if self.num_steps > 0: 347 thread.StepOver() 348 self.num_steps = self.num_steps - 1 349 else: 350 thread.process.Kill() 351 352 def Run(self, args): 353 self.Setup(args) 354 with Timer() as total_time: 355 self.target = self.debugger.CreateTarget(args[0]) 356 if self.target: 357 with Timer() as breakpoint_timer: 358 bp = self.target.BreakpointCreateByName("main") 359 print( 360 'Breakpoint time = %.03f sec.' % 361 breakpoint_timer.interval) 362 363 self.user_actions.append( 364 BreakpointAction( 365 breakpoint=bp, 366 callback=TesterTestCase.BreakpointHit, 367 callback_owner=self)) 368 self.user_actions.append( 369 PlanCompleteAction( 370 callback=TesterTestCase.PlanComplete, 371 callback_owner=self)) 372 373 if self.Launch(): 374 while not self.done: 375 self.WaitForNextProcessEvent() 376 else: 377 print "error: failed to launch process" 378 else: 379 print "error: failed to create target with '%s'" % (args[0]) 380 print('Total time = %.03f sec.' % total_time.interval) 381 382 383if __name__ == '__main__': 384 lldb.SBDebugger.Initialize() 385 test = TesterTestCase() 386 test.Run(sys.argv[1:]) 387 mem = MemoryMeasurement(os.getpid()) 388 mem.Measure() 389 print str(mem) 390 lldb.SBDebugger.Terminate() 391 # print "sleeeping for 100 seconds" 392 # time.sleep(100) 393