Mercurial > hg-stable
changeset 29794:5d44197c208b
profiling: make profiling functions context managers (API)
This makes profiling more flexible since we can now call multiple
functions when a profiler is active. But the real reason for this
is to enable a future consumer to profile a function that returns
a generator. We can't do this from the profiling function itself
because functions can either be generators or have return values:
they can't be both. So therefore it isn't possible to have a generic
profiling function that can both consume and re-emit a generator
and return a value.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sun, 14 Aug 2016 18:25:22 -0700 |
parents | 97bfc2e5fba5 |
children | e3501546f7e4 |
files | mercurial/dispatch.py mercurial/profiling.py |
diffstat | 2 files changed, 26 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/dispatch.py Sun Aug 14 16:35:58 2016 -0700 +++ b/mercurial/dispatch.py Sun Aug 14 18:25:22 2016 -0700 @@ -909,7 +909,8 @@ raise error.CommandError(cmd, _("invalid arguments")) if ui.configbool('profiling', 'enabled'): - return profiling.profile(ui, checkargs) + with profiling.profile(ui): + return checkargs() else: return checkargs()
--- a/mercurial/profiling.py Sun Aug 14 16:35:58 2016 -0700 +++ b/mercurial/profiling.py Sun Aug 14 18:25:22 2016 -0700 @@ -7,6 +7,7 @@ from __future__ import absolute_import, print_function +import contextlib import os import sys import time @@ -17,7 +18,8 @@ util, ) -def lsprofile(ui, func, fp): +@contextlib.contextmanager +def lsprofile(ui, fp): format = ui.config('profiling', 'format', default='text') field = ui.config('profiling', 'sort', default='inlinetime') limit = ui.configint('profiling', 'limit', default=30) @@ -37,7 +39,7 @@ p = lsprof.Profiler() p.enable(subcalls=True) try: - return func() + yield finally: p.disable() @@ -51,7 +53,8 @@ stats.sort(field) stats.pprint(limit=limit, file=fp, climit=climit) -def flameprofile(ui, func, fp): +@contextlib.contextmanager +def flameprofile(ui, fp): try: from flamegraph import flamegraph except ImportError: @@ -67,7 +70,7 @@ start_time = time.clock() try: thread.start() - func() + yield finally: thread.stop() thread.join() @@ -75,7 +78,8 @@ time.clock() - start_time, thread.num_frames(), thread.num_frames(unique=True))) -def statprofile(ui, func, fp): +@contextlib.contextmanager +def statprofile(ui, fp): try: import statprof except ImportError: @@ -90,13 +94,18 @@ statprof.start() try: - return func() + yield finally: statprof.stop() statprof.display(fp) -def profile(ui, fn): - """Profile a function call.""" +@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. + """ profiler = os.getenv('HGPROF') if profiler is None: profiler = ui.config('profiling', 'type', default='ls') @@ -116,11 +125,15 @@ try: if profiler == 'ls': - return lsprofile(ui, fn, fp) + proffn = lsprofile elif profiler == 'flame': - return flameprofile(ui, fn, fp) + proffn = flameprofile else: - return statprofile(ui, fn, fp) + proffn = statprofile + + with proffn(ui, fp): + yield + finally: if output: if output == 'blackbox':