##// END OF EJS Templates
wireprotov2: define and implement "manifestdata" command...
wireprotov2: define and implement "manifestdata" command The added command can be used for obtaining manifest data. Given a manifest path and set of manifest nodes, data about manifests can be retrieved. Unlike changeset data, we wish to emit deltas to describe manifest revisions. So the command uses the relatively new API for building delta requests and emitting them. The code calls into deltaparent(), which I'm not very keen of. There's still work to be done in delta generation land so implementation details of storage (e.g. exactly one delta is stored/available) don't creep into higher levels. But we can worry about this later (there is already a TODO on imanifestorage tracking this). On the subject of parent deltas, the server assumes parent revisions exist on the receiving end. This is obviously wrong for shallow clone. I've added TODOs to add a mechanism to the command to allow clients to specify desired behavior. This shouldn't be too difficult to implement. Another big change is that the client must explicitly request manifest nodes to retrieve. This is a major departure from "getbundle," where the server derives relevant manifests as it iterates changesets and sends them automatically. As implemented, the client must transmit each requested node to the server. At 20 bytes per node, we're looking at 2 MB per 100,000 nodes. Plus wire encoding overhead. This isn't ideal for clients with limited upload bandwidth. I plan to address this in the future by allowing alternate mechanisms for defining the revisions to retrieve. One idea is to define a range of changeset revisions whose manifest revisions to retrieve (similar to how "changesetdata" works). We almost certainly want an API to look up an individual manifest by node. And that's where I've chosen to start with the implementation. Again, a theme of this early exchangev2 work is I want to start by building primitives for accessing raw repository data first and see how far we can get with those before we need more complexity. Differential Revision: https://phab.mercurial-scm.org/D4488

File last commit:

r38279:15a1e37f default
r39673:c7a7c7e8 default
Show More
profiling.py
251 lines | 7.9 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,
Matt Harbison
profile: colorize output on Windows...
r36701 pycompat,
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)
Boris Feld
profiling: introduce a "profiling.time-track" option...
r38279 track = ui.config('profiling', 'time-track')
statprof.start(mechanism='thread', track=track)
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316
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')
Matt Harbison
profile: colorize output on Windows...
r36701 elif pycompat.iswindows:
# parse escape sequence by win32print()
class uifp(object):
def __init__(self, ui):
self._ui = ui
def write(self, data):
self._ui.write_err(data)
def flush(self):
self._ui.flush()
self._fpdoclose = False
self._fp = uifp(self._ui)
profile: indent part of '__enter__'...
r32807 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()