diff mercurial/dispatch.py @ 29781:2654a0aac80d

profiling: move profiling code from dispatch.py (API) Currently, profiling code lives in dispatch.py, which is a low-level module centered around command dispatch. Furthermore, dispatch.py imports a lot of other modules, meaning that importing dispatch.py to get at profiling functionality would often result in a module import cycle. Profiling is a generic activity. It shouldn't be limited to command dispatch. This patch moves profiling code from dispatch.py to the new profiling.py. The low-level "run a profiler against a function" functions have been moved verbatim. The code for determining how to invoke the profiler has been extracted to its own function. I decided to create a new module rather than stick this code elsewhere (such as util.py) because util.py is already quite large. And, I foresee this file growing larger once Facebook's profiling enhancements get added to it.
author Gregory Szorc <gregory.szorc@gmail.com>
date Sun, 14 Aug 2016 16:30:44 -0700
parents 39149b6036e6
children 97bfc2e5fba5
line wrap: on
line diff
--- a/mercurial/dispatch.py	Mon Aug 15 12:26:02 2016 -0400
+++ b/mercurial/dispatch.py	Sun Aug 14 16:30:44 2016 -0700
@@ -34,6 +34,7 @@
     fileset,
     hg,
     hook,
+    profiling,
     revset,
     templatefilters,
     templatekw,
@@ -892,85 +893,6 @@
         if repo and repo != req.repo:
             repo.close()
 
-def lsprofile(ui, func, fp):
-    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:
-        return func()
-    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)
-
-def flameprofile(ui, func, fp):
-    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()
-        func()
-    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)))
-
-
-def statprofile(ui, func, fp):
-    try:
-        import statprof
-    except ImportError:
-        raise error.Abort(_(
-            'statprof not available - install using "easy_install statprof"'))
-
-    freq = ui.configint('profiling', 'freq', default=1000)
-    if freq > 0:
-        statprof.reset(freq)
-    else:
-        ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
-
-    statprof.start()
-    try:
-        return func()
-    finally:
-        statprof.stop()
-        statprof.display(fp)
-
 def _runcommand(ui, options, cmd, cmdfunc):
     """Enables the profiler if applicable.
 
@@ -983,39 +905,7 @@
             raise error.CommandError(cmd, _("invalid arguments"))
 
     if options['profile'] or ui.configbool('profiling', 'enabled'):
-        profiler = os.getenv('HGPROF')
-        if profiler is None:
-            profiler = ui.config('profiling', 'type', default='ls')
-        if profiler not in ('ls', 'stat', 'flame'):
-            ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
-            profiler = 'ls'
-
-        output = ui.config('profiling', 'output')
-
-        if output == 'blackbox':
-            fp = util.stringio()
-        elif output:
-            path = ui.expandpath(output)
-            fp = open(path, 'wb')
-        else:
-            fp = sys.stderr
-
-        try:
-            if profiler == 'ls':
-                return lsprofile(ui, checkargs, fp)
-            elif profiler == 'flame':
-                return flameprofile(ui, checkargs, fp)
-            else:
-                return statprofile(ui, checkargs, fp)
-        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()
+        return profiling.profile(ui, checkargs)
     else:
         return checkargs()