##// END OF EJS Templates
tests: add more tests around hook output and getbundle...
tests: add more tests around hook output and getbundle The previous tests around hook output only tested Python hooks. Let's add some shell hooks in for additional test coverage. Differential Revision: https://phab.mercurial-scm.org/D2550

File last commit:

r36418:7b86aa31 default
r36632:1fa02265 default
Show More
profiling.py
238 lines | 7.4 KiB | text/x-python | PythonLexer
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 # profiling.py - profiling functions
#
# Copyright 2016 Gregory Szorc <gregory.szorc@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import, print_function
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 import contextlib
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
from .i18n import _
from . import (
Pulkit Goyal
py3: replace pycompat.getenv with encoding.environ.get...
r30820 encoding,
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 error,
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 extensions,
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 util,
)
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 def _loadprofiler(ui, profiler):
"""load profiler extension. return profile method, or None on failure"""
extname = profiler
extensions.loadall(ui, whitelist=[extname])
try:
mod = extensions.find(extname)
except KeyError:
return None
else:
return getattr(mod, 'profile', None)
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def lsprofile(ui, fp):
Jun Wu
codemod: register core configitems using a script...
r33499 format = ui.config('profiling', 'format')
field = ui.config('profiling', 'sort')
limit = ui.configint('profiling', 'limit')
climit = ui.configint('profiling', 'nested')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
if format not in ['text', 'kcachegrind']:
ui.warn(_("unrecognized profiling format '%s'"
" - Ignored\n") % format)
format = 'text'
try:
from . import lsprof
except ImportError:
raise error.Abort(_(
'lsprof not available - install from '
'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
p = lsprof.Profiler()
p.enable(subcalls=True)
try:
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
p.disable()
if format == 'kcachegrind':
from . import lsprofcalltree
calltree = lsprofcalltree.KCacheGrind(p)
calltree.output(fp)
else:
# format == 'text'
stats = lsprof.Stats(p.getstats())
stats.sort(field)
stats.pprint(limit=limit, file=fp, climit=climit)
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def flameprofile(ui, fp):
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 try:
from flamegraph import flamegraph
except ImportError:
raise error.Abort(_(
'flamegraph not available - install from '
'https://github.com/evanhempel/python-flamegraph'))
# developer config: profiling.freq
Jun Wu
codemod: register core configitems using a script...
r33499 freq = ui.configint('profiling', 'freq')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 filter_ = None
collapse_recursion = True
thread = flamegraph.ProfileThread(fp, 1.0 / freq,
filter_, collapse_recursion)
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 start_time = util.timer()
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 try:
thread.start()
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
thread.stop()
thread.join()
print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 util.timer() - start_time, thread.num_frames(),
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 thread.num_frames(unique=True)))
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def statprofile(ui, fp):
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 from . import statprof
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
Jun Wu
codemod: register core configitems using a script...
r33499 freq = ui.configint('profiling', 'freq')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 if freq > 0:
Gregory Szorc
profiling: don't error with statprof when profiling has already started...
r29785 # Cannot reset when profiler is already active. So silently no-op.
if statprof.state.profile_level == 0:
statprof.reset(freq)
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 else:
ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 statprof.start(mechanism='thread')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 try:
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 data = statprof.stop()
Jun Wu
codemod: register core configitems using a script...
r33499 profformat = ui.config('profiling', 'statformat')
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316
formats = {
'byline': statprof.DisplayFormats.ByLine,
'bymethod': statprof.DisplayFormats.ByMethod,
'hotpath': statprof.DisplayFormats.Hotpath,
'json': statprof.DisplayFormats.Json,
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 'chrome': statprof.DisplayFormats.Chrome,
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 }
if profformat in formats:
displayformat = formats[profformat]
else:
ui.warn(_('unknown profiler output format: %s\n') % profformat)
displayformat = statprof.DisplayFormats.Hotpath
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 kwargs = {}
def fraction(s):
profiling: cope with configwith default value handling changes...
r32978 if isinstance(s, (float, int)):
return float(s)
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 if s.endswith('%'):
v = float(s[:-1]) / 100
else:
v = float(s)
if 0 <= v <= 1:
return v
raise ValueError(s)
if profformat == 'chrome':
showmin = ui.configwith(fraction, 'profiling', 'showmin', 0.005)
Boris Feld
configitems: register the 'profiling.showmax' config
r34411 showmax = ui.configwith(fraction, 'profiling', 'showmax')
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 kwargs.update(minthreshold=showmin, maxthreshold=showmax)
Gregory Szorc
profiling: allow configuring minimum display threshold for hotpath...
r32851 elif profformat == 'hotpath':
Gregory Szorc
check-config: syntax to allow inconsistent config values...
r33192 # inconsistent config: profiling.showmin
Gregory Szorc
profiling: allow configuring minimum display threshold for hotpath...
r32851 limit = ui.configwith(fraction, 'profiling', 'showmin', 0.05)
Pulkit Goyal
py3: fix handling of keyword arguments at more places...
r36418 kwargs[r'limit'] = limit
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930
statprof.display(fp, data=data, format=displayformat, **kwargs)
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: upgrade the "profile" context manager to a full class...
r32783 class profile(object):
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 """Start profiling.
Profiling is active when the context manager is active. When the context
manager exits, profiling results will be written to the configured output.
"""
profile: introduce a knob to control if the context is actually profiling...
r32785 def __init__(self, ui, enabled=True):
profile: upgrade the "profile" context manager to a full class...
r32783 self._ui = ui
self._output = None
self._fp = None
profile: use explicit logic to control file closing...
r32805 self._fpdoclose = True
profile: upgrade the "profile" context manager to a full class...
r32783 self._profiler = None
profile: introduce a knob to control if the context is actually profiling...
r32785 self._enabled = enabled
profile: introduce a "start" method to the profile context...
r32784 self._entered = False
self._started = False
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: upgrade the "profile" context manager to a full class...
r32783 def __enter__(self):
profile: introduce a "start" method to the profile context...
r32784 self._entered = True
profile: introduce a knob to control if the context is actually profiling...
r32785 if self._enabled:
self.start()
profile: make the contextmanager object available to the callers...
r32786 return self
profile: introduce a "start" method to the profile context...
r32784
def start(self):
"""Start profiling.
The profiling will stop at the context exit.
If the profiler was already started, this has no effect."""
if not self._entered:
raise error.ProgrammingError()
if self._started:
return
self._started = True
profile: upgrade the "profile" context manager to a full class...
r32783 profiler = encoding.environ.get('HGPROF')
proffn = None
if profiler is None:
Boris Feld
configitems: register the 'profiling.type' config
r34413 profiler = self._ui.config('profiling', 'type')
profile: upgrade the "profile" context manager to a full class...
r32783 if profiler not in ('ls', 'stat', 'flame'):
# try load profiler from extension with the same name
proffn = _loadprofiler(self._ui, profiler)
if proffn is None:
self._ui.warn(_("unrecognized profiler '%s' - ignored\n")
% profiler)
profiler = 'stat'
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: upgrade the "profile" context manager to a full class...
r32783 self._output = self._ui.config('profiling', 'output')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: close 'fp' on error within '__enter__'...
r32808 try:
profile: indent part of '__enter__'...
r32807 if self._output == 'blackbox':
self._fp = util.stringio()
elif self._output:
path = self._ui.expandpath(self._output)
self._fp = open(path, 'wb')
else:
self._fpdoclose = False
self._fp = self._ui.ferr
profile: upgrade the "profile" context manager to a full class...
r32783
profile: indent part of '__enter__'...
r32807 if proffn is not None:
pass
elif profiler == 'ls':
proffn = lsprofile
elif profiler == 'flame':
proffn = flameprofile
else:
proffn = statprofile
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783
profile: indent part of '__enter__'...
r32807 self._profiler = proffn(self._ui, self._fp)
self._profiler.__enter__()
profile: close 'fp' on error within '__enter__'...
r32808 except: # re-raises
self._closefp()
raise
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783
profile: upgrade the "profile" context manager to a full class...
r32783 def __exit__(self, exception_type, exception_value, traceback):
profile: properly propagate exception from the sub-context manager...
r32810 propagate = None
profile: close 'fp' in all cases...
r32809 if self._profiler is not None:
profile: properly propagate exception from the sub-context manager...
r32810 propagate = self._profiler.__exit__(exception_type, exception_value,
traceback)
profile: close 'fp' in all cases...
r32809 if self._output == 'blackbox':
val = 'Profile:\n%s' % self._fp.getvalue()
# ui.log treats the input as a format string,
# so we need to escape any % signs.
val = val.replace('%', '%%')
self._ui.log('profile', val)
profile: remove now useless indent...
r32806 self._closefp()
profile: properly propagate exception from the sub-context manager...
r32810 return propagate
profiling: move 'fp' closing logic into its own function...
r32804
def _closefp(self):
profile: use explicit logic to control file closing...
r32805 if self._fpdoclose and self._fp is not None:
self._fp.close()