Last active
June 5, 2023 19:59
-
-
Save kouk/5c2e725bef8b54aae6e0 to your computer and use it in GitHub Desktop.
pyfields.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ python foo.py & | |
[1] 21377 | |
$ gdb -p 21377 | |
GNU gdb (GDB) Fedora 7.7.1-17.fc20 | |
[... gdb loading messages ...] | |
0x00007f9a09af46e3 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:81 | |
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) | |
(gdb) py-bt | |
#4 Frame 0x22d9c70, for file foo.py, line 12, in forever () | |
sleep(1) | |
#7 Frame 0x224f830, for file foo.py, line 14, in <module> () | |
forever() | |
(gdb) py-print f | |
global 'f' = <Foo(foo=1) at remote 0x7f9a0abba830> | |
(gdb) py-print f.foo | |
'f.foo' not found | |
(gdb) source pyfields.py | |
(gdb) py-fields f.foo | |
global 'foo' = 1 | |
(gdb) quit | |
A debugging session is active. | |
Inferior 1 [process 21377] will be detached. | |
Quit anyway? (y or n) y | |
Detaching from program: /usr/bin/python2.7, process 21377 | |
$ exit | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from time import sleep | |
class Foo: | |
def __init__(self): | |
self.foo = 1 | |
f = Foo() | |
def forever(): | |
while f.foo == 1: | |
sleep(1) | |
forever() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PyPrintFields(gdb.Command): | |
'Look up the given python variable name, and print it' | |
def __init__(self): | |
gdb.Command.__init__ (self, | |
"py-fields", | |
gdb.COMMAND_DATA, | |
gdb.COMPLETE_NONE) | |
def invoke(self, args, from_tty): | |
name = str(args) | |
frame = Frame.get_selected_python_frame() | |
if not frame: | |
print 'Unable to locate python frame' | |
return | |
pyop_frame = frame.get_pyop() | |
if not pyop_frame: | |
print 'Unable to read information on python frame' | |
return | |
def get_var_dict(v): | |
if isinstance(v, PyInstanceObjectPtr): | |
return v.pyop_field('in_dict') | |
elif isinstance(v, HeapTypeObjectPtr): | |
return v.get_attr_dict() | |
else: | |
return {} | |
parent = None | |
fields = name.split('.') | |
for name in fields: | |
if parent is None: | |
pyop_var, scope = pyop_frame.get_var_by_name(name) | |
else: | |
for k, pyop_var in get_var_dict(parent).iteritems(): | |
if str(k) == name: | |
break | |
else: | |
print "Can't find field %s of %s" % (name, parent) | |
return | |
parent = pyop_var | |
if pyop_var: | |
print ('%s %r = %s' | |
% (scope, | |
name, | |
pyop_var.get_truncated_repr(MAX_OUTPUT_LEN))) | |
else: | |
print '%r not found' % name | |
PyPrintFields() |
Here is a replacement for invoke that works (at least somewhat) on python3:
def invoke(self, args, from_tty):
name = str(args)
frame = Frame.get_selected_python_frame()
if not frame:
print 'Unable to locate python frame'
return
pyop_frame = frame.get_pyop()
if not pyop_frame:
print 'Unable to read information on python frame'
return
def get_var_dict(v):
if isinstance(v, HeapTypeObjectPtr):
return v.get_attr_dict()
elif 'pyop_field' in v:
return v.pyop_field('in_dict')
else:
return {}
parent = None
fields = name.split('.')
for name in fields:
if parent is None:
pyop_var, scope = pyop_frame.get_var_by_name(name)
else:
pyop_var = next((_v for _k, _v in get_var_dict(parent).iteritems() if str(_k.proxyval(set())) == name), None)
if not pyop_var:
names = [str(_k.proxyval(set())) for _k, _v in get_var_dict(parent).iteritems()]
print "Can't find field %s of %s. Available keys: %s" % (name, parent, names)
return
parent = pyop_var
if pyop_var:
print ('%s %r = %s'
% (scope,
name,
pyop_var.get_truncated_repr(MAX_OUTPUT_LEN)))
else:
print '%r not found' % name
Hey that worked! Thanks a lot @PhracturedBlue!
Here's a full version of pyfields.py including @PhracturedBlue's change, as well as switching from print statements to print function:
class PyPrintFields(gdb.Command):
'Look up the given python variable name, and print it'
def __init__(self):
gdb.Command.__init__ (self,
"py-fields",
gdb.COMMAND_DATA,
gdb.COMPLETE_NONE)
def invoke(self, args, from_tty):
name = str(args)
frame = Frame.get_selected_python_frame()
if not frame:
print('Unable to locate python frame')
return
pyop_frame = frame.get_pyop()
if not pyop_frame:
print('Unable to read information on python frame')
return
def get_var_dict(v):
if isinstance(v, HeapTypeObjectPtr):
return v.get_attr_dict()
elif 'pyop_field' in v:
return v.pyop_field('in_dict')
else:
return {}
parent = None
fields = name.split('.')
for name in fields:
if parent is None:
pyop_var, scope = pyop_frame.get_var_by_name(name)
else:
pyop_var = next((_v for _k, _v in get_var_dict(parent).iteritems() if str(_k.proxyval(set())) == name), None)
if not pyop_var:
names = [str(_k.proxyval(set())) for _k, _v in get_var_dict(parent).iteritems()]
print("Can't find field %s of %s. Available keys: %s" % (name, parent, names))
return
parent = pyop_var
if pyop_var:
print ('%s %r = %s'
% (scope,
name,
pyop_var.get_truncated_repr(MAX_OUTPUT_LEN)))
else:
print('%r not found' % name)
PyPrintFields()
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Any ideas why this wouldn't work in python3? I get this error