##// END OF EJS Templates
cmdutil: use sorted(dict) instead of x = dict.keys(); x.sort()...
cmdutil: use sorted(dict) instead of x = dict.keys(); x.sort() The former both does less work and has the virtue of working on Python 3.

File last commit:

r32417:f40dc6f7 default
r32528:9f56d462 default
Show More
profiling.py
210 lines | 6.3 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):
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')
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 proffn = None
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'):
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 # try load profiler from extension with the same name
proffn = _loadprofiler(ui, profiler)
if proffn is None:
ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
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:
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 if proffn is not None:
pass
elif 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