[Python-Dev] GDB macros in Misc/gdbinit are broken
Victor Stinner
victor.stinner at gmail.com
Tue Feb 28 15:40:47 EST 2017
Ok, it seems like very few people know how to use python-gdb.py :-/
Sorry, I expect that everybody was using it!
python-gdb.py was written by Dave Malcolm, it includes:
* a pretty printer for many builtin Python types: str, tuple, list,
dict, frame, etc. It means that a regular gdb "print obj" command
displays the Python object content instead of a raw pointer. This
feature is super useful, it avoids to call _PyObject_Dump(obj) which
is likely to crash!
* commands to navigate between Python frames: py-up, py-down
* command to dump the Python traceback: py-bt, py-bt-full (also
include C frames)
* a few more commands
For me, these commands are really amazing! It's impressive to be able
to that in a debugger which doesn't know Python internals at all! It's
cool to be able to "program" (extend) a debugger!
I never tried to install it, I always use it with a ./python binary
compiled in the source code tree:
---
$ make
$ gdb ./python
# load ./python-gdb.py
...
---
Note: ./python-gdb.py is simply a copy of Tools/gdb/libpython.py,
copied by Makefile.
On Fedora, gdb doesn't load python-gdb.py because it doesn't trust my
$HOME/prog directory (root of all my development directories). I had
to trust it using this ~/.gdbinit config:
---
add-auto-load-safe-path ~/prog/
---
More generally, when gdb loads a "program", it tries to load
"program-gdb.py" in the same directory. Maybe it can load
"program-gdb.py" from other directories, but I never understood this
part, and hopefully I never had to understand it :-D
Maybe the debug package of Python on Debian and Fedora installs
pythonX.Y-gdb.py in the right directory, I didn't check, but I always
debug using a freshly compiled Python, so I never tried to understand
how these things work.
Example:
----
haypo at selma$ gdb ./python
(gdb) b _PyEval_EvalFrameDefault
(gdb) run
Breakpoint 1, _PyEval_EvalFrameDefault (
f=Frame 0x7ffff7f22058, for file <frozen importlib._bootstrap>,
line 25, in <module> (), throwflag=0) at Python/ceval.c:678
678 PyObject *retval = NULL; /* Return value */
=> gdb displays the content of the frame "f", you can see immediately
the filename and the line number (well, this frame is a little bit
special, it's the frozen module importlib)
(gdb) py-bt
Traceback (most recent call first):
File "<frozen importlib._bootstrap>", line 25, in <module>
---
Example of Python traceback:
----
haypo at selma$ gdb -args ./python -m test -r
(gdb) run
...
^C
(gdb) py-bt
Traceback (most recent call first):
File "/home/haypo/prog/python/master/Lib/test/test_long.py", line
947, in test_bit_length
self.assertEqual(k, len(bin(x).lstrip('-0b')))
File "/home/haypo/prog/python/master/Lib/unittest/case.py", line 601, in run
testMethod()
File "/home/haypo/prog/python/master/Lib/unittest/case.py", line
649, in __call__
return self.run(*args, **kwds)
File "/home/haypo/prog/python/master/Lib/unittest/suite.py", line 122, in run
test(result)
File "/home/haypo/prog/python/master/Lib/unittest/suite.py", line
84, in __call__
return self.run(*args, **kwds)
...
=> you get filename, line number, function name and even the Python
line! It seems obvious to get such information, but remind that you
are in gdb, not in Python
(gdb) py-list
942 def test_bit_length(self):
943 tiny = 1e-10
944 for x in range(-65000, 65000):
945 k = x.bit_length()
946 # Check equivalence with Python version
>947 self.assertEqual(k, len(bin(x).lstrip('-0b')))
948 # Behaviour as specified in the docs
949 if x != 0:
950 self.assertTrue(2**(k-1) <= abs(x) < 2**k)
951 else:
952 self.assertEqual(k, 0)
# move to the parent Python frame (= skip mutiple C frames until the
next Python frame)
(gdb) py-up
(...)
(gdb) py-list
596 with outcome.testPartExecutor(self):
597 self.setUp()
598 if outcome.success:
599 outcome.expecting_failure = expecting_failure
600 with outcome.testPartExecutor(self, isTest=True):
>601 testMethod()
602 outcome.expecting_failure = False
603 with outcome.testPartExecutor(self):
604 self.tearDown()
605
606 self.doCleanups()
(gdb) py-locals
self = <LongTest(_testMethodName='...
result = <TestResult(...
testMethod = <method at remote 0x7fffe8bf4d78>
expecting_failure_method = False
expecting_failure_class = False
expecting_failure = False
...
=> dump all variables of the current Python frame!
--------
Victor
More information about the Python-Dev
mailing list