使用gdb来调试linux内核链表结构

最近在调试程序的时候遇到linux内核链表结构的问题. 因为应用程序中大量使用了链表, 但是由于其特性, 导致在调试的时候十分不方便. 为了方便以后的处理, 决定解决一下这个问题.

有关linux内核链表的说明推荐一下这篇文章, 讲的蛮好的.

解决方案

搜索一下, 发现还有不少同学有讲到这个问题的, 这里推荐两个现成的方案:

  1. 一个现成的gdb脚本
  2. 一个非常详细的解析

两个链接都是通过gdb脚本来实现的. 对gdb脚本感兴趣的同学不妨看一下这两个教程(貌似是用ada做的例子): part1part2.

尝试一下

学习了以上的方法, 自己尝试一下. 先写个简单的测试小程序:

aptx.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include "list.h"

typedef struct fun_s {
    struct list_head list;

    int data;
} fun_t;

int
main(void)
{
    struct list_head fun_list;

    fun_t *f;

    fun_t fun[] = {
        { {NULL, NULL}, 4  },
        { {NULL, NULL}, 8  },
        { {NULL, NULL}, 6  },
        { {NULL, NULL}, 9  },
        { {NULL, NULL}, -1 },
    };

    INIT_LIST_HEAD(&fun_list);

    for (f = fun; f->data != -1; f++) {
        list_add_tail(&f->list, &fun_list);
    }

    list_for_each_entry(f, &fun_list, list) {
        printf("%d\n", f->data);
    }

    return 0;
}

好, 然后根据上面google出的两个链接的相关代码, 自己写个对应的gdb脚本:

test.gdb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
define find_next_node
    set $_list = $arg0

    set $offset = (char *)&((fun_t *)0)->list - (char *)0

    set $node = (fun_t *)((char *)$_list->next - $offset)

    if &$node->list == $list_head
        set $node = 0
    end
end

define dump_fun_list
    # $arg0 should be the address of the list_head
    set $list_head = $arg0
    set $loop = $arg0

    find_next_node $loop
    while $node
        p *$node

        set $loop = &$node->list

        find_next_node $loop
    end
end

break 30

这里需要提一下, gdb启动的时候会默认加载本地.gdbinit文件, 但是不太推荐这么做(因为我的环境默认禁止了^_^). 我们可以通过-x gdb_script_file来指定加载对应的脚本.

接下来我们直接在gdb中调用dump_fun_list &fun_list即可以看到对应的链表情况了.

c, debug

Comments