[PATCH] dispatch: add support for statprof as a profiler

Bryan O'Sullivan bos at serpentine.com
Mon Apr 9 16:16:39 CDT 2012


# HG changeset patch
# User Bryan O'Sullivan <bryano at fb.com>
# Date 1334004525 25200
# Node ID fad43027223f9336534cfc592ed150e5ebbf822c
# Parent  0f5e72a3fbd2e9df7e9ce57874187e4ffb0f4556
dispatch: add support for statprof as a profiler

This can be selected using the config variable profiling.type or
the environment variable HGPROF ("ls" for the default, "stat" for
statprof).  The only tuneable is the frequency, profiling.freq,
which defaults to 1000 Hz.

If statprof is not available, a warning is printed.

diff -r 0f5e72a3fbd2 -r fad43027223f mercurial/dispatch.py
--- a/mercurial/dispatch.py	Mon Apr 09 08:50:02 2012 -0700
+++ b/mercurial/dispatch.py	Mon Apr 09 13:48:45 2012 -0700
@@ -687,6 +687,59 @@
         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')
+    climit = ui.configint('profiling', 'nested', default=5)
+
+    if not format in ['text', 'kcachegrind']:
+        ui.warn(_("unrecognized profiling format '%s'"
+                    " - Ignored\n") % format)
+        format = 'text'
+
+    try:
+        from mercurial import lsprof
+    except ImportError:
+        raise util.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':
+            import lsprofcalltree
+            calltree = lsprofcalltree.KCacheGrind(p)
+            calltree.output(fp)
+        else:
+            # format == 'text'
+            stats = lsprof.Stats(p.getstats())
+            stats.sort(field)
+            stats.pprint(limit=30, file=fp, climit=climit)
+
+def statprofile(ui, func, fp):
+    try:
+        import statprof
+    except ImportError:
+        raise util.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):
     def checkargs():
         try:
@@ -695,47 +748,28 @@
             raise error.CommandError(cmd, _("invalid arguments"))
 
     if options['profile']:
-        format = ui.config('profiling', 'format', default='text')
-        field = ui.config('profiling', 'sort', default='inlinetime')
-        climit = ui.configint('profiling', 'nested', default=5)
-
-        if not format in ['text', 'kcachegrind']:
-            ui.warn(_("unrecognized profiling format '%s'"
-                        " - Ignored\n") % format)
-            format = 'text'
+        profiler = os.getenv('HGPROF')
+        if profiler is None:
+            profiler = ui.config('profiling', 'type', default='ls')
+        if profiler not in ('ls', 'stat'):
+            ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
+            profiler = 'ls'
 
         output = ui.config('profiling', 'output')
 
         if output:
             path = ui.expandpath(output)
-            ostream = open(path, 'wb')
+            fp = open(path, 'wb')
         else:
-            ostream = sys.stderr
+            fp = sys.stderr
 
         try:
-            from mercurial import lsprof
-        except ImportError:
-            raise util.Abort(_(
-                'lsprof not available - install from '
-                'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
-        p = lsprof.Profiler()
-        p.enable(subcalls=True)
-        try:
-            return checkargs()
+            if profiler == 'ls':
+                return lsprofile(ui, checkargs, fp)
+            else:
+                return statprofile(ui, checkargs, fp)
         finally:
-            p.disable()
-
-            if format == 'kcachegrind':
-                import lsprofcalltree
-                calltree = lsprofcalltree.KCacheGrind(p)
-                calltree.output(ostream)
-            else:
-                # format == 'text'
-                stats = lsprof.Stats(p.getstats())
-                stats.sort(field)
-                stats.pprint(limit=30, file=ostream, climit=climit)
-
             if output:
-                ostream.close()
+                fp.close()
     else:
         return checkargs()
diff -r 0f5e72a3fbd2 -r fad43027223f mercurial/help/config.txt
--- a/mercurial/help/config.txt	Mon Apr 09 08:50:02 2012 -0700
+++ b/mercurial/help/config.txt	Mon Apr 09 13:48:45 2012 -0700
@@ -933,14 +933,31 @@
 ``profiling``
 """""""""""""
 
-Specifies profiling format and file output. In this section
-description, 'profiling data' stands for the raw data collected
-during profiling, while 'profiling report' stands for a statistical
-text report generated from the profiling data. The profiling is done
-using lsprof.
+Specifies profiling type, format, and file output. Two profilers are
+supported: an instrumenting profiler (named ``ls``), and a sampling
+profiler (named ``stat``).
+
+In this section description, 'profiling data' stands for the raw data
+collected during profiling, while 'profiling report' stands for a
+statistical text report generated from the profiling data. The
+profiling is done using lsprof.
+
+``type``
+    The type of profiler to use.
+    Default: ls.
+
+    ``ls``
+      Use Python's built-in instrumenting profiler. This profiler
+      works on all platforms, but each line number it reports is the
+      first line of a function. This restriction makes it difficult to
+      identify the expensive parts of a non-trivial function.
+    ``stat``
+      Use a third-party statistical profiler, statprof. This profiler
+      currently runs only on Unix systems, and is most useful for
+      profiling commands that run for longer than about 0.1 seconds.
 
 ``format``
-    Profiling format.
+    Profiling format.  Specific to the ``ls`` instrumenting profiler.
     Default: text.
 
     ``text``
@@ -952,6 +969,10 @@
       file, the generated file can directly be loaded into
       kcachegrind.
 
+``frequency``
+    Sampling frequency.  Specific to the ``stat`` sampling profiler.
+    Default: 1000.
+
 ``output``
     File path where profiling data or report should be saved. If the
     file exists, it is replaced. Default: None, data is printed on


More information about the Mercurial-devel mailing list