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