Patchwork [08,of,10,V2] profiling: use vendored statprof and upstream enhancements (BC)

login
register
mail settings
Submitter Gregory Szorc
Date Aug. 17, 2016, 4:03 p.m.
Message ID <b42fbfe6196215490d6f.1471449826@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/16334/
State Changes Requested
Headers show

Comments

Gregory Szorc - Aug. 17, 2016, 4:03 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1471448933 25200
#      Wed Aug 17 08:48:53 2016 -0700
# Node ID b42fbfe6196215490d6f3a395440d908babcc0e9
# Parent  bf247c2f0136d23100b0098fe152cfabbb511c1b
profiling: use vendored statprof and upstream enhancements (BC)

Now that the statprof module is vendored and suitable for use, we
switch our statprof profiler to use it. This required some changes
because of API changes between the official statprof profiler and
our vendored copy.

We also incorporate Facebook's improvements from the "statprofext"
extension at
https://bitbucket.org/facebook/hg-experimental, notably support for
different display formats.

Because statprof output is different, this is marked as BC. Although
most users likely won't notice since most users don't profile.

Patch

diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -1404,33 +1404,43 @@  profiling is done using lsprof.
     (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.
+      Use a statistical profiler, statprof. This profiler is most
+      most useful for profiling commands that run for longer than
+      about 0.1 seconds.
 
 ``format``
     Profiling format.  Specific to the ``ls`` instrumenting profiler.
     (default: text)
 
     ``text``
       Generate a profiling report. When saving to a file, it should be
       noted that only the report is saved, and the profiling data is
       not kept.
     ``kcachegrind``
       Format profiling data for kcachegrind use: when saving to a
       file, the generated file can directly be loaded into
       kcachegrind.
 
+``statformat``
+    Profiling format for the ``stat`` profiler.
+    (default: hotpath)
+
+    ``hotpath``
+      Show a tree-based display containing the hot path of execution (where
+      most time was spent).
+    ``json``
+      Render profiling data as JSON.
+
 ``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
     stderr)
diff --git a/mercurial/profiling.py b/mercurial/profiling.py
--- a/mercurial/profiling.py
+++ b/mercurial/profiling.py
@@ -75,36 +75,44 @@  def flameprofile(ui, fp):
         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)))
 
 @contextlib.contextmanager
 def statprofile(ui, fp):
-    try:
-        import statprof
-    except ImportError:
-        raise error.Abort(_(
-            'statprof not available - install using "easy_install statprof"'))
+    from . import statprof
 
     freq = ui.configint('profiling', 'freq', default=1000)
     if freq > 0:
         # Cannot reset when profiler is already active. So silently no-op.
         if statprof.state.profile_level == 0:
             statprof.reset(freq)
     else:
         ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
 
-    statprof.start()
+    statprof.start(mechanism='thread')
+
     try:
         yield
     finally:
-        statprof.stop()
-        statprof.display(fp)
+        data = statprof.stop()
+
+        profformat = ui.config('profiling', 'statformat', 'hotpath')
+
+        if profformat == 'hotpath':
+            displayformat = statprof.DisplayFormats.Hotpath
+        elif profformat == 'json':
+            displayformat = statprof.DisplayFormats.Json
+        else:
+            ui.warn(_('unknown profiler output format: %s') % profformat)
+            displayformat = statprof.DisplayFormats.Hotpath
+
+        statprof.display(fp, data=data, format=displayformat)
 
 @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.
     """