439 lines · python
1#!/usr/bin/env python2 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/Python7# On MacOSX sh, bash:8# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python9# ----------------------------------------------------------------------10 11import optparse12import os13import platform14import re15import resource16import sys17import subprocess18import time19 20# ----------------------------------------------------------------------21# Code that auto imports LLDB22# ----------------------------------------------------------------------23try:24 # Just try for LLDB in case PYTHONPATH is already correctly setup25 import lldb26except ImportError:27 lldb_python_dirs = list()28 # lldb is not in the PYTHONPATH, try some defaults for the current platform29 platform_system = platform.system()30 if platform_system == "Darwin":31 # On Darwin, try the currently selected Xcode directory32 xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True)33 if xcode_dir:34 lldb_python_dirs.append(35 os.path.realpath(36 xcode_dir + "/../SharedFrameworks/LLDB.framework/Resources/Python"37 )38 )39 lldb_python_dirs.append(40 xcode_dir + "/Library/PrivateFrameworks/LLDB.framework/Resources/Python"41 )42 lldb_python_dirs.append(43 "/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python"44 )45 success = False46 for lldb_python_dir in lldb_python_dirs:47 if os.path.exists(lldb_python_dir):48 if not (sys.path.__contains__(lldb_python_dir)):49 sys.path.append(lldb_python_dir)50 try:51 import lldb52 except ImportError:53 pass54 else:55 print('imported lldb from: "%s"' % (lldb_python_dir))56 success = True57 break58 if not success:59 print(60 "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"61 )62 sys.exit(1)63 64 65class Timer:66 def __enter__(self):67 self.start = time.clock()68 return self69 70 def __exit__(self, *args):71 self.end = time.clock()72 self.interval = self.end - self.start73 74 75class Action(object):76 """Class that encapsulates actions to take when a thread stops for a reason."""77 78 def __init__(self, callback=None, callback_owner=None):79 self.callback = callback80 self.callback_owner = callback_owner81 82 def ThreadStopped(self, thread):83 assert (84 False85 ), "performance.Action.ThreadStopped(self, thread) must be overridden in a subclass"86 87 88class PlanCompleteAction(Action):89 def __init__(self, callback=None, callback_owner=None):90 Action.__init__(self, callback, callback_owner)91 92 def ThreadStopped(self, thread):93 if thread.GetStopReason() == lldb.eStopReasonPlanComplete:94 if self.callback:95 if self.callback_owner:96 self.callback(self.callback_owner, thread)97 else:98 self.callback(thread)99 return True100 return False101 102 103class BreakpointAction(Action):104 def __init__(105 self,106 callback=None,107 callback_owner=None,108 name=None,109 module=None,110 file=None,111 line=None,112 breakpoint=None,113 ):114 Action.__init__(self, callback, callback_owner)115 self.modules = lldb.SBFileSpecList()116 self.files = lldb.SBFileSpecList()117 self.breakpoints = list()118 # "module" can be a list or a string119 if breakpoint:120 self.breakpoints.append(breakpoint)121 else:122 if module:123 if isinstance(module, list):124 for module_path in module:125 self.modules.Append(lldb.SBFileSpec(module_path, False))126 elif isinstance(module, str):127 self.modules.Append(lldb.SBFileSpec(module, False))128 if name:129 # "file" can be a list or a string130 if file:131 if isinstance(file, list):132 self.files = lldb.SBFileSpecList()133 for f in file:134 self.files.Append(lldb.SBFileSpec(f, False))135 elif isinstance(file, str):136 self.files.Append(lldb.SBFileSpec(file, False))137 self.breakpoints.append(138 self.target.BreakpointCreateByName(name, self.modules, self.files)139 )140 elif file and line:141 self.breakpoints.append(142 self.target.BreakpointCreateByLocation(file, line)143 )144 145 def ThreadStopped(self, thread):146 if thread.GetStopReason() == lldb.eStopReasonBreakpoint:147 for bp in self.breakpoints:148 if bp.GetID() == thread.GetStopReasonDataAtIndex(0):149 if self.callback:150 if self.callback_owner:151 self.callback(self.callback_owner, thread)152 else:153 self.callback(thread)154 return True155 return False156 157 158class TestCase:159 """Class that aids in running performance tests."""160 161 def __init__(self):162 self.verbose = False163 self.debugger = lldb.SBDebugger.Create()164 self.target = None165 self.process = None166 self.thread = None167 self.launch_info = None168 self.done = False169 self.listener = self.debugger.GetListener()170 self.user_actions = list()171 self.builtin_actions = list()172 self.bp_id_to_dict = dict()173 174 def Setup(self, args):175 self.launch_info = lldb.SBLaunchInfo(args)176 177 def Run(self, args):178 assert False, "performance.TestCase.Run(self, args) must be subclassed"179 180 def Launch(self):181 if self.target:182 error = lldb.SBError()183 self.process = self.target.Launch(self.launch_info, error)184 if not error.Success():185 print("error: %s" % error.GetCString())186 if self.process:187 self.process.GetBroadcaster().AddListener(188 self.listener,189 lldb.SBProcess.eBroadcastBitStateChanged190 | lldb.SBProcess.eBroadcastBitInterrupt,191 )192 return True193 return False194 195 def WaitForNextProcessEvent(self):196 event = None197 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 continue206 if (207 state == lldb.eStateInvalid208 or state == lldb.eStateDetached209 or state == lldb.eStateCrashed210 or state == lldb.eStateUnloaded211 or state == lldb.eStateExited212 ):213 event = process_event214 self.done = True215 elif (216 state == lldb.eStateConnected217 or state == lldb.eStateAttaching218 or state == lldb.eStateLaunching219 or state == lldb.eStateRunning220 or state == lldb.eStateStepping221 or state == lldb.eStateSuspended222 ):223 continue224 elif state == lldb.eStateStopped:225 event = process_event226 call_test_step = True227 fatal = False228 selected_thread = False229 for thread in self.process:230 frame = thread.GetFrameAtIndex(0)231 select_thread = False232 233 stop_reason = thread.GetStopReason()234 if self.verbose:235 print(236 "tid = %#x pc = %#x "237 % (thread.GetThreadID(), frame.GetPC()),238 end=" ",239 )240 if stop_reason == lldb.eStopReasonNone:241 if self.verbose:242 print("none")243 elif stop_reason == lldb.eStopReasonTrace:244 select_thread = True245 if self.verbose:246 print("trace")247 elif stop_reason == lldb.eStopReasonPlanComplete:248 select_thread = True249 if self.verbose:250 print("plan complete")251 elif stop_reason == lldb.eStopReasonThreadExiting:252 if self.verbose:253 print("thread exiting")254 elif stop_reason == lldb.eStopReasonExec:255 if self.verbose:256 print("exec")257 elif stop_reason == lldb.eStopReasonInvalid:258 if self.verbose:259 print("invalid")260 elif stop_reason == lldb.eStopReasonException:261 select_thread = True262 if self.verbose:263 print("exception")264 fatal = True265 elif stop_reason == lldb.eStopReasonBreakpoint:266 select_thread = True267 bp_id = thread.GetStopReasonDataAtIndex(0)268 bp_loc_id = thread.GetStopReasonDataAtIndex(1)269 if self.verbose:270 print("breakpoint id = %d.%d" % (bp_id, bp_loc_id))271 elif stop_reason == lldb.eStopReasonWatchpoint:272 select_thread = True273 if self.verbose:274 print(275 "watchpoint id = %d"276 % (thread.GetStopReasonDataAtIndex(0))277 )278 elif stop_reason == lldb.eStopReasonSignal:279 select_thread = True280 if self.verbose:281 print(282 "signal %d"283 % (thread.GetStopReasonDataAtIndex(0))284 )285 elif stop_reason == lldb.eStopReasonFork:286 if self.verbose:287 print(288 "fork pid = %d"289 % (thread.GetStopReasonDataAtIndex(0))290 )291 elif stop_reason == lldb.eStopReasonVFork:292 if self.verbose:293 print(294 "vfork pid = %d"295 % (thread.GetStopReasonDataAtIndex(0))296 )297 elif stop_reason == lldb.eStopReasonVForkDone:298 if self.verbose:299 print("vfork done")300 301 if select_thread and not selected_thread:302 self.thread = thread303 selected_thread = self.process.SetSelectedThread(thread)304 305 for action in self.user_actions:306 action.ThreadStopped(thread)307 308 if fatal:309 # if self.verbose:310 # Xcode.RunCommand(self.debugger,"bt all",true)311 sys.exit(1)312 return event313 314 315class Measurement:316 """A class that encapsulates a measurement"""317 318 def __init__(self):319 object.__init__(self)320 321 def Measure(self):322 assert False, "performance.Measurement.Measure() must be subclassed"323 324 325class MemoryMeasurement(Measurement):326 """A class that can measure memory statistics for a process."""327 328 def __init__(self, pid):329 Measurement.__init__(self)330 self.pid = pid331 self.stats = [332 "rprvt",333 "rshrd",334 "rsize",335 "vsize",336 "vprvt",337 "kprvt",338 "kshrd",339 "faults",340 "cow",341 "pageins",342 ]343 self.command = "top -l 1 -pid %u -stats %s" % (self.pid, ",".join(self.stats))344 self.value = dict()345 346 def Measure(self):347 output = subprocess.getoutput(self.command).split("\n")[-1]348 values = re.split(r"[-+\s]+", output)349 for idx, stat in enumerate(values):350 multiplier = 1351 if stat:352 if stat[-1] == "K":353 multiplier = 1024354 stat = stat[:-1]355 elif stat[-1] == "M":356 multiplier = 1024 * 1024357 stat = stat[:-1]358 elif stat[-1] == "G":359 multiplier = 1024 * 1024 * 1024360 elif stat[-1] == "T":361 multiplier = 1024 * 1024 * 1024 * 1024362 stat = stat[:-1]363 self.value[self.stats[idx]] = int(stat) * multiplier364 365 def __str__(self):366 """Dump the MemoryMeasurement current value"""367 s = ""368 for key in self.value.keys():369 if s:370 s += "\n"371 s += "%8s = %s" % (key, self.value[key])372 return s373 374 375class TesterTestCase(TestCase):376 def __init__(self):377 TestCase.__init__(self)378 self.verbose = True379 self.num_steps = 5380 381 def BreakpointHit(self, thread):382 bp_id = thread.GetStopReasonDataAtIndex(0)383 loc_id = thread.GetStopReasonDataAtIndex(1)384 print(385 "Breakpoint %i.%i hit: %s"386 % (bp_id, loc_id, thread.process.target.FindBreakpointByID(bp_id))387 )388 thread.StepOver()389 390 def PlanComplete(self, thread):391 if self.num_steps > 0:392 thread.StepOver()393 self.num_steps = self.num_steps - 1394 else:395 thread.process.Kill()396 397 def Run(self, args):398 self.Setup(args)399 with Timer() as total_time:400 self.target = self.debugger.CreateTarget(args[0])401 if self.target:402 with Timer() as breakpoint_timer:403 bp = self.target.BreakpointCreateByName("main")404 print("Breakpoint time = %.03f sec." % breakpoint_timer.interval)405 406 self.user_actions.append(407 BreakpointAction(408 breakpoint=bp,409 callback=TesterTestCase.BreakpointHit,410 callback_owner=self,411 )412 )413 self.user_actions.append(414 PlanCompleteAction(415 callback=TesterTestCase.PlanComplete, callback_owner=self416 )417 )418 419 if self.Launch():420 while not self.done:421 self.WaitForNextProcessEvent()422 else:423 print("error: failed to launch process")424 else:425 print("error: failed to create target with '%s'" % (args[0]))426 print("Total time = %.03f sec." % total_time.interval)427 428 429if __name__ == "__main__":430 lldb.SBDebugger.Initialize()431 test = TesterTestCase()432 test.Run(sys.argv[1:])433 mem = MemoryMeasurement(os.getpid())434 mem.Measure()435 print(str(mem))436 lldb.SBDebugger.Terminate()437 # print "sleeeping for 100 seconds"438 # time.sleep(100)439