# HG changeset patch # User Simon Farnsworth # Date 2017-02-15 21:17:45 # Node ID e92daf156d5c5be0eb85cd77a352d0512c8c89f8 # Parent 22fbca1d11ed42193eddcbc1b0371f49b6c0b0c2 ui: provide a mechanism to track and log blocked time We want to log the time Mercurial spends trapped in things outside programmatic control. Provide a mechanism to give us both command runtime and as many different sources of blocking as we deem useful. diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py --- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -137,6 +137,9 @@ def dispatch(req): finally: duration = util.timer() - starttime req.ui.flush() + if req.ui.logblockedtimes: + req.ui._blockedtimes['command_duration'] = duration * 1000 + req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes) req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n", msg, ret or 0, duration) return ret diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -7,6 +7,7 @@ from __future__ import absolute_import +import collections import contextlib import errno import getpass @@ -138,6 +139,8 @@ class ui(object): self.callhooks = True # Insecure server connections requested. self.insecureconnections = False + # Blocked time + self.logblockedtimes = False if src: self.fout = src.fout @@ -155,6 +158,7 @@ class ui(object): self.fixconfig() self.httppasswordmgrdb = src.httppasswordmgrdb + self._blockedtimes = src._blockedtimes else: self.fout = util.stdout self.ferr = util.stderr @@ -164,6 +168,7 @@ class ui(object): self.environ = encoding.environ self.httppasswordmgrdb = httppasswordmgrdbproxy() + self._blockedtimes = collections.defaultdict(int) allowed = self.configlist('experimental', 'exportableenviron') if '*' in allowed: @@ -192,6 +197,15 @@ class ui(object): self._progbar.resetstate() # reset last-print time of progress bar self.httppasswordmgrdb = httppasswordmgrdbproxy() + @contextlib.contextmanager + def timeblockedsection(self, key): + starttime = util.timer() + try: + yield + finally: + self._blockedtimes[key + '_blocked'] += \ + (util.timer() - starttime) * 1000 + def formatter(self, topic, opts): return formatter.formatter(self, topic, opts) @@ -295,6 +309,7 @@ class ui(object): self._reportuntrusted = self.debugflag or self.configbool("ui", "report_untrusted", True) self.tracebackflag = self.configbool('ui', 'traceback', False) + self.logblockedtimes = self.configbool('ui', 'logblockedtimes') if section in (None, 'trusted'): # update trust information diff --git a/tests/test-logtoprocess.t b/tests/test-logtoprocess.t --- a/tests/test-logtoprocess.t +++ b/tests/test-logtoprocess.t @@ -10,6 +10,7 @@ Test if logtoprocess correctly captures > def foo(ui, repo): > ui.log('foo', 'a message: %(bar)s\n', bar='spam') > EOF + $ cp $HGRCPATH $HGRCPATH.bak $ cat >> $HGRCPATH << EOF > [extensions] > logtoprocess= @@ -52,3 +53,18 @@ ordering issues between the various proc logtoprocess commandfinish output: logtoprocess foo output: spam + +Confirm that logging blocked time catches stdio properly: + $ cp $HGRCPATH.bak $HGRCPATH + $ cat >> $HGRCPATH << EOF + > [extensions] + > logtoprocess= + > pager= + > [logtoprocess] + > uiblocked=echo "\$EVENT command \$OPT_COMMAND_DURATION ms" + > [ui] + > logblockedtimes=True + > EOF + + $ hg log + uiblocked command [0-9]+.[0-9]* ms (re)