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