Patchwork [v2] util: introduce util.debugstacktrace for showing a stack trace without crashing

login
register
mail settings
Submitter Mads Kiilerich
Date Jan. 12, 2014, 10:28 p.m.
Message ID <72b1891f41b81efa6ec1.1389565723@localhost.localdomain>
Download mbox | patch
Permalink /patch/3315/
State Accepted
Commit 47d0843647d1e32f6af4482867327cec5db11a1f
Headers show

Comments

Mads Kiilerich - Jan. 12, 2014, 10:28 p.m.
# HG changeset patch
# User Mads Kiilerich <madski@unity3d.com>
# Date 1389565701 -3600
#      Sun Jan 12 23:28:21 2014 +0100
# Node ID 72b1891f41b81efa6ec13f04371c2c0740f01300
# Parent  d2704c48f4176d8cd6f21d33500820d44763585c
util: introduce util.debugstacktrace for showing a stack trace without crashing

This is often very handy when hacking/debugging.

Calling util.debugstacktrace('hey') from a place in hg will give something like:
  hey at:
   ./hg:38                                     in <module>
   /home/user/hgsrc/mercurial/dispatch.py:28   in run
   /home/user/hgsrc/mercurial/dispatch.py:65   in dispatch
   /home/user/hgsrc/mercurial/dispatch.py:88   in _runcatch
   /home/user/hgsrc/mercurial/dispatch.py:740  in _dispatch
   /home/user/hgsrc/mercurial/dispatch.py:514  in runcommand
   /home/user/hgsrc/mercurial/dispatch.py:830  in _runcommand
   /home/user/hgsrc/mercurial/dispatch.py:801  in checkargs
   /home/user/hgsrc/mercurial/dispatch.py:737  in <lambda>
   /home/user/hgsrc/mercurial/util.py:472      in check
...
Augie Fackler - Jan. 14, 2014, 7:07 p.m.
On Sun, Jan 12, 2014 at 11:28:43PM +0100, Mads Kiilerich wrote:
> # HG changeset patch
> # User Mads Kiilerich <madski@unity3d.com>
> # Date 1389565701 -3600
> #      Sun Jan 12 23:28:21 2014 +0100
> # Node ID 72b1891f41b81efa6ec13f04371c2c0740f01300
> # Parent  d2704c48f4176d8cd6f21d33500820d44763585c
> util: introduce util.debugstacktrace for showing a stack trace without crashing

sure, queued

>
> This is often very handy when hacking/debugging.
>
> Calling util.debugstacktrace('hey') from a place in hg will give something like:
>   hey at:
>    ./hg:38                                     in <module>
>    /home/user/hgsrc/mercurial/dispatch.py:28   in run
>    /home/user/hgsrc/mercurial/dispatch.py:65   in dispatch
>    /home/user/hgsrc/mercurial/dispatch.py:88   in _runcatch
>    /home/user/hgsrc/mercurial/dispatch.py:740  in _dispatch
>    /home/user/hgsrc/mercurial/dispatch.py:514  in runcommand
>    /home/user/hgsrc/mercurial/dispatch.py:830  in _runcommand
>    /home/user/hgsrc/mercurial/dispatch.py:801  in checkargs
>    /home/user/hgsrc/mercurial/dispatch.py:737  in <lambda>
>    /home/user/hgsrc/mercurial/util.py:472      in check
> ...
>
> diff --git a/contrib/lock-checker.py b/contrib/lock-checker.py
> --- a/contrib/lock-checker.py
> +++ b/contrib/lock-checker.py
> @@ -7,21 +7,12 @@ traceback printed to stderr.
>  This currently only checks store locks, not working copy locks.
>  """
>  import os
> -import traceback
> -
> -def _warnstack(ui, msg, skip=1):
> -    '''issue warning with the message and the current stack, skipping the
> -    skip last entries'''
> -    ui.warn('%s at:\n' % msg)
> -    entries = traceback.extract_stack()[:-skip]
> -    fnmax = max(len(entry[0]) for entry in entries)
> -    for fn, ln, func, _text in entries:
> -        ui.warn(' %*s:%-4s in %s\n' % (fnmax, fn, ln, func))
> +from mercurial import util
>
>  def _checklock(repo):
>      l = repo._lockref and repo._lockref()
>      if l is None or not l.held:
> -        _warnstack(repo.ui, 'missing lock', skip=2)
> +        util.debugstacktrace('missing lock', skip=1)
>
>  def reposetup(ui, repo):
>      orig = repo.__class__
> diff --git a/mercurial/util.py b/mercurial/util.py
> --- a/mercurial/util.py
> +++ b/mercurial/util.py
> @@ -1983,3 +1983,20 @@ class hooks(object):
>          self._hooks.sort(key=lambda x: x[0])
>          for source, hook in self._hooks:
>              hook(*args)
> +
> +def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr):
> +    '''Writes a message to f (stderr) with a nicely formatted stacktrace.
> +    Skips the 'skip' last entries.
> +    It can be used everywhere and do intentionally not require an ui object.
> +    Not be used in production code but very convenient while developing.
> +    '''
> +    f.write('%s at:\n' % msg)
> +    entries = [('%s:%s' % (fn, ln), func)
> +        for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
> +    if entries:
> +        fnmax = max(len(entry[0]) for entry in entries)
> +        for fnln, func in entries:
> +            f.write(' %-*s in %s\n' % (fnmax, fnln, func))
> +
> +# convenient shortcut
> +dst = debugstacktrace
> diff --git a/tests/test-debugcommands.t b/tests/test-debugcommands.t
> --- a/tests/test-debugcommands.t
> +++ b/tests/test-debugcommands.t
> @@ -23,3 +23,25 @@
>    uncompressed data size (min/max/avg) : 43 / 43 / 43
>    full revision size (min/max/avg)     : 44 / 44 / 44
>    delta size (min/max/avg)             : 0 / 0 / 0
> +
> +
> +Test internal debugstacktrace command
> +
> +  $ cat > debugstacktrace.py << EOF
> +  > from mercurial.util import debugstacktrace, dst, sys
> +  > def f():
> +  >     dst('hello world')
> +  > def g():
> +  >     f()
> +  >     debugstacktrace(skip=-5, f=sys.stdout)
> +  > g()
> +  > EOF
> +  $ python debugstacktrace.py
> +  hello world at:
> +   debugstacktrace.py:7 in <module>
> +   debugstacktrace.py:5 in g
> +   debugstacktrace.py:3 in f
> +  stacktrace at:
> +   debugstacktrace.py:7 *in <module> (glob)
> +   debugstacktrace.py:6 *in g (glob)
> +   */util.py:* in debugstacktrace (glob)
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel

Patch

diff --git a/contrib/lock-checker.py b/contrib/lock-checker.py
--- a/contrib/lock-checker.py
+++ b/contrib/lock-checker.py
@@ -7,21 +7,12 @@  traceback printed to stderr.
 This currently only checks store locks, not working copy locks.
 """
 import os
