mercurial/profiling.py
author Gregory Szorc <gregory.szorc@gmail.com>
Sat, 24 Dec 2016 15:29:32 -0700
changeset 30764 e75463e3179f
parent 30664 69acfd2ca11e
child 30820 6a70cf94d1b5
permissions -rw-r--r--
protocol: send application/mercurial-0.2 responses to capable clients With this commit, the HTTP transport now parses the X-HgProto-<N> header to determine what media type and compression engine to use for responses. So far, we only compress responses that are already being compressed with zlib today (stream response types to specific commands). We can expand things to cover additional response types later. The practical side-effect of this commit is that non-zlib compression engines will be used if both ends support them. This means if both ends have zstd support, zstd - not zlib - will be used to compress data! When cloning the mozilla-unified repository between a local HTTP server and client, the benefits of non-zlib compression are quite noticeable: engine server CPU (s) client CPU (s) bundle size zlib (l=6) 174.1 283.2 1,148,547,026 zstd (l=1) 99.2 267.3 1,127,513,841 zstd (l=3) 103.1 266.9 1,018,861,363 zstd (l=7) 128.3 269.7 919,190,278 zstd (l=10) 162.0 - 894,547,179 none 95.3 277.2 4,097,566,064 The default zstd compression level is 3. So if you deploy zstd capable Mercurial to your clients and servers and CPU time on your server is dominated by "getbundle" requests (clients cloning and pulling) - and my experience at Mozilla tells me this is often the case - this commit could drastically reduce your server-side CPU usage *and* save on bandwidth costs! Another benefit of this change is that server operators can install *any* compression engine. While it isn't enabled by default, the "none" compression engine can now be used to disable wire protocol compression completely. Previously, commands like "getbundle" always zlib compressed output, adding considerable overhead to generating responses. If you are on a high speed network and your server is under high load, it might be advantageous to trade bandwidth for CPU. Although, zstd at level 1 doesn't use that much CPU, so I'm not convinced that disabling compression wholesale is worthwhile. And, my data seems to indicate a slow down on the client without compression. I suspect this is due to a lack of buffering resulting in an increase in socket read() calls and/or the fact we're transferring an extra 3 GB of data (parsing HTTP chunked transfer and processing extra TCP packets can add up). This is definitely worth investigating and optimizing. But since the "none" compressor isn't enabled by default, I'm inclined to punt on this issue. This commit introduces tons of tests. Some of these should arguably have been implemented on previous commits. But it was difficult to test without the server functionality in place.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     1
# profiling.py - profiling functions
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     2
#
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     3
# Copyright 2016 Gregory Szorc <gregory.szorc@gmail.com>
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     4
#
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms of the
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     6
# GNU General Public License version 2 or any later version.
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     7
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     8
from __future__ import absolute_import, print_function
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     9
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    10
import contextlib
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    11
import time
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    12
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    13
from .i18n import _
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    14
from . import (
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    15
    error,
30664
69acfd2ca11e py3: replace os.getenv with pycompat.osgetenv
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30322
diff changeset
    16
    pycompat,
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    17
    util,
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    18
)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    19
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    20
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    21
def lsprofile(ui, fp):
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    22
    format = ui.config('profiling', 'format', default='text')
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    23
    field = ui.config('profiling', 'sort', default='inlinetime')
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    24
    limit = ui.configint('profiling', 'limit', default=30)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    25
    climit = ui.configint('profiling', 'nested', default=0)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    26
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    27
    if format not in ['text', 'kcachegrind']:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    28
        ui.warn(_("unrecognized profiling format '%s'"
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    29
                    " - Ignored\n") % format)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    30
        format = 'text'
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    31
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    32
    try:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    33
        from . import lsprof
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    34
    except ImportError:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    35
        raise error.Abort(_(
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    36
            'lsprof not available - install from '
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    37
            'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    38
    p = lsprof.Profiler()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    39
    p.enable(subcalls=True)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    40
    try:
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    41
        yield
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    42
    finally:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    43
        p.disable()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    44
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    45
        if format == 'kcachegrind':
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    46
            from . import lsprofcalltree
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    47
            calltree = lsprofcalltree.KCacheGrind(p)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    48
            calltree.output(fp)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    49
        else:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    50
            # format == 'text'
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    51
            stats = lsprof.Stats(p.getstats())
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    52
            stats.sort(field)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    53
            stats.pprint(limit=limit, file=fp, climit=climit)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    54
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    55
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    56
def flameprofile(ui, fp):
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    57
    try:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    58
        from flamegraph import flamegraph
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    59
    except ImportError:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    60
        raise error.Abort(_(
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    61
            'flamegraph not available - install from '
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    62
            'https://github.com/evanhempel/python-flamegraph'))
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    63
    # developer config: profiling.freq
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    64
    freq = ui.configint('profiling', 'freq', default=1000)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    65
    filter_ = None
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    66
    collapse_recursion = True
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    67
    thread = flamegraph.ProfileThread(fp, 1.0 / freq,
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    68
                                      filter_, collapse_recursion)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    69
    start_time = time.clock()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    70
    try:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    71
        thread.start()
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    72
        yield
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    73
    finally:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    74
        thread.stop()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    75
        thread.join()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    76
        print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    77
            time.clock() - start_time, thread.num_frames(),
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    78
            thread.num_frames(unique=True)))
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    79
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    80
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    81
def statprofile(ui, fp):
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
    82
    from . import statprof
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    83
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    84
    freq = ui.configint('profiling', 'freq', default=1000)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    85
    if freq > 0:
