X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=eventlet%2Feventlet%2Fgreen%2Fprofile.py;fp=eventlet%2Feventlet%2Fgreen%2Fprofile.py;h=0000000000000000000000000000000000000000;hb=358bd9258c2b6d2ee74de4dfd07a5123107abad4;hp=e323adc23a671e0340dc5ef963a7263b7b5242e9;hpb=376ff3bfe7071cc0793184a378c4e74508fb0d97;p=packages%2Ftrusty%2Fpython-eventlet.git diff --git a/eventlet/eventlet/green/profile.py b/eventlet/eventlet/green/profile.py deleted file mode 100644 index e323adc..0000000 --- a/eventlet/eventlet/green/profile.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) 2010, CCP Games -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of CCP Games nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY CCP GAMES ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL CCP GAMES BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""This module is API-equivalent to the standard library :mod:`profile` module -lbut it is greenthread-aware as well as thread-aware. Use this module -to profile Eventlet-based applications in preference to either :mod:`profile` or :mod:`cProfile`. -FIXME: No testcases for this module. -""" - -profile_orig = __import__('profile') -__all__ = profile_orig.__all__ - -from eventlet.patcher import slurp_properties -slurp_properties(profile_orig, globals(), srckeys=dir(profile_orig)) - -import sys -import functools - -from eventlet import greenthread -from eventlet import patcher -from eventlet.support import six - -thread = patcher.original('thread') # non-monkeypatched module needed - - -# This class provides the start() and stop() functions -class Profile(profile_orig.Profile): - base = profile_orig.Profile - - def __init__(self, timer=None, bias=None): - self.current_tasklet = greenthread.getcurrent() - self.thread_id = thread.get_ident() - self.base.__init__(self, timer, bias) - self.sleeping = {} - - def __call__(self, *args): - """make callable, allowing an instance to be the profiler""" - self.dispatcher(*args) - - def _setup(self): - self._has_setup = True - self.cur = None - self.timings = {} - self.current_tasklet = greenthread.getcurrent() - self.thread_id = thread.get_ident() - self.simulate_call("profiler") - - def start(self, name="start"): - if getattr(self, "running", False): - return - self._setup() - self.simulate_call("start") - self.running = True - sys.setprofile(self.dispatcher) - - def stop(self): - sys.setprofile(None) - self.running = False - self.TallyTimings() - - # special cases for the original run commands, makin sure to - # clear the timer context. - def runctx(self, cmd, globals, locals): - if not getattr(self, "_has_setup", False): - self._setup() - try: - return profile_orig.Profile.runctx(self, cmd, globals, locals) - finally: - self.TallyTimings() - - def runcall(self, func, *args, **kw): - if not getattr(self, "_has_setup", False): - self._setup() - try: - return profile_orig.Profile.runcall(self, func, *args, **kw) - finally: - self.TallyTimings() - - def trace_dispatch_return_extend_back(self, frame, t): - """A hack function to override error checking in parent class. It - allows invalid returns (where frames weren't preveiously entered into - the profiler) which can happen for all the tasklets that suddenly start - to get monitored. This means that the time will eventually be attributed - to a call high in the chain, when there is a tasklet switch - """ - if isinstance(self.cur[-2], Profile.fake_frame): - return False - self.trace_dispatch_call(frame, 0) - return self.trace_dispatch_return(frame, t) - - def trace_dispatch_c_return_extend_back(self, frame, t): - # same for c return - if isinstance(self.cur[-2], Profile.fake_frame): - return False # ignore bogus returns - self.trace_dispatch_c_call(frame, 0) - return self.trace_dispatch_return(frame, t) - - # Add "return safety" to the dispatchers - dispatch = dict(profile_orig.Profile.dispatch) - dispatch.update({ - "return": trace_dispatch_return_extend_back, - "c_return": trace_dispatch_c_return_extend_back, - }) - - def SwitchTasklet(self, t0, t1, t): - # tally the time spent in the old tasklet - pt, it, et, fn, frame, rcur = self.cur - cur = (pt, it + t, et, fn, frame, rcur) - - # we are switching to a new tasklet, store the old - self.sleeping[t0] = cur, self.timings - self.current_tasklet = t1 - - # find the new one - try: - self.cur, self.timings = self.sleeping.pop(t1) - except KeyError: - self.cur, self.timings = None, {} - self.simulate_call("profiler") - self.simulate_call("new_tasklet") - - def ContextWrap(f): - @functools.wraps(f) - def ContextWrapper(self, arg, t): - current = greenthread.getcurrent() - if current != self.current_tasklet: - self.SwitchTasklet(self.current_tasklet, current, t) - t = 0.0 # the time was billed to the previous tasklet - return f(self, arg, t) - return ContextWrapper - - # Add automatic tasklet detection to the callbacks. - dispatch = dict([(key, ContextWrap(val)) for key, val in six.iteritems(dispatch)]) - - def TallyTimings(self): - oldtimings = self.sleeping - self.sleeping = {} - - # first, unwind the main "cur" - self.cur = self.Unwind(self.cur, self.timings) - - # we must keep the timings dicts separate for each tasklet, since it contains - # the 'ns' item, recursion count of each function in that tasklet. This is - # used in the Unwind dude. - for tasklet, (cur, timings) in six.iteritems(oldtimings): - self.Unwind(cur, timings) - - for k, v in six.iteritems(timings): - if k not in self.timings: - self.timings[k] = v - else: - # accumulate all to the self.timings - cc, ns, tt, ct, callers = self.timings[k] - # ns should be 0 after unwinding - cc += v[0] - tt += v[2] - ct += v[3] - for k1, v1 in six.iteritems(v[4]): - callers[k1] = callers.get(k1, 0) + v1 - self.timings[k] = cc, ns, tt, ct, callers - - def Unwind(self, cur, timings): - "A function to unwind a 'cur' frame and tally the results" - "see profile.trace_dispatch_return() for details" - # also see simulate_cmd_complete() - while(cur[-1]): - rpt, rit, ret, rfn, frame, rcur = cur - frame_total = rit + ret - - if rfn in timings: - cc, ns, tt, ct, callers = timings[rfn] - else: - cc, ns, tt, ct, callers = 0, 0, 0, 0, {} - - if not ns: - ct = ct + frame_total - cc = cc + 1 - - if rcur: - ppt, pit, pet, pfn, pframe, pcur = rcur - else: - pfn = None - - if pfn in callers: - callers[pfn] = callers[pfn] + 1 # hack: gather more - elif pfn: - callers[pfn] = 1 - - timings[rfn] = cc, ns - 1, tt + rit, ct, callers - - ppt, pit, pet, pfn, pframe, pcur = rcur - rcur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur - cur = rcur - return cur - - -# run statements shamelessly stolen from profile.py -def run(statement, filename=None, sort=-1): - """Run statement under profiler optionally saving results in filename - - This function takes a single argument that can be passed to the - "exec" statement, and an optional file name. In all cases this - routine attempts to "exec" its first argument and gather profiling - statistics from the execution. If no file name is present, then this - function automatically prints a simple profiling report, sorted by the - standard name string (file/line/function-name) that is presented in - each line. - """ - prof = Profile() - try: - prof = prof.run(statement) - except SystemExit: - pass - if filename is not None: - prof.dump_stats(filename) - else: - return prof.print_stats(sort) - - -def runctx(statement, globals, locals, filename=None): - """Run statement under profiler, supplying your own globals and locals, - optionally saving results in filename. - - statement and filename have the same semantics as profile.run - """ - prof = Profile() - try: - prof = prof.runctx(statement, globals, locals) - except SystemExit: - pass - - if filename is not None: - prof.dump_stats(filename) - else: - return prof.print_stats()