buildframework/helium/external/python/lib/common/psyco-1.6-py2.5.egg/psyco/profiler.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 ###########################################################################
       
     2 # 
       
     3 #  Psyco profiler (Python part).
       
     4 #   Copyright (C) 2001-2002  Armin Rigo et.al.
       
     5 
       
     6 """Psyco profiler (Python part).
       
     7 
       
     8 The implementation of the non-time-critical parts of the profiler.
       
     9 See profile() and full() in core.py for the easy interface.
       
    10 """
       
    11 ###########################################################################
       
    12 
       
    13 import _psyco
       
    14 from support import *
       
    15 import math, time, types, atexit
       
    16 now = time.time
       
    17 try:
       
    18     import thread
       
    19 except ImportError:
       
    20     import dummy_thread as thread
       
    21 
       
    22 
       
    23 # current profiler instance
       
    24 current = None
       
    25 
       
    26 # enabled profilers, in order of priority
       
    27 profilers = []
       
    28 
       
    29 # logger module (when enabled by core.log())
       
    30 logger = None
       
    31 
       
    32 # a lock for a thread-safe go()
       
    33 go_lock = thread.allocate_lock()
       
    34 
       
    35 def go(stop=0):
       
    36     # run the highest-priority profiler in 'profilers'
       
    37     global current
       
    38     go_lock.acquire()
       
    39     try:
       
    40         prev = current
       
    41         if stop:
       
    42             del profilers[:]
       
    43         if prev:
       
    44             if profilers and profilers[0] is prev:
       
    45                 return    # best profiler already running
       
    46             prev.stop()
       
    47             current = None
       
    48         for p in profilers[:]:
       
    49             if p.start():
       
    50                 current = p
       
    51                 if logger: # and p is not prev:
       
    52                     logger.write("%s: starting" % p.__class__.__name__, 5)
       
    53                 return
       
    54     finally:
       
    55         go_lock.release()
       
    56     # no profiler is running now
       
    57     if stop:
       
    58         if logger:
       
    59             logger.writefinalstats()
       
    60     else:
       
    61         tag2bind()
       
    62 
       
    63 atexit.register(go, 1)
       
    64 
       
    65 
       
    66 def buildfncache(globals, cache):
       
    67     if hasattr(types.IntType, '__dict__'):
       
    68         clstypes = (types.ClassType, types.TypeType)
       
    69     else:
       
    70         clstypes = types.ClassType
       
    71     for x in globals.values():
       
    72         if isinstance(x, types.MethodType):
       
    73             x = x.im_func
       
    74         if isinstance(x, types.FunctionType):
       
    75             cache[x.func_code] = x, ''
       
    76         elif isinstance(x, clstypes):
       
    77             for y in x.__dict__.values():
       
    78                 if isinstance(y, types.MethodType):
       
    79                     y = y.im_func
       
    80                 if isinstance(y, types.FunctionType):
       
    81                     cache[y.func_code] = y, x.__name__
       
    82 
       
    83 # code-to-function mapping (cache)
       
    84 function_cache = {}
       
    85 
       
    86 def trytobind(co, globals, log=1):
       
    87     try:
       
    88         f, clsname = function_cache[co]
       
    89     except KeyError:
       
    90         buildfncache(globals, function_cache)
       
    91         try:
       
    92             f, clsname = function_cache[co]
       
    93         except KeyError:
       
    94             if logger:
       
    95                 logger.write('warning: cannot find function %s in %s' %
       
    96                              (co.co_name, globals.get('__name__', '?')), 3)
       
    97             return  # give up
       
    98     if logger and log:
       
    99         modulename = globals.get('__name__', '?')
       
   100         if clsname:
       
   101             modulename += '.' + clsname
       
   102         logger.write('bind function: %s.%s' % (modulename, co.co_name), 1)
       
   103     f.func_code = _psyco.proxycode(f)
       
   104 
       
   105 
       
   106 # the list of code objects that have been tagged
       
   107 tagged_codes = []
       
   108 
       
   109 def tag(co, globals):
       
   110     if logger:
       
   111         try:
       
   112             f, clsname = function_cache[co]
       
   113         except KeyError:
       
   114             buildfncache(globals, function_cache)
       
   115             try:
       
   116                 f, clsname = function_cache[co]
       
   117             except KeyError:
       
   118                 clsname = ''  # give up
       
   119         modulename = globals.get('__name__', '?')
       
   120         if clsname:
       
   121             modulename += '.' + clsname
       
   122         logger.write('tag function: %s.%s' % (modulename, co.co_name), 1)
       
   123     tagged_codes.append((co, globals))
       
   124     _psyco.turbo_frame(co)
       
   125     _psyco.turbo_code(co)
       
   126 
       
   127 def tag2bind():
       
   128     if tagged_codes:
       
   129         if logger:
       
   130             logger.write('profiling stopped, binding %d functions' %
       
   131                          len(tagged_codes), 2)
       
   132         for co, globals in tagged_codes:
       
   133             trytobind(co, globals, 0)
       
   134         function_cache.clear()
       
   135         del tagged_codes[:]
       
   136 
       
   137 
       
   138 class Profiler:
       
   139     MemoryTimerResolution = 0.103
       
   140 
       
   141     def run(self, memory, time, memorymax, timemax):
       
   142         self.memory = memory
       
   143         self.memorymax = memorymax
       
   144         self.time = time
       
   145         if timemax is None:
       
   146             self.endtime = None
       
   147         else:
       
   148             self.endtime = now() + timemax
       
   149         self.alarms = []
       
   150         profilers.append(self)
       
   151         go()
       
   152     
       
   153     def start(self):
       
   154         curmem = _psyco.memory()
       
   155         memlimits = []
       
   156         if self.memorymax is not None:
       
   157             if curmem >= self.memorymax:
       
   158                 if logger:
       
   159                     logger.writememory()
       
   160                 return self.limitreached('memorymax')
       
   161             memlimits.append(self.memorymax)
       
   162         if self.memory is not None:
       
   163             if self.memory <= 0:
       
   164                 if logger:
       
   165                     logger.writememory()
       
   166                 return self.limitreached('memory')
       
   167             memlimits.append(curmem + self.memory)
       
   168             self.memory_at_start = curmem
       
   169 
       
   170         curtime = now()
       
   171         timelimits = []
       
   172         if self.endtime is not None:
       
   173             if curtime >= self.endtime:
       
   174                 return self.limitreached('timemax')
       
   175             timelimits.append(self.endtime - curtime)
       
   176         if self.time is not None:
       
   177             if self.time <= 0.0:
       
   178                 return self.limitreached('time')
       
   179             timelimits.append(self.time)
       
   180             self.time_at_start = curtime
       
   181         
       
   182         try:
       
   183             self.do_start()
       
   184         except error, e:
       
   185             if logger:
       
   186                 logger.write('%s: disabled by psyco.error:' % (
       
   187                     self.__class__.__name__), 4)
       
   188                 logger.write('    %s' % str(e), 3)
       
   189             return 0
       
   190         
       
   191         if memlimits:
       
   192             self.memlimits_args = (time.sleep, (self.MemoryTimerResolution,),
       
   193                                    self.check_memory, (min(memlimits),))
       
   194             self.alarms.append(_psyco.alarm(*self.memlimits_args))
       
   195         if timelimits:
       
   196             self.alarms.append(_psyco.alarm(time.sleep, (min(timelimits),),
       
   197                                             self.time_out))
       
   198         return 1
       
   199     
       
   200     def stop(self):
       
   201         for alarm in self.alarms:
       
   202             alarm.stop(0)
       
   203         for alarm in self.alarms:
       
   204             alarm.stop(1)   # wait for parallel threads to stop
       
   205         del self.alarms[:]
       
   206         if self.time is not None:
       
   207             self.time -= now() - self.time_at_start
       
   208         if self.memory is not None:
       
   209             self.memory -= _psyco.memory() - self.memory_at_start
       
   210 
       
   211         try:
       
   212             self.do_stop()
       
   213         except error:
       
   214             return 0
       
   215         return 1
       
   216 
       
   217     def check_memory(self, limit):
       
   218         if _psyco.memory() < limit:
       
   219             return self.memlimits_args
       
   220         go()
       
   221 
       
   222     def time_out(self):
       
   223         self.time = 0.0
       
   224         go()
       
   225 
       
   226     def limitreached(self, limitname):
       
   227         try:
       
   228             profilers.remove(self)
       
   229         except ValueError:
       
   230             pass
       
   231         if logger:
       
   232             logger.write('%s: disabled (%s limit reached)' % (
       
   233                 self.__class__.__name__, limitname), 4)
       
   234         return 0
       
   235 
       
   236 
       
   237 class FullCompiler(Profiler):
       
   238 
       
   239     def do_start(self):
       
   240         _psyco.profiling('f')
       
   241 
       
   242     def do_stop(self):
       
   243         _psyco.profiling('.')
       
   244 
       
   245 
       
   246 class RunOnly(Profiler):
       
   247 
       
   248     def do_start(self):
       
   249         _psyco.profiling('n')
       
   250 
       
   251     def do_stop(self):
       
   252         _psyco.profiling('.')
       
   253 
       
   254 
       
   255 class ChargeProfiler(Profiler):
       
   256 
       
   257     def __init__(self, watermark, parentframe):
       
   258         self.watermark = watermark
       
   259         self.parent2 = parentframe * 2.0
       
   260         self.lock = thread.allocate_lock()
       
   261 
       
   262     def init_charges(self):
       
   263         _psyco.statwrite(watermark = self.watermark,
       
   264                          parent2   = self.parent2)
       
   265 
       
   266     def do_stop(self):
       
   267         _psyco.profiling('.')
       
   268         _psyco.statwrite(callback = None)
       
   269 
       
   270 
       
   271 class ActiveProfiler(ChargeProfiler):
       
   272 
       
   273     def active_start(self):
       
   274         _psyco.profiling('p')
       
   275 
       
   276     def do_start(self):
       
   277         self.init_charges()
       
   278         self.active_start()
       
   279         _psyco.statwrite(callback = self.charge_callback)
       
   280 
       
   281     def charge_callback(self, frame, charge):
       
   282         tag(frame.f_code, frame.f_globals)
       
   283 
       
   284 
       
   285 class PassiveProfiler(ChargeProfiler):
       
   286 
       
   287     initial_charge_unit   = _psyco.statread('unit')
       
   288     reset_stats_after     = 120      # half-lives (maximum 200!)
       
   289     reset_limit           = initial_charge_unit * (2.0 ** reset_stats_after)
       
   290 
       
   291     def __init__(self, watermark, halflife, pollfreq, parentframe):
       
   292         ChargeProfiler.__init__(self, watermark, parentframe)
       
   293         self.pollfreq = pollfreq
       
   294         # self.progress is slightly more than 1.0, and computed so that
       
   295         # do_profile() will double the change_unit every 'halflife' seconds.
       
   296         self.progress = 2.0 ** (1.0 / (halflife * pollfreq))
       
   297 
       
   298     def reset(self):
       
   299         _psyco.statwrite(unit = self.initial_charge_unit, callback = None)
       
   300         _psyco.statreset()
       
   301         if logger:
       
   302             logger.write("%s: resetting stats" % self.__class__.__name__, 1)
       
   303 
       
   304     def passive_start(self):
       
   305         self.passivealarm_args = (time.sleep, (1.0 / self.pollfreq,),
       
   306                                   self.do_profile)
       
   307         self.alarms.append(_psyco.alarm(*self.passivealarm_args))
       
   308 
       
   309     def do_start(self):
       
   310         tag2bind()
       
   311         self.init_charges()
       
   312         self.passive_start()
       
   313 
       
   314     def do_profile(self):
       
   315         _psyco.statcollect()
       
   316         if logger:
       
   317             logger.dumpcharges()
       
   318         nunit = _psyco.statread('unit') * self.progress
       
   319         if nunit > self.reset_limit:
       
   320             self.reset()
       
   321         else:
       
   322             _psyco.statwrite(unit = nunit, callback = self.charge_callback)
       
   323         return self.passivealarm_args
       
   324 
       
   325     def charge_callback(self, frame, charge):
       
   326         trytobind(frame.f_code, frame.f_globals)
       
   327 
       
   328 
       
   329 class ActivePassiveProfiler(PassiveProfiler, ActiveProfiler):
       
   330 
       
   331     def do_start(self):
       
   332         self.init_charges()
       
   333         self.active_start()
       
   334         self.passive_start()
       
   335 
       
   336     def charge_callback(self, frame, charge):
       
   337         tag(frame.f_code, frame.f_globals)
       
   338 
       
   339 
       
   340 
       
   341 #
       
   342 # we register our own version of sys.settrace(), sys.setprofile()
       
   343 # and thread.start_new_thread().
       
   344 #
       
   345 
       
   346 def psyco_settrace(*args, **kw):
       
   347     "This is the Psyco-aware version of sys.settrace()."
       
   348     result = original_settrace(*args, **kw)
       
   349     go()
       
   350     return result
       
   351 
       
   352 def psyco_setprofile(*args, **kw):
       
   353     "This is the Psyco-aware version of sys.setprofile()."
       
   354     result = original_setprofile(*args, **kw)
       
   355     go()
       
   356     return result
       
   357 
       
   358 def psyco_thread_stub(callable, args, kw):
       
   359     _psyco.statcollect()
       
   360     if kw is None:
       
   361         return callable(*args)
       
   362     else:
       
   363         return callable(*args, **kw)
       
   364 
       
   365 def psyco_start_new_thread(callable, args, kw=None):
       
   366     "This is the Psyco-aware version of thread.start_new_thread()."
       
   367     return original_start_new_thread(psyco_thread_stub, (callable, args, kw))
       
   368 
       
   369 original_settrace         = sys.settrace
       
   370 original_setprofile       = sys.setprofile
       
   371 original_start_new_thread = thread.start_new_thread
       
   372 sys.settrace            = psyco_settrace
       
   373 sys.setprofile          = psyco_setprofile
       
   374 thread.start_new_thread = psyco_start_new_thread
       
   375 # hack to patch threading._start_new_thread if the module is
       
   376 # already loaded
       
   377 if (sys.modules.has_key('threading') and
       
   378     hasattr(sys.modules['threading'], '_start_new_thread')):
       
   379     sys.modules['threading']._start_new_thread = psyco_start_new_thread