-import traceback
-
-def _warnstack(ui, msg, skip=1):
-    '''issue warning with the message and the current stack, skipping the
-    skip last entries'''
-    ui.warn('%s at:\n' % msg)
-    entries = traceback.extract_stack()[:-skip]
-    fnmax = max(len(entry[0]) for entry in entries)
-    for fn, ln, func, _text in entries:
-        ui.warn(' %*s:%-4s in %s\n' % (fnmax, fn, ln, func))
+from mercurial import util
 
 def _checklock(repo):
     l = repo._lockref and repo._lockref()
     if l is None or not l.held:
-        _warnstack(repo.ui, 'missing lock', skip=2)
+        util.debugstacktrace('missing lock', skip=1)
 
 def reposetup(ui, repo):
     orig = repo.__class__
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -1983,3 +1983,20 @@  class hooks(object):
         self._hooks.sort(key=lambda x: x[0])
         for source, hook in self._hooks:
             hook(*args)
+
+def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr):
+    '''Writes a message to f (stderr) with a nicely formatted stacktrace.
+    Skips the 'skip' last entries.
+    It can be used everywhere and do intentionally not require an ui object.
+    Not be used in production code but very convenient while developing.
+    '''
+    f.write('%s at:\n' % msg)
+    entries = [('%s:%s' % (fn, ln), func)
+        for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
+    if entries:
+        fnmax = max(len(entry[0]) for entry in entries)
+        for fnln, func in entries:
+            f.write(' %-*s in %s\n' % (fnmax, fnln, func))
+
+# convenient shortcut
+dst = debugstacktrace
diff --git a/tests/test-debugcommands.t b/tests/test-debugcommands.t
--- a/tests/test-debugcommands.t
+++ b/tests/test-debugcommands.t
@@ -23,3 +23,25 @@ 
   uncompressed data size (min/max/avg) : 43 / 43 / 43
   full revision size (min/max/avg)     : 44 / 44 / 44
   delta size (min/max/avg)             : 0 / 0 / 0
+
+
+Test internal debugstacktrace command
+
+  $ cat > debugstacktrace.py << EOF
+  > from mercurial.util import debugstacktrace, dst, sys
+  > def f():
+  >     dst('hello world')
+  > def g():
+  >     f()
+  >     debugstacktrace(skip=-5, f=sys.stdout)
+  > g()
+  > EOF
+  $ python debugstacktrace.py
+  hello world at:
+   debugstacktrace.py:7 in <module>
+   debugstacktrace.py:5 in g
+   debugstacktrace.py:3 in f
+  stacktrace at:
+   debugstacktrace.py:7 *in <module> (glob)
+   debugstacktrace.py:6 *in g (glob)
+   */util.py:* in debugstacktrace (glob)