# HG changeset patch # User Nicolas Dumazet # Date 1287388636 -32400 # Node ID 2a2697be51688d3c5f3284bd6c55074e0d26ec40 # Parent d10369fefd01f165f76263d9b92423ae018b84bc blackblox extension to help with debugging diff --git a/hgext/blackbox.py b/hgext/blackbox.py new file mode 100644 --- /dev/null +++ b/hgext/blackbox.py @@ -0,0 +1,70 @@ +"""log repository events to help debugging + +Appends to .hg/blackbox events happening in the repository to help +debugging and diagnosis. So far, this blackbox will track: + - hg commands and their return codes + - tracebacks + - hooks + - incoming changesets +""" + +from mercurial import dispatch, extensions, hook, util +import os, traceback + +def runcommand(orig, lui, repo, cmd, fullargs, *args, **kwargs): + if repo is None: + # not much we can log + return orig(lui, repo, cmd, fullargs, *args, **kwargs) + + repo.blackbox(' '.join(fullargs)) + try: + ret = orig(lui, repo, cmd, fullargs, *args, **kwargs) + if ret: + repo._blackbox.write(' [%d]\n' % ret) + return ret + except: + traceback.print_exc(None, repo._blackbox) + raise + +def exthook(orig, ui, repo, name, cmd, *args, **kw): + if repo: + repo.blackbox('exthook: %s: %s' % (name, cmd)) + return orig(ui, repo, name, cmd, *args, **kw) +def pythonhook(orig, ui, repo, name, hname, funcname, *args, **kw): + if repo and not hname.endswith('.blackbox'): + repo.blackbox('pythonhook: %s: %s' % (hname, funcname)) + return orig(ui, repo, name, hname, funcname, *args, **kw) + +def uisetup(ui): + extensions.wrapfunction(dispatch, 'runcommand', runcommand) + extensions.wrapfunction(hook, '_exthook', exthook) + extensions.wrapfunction(hook, '_pythonhook', pythonhook) + +def incoming(ui, repo, hooktype, node, source, url): + repo.blackbox('incoming: %s: %s from %s' % (source, node[:12], url)) +def reposetup(ui, repo): + if not repo.local(): + return + + class blackboxrepo(repo.__class__): + @util.propertycache + def _blackbox(self): + introduction = not os.path.exists(self.join('blackbox')) + blackbox = self.opener('blackbox', 'a') + if introduction: + blackbox.write( +""" +blackbox extension logs here hg events to help diagnosing errors. +You can truncate or delete this file, or include parts of the log +in bug reports on http://mercurial.selenic.com/bts/ + +""") + blackbox.write(util.datestr() + '\n') + return blackbox + + def blackbox(self, msg): + self._blackbox.write(' %s\n' % msg) + + repo.__class__ = blackboxrepo + ui.setconfig('hooks', 'incoming.blackbox', incoming) + diff --git a/tests/test-blackbox.t b/tests/test-blackbox.t new file mode 100644 --- /dev/null +++ b/tests/test-blackbox.t @@ -0,0 +1,97 @@ + $ echo '[extensions]' >> $HGRCPATH + $ echo 'blackbox =' >> $HGRCPATH + + $ blackbox() { + > # trash the header + > tail --lines=+6 .hg/blackbox + > } + +== Non-zero exit codes / header == + + $ hg init empty; cd empty + $ hg log + $ hg add empty + empty: No such file or directory + [1] + $ cat .hg/blackbox + + blackbox extension logs here hg events to help diagnosing errors. + You can truncate or delete this file, or include parts of the log + in bug reports on http://mercurial.selenic.com/bts/ + + * (glob) + log + * (glob) + add empty + [1] + +== Logging hooks == + + $ rm .hg/blackbox + $ hg --config "hooks.pre-log.foo=echo 'hook'" log + hook + $ blackbox + * (glob) + log + exthook: pre-log.foo: echo 'hook' + $ cd .. + +== Incoming changesets == + +Create a repo with a dummy changeset + + $ hg init foo; cd foo + $ touch a + $ hg ci -Am'a' + adding a + $ hg clone . ../remote + updating to branch default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +And pretend we push to remote a new changeset + + $ echo foo > a + $ hg ci -Am'modify a' + $ hg push ../remote + pushing to ../remote + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + +And check that the incoming operation was recorded + + $ cd ../remote + $ blackbox + * (glob) + incoming: push: c3cdbe7bbdc4 from file:$TESTTMP/foo + +== Tracebacks == + + $ rm .hg/blackbox + $ hg diff -c notexisting + abort: unknown revision 'notexisting'! + [255] + $ blackbox + * (glob) + diff -c notexisting + Traceback (most recent call last): + File "*/hgext/blackbox.py", line *, in runcommand (glob) + ret = orig(lui, repo, cmd, fullargs, *args, **kwargs) + File "*/mercurial/dispatch.py", line *, in runcommand (glob) + ret = _runcommand(ui, options, cmd, d) + File "*/mercurial/dispatch.py", line *, in _runcommand (glob) + return checkargs() + File "*/mercurial/dispatch.py", line *, in checkargs (glob) + return cmdfunc() + File "*/mercurial/dispatch.py", line *, in (glob) + d = lambda: util.checksignature(func)(ui, *args, **cmdoptions) + File "*/mercurial/util.py", line *, in check (glob) + return func(*args, **kwargs) + File "*/mercurial/commands.py", line *, in diff (glob) + node2 = repo.lookup(change) + File "*/mercurial/localrepo.py", line *, in lookup (glob) + raise error.RepoLookupError(_("unknown revision '%s'") % key) + RepoLookupError: unknown revision 'notexisting' +