gdb-python:解析结构&x27;s每个字段,并使用正确的值(如果存在)打印它们

gdb-python:解析结构&x27;s每个字段,并使用正确的值(如果存在)打印它们,python,gdb,coredump,gdb-python,Python,Gdb,Coredump,Gdb Python,我正在编写一个python脚本来自动调试来自gdb的核心转储。我正在尝试打印数据结构,其中包括内核数据结构和列表(例如struct list_head)。例如,结构如下所示: struct my_struct { struct my_hardware_context ahw; struct net_device *netdev; struct pci_dev *pdev; struct list_head mac_list; .... .... }; struct my

我正在编写一个python脚本来自动调试来自gdb的核心转储。我正在尝试打印数据结构,其中包括内核数据结构和列表(例如struct list_head)。例如,结构如下所示:

struct my_struct {
  struct my_hardware_context ahw;
  struct net_device *netdev;
  struct pci_dev *pdev;
  struct list_head mac_list;
  ....
  ....
};
struct my_struct {
   struct my_hardware_context ahw;
   struct net_device *netdev;
   struct pci_dev *pdev;
   struct list_head mac_list;
   ....
   ....
};
我使用以下API tp打印此结构:

执行('p(*(struct my_struct*)dev_base->priv))

因此,我可以自动打印“struct my_struct”、struct my_hardware_context ahw的内容,但不能打印指针和列表的内容(例如,struct net_device*netdev、struct pci_dev*pdev、struct list\u head mac_list)(仅打印地址)。那么,如何使用gdb python脚本打印*netdev、*pdev和mac_列表的内容呢

已编辑:使我的问题更清楚

我正在编写一个python脚本来自动调试来自gdb的核心转储。我正在尝试打印数据结构,其中包括内核数据结构和列表(例如struct list_head)。例如,结构如下所示:

struct my_struct {
  struct my_hardware_context ahw;
  struct net_device *netdev;
  struct pci_dev *pdev;
  struct list_head mac_list;
  ....
  ....
};
struct my_struct {
   struct my_hardware_context ahw;
   struct net_device *netdev;
   struct pci_dev *pdev;
   struct list_head mac_list;
   ....
   ....
};
我使用以下API来打印这个结构:(可以假设我有正确的内核转储并添加了正确的符号

main\u struct=gdb.execute('p(*(struct my\u struct*)dev\u base->priv)

打印主结构

现在它将打印struct my_struct的所有成员的值,但最多打印一级,这意味着它将打印struct my_hardware_context ahw的全部内容,因为它是一个实例,但不会打印struct net_device*netdev、struct pci_dev*pdev、struct list\u head mac\u list等的内容。因此,现在我需要手动执行以下操作:

netdev=gdb.parse_和_eval('*(*(struct my_struct*)dev_base->next->priv.netdev')

print netdev

pdev=gdb.parse_和_eval('*(*(struct my_struct*)dev_base->next->priv.pdev')

打印pdev

所以我想自动化这些步骤。是否有任何gdb python API或方法可以让它迭代结构my_struct并自动打印指针、数组和列表值


谢谢。

struct net_device
struct pci_dev
来自Linux,它们是供内核使用的,而不是用户空间代码。它们甚至没有导出到您通过
make headers\u install
获得的经过消毒的内核头中,以便与libc一起使用

GDB无法打印
struct net_device
struct pci_dev
,因为它没有描述这些结构定义的调试信息。您的用户空间
struct my_struct
被声明为具有指向这些结构的不透明指针。我认为您首先不应该这样做

堆芯转储澄清后编辑 诀窍是将调试信息从内核和驱动程序模块加载到GDB中:

  • 获取具有debuginfo(CONFIG_DEBUG_INFO)的内核。例如,对于Centos,从获取匹配的内核debuginfo
  • 通过在正常操作下从运行驱动程序的系统中检查/sys/module/MY-driver/sections/{.text、.data、.bss},获取驱动程序模块的.text.data.bss加载地址
假设具有调试信息的内核位于/usr/lib/debug/lib/modules/3.9.4-200.fc18.x86_64/vmlinux,请运行:

$ gdb /usr/lib/debug/lib/modules/3.9.4-200.fc18.x86_64/vmlinux vmcore
(gdb) add-symbol-file MY-DRIVER.ko TEXT-ADDR -s .data DATA-ADDR -s .bss BSS-ADDR
用/sys/module/MY-DRIVER/sections/下文件中的地址替换TEXT-ADDRDATA-ADDRBSS-ADDR(我认为在这种情况下,只需撒谎并使用地址0就可以了)

验证ptype struct net_deviceptype struct pci_devptype my_struct是否正常工作。然后按照之前的方式获取
struct*my_struct
的地址后,您应该能够打印其内容

在遵循指针的同时遍历结构 打印结构跟随指针.py

import gdb

def is_container(v):
    c = v.type.code
    return (c == gdb.TYPE_CODE_STRUCT or c == gdb.TYPE_CODE_UNION)

def is_pointer(v):
    return (v.type.code == gdb.TYPE_CODE_PTR)

def print_struct_follow_pointers(s, level_limit = 3, level = 0):
    indent = ' ' * level

    if not is_container(s):
        gdb.write('%s\n' % (s,))
        return

    if level >= level_limit:
        gdb.write('%s { ... },\n' % (s.type,))
        return

    gdb.write('%s {\n' % (s.type,))
    for k in s.type.keys():
        v = s[k]
        if is_pointer(v):
            gdb.write('%s %s: %s' % (indent, k, v))
            try:
                v1 = v.dereference()
                v1.fetch_lazy()
            except gdb.error:
                gdb.write(',\n')
                continue
            else:
                gdb.write(' -> ')
            print_struct_follow_pointers(v1, level_limit, level + 1)
        elif is_container(v):
            gdb.write('%s %s: ' % (indent, k))
            print_struct_follow_pointers(v, level_limit, level + 1)
        else:
            gdb.write('%s %s: %s,\n' % (indent, k, v))
    gdb.write('%s},\n' % (indent,))

class PrintStructFollowPointers(gdb.Command):
    '''
    print-struct-follow-pointers [/LEVEL_LIMIT] STRUCT-VALUE
    '''
    def __init__(self): 
        super(PrintStructFollowPointers, self).__init__(
            'print-struct-follow-pointers',
            gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, False)

    def invoke(self, arg, from_tty):
        s = arg.find('/')
        if s == -1:
            (expr, limit) = (arg, 3)
        else:
            if arg[:s].strip():
                (expr, limit) = (arg, 3)
            else:
                i = s + 1
                for (i, c) in enumerate(arg[s+1:], s + 1):
                    if not c.isdigit():
                        break
                end = i
                digits = arg[s+1:end]
                try:
                    limit = int(digits)
                except ValueError:
                    raise gdb.GdbError(PrintStructFollowPointers.__doc__)
                (expr, limit) = (arg[end:], limit)
        try:
            v = gdb.parse_and_eval(expr)
        except gdb.error, e:
            raise gdb.GdbError(e.message)

        print_struct_follow_pointers(v, limit)

PrintStructFollowPointers()
示例会话 您可以限制打印的嵌入结构的级别:

(gdb) print-struct-follow-pointers/4 *p

您是如何生成coredump的?@Scott,使用
ulimit-c unlimited
所以您想将答案更改为“您是如何生成coredump的?”)是的,实际上它是在驱动程序崩溃到/var/core目录时自动生成的。通过重定向
(gdb)的输出,是否可以这样做p将struct my_struct
键入一个文件,并解析指针、数组或列表中的每个字段(符号),然后获取该符号的类型。最后,
(gdb)p*
?谢谢Scott。我已经完成了您提到的所有步骤,意味着添加了正确的符号(从内核日志文件中选择了相应的地址),并且还验证了
gdb.execute((*(struct my_struct*)dev_base->priv.netdev')
工作正常,但是有很多结构指针,比如netdev和pdev。所以我想自动获取所有这些指针和数组并打印它们的内容。您可以假设我添加了正确的驱动程序和内核符号。我再次修改了代码,添加了
-l
选项。这段代码实际上只是使用
gdb.Value
gdb.Type
进行递归遍历。只需阅读GDB手册中的这些章节。通过阅读官方语言教程,您可以在一小时内掌握所需的Python知识、如何定义函数、类、如何处理异常等:)