Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GDB-Python scripting: any samples iterating through C/C++ struct fields

The new GDB-Python scripting API looks quite powerful and should be very useful. However writing a useful script to iterate through the fields in a struct of C or C++ is not trivial. Does any one know some solid samples that does exactly that?

Thanks in advance.


Update the final sample: Replace the _print_fields() in early sample.

    if l.type.code == gdb.TYPE_CODE_STRUCT:
        print "Found a struct  %s " % n
        #self._print_fields(n, t)
        self._print_deep_items(n, t, l)
    else:
        print "Found no struct"

def _print_deep_items (self, n_, type_, instance_):
    for fld in type_.fields():
        fn = fld.name
        ft = fld.type
        fv = instance_[fn]
        if fv.type.code == gdb.TYPE_CODE_STRUCT:
            print "  Found a sub struct  %s " % fn
            self._print_deep_items(fn, ft, fv)
        else:
            print "    Field %s " % fn, " type %s " % ft.tag, " value %s " % fv

And the output:

  variable s1   type S1
Found a struct  s1
    Field v1   type None   value 0
    Field v2   type None   value 0
  Found a sub struct  v3
    Field w3   type None   value 0

Update with the first sample: Got the following sample code working. This is not the optimal as it does a look-up on every field after composing the string field name. abarnert is showing a promising and elegant approach, the working code is updated in the above final update section.

import gdb
class PrintGList(gdb.Command):
    """print fields of a struct: wzd struct_object

Iterate through the fields of a struct, and display
a human-readable form of the objects."""
    def __init__(self):
        gdb.Command.__init__(self, "wzd", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, True)

    def invoke(self, arg, from_tty):

        arg_list = gdb.string_to_argv(arg)
        if len(arg_list) < 1:
            print "usage: wzd struct"
            return

        n = arg_list[0]
        l = gdb.parse_and_eval(arg_list[0])
        m = l.type.tag

        print "  variable %s " % n, " type %s " % m
        try:
            t = gdb.lookup_type(m)
        except RuntimeError, e:
            print "type %s not found" % t
            return

        if l.type.code == gdb.TYPE_CODE_STRUCT:
            print "Found a struct  %s " % n
            self._print_fields(n, t)
        else:
            print "Found no struct"

    def _print_fields(self, n, typeobject):
        print typeobject
        flds = typeobject.fields()
        for x in flds:
            sn = n + "." + x.name
            print "  field %s" % sn, " code %s " % x.type.code, " type %s " % x.type.tag
            if x.type.code == gdb.TYPE_CODE_STRUCT:
                print "Found sub level struct  %s " % sn
                sl = gdb.parse_and_eval(sn)
                sm = sl.type.tag
                st = gdb.lookup_type( sm )
                self._print_fields(sn, x.type)

    def _deep_items (self, type_):
        for k, v in type_.iteritems():
            if k:
                print " k v %s " % k , " %s " % v
            else:
                print "   v    ",      " %s " % v

PrintGList()

The source file to test with:

struct S2 {        int w3;    };
struct S1 {        int v1, v2;      struct S2 v3; } s1;
int main(int argc, char *argv[]) {   return 0; }

Example output:

  variable s1   type S1
Found a struct  s1
S1
  field s1.v1  typecode 8   type None
  field s1.v2  typecode 8   type None
  field s1.v3  typecode 3   type S2
Found sub level struct  s1.v3
S2
  field s1.v3.w3  typecode 8   type None

GDB session to get the: source /home/me/testpath/wzdfile.py file a.out b main r wzd s1 quit

like image 937
minghua Avatar asked Feb 05 '13 00:02

minghua


1 Answers

According to the docs, iterating through the fields of a C struct should be pretty trivial:

If the type is a structure or class type, or an enum type, the fields of that type can be accessed using the Python dictionary syntax. For example, if some_type is a gdb.Type instance holding a structure type, you can access its foo field with:

bar = some_type['foo']

bar will be a gdb.Field object; see below under the description of the Type.fields method for a description of the gdb.Field class.

You can also use Type.fields to get the fields of a struct explicitly, but (as of 7.4) you can just use the normal dict methods as well, so to get a list of name/Field pairs:

for name, field in foo.type.iteritems():

Or, for just the names:

for name, field in foo.type.iterkeys():

And so on.

This doesn't seem to be directly documented on that page, but gdb.types implies it pretty strongly when it says that deep_items:

Returns a Python iterator similar to the standard gdb.Type.iteritems method.

For example, given this C type:

struct S {
    int x;
    int y;
};

You can do this:

(gdb) python struct_S = my_s.type # or gdb.lookup_type("struct S"), etc.
(gdb) python print struct_S.keys()
{['a', 'b']}
(gdb) python print my_s['a']
0

From a quick glance at the types.py source, look at how gdb.types.deep_item(type_) is implemented, and that does seem to be all there is to it.


Before gdb 7.4, you could not treat a type directly as a dict. That is, no for name in instance_.type: or instance_.type.iteritems(), etc. You had to explicitly call fields. Anyway, putting it all together, here's a simple example for iterating over all of the fields of a structure with gdb 7.2:

for field in inst.fields:
    fname = field.name
    ftype = field.type
    fval = inst[fname]

Except that this won't work if your struct has an anonymous struct inside it. For that, you'll need deep_items (and, if that isn't there in 7.2, you'll need to look at the code and figure out how to implement it yourself).

So, not quite trivial in 7.2, but pretty simple. And, if you want trivial, just upgrade to 7.4.

like image 185
abarnert Avatar answered Oct 11 '22 12:10

abarnert