# pahole command for gdb

# Copyright (C) 2008, 2009, 2012 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import gdb

class Pahole (gdb.Command):
    """Show the holes in a structure.
This command takes a single argument, a type name.
It prints the type and displays comments showing where holes are."""

    def __init__ (self):
        super (Pahole, self).__init__ ("pahole", gdb.COMMAND_NONE,
                                       gdb.COMPLETE_SYMBOL)

    def maybe_print_hole(self, bitpos, field_bitpos):
        if bitpos != field_bitpos:
            hole = field_bitpos - bitpos
            print '  /* XXX %d bit hole, try to pack */' % hole

    def pahole (self, type, level, name):
        if name is None:
            name = ''
        tag = type.tag
        if tag is None:
            tag = ''
        print '%sstruct %s {' % (' ' * (2 * level), tag)
        bitpos = 0
        for field in type.fields ():
            # Skip static fields.
            if not hasattr (field, ('bitpos')):
                continue

            ftype = field.type.strip_typedefs()

            self.maybe_print_hole(bitpos, field.bitpos)
            bitpos = field.bitpos
            if field.bitsize > 0:
                fieldsize = field.bitsize
            else:
                # TARGET_CHAR_BIT here...
                fieldsize = 8 * ftype.sizeof

            # TARGET_CHAR_BIT
            print ' /* %3d %3d */' % (int (bitpos / 8), int (fieldsize / 8)),
            bitpos = bitpos + fieldsize

            if ftype.code == gdb.TYPE_CODE_STRUCT:
                self.pahole (ftype, level + 1, field.name)
            else:
                print ' ' * (2 + 2 * level),
                print '%s %s' % (str (ftype), field.name)

        if level == 0:
            self.maybe_print_hole(bitpos, 8 * type.sizeof)

        print ' ' * (14 + 2 * level),
        print '} %s' % name

    def invoke (self, arg, from_tty):
        type = gdb.lookup_type (arg)
        type = type.strip_typedefs ()
        if type.code != gdb.TYPE_CODE_STRUCT:
            raise TypeError, '%s is not a struct type' % arg
        print ' ' * 14,
        self.pahole (type, 0, '')

Pahole()
