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