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