mirror of
https://github.com/noDRM/DeDRM_tools.git
synced 2024-11-05 13:36:10 +06:00
380 lines
11 KiB
Python
380 lines
11 KiB
Python
|
###########################################################################
|
||
|
#
|
||
|
# Psyco profiler (Python part).
|
||
|
# Copyright (C) 2001-2002 Armin Rigo et.al.
|
||
|
|
||
|
"""Psyco profiler (Python part).
|
||
|
|
||
|
The implementation of the non-time-critical parts of the profiler.
|
||
|
See profile() and full() in core.py for the easy interface.
|
||
|
"""
|
||
|
###########################################################################
|
||
|
|
||
|
import _psyco
|
||
|
from support import *
|
||
|
import math, time, types, atexit
|
||
|
now = time.time
|
||
|
try:
|
||
|
import thread
|
||
|
except ImportError:
|
||
|
import dummy_thread as thread
|
||
|
|
||
|
|
||
|
# current profiler instance
|
||
|
current = None
|
||
|
|
||
|
# enabled profilers, in order of priority
|
||
|
profilers = []
|
||
|
|
||
|
# logger module (when enabled by core.log())
|
||
|
logger = None
|
||
|
|
||
|
# a lock for a thread-safe go()
|
||
|
go_lock = thread.allocate_lock()
|
||
|
|
||
|
def go(stop=0):
|
||
|
# run the highest-priority profiler in 'profilers'
|
||
|
global current
|
||
|
go_lock.acquire()
|
||
|
try:
|
||
|
prev = current
|
||
|
if stop:
|
||
|
del profilers[:]
|
||
|
if prev:
|
||
|
if profilers and profilers[0] is prev:
|
||
|
return # best profiler already running
|
||
|
prev.stop()
|
||
|
current = None
|
||
|
for p in profilers[:]:
|
||
|
if p.start():
|
||
|
current = p
|
||
|
if logger: # and p is not prev:
|
||
|
logger.write("%s: starting" % p.__class__.__name__, 5)
|
||
|
return
|
||
|
finally:
|
||
|
go_lock.release()
|
||
|
# no profiler is running now
|
||
|
if stop:
|
||
|
if logger:
|
||
|
logger.writefinalstats()
|
||
|
else:
|
||
|
tag2bind()
|
||
|
|
||
|
atexit.register(go, 1)
|
||
|
|
||
|
|
||
|
def buildfncache(globals, cache):
|
||
|
if hasattr(types.IntType, '__dict__'):
|
||
|
clstypes = (types.ClassType, types.TypeType)
|
||
|
else:
|
||
|
clstypes = types.ClassType
|
||
|
for x in globals.values():
|
||
|
if isinstance(x, types.MethodType):
|
||
|
x = x.im_func
|
||
|
if isinstance(x, types.FunctionType):
|
||
|
cache[x.func_code] = x, ''
|
||
|
elif isinstance(x, clstypes):
|
||
|
for y in x.__dict__.values():
|
||
|
if isinstance(y, types.MethodType):
|
||
|
y = y.im_func
|
||
|
if isinstance(y, types.FunctionType):
|
||
|
cache[y.func_code] = y, x.__name__
|
||
|
|
||
|
# code-to-function mapping (cache)
|
||
|
function_cache = {}
|
||
|
|
||
|
def trytobind(co, globals, log=1):
|
||
|
try:
|
||
|
f, clsname = function_cache[co]
|
||
|
except KeyError:
|
||
|
buildfncache(globals, function_cache)
|
||
|
try:
|
||
|
f, clsname = function_cache[co]
|
||
|
except KeyError:
|
||
|
if logger:
|
||
|
logger.write('warning: cannot find function %s in %s' %
|
||
|
(co.co_name, globals.get('__name__', '?')), 3)
|
||
|
return # give up
|
||
|
if logger and log:
|
||
|
modulename = globals.get('__name__', '?')
|
||
|
if clsname:
|
||
|
modulename += '.' + clsname
|
||
|
logger.write('bind function: %s.%s' % (modulename, co.co_name), 1)
|
||
|
f.func_code = _psyco.proxycode(f)
|
||
|
|
||
|
|
||
|
# the list of code objects that have been tagged
|
||
|
tagged_codes = []
|
||
|
|
||
|
def tag(co, globals):
|
||
|
if logger:
|
||
|
try:
|
||
|
f, clsname = function_cache[co]
|
||
|
except KeyError:
|
||
|
buildfncache(globals, function_cache)
|
||
|
try:
|
||
|
f, clsname = function_cache[co]
|
||
|
except KeyError:
|
||
|
clsname = '' # give up
|
||
|
modulename = globals.get('__name__', '?')
|
||
|
if clsname:
|
||
|
modulename += '.' + clsname
|
||
|
logger.write('tag function: %s.%s' % (modulename, co.co_name), 1)
|
||
|
tagged_codes.append((co, globals))
|
||
|
_psyco.turbo_frame(co)
|
||
|
_psyco.turbo_code(co)
|
||
|
|
||
|
def tag2bind():
|
||
|
if tagged_codes:
|
||
|
if logger:
|
||
|
logger.write('profiling stopped, binding %d functions' %
|
||
|
len(tagged_codes), 2)
|
||
|
for co, globals in tagged_codes:
|
||
|
trytobind(co, globals, 0)
|
||
|
function_cache.clear()
|
||
|
del tagged_codes[:]
|
||
|
|
||
|
|
||
|
class Profiler:
|
||
|
MemoryTimerResolution = 0.103
|
||
|
|
||
|
def run(self, memory, time, memorymax, timemax):
|
||
|
self.memory = memory
|
||
|
self.memorymax = memorymax
|
||
|
self.time = time
|
||
|
if timemax is None:
|
||
|
self.endtime = None
|
||
|
else:
|
||
|
self.endtime = now() + timemax
|
||
|
self.alarms = []
|
||
|
profilers.append(self)
|
||
|
go()
|
||
|
|
||
|
def start(self):
|
||
|
curmem = _psyco.memory()
|
||
|
memlimits = []
|
||
|
if self.memorymax is not None:
|
||
|
if curmem >= self.memorymax:
|
||
|
if logger:
|
||
|
logger.writememory()
|
||
|
return self.limitreached('memorymax')
|
||
|
memlimits.append(self.memorymax)
|
||
|
if self.memory is not None:
|
||
|
if self.memory <= 0:
|
||
|
if logger:
|
||
|
logger.writememory()
|
||
|
return self.limitreached('memory')
|
||
|
memlimits.append(curmem + self.memory)
|
||
|
self.memory_at_start = curmem
|
||
|
|
||
|
curtime = now()
|
||
|
timelimits = []
|
||
|
if self.endtime is not None:
|
||
|
if curtime >= self.endtime:
|
||
|
return self.limitreached('timemax')
|
||
|
timelimits.append(self.endtime - curtime)
|
||
|
if self.time is not None:
|
||
|
if self.time <= 0.0:
|
||
|
return self.limitreached('time')
|
||
|
timelimits.append(self.time)
|
||
|
self.time_at_start = curtime
|
||
|
|
||
|
try:
|
||
|
self.do_start()
|
||
|
except error, e:
|
||
|
if logger:
|
||
|
logger.write('%s: disabled by psyco.error:' % (
|
||
|
self.__class__.__name__), 4)
|
||
|
logger.write(' %s' % str(e), 3)
|
||
|
return 0
|
||
|
|
||
|
if memlimits:
|
||
|
self.memlimits_args = (time.sleep, (self.MemoryTimerResolution,),
|
||
|
self.check_memory, (min(memlimits),))
|
||
|
self.alarms.append(_psyco.alarm(*self.memlimits_args))
|
||
|
if timelimits:
|
||
|
self.alarms.append(_psyco.alarm(time.sleep, (min(timelimits),),
|
||
|
self.time_out))
|
||
|
return 1
|
||
|
|
||
|
def stop(self):
|
||
|
for alarm in self.alarms:
|
||
|
alarm.stop(0)
|
||
|
for alarm in self.alarms:
|
||
|
alarm.stop(1) # wait for parallel threads to stop
|
||
|
del self.alarms[:]
|
||
|
if self.time is not None:
|
||
|
self.time -= now() - self.time_at_start
|
||
|
if self.memory is not None:
|
||
|
self.memory -= _psyco.memory() - self.memory_at_start
|
||
|
|
||
|
try:
|
||
|
self.do_stop()
|
||
|
except error:
|
||
|
return 0
|
||
|
return 1
|
||
|
|
||
|
def check_memory(self, limit):
|
||
|
if _psyco.memory() < limit:
|
||
|
return self.memlimits_args
|
||
|
go()
|
||
|
|
||
|
def time_out(self):
|
||
|
self.time = 0.0
|
||
|
go()
|
||
|
|
||
|
def limitreached(self, limitname):
|
||
|
try:
|
||
|
profilers.remove(self)
|
||
|
except ValueError:
|
||
|
pass
|
||
|
if logger:
|
||
|
logger.write('%s: disabled (%s limit reached)' % (
|
||
|
self.__class__.__name__, limitname), 4)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
class FullCompiler(Profiler):
|
||
|
|
||
|
def do_start(self):
|
||
|
_psyco.profiling('f')
|
||
|
|
||
|
def do_stop(self):
|
||
|
_psyco.profiling('.')
|
||
|
|
||
|
|
||
|
class RunOnly(Profiler):
|
||
|
|
||
|
def do_start(self):
|
||
|
_psyco.profiling('n')
|
||
|
|
||
|
def do_stop(self):
|
||
|
_psyco.profiling('.')
|
||
|
|
||
|
|
||
|
class ChargeProfiler(Profiler):
|
||
|
|
||
|
def __init__(self, watermark, parentframe):
|
||
|
self.watermark = watermark
|
||
|
self.parent2 = parentframe * 2.0
|
||
|
self.lock = thread.allocate_lock()
|
||
|
|
||
|
def init_charges(self):
|
||
|
_psyco.statwrite(watermark = self.watermark,
|
||
|
parent2 = self.parent2)
|
||
|
|
||
|
def do_stop(self):
|
||
|
_psyco.profiling('.')
|
||
|
_psyco.statwrite(callback = None)
|
||
|
|
||
|
|
||
|
class ActiveProfiler(ChargeProfiler):
|
||
|
|
||
|
def active_start(self):
|
||
|
_psyco.profiling('p')
|
||
|
|
||
|
def do_start(self):
|
||
|
self.init_charges()
|
||
|
self.active_start()
|
||
|
_psyco.statwrite(callback = self.charge_callback)
|
||
|
|
||
|
def charge_callback(self, frame, charge):
|
||
|
tag(frame.f_code, frame.f_globals)
|
||
|
|
||
|
|
||
|
class PassiveProfiler(ChargeProfiler):
|
||
|
|
||
|
initial_charge_unit = _psyco.statread('unit')
|
||
|
reset_stats_after = 120 # half-lives (maximum 200!)
|
||
|
reset_limit = initial_charge_unit * (2.0 ** reset_stats_after)
|
||
|
|
||
|
def __init__(self, watermark, halflife, pollfreq, parentframe):
|
||
|
ChargeProfiler.__init__(self, watermark, parentframe)
|
||
|
self.pollfreq = pollfreq
|
||
|
# self.progress is slightly more than 1.0, and computed so that
|
||
|
# do_profile() will double the change_unit every 'halflife' seconds.
|
||
|
self.progress = 2.0 ** (1.0 / (halflife * pollfreq))
|
||
|
|
||
|
def reset(self):
|
||
|
_psyco.statwrite(unit = self.initial_charge_unit, callback = None)
|
||
|
_psyco.statreset()
|
||
|
if logger:
|
||
|
logger.write("%s: resetting stats" % self.__class__.__name__, 1)
|
||
|
|
||
|
def passive_start(self):
|
||
|
self.passivealarm_args = (time.sleep, (1.0 / self.pollfreq,),
|
||
|
self.do_profile)
|
||
|
self.alarms.append(_psyco.alarm(*self.passivealarm_args))
|
||
|
|
||
|
def do_start(self):
|
||
|
tag2bind()
|
||
|
self.init_charges()
|
||
|
self.passive_start()
|
||
|
|
||
|
def do_profile(self):
|
||
|
_psyco.statcollect()
|
||
|
if logger:
|
||
|
logger.dumpcharges()
|
||
|
nunit = _psyco.statread('unit') * self.progress
|
||
|
if nunit > self.reset_limit:
|
||
|
self.reset()
|
||
|
else:
|
||
|
_psyco.statwrite(unit = nunit, callback = self.charge_callback)
|
||
|
return self.passivealarm_args
|
||
|
|
||
|
def charge_callback(self, frame, charge):
|
||
|
trytobind(frame.f_code, frame.f_globals)
|
||
|
|
||
|
|
||
|
class ActivePassiveProfiler(PassiveProfiler, ActiveProfiler):
|
||
|
|
||
|
def do_start(self):
|
||
|
self.init_charges()
|
||
|
self.active_start()
|
||
|
self.passive_start()
|
||
|
|
||
|
def charge_callback(self, frame, charge):
|
||
|
tag(frame.f_code, frame.f_globals)
|
||
|
|
||
|
|
||
|
|
||
|
#
|
||
|
# we register our own version of sys.settrace(), sys.setprofile()
|
||
|
# and thread.start_new_thread().
|
||
|
#
|
||
|
|
||
|
def psyco_settrace(*args, **kw):
|
||
|
"This is the Psyco-aware version of sys.settrace()."
|
||
|
result = original_settrace(*args, **kw)
|
||
|
go()
|
||
|
return result
|
||
|
|
||
|
def psyco_setprofile(*args, **kw):
|
||
|
"This is the Psyco-aware version of sys.setprofile()."
|
||
|
result = original_setprofile(*args, **kw)
|
||
|
go()
|
||
|
return result
|
||
|
|
||
|
def psyco_thread_stub(callable, args, kw):
|
||
|
_psyco.statcollect()
|
||
|
if kw is None:
|
||
|
return callable(*args)
|
||
|
else:
|
||
|
return callable(*args, **kw)
|
||
|
|
||
|
def psyco_start_new_thread(callable, args, kw=None):
|
||
|
"This is the Psyco-aware version of thread.start_new_thread()."
|
||
|
return original_start_new_thread(psyco_thread_stub, (callable, args, kw))
|
||
|
|
||
|
original_settrace = sys.settrace
|
||
|
original_setprofile = sys.setprofile
|
||
|
original_start_new_thread = thread.start_new_thread
|
||
|
sys.settrace = psyco_settrace
|
||
|
sys.setprofile = psyco_setprofile
|
||
|
thread.start_new_thread = psyco_start_new_thread
|
||
|
# hack to patch threading._start_new_thread if the module is
|
||
|
# already loaded
|
||
|
if ('threading' in sys.modules and
|
||
|
hasattr(sys.modules['threading'], '_start_new_thread')):
|
||
|
sys.modules['threading']._start_new_thread = psyco_start_new_thread
|