Skip to content

Instantly share code, notes, and snippets.

@kouk
Last active June 5, 2023 19:59
Show Gist options
  • Save kouk/5c2e725bef8b54aae6e0 to your computer and use it in GitHub Desktop.
Save kouk/5c2e725bef8b54aae6e0 to your computer and use it in GitHub Desktop.
pyfields.py
$ 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
from time import sleep
class Foo:
def __init__(self):
self.foo = 1
f = Foo()
def forever():
while f.foo == 1:
sleep(1)
forever()
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()
@asinha94
Copy link

Any ideas why this wouldn't work in python3? I get this error

(gdb) py-fields f.foo
Python Exception <class 'NameError'> name 'PyInstanceObjectPtr' is not defined:
Error occurred in Python command: name 'PyInstanceObjectPtr' is not defined

@PhracturedBlue
Copy link

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

@asinha94
Copy link

Hey that worked! Thanks a lot @PhracturedBlue!

@EvanKrall
Copy link

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