##// END OF EJS Templates
dispatch: protect against malicious 'hg serve --stdio' invocations (sec)...
dispatch: protect against malicious 'hg serve --stdio' invocations (sec) Some shared-ssh installations assume that 'hg serve --stdio' is a safe command to run for minimally trusted users. Unfortunately, the messy implementation of argument parsing here meant that trying to access a repo named '--debugger' would give the user a pdb prompt, thereby sidestepping any hoped-for sandboxing. Serving repositories over HTTP(S) is unaffected. We're not currently hardening any subcommands other than 'serve'. If your service exposes other commands to users with arbitrary repository names, it is imperative that you defend against repository names of '--debugger' and anything starting with '--config'. The read-only mode of hg-ssh stopped working because it provided its hook configuration to "hg serve --stdio" via --config parameter. This is banned for security reasons now. This patch switches it to directly call ui.setconfig(). If your custom hosting infrastructure relies on passing --config to "hg serve --stdio", you'll need to find a different way to get that configuration into Mercurial, either by using ui.setconfig() as hg-ssh does in this patch, or by placing an hgrc file someplace where Mercurial will read it. mitrandir@fb.com provided some extra fixes for the dispatch code and for hg-ssh in places that I overlooked.

File last commit:

r30975:22fbca1d default
r32050:77eaf953 4.1.3 stable
Show More
profiling.py
176 lines | 5.2 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 import time
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)
start_time = time.clock()
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.' % (
time.clock() - start_time, thread.num_frames(),
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,
}
if profformat in formats:
displayformat = formats[profformat]
else:
ui.warn(_('unknown profiler output format: %s\n') % profformat)
displayformat = statprof.DisplayFormats.Hotpath
statprof.display(fp, data=data, format=displayformat)
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