mercurial/profiling.py
author Matt Harbison <matt_harbison@yahoo.com>
Thu, 26 Sep 2024 02:58:50 +0200
changeset 51897 499b19683c1b
parent 51863 f4733654f144
permissions -rw-r--r--
profiling: pass bytes to `_()` and `error.Abort()` And of course `other_tool_name` is str too, so that needs to be converted. The type hints from PyCharm say `sys.monitoring.get_tool()` can return None, so handle that case explicitly before it trips up pytype.
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
51863
f4733654f144 typing: add `from __future__ import annotations` to most files
Matt Harbison <matt_harbison@yahoo.com>
parents: 51842
diff changeset
     8
from __future__ import annotations
29781
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
51761
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
    11
import os
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
    12
import signal
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
    13
import subprocess
51840
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    14
import sys
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    15
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    16
from .i18n import _
43089
c59eb1560c44 py3: manually import getattr where it is needed
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43085
diff changeset
    17
from .pycompat import (
c59eb1560c44 py3: manually import getattr where it is needed
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43085
diff changeset
    18
    open,
c59eb1560c44 py3: manually import getattr where it is needed
Gregory Szorc <gregory.szorc@gmail.com>
parents: 43085
diff changeset
    19
)
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    20
from . import (
30820
6a70cf94d1b5 py3: replace pycompat.getenv with encoding.environ.get
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30664
diff changeset
    21
    encoding,
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    22
    error,
32417
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    23
    extensions,
36683
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
    24
    pycompat,
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    25
    util,
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
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    28
32417
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    29
def _loadprofiler(ui, profiler):
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    30
    """load profiler extension. return profile method, or None on failure"""
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    31
    extname = profiler
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    32
    extensions.loadall(ui, whitelist=[extname])
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    33
    try:
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    34
        mod = extensions.find(extname)
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    35
    except KeyError:
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    36
        return None
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    37
    else:
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    38
        return getattr(mod, 'profile', None)
f40dc6f7c12f profiling: allow loading profiling extension before everything else
Jun Wu <quark@fb.com>
parents: 30975
diff changeset
    39
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    40
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    41
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    42
def lsprofile(ui, fp):
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    43
    format = ui.config(b'profiling', b'format')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    44
    field = ui.config(b'profiling', b'sort')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    45
    limit = ui.configint(b'profiling', b'limit')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    46
    climit = ui.configint(b'profiling', b'nested')
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    47
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    48
    if format not in [b'text', b'kcachegrind']:
43117
8ff1ecfadcd1 cleanup: join string literals that are already on one line
Martin von Zweigbergk <martinvonz@google.com>
parents: 43089
diff changeset
    49
        ui.warn(_(b"unrecognized profiling format '%s' - Ignored\n") % format)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    50
        format = b'text'
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    51
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    52
    try:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    53
        from . import lsprof
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    54
    except ImportError:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    55
        raise error.Abort(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    56
            _(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    57
                b'lsprof not available - install from '
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    58
                b'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    59
            )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    60
        )
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    61
    p = lsprof.Profiler()
51840
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    62
    try:
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    63
        p.enable(subcalls=True)
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    64
    except ValueError as exc:
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    65
        if str(exc) != "Another profiling tool is already active":
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    66
            raise
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    67
        if not hasattr(sys, "monitoring"):
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    68
            raise
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    69
        # python >=3.12 prevent more than one profiler to run at the same
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    70
        # time, tries to improve the report to help the user understand
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    71
        # what is going on.
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    72
        other_tool_name = sys.monitoring.get_tool(sys.monitoring.PROFILER_ID)
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    73
        if other_tool_name == "cProfile":
51897
499b19683c1b profiling: pass bytes to `_()` and `error.Abort()`
Matt Harbison <matt_harbison@yahoo.com>
parents: 51863
diff changeset
    74
            msg = b'cannot recursively call `lsprof`'
51840
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    75
            raise error.Abort(msg) from None
1bb71046f5e0 profiling: improve 3.12 error message for calling lsprof twice
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 51588
diff changeset
    76
        else:
51897
499b19683c1b profiling: pass bytes to `_()` and `error.Abort()`
Matt Harbison <matt_harbison@yahoo.com>
parents: 51863
diff changeset
    77
            tool = b'<unknown>'
499b19683c1b profiling: pass bytes to `_()` and `error.Abort()`
Matt Harbison <matt_harbison@yahoo.com>
parents: 51863
diff changeset
    78
            if other_tool_name:
499b19683c1b profiling: pass bytes to `_()` and `error.Abort()`
Matt Harbison <matt_harbison@yahoo.com>
parents: 51863
diff changeset
    79
                tool = encoding.strtolocal(other_tool_name)
499b19683c1b profiling: pass bytes to `_()` and `error.Abort()`
Matt Harbison <matt_harbison@yahoo.com>
parents: 51863
diff changeset
    80
            m = b'failed to start "lsprofile"; another profiler already running: %s'
499b19683c1b profiling: pass bytes to `_()` and `error.Abort()`
Matt Harbison <matt_harbison@yahoo.com>
parents: 51863
diff changeset
    81
            raise error.Abort(_(m) % tool) from None
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    82
    try:
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
    83
        yield
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    84
    finally:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    85
        p.disable()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    86
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    87
        if format == b'kcachegrind':
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    88
            from . import lsprofcalltree
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    89
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    90
            calltree = lsprofcalltree.KCacheGrind(p)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    91
            calltree.output(fp)
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    92
        else:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    93
            # format == 'text'
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    94
            stats = lsprof.Stats(p.getstats())
40192
b8f6a99ad89b py3: convert sorting field to sysstr
Gregory Szorc <gregory.szorc@gmail.com>
parents: 38260
diff changeset
    95
            stats.sort(pycompat.sysstr(field))
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    96
            stats.pprint(limit=limit, file=fp, climit=climit)
51588
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
    97
        fp.flush()
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    98
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
    99
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   100
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   101
def flameprofile(ui, fp):
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   102
    try:
43731
6c8ba31405d9 profiling: disable the import-error warning for the flamegraph module
Matt Harbison <matt_harbison@yahoo.com>
parents: 43506
diff changeset
   103
        from flamegraph import flamegraph  # pytype: disable=import-error
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   104
    except ImportError:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   105
        raise error.Abort(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   106
            _(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   107
                b'flamegraph not available - install from '
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   108
                b'https://github.com/evanhempel/python-flamegraph'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   109
            )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   110
        )
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   111
    # developer config: profiling.freq
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   112
    freq = ui.configint(b'profiling', b'freq')
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   113
    filter_ = None
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   114
    collapse_recursion = True
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   115
    thread = flamegraph.ProfileThread(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   116
        fp, 1.0 / freq, filter_, collapse_recursion
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   117
    )
30975
22fbca1d11ed mercurial: switch to util.timer for all interval timings
Simon Farnsworth <simonfar@fb.com>
parents: 30930
diff changeset
   118
    start_time = util.timer()
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   119
    try:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   120
        thread.start()
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   121
        yield
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   122
    finally:
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   123
        thread.stop()
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   124
        thread.join()
51588
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
   125
        m = b'Collected %d stack frames (%d unique) in %2.2f seconds.'
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
   126
        m %= (
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
   127
            (
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   128
                util.timer() - start_time,
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   129
                thread.num_frames(),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   130
                thread.num_frames(unique=True),
51588
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
   131
            ),
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   132
        )
51588
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
   133
        print(m, flush=True)
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   134
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   135
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   136
@contextlib.contextmanager
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   137
def statprofile(ui, fp):
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   138
    from . import statprof
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   139
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   140
    freq = ui.configint(b'profiling', b'freq')
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   141
    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
   142
        # 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
   143
        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
   144
            statprof.reset(freq)
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   145
    else:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   146
        ui.warn(_(b"invalid sampling frequency '%s' - ignoring\n") % freq)
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   147
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   148
    track = ui.config(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   149
        b'profiling', b'time-track', pycompat.iswindows and b'cpu' or b'real'
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   150
    )
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   151
    statprof.start(mechanism=b'thread', track=track)
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   152
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   153
    try:
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   154
        yield
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   155
    finally:
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   156
        data = statprof.stop()
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   157
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   158
        profformat = ui.config(b'profiling', b'statformat')
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   159
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   160
        formats = {
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   161
            b'byline': statprof.DisplayFormats.ByLine,
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   162
            b'bymethod': statprof.DisplayFormats.ByMethod,
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   163
            b'hotpath': statprof.DisplayFormats.Hotpath,
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   164
            b'json': statprof.DisplayFormats.Json,
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   165
            b'chrome': statprof.DisplayFormats.Chrome,
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   166
        }
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   167
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   168
        if profformat in formats:
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   169
            displayformat = formats[profformat]
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   170
        else:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   171
            ui.warn(_(b'unknown profiler output format: %s\n') % profformat)
30316
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   172
            displayformat = statprof.DisplayFormats.Hotpath
faf1b8923da2 profiling: use vendored statprof and upstream enhancements (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29785
diff changeset
   173
30930
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   174
        kwargs = {}
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   175
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   176
        def fraction(s):
32978
41b081ac2145 profiling: cope with configwith default value handling changes
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32851
diff changeset
   177
            if isinstance(s, (float, int)):
41b081ac2145 profiling: cope with configwith default value handling changes
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32851
diff changeset
   178
                return float(s)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   179
            if s.endswith(b'%'):
30930
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   180
                v = float(s[:-1]) / 100
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   181
            else:
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   182
                v = float(s)
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   183
            if 0 <= v <= 1:
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   184
                return v
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   185
            raise ValueError(s)
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   186
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   187
        if profformat == b'chrome':
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   188
            showmin = ui.configwith(fraction, b'profiling', b'showmin', 0.005)
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   189
            showmax = ui.configwith(fraction, b'profiling', b'showmax')
30930
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   190
            kwargs.update(minthreshold=showmin, maxthreshold=showmax)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   191
        elif profformat == b'hotpath':
33195
5d8942dbe49e check-config: syntax to allow inconsistent config values
Gregory Szorc <gregory.szorc@gmail.com>
parents: 32978
diff changeset
   192
            # inconsistent config: profiling.showmin
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   193
            limit = ui.configwith(fraction, b'profiling', b'showmin', 0.05)
43506
9f70512ae2cf cleanup: remove pointless r-prefixes on single-quoted strings
Augie Fackler <augie@google.com>
parents: 43117
diff changeset
   194
            kwargs['limit'] = limit
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   195
            showtime = ui.configbool(b'profiling', b'showtime')
43506
9f70512ae2cf cleanup: remove pointless r-prefixes on single-quoted strings
Augie Fackler <augie@google.com>
parents: 43117
diff changeset
   196
            kwargs['showtime'] = showtime
30930
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   197
517bc1cd7033 profiling: add statprof support for Chrome trace viewer rendering
Bryan O'Sullivan <bryano@fb.com>
parents: 30820
diff changeset
   198
        statprof.display(fp, data=data, format=displayformat, **kwargs)
51588
1574718fa62f profiler: flush after writing the profiler output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 50929
diff changeset
   199
        fp.flush()
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   200
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   201
51761
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   202
@contextlib.contextmanager
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   203
def pyspy_profile(ui, fp):
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   204
    exe = ui.config(b'profiling', b'py-spy.exe')
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   205
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   206
    freq = ui.configint(b'profiling', b'py-spy.freq')
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   207
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   208
    format = ui.config(b'profiling', b'py-spy.format')
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   209
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   210
    fd = fp.fileno()
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   211
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   212
    output_path = "/dev/fd/%d" % (fd)
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   213
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   214
    my_pid = os.getpid()
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   215
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   216
    cmd = [
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   217
        exe,
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   218
        "record",
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   219
        "--pid",
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   220
        str(my_pid),
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   221
        "--native",
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   222
        "--rate",
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   223
        str(freq),
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   224
        "--output",
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   225
        output_path,
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   226
    ]
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   227
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   228
    if format:
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   229
        cmd.extend(["--format", format])
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   230
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   231
    proc = subprocess.Popen(
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   232
        cmd,
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   233
        pass_fds={fd},
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   234
        stdout=subprocess.PIPE,
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   235
    )
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   236
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   237
    _ = proc.stdout.readline()
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   238
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   239
    try:
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   240
        yield
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   241
    finally:
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   242
        os.kill(proc.pid, signal.SIGINT)
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   243
        proc.communicate()
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   244
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   245
48946
642e31cb55f0 py3: use class X: instead of class X(object):
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
   246
class profile:
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   247
    """Start profiling.
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   248
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   249
    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
   250
    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
   251
    """
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   252
32785
37ec8f24d912 profile: introduce a knob to control if the context is actually profiling
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32784
diff changeset
   253
    def __init__(self, ui, enabled=True):
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   254
        self._ui = ui
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   255
        self._output = None
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   256
        self._fp = None
32805
2b0fc56840d0 profile: use explicit logic to control file closing
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32804
diff changeset
   257
        self._fpdoclose = True
44192
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   258
        self._flushfp = None
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   259
        self._profiler = None
32785
37ec8f24d912 profile: introduce a knob to control if the context is actually profiling
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32784
diff changeset
   260
        self._enabled = enabled
32784
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   261
        self._entered = False
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   262
        self._started = False
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   263
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   264
    def __enter__(self):
32784
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   265
        self._entered = True
32785
37ec8f24d912 profile: introduce a knob to control if the context is actually profiling
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32784
diff changeset
   266
        if self._enabled:
37ec8f24d912 profile: introduce a knob to control if the context is actually profiling
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32784
diff changeset
   267
            self.start()
32786
0ead06d54ffe profile: make the contextmanager object available to the callers
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32785
diff changeset
   268
        return self
32784
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   269
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   270
    def start(self):
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   271
        """Start profiling.
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   272
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   273
        The profiling will stop at the context exit.
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   274
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   275
        If the profiler was already started, this has no effect."""
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   276
        if not self._entered:
43732
882e633ac92c profiling: add a missing argument to the ProgrammingError constructor
Matt Harbison <matt_harbison@yahoo.com>
parents: 43731
diff changeset
   277
            raise error.ProgrammingError(b'use a context manager to start')
32784
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   278
        if self._started:
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   279
            return
086c1ef0f666 profile: introduce a "start" method to the profile context
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32783
diff changeset
   280
        self._started = True
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   281
        profiler = encoding.environ.get(b'HGPROF')
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   282
        proffn = None
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   283
        if profiler is None:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   284
            profiler = self._ui.config(b'profiling', b'type')
51761
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   285
        if profiler not in (b'ls', b'stat', b'flame', b'py-spy'):
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   286
            # try load profiler from extension with the same name
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   287
            proffn = _loadprofiler(self._ui, profiler)
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   288
            if proffn is None:
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   289
                self._ui.warn(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   290
                    _(b"unrecognized profiler '%s' - ignored\n") % profiler
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   291
                )
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   292
                profiler = b'stat'
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   293
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   294
        self._output = self._ui.config(b'profiling', b'output')
29781
2654a0aac80d profiling: move profiling code from dispatch.py (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   295
32808
336700745a5c profile: close 'fp' on error within '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32807
diff changeset
   296
        try:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   297
            if self._output == b'blackbox':
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   298
                self._fp = util.stringio()
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   299
            elif self._output:
46958
5a6a1cd21f09 profiling: use `util.expandpath` instead of `ui.expandpath` for output
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 44192
diff changeset
   300
                path = util.expandpath(self._output)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   301
                self._fp = open(path, b'wb')
36683
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   302
            elif pycompat.iswindows:
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   303
                # parse escape sequence by win32print()
48946
642e31cb55f0 py3: use class X: instead of class X(object):
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
   304
                class uifp:
36683
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   305
                    def __init__(self, ui):
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   306
                        self._ui = ui
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   307
36683
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   308
                    def write(self, data):
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   309
                        self._ui.write_err(data)
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   310
36683
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   311
                    def flush(self):
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   312
                        self._ui.flush()
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   313
36683
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   314
                self._fpdoclose = False
e39953fdd924 profile: colorize output on Windows
Matt Harbison <matt_harbison@yahoo.com>
parents: 36400
diff changeset
   315
                self._fp = uifp(self._ui)
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   316
            else:
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   317
                self._fpdoclose = False
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   318
                self._fp = self._ui.ferr
44192
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   319
                # Ensure we've flushed fout before writing to ferr.
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   320
                self._flushfp = self._ui.fout
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   321
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   322
            if proffn is not None:
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   323
                pass
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   324
            elif profiler == b'ls':
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   325
                proffn = lsprofile
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   326
            elif profiler == b'flame':
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   327
                proffn = flameprofile
51761
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   328
            elif profiler == b'py-spy':
812a094a7477 profiling: add a py-spy profiling backend
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 51588
diff changeset
   329
                proffn = pyspy_profile
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   330
            else:
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   331
                proffn = statprofile
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   332
32807
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   333
            self._profiler = proffn(self._ui, self._fp)
54b356d65079 profile: indent part of '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32806
diff changeset
   334
            self._profiler.__enter__()
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   335
        except:  # re-raises
32808
336700745a5c profile: close 'fp' on error within '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32807
diff changeset
   336
            self._closefp()
336700745a5c profile: close 'fp' on error within '__enter__'
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32807
diff changeset
   337
            raise
29783
5d44197c208b profiling: make profiling functions context managers (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 29781
diff changeset
   338
32783
4483696dacee profile: upgrade the "profile" context manager to a full class
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32417
diff changeset
   339
    def __exit__(self, exception_type, exception_value, traceback):
32810
6675d23da748 profile: properly propagate exception from the sub-context manager
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32809
diff changeset
   340
        propagate = None
32809
062eb859d3ee profile: close 'fp' in all cases
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32808
diff changeset
   341
        if self._profiler is not None:
44192
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   342
            self._uiflush()
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   343
            propagate = self._profiler.__exit__(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   344
                exception_type, exception_value, traceback
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42423
diff changeset
   345
            )
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   346
            if self._output == b'blackbox':
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   347
                val = b'Profile:\n%s' % self._fp.getvalue()
32809
062eb859d3ee profile: close 'fp' in all cases
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32808
diff changeset
   348
                # ui.log treats the input as a format string,
062eb859d3ee profile: close 'fp' in all cases
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32808
diff changeset
   349
                # so we need to escape any % signs.
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   350
                val = val.replace(b'%', b'%%')
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   351
                self._ui.log(b'profile', val)
32806
3a4c677cbd6e profile: remove now useless indent
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32805
diff changeset
   352
        self._closefp()
32810
6675d23da748 profile: properly propagate exception from the sub-context manager
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32809
diff changeset
   353
        return propagate
32804
c0b2c8f25ad9 profiling: move 'fp' closing logic into its own function
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32788
diff changeset
   354
c0b2c8f25ad9 profiling: move 'fp' closing logic into its own function
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32788
diff changeset
   355
    def _closefp(self):
32805
2b0fc56840d0 profile: use explicit logic to control file closing
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32804
diff changeset
   356
        if self._fpdoclose and self._fp is not None:
2b0fc56840d0 profile: use explicit logic to control file closing
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 32804
diff changeset
   357
            self._fp.close()
44192
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   358
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   359
    def _uiflush(self):
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   360
        if self._flushfp:
d6d4170882cd profiling: flush stdout before writing profile to stderr
Kyle Lippincott <spectral@google.com>
parents: 43732
diff changeset
   361
            self._flushfp.flush()