29785
88d3c1ab03a7 profiling: don't error with statprof when profiling has already started
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29784
diff changeset
    86
        # Cannot reset when profiler is already active. So silently no-op.
88d3c1ab03a7 profiling: don't error with statprof when profiling has already started
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29784
diff changeset
    87
        if statprof.state.profile_level == 0:
88d3c1ab03a7 profiling: don't error with statprof when profiling has already started
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29784
diff changeset
    88
            statprof.reset(freq)
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    89
    else:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    90
        ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    91
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
    92
    statprof.start(mechanism='thread')
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
    93
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    94
    try:
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    95
        yield
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    96
    finally:
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
    97
        data = statprof.stop()
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
    98
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
    99
        profformat = ui.config('profiling', 'statformat', 'hotpath')
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   100
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   101
        formats = {
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   102
            'byline': statprof.DisplayFormats.ByLine,
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   103
            'bymethod': statprof.DisplayFormats.ByMethod,
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   104
            'hotpath': statprof.DisplayFormats.Hotpath,
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   105
            'json': statprof.DisplayFormats.Json,
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   106
        }
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   107
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   108
        if profformat in formats:
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   109
            displayformat = formats[profformat]
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   110
        else:
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   111
            ui.warn(_('unknown profiler output format: %s\n') % profformat)
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   112
            displayformat = statprof.DisplayFormats.Hotpath
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   113
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   114
        statprof.display(fp, data=data, format=displayformat)
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   115
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   116
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   117
def profile(ui):
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   118
    """Start profiling.
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   119
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   120
    Profiling is active when the context manager is active. When the context
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   121
    manager exits, profiling results will be written to the configured output.
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   122
    """
30664
69acfd2ca11e py3: replace os.getenv with pycompat.osgetenv
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30322
diff changeset
   123
    profiler = pycompat.osgetenv('HGPROF')
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   124
    if profiler is None:
30317
3fd53cc1aad8 profiling: make statprof the default profiler (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30316
diff changeset
   125
        profiler = ui.config('profiling', 'type', default='stat')
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   126
    if profiler not in ('ls', 'stat', 'flame'):
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   127
        ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
30317
3fd53cc1aad8 profiling: make statprof the default profiler (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30316
diff changeset
   128
        profiler = 'stat'
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   129
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   130
    output = ui.config('profiling', 'output')
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   131
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   132
    if output == 'blackbox':
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   133
        fp = util.stringio()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   134
    elif output:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   135
        path = ui.expandpath(output)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   136
        fp = open(path, 'wb')
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   137
    else:
30322
189a1030affb profiling: obtain stderr from ui
Yuya Nishihara <yuya@tcha.org>
parents: 30317
diff changeset
   138
        fp = ui.ferr
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   139
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   140
    try:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   141
        if profiler == 'ls':
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   142
            proffn = lsprofile
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   143
        elif profiler == 'flame':
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   144
            proffn = flameprofile
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   145
        else:
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   146
            proffn = statprofile
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   147
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   148
        with proffn(ui, fp):
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   149
            yield
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   150
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   151
    finally:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   152
        if output:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   153
            if output == 'blackbox':
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   154
                val = 'Profile:\n%s' % fp.getvalue()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   155
                # ui.log treats the input as a format string,
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   156
                # so we need to escape any % signs.
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   157
                val = val.replace('%', '%%')
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   158
                ui.log('profile', val)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   159
            fp.close()
29784
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   160
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   161
@contextlib.contextmanager
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   162
def maybeprofile(ui):
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   163
    """Profile if enabled, else do nothing.
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   164
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   165
    This context manager can be used to optionally profile if profiling
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   166
    is enabled. Otherwise, it does nothing.
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   167
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   168
    The purpose of this context manager is to make calling code simpler:
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   169
    just use a single code path for calling into code you may want to profile
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   170
    and this function determines whether to start profiling.
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   171
    """
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   172
    if ui.configbool('profiling', 'enabled'):
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   173
        with profile(ui):
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   174
            yield
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   175
    else:
e3501546f7e4 profiling: add a context manager that no-ops if profiling isn't enabled
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29783
diff changeset
   176
        yield