##// END OF EJS Templates
manifest: refer to bytestrings as bytes, not str...
manifest: refer to bytestrings as bytes, not str Required on Python 3.

File last commit:

r30975:22fbca1d default
r31531:326bca54 default
Show More
profiling.py
192 lines | 5.8 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,
util,
)
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def lsprofile(ui, fp):
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 format = ui.config('profiling', 'format', default='text')
field = ui.config('profiling', 'sort', default='inlinetime')
limit = ui.configint('profiling', 'limit', default=30)
climit = ui.configint('profiling', 'nested', default=0)
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
freq = ui.configint('profiling', 'freq', default=1000)
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
freq = ui.configint('profiling', 'freq', default=1000)
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()
profformat = ui.config('profiling', 'statformat', 'hotpath')
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):
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)
showmax = ui.configwith(fraction, 'profiling', 'showmax', 0.999)
kwargs.update(minthreshold=showmin, maxthreshold=showmax)
statprof.display(fp, data=data, format=displayformat, **kwargs)
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def profile(ui):
"""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.
"""
Pulkit Goyal
py3: replace pycompat.getenv with encoding.environ.get...
r30820 profiler = encoding.environ.get('HGPROF')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 if profiler is None:
Gregory Szorc
profiling: make statprof the default profiler (BC)...
r30317 profiler = ui.config('profiling', 'type', default='stat')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 if profiler not in ('ls', 'stat', 'flame'):
ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
Gregory Szorc
profiling: make statprof the default profiler (BC)...
r30317 profiler = 'stat'
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
output = ui.config('profiling', 'output')
if output == 'blackbox':
fp = util.stringio()
elif output:
path = ui.expandpath(output)
fp = open(path, 'wb')
else:
Yuya Nishihara
profiling: obtain stderr from ui...
r30322 fp = ui.ferr
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
try:
if profiler == 'ls':
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 proffn = lsprofile
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 elif profiler == 'flame':
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 proffn = flameprofile
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 else:
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 proffn = statprofile
with proffn(ui, fp):
yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
if output:
if output == 'blackbox':
val = 'Profile:\n%s' % fp.getvalue()
# ui.log treats the input as a format string,
# so we need to escape any % signs.
val = val.replace('%', '%%')
ui.log('profile', val)
fp.close()
Gregory Szorc
profiling: add a context manager that no-ops if profiling isn't enabled...
r29784
@contextlib.contextmanager
def maybeprofile(ui):
"""Profile if enabled, else do nothing.
This context manager can be used to optionally profile if profiling
is enabled. Otherwise, it does nothing.
The purpose of this context manager is to make calling code simpler:
just use a single code path for calling into code you may want to profile
and this function determines whether to start profiling.
"""
if ui.configbool('profiling', 'enabled'):
with profile(ui):
yield
else:
yield