关于 Kdump 和 Crash 的背景知识,不是本文的重点,可以参考下列文章:
这次碰到的问题是,系统当时死锁了,抓了个 vmcore 来研究,现在要看层层包含的某个结构体的一个 rw_semaphore
结构体变量的信息。当时这一层层的基本结构大体是这样的:
有两个链表,分别叫做: sets
和 set_devs
; 顾名思义,第一个 sets
是一堆 set
结构体组成的链表;第二个 set_devs
是一堆 set_dev
结构体组成的链表。两个结构体如下:
1
2
3
4
5
6
7
8
9
10
11
| struct set {
struct list_head *list;
blah blah blah;
struct list_head *set_devs;
};
struct set_dev {
struct list_head *list;
blah blah blah;
struct rw_semaphore rw_sem;
};
|
最终任务是要找到 set_devs
这个列表里所有 set_dev
的 lock
这个信号量成员变量的信息。
首先,打开 vmcore (确保 kernel debuginfo 都在):
1
| sudo crash /usr/lib/debug/lib/modules/3.10.0/vmlinux /var/crash/vmcore
|
如果要分析的结构体在内核模块中,还需要用 mod -s/-S
来加载内核模块的 debuginfo.
然后找到 sets
这个链表的入口地址。很幸运,模块里这个链表是全局的,直接找到 symbol 就可以用:
1
2
3
4
5
6
7
| crash> p sets
sets = $1 = {
next = 0xffffc90056696030,
prev = 0xffffc90056696030
}
crash> p &sets // 顺手确认一下链表是否为空,如果 &sets == sets->next 就为空
$2 = (struct list_head *) 0xffffffffa04401f0 <sets>
|
我们就得到了一个不为空的链表的第一个元素(从上面也可以看到是唯一的一个)。由于内核里的链表只有指针没有元素内容,我们需要用类似 container_of
的机制来从成员反查链表元素的地址:
1
2
3
4
5
6
7
8
9
10
11
12
13
| crash> set -l set.list 0xffffc90056696030
struct set {
<snip>
list = {
next = 0xffffffffa04401f0 <sets>,
prev = 0xffffffffa04401f0 <sets>
},
<snip>
set_devs = {
next = 0xffffc90050f67000,
prev = 0xffffc90056ee4000
},
}
|
这样我们又得到了一个链表的 LIST_HEAD 地址:
1
2
3
4
5
| crash> list_head 0xffffc90050f67000
struct list_head {
next = 0xffffc90065f2a000,
prev = 0xffffc90056696da8
}
|
得到第一个元素地址后反查 set_dev
结构体:
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
| crash> struct set_dev -l set_dev.list 0xffffc90065f2a000
struct set_dev {
list = {
next = 0xffffc900583dc000,
prev = 0xffffc90050f67000
},
<snip>
rw_sem = {
count = 0,
wait_lock = {
raw_lock = {
{
head_tail = 0,
tickets = {
head = 0,
tail = 0
}
}
}
},
wait_list = {
next = 0xffffc90065f2ab48,
prev = 0xffffc90065f2ab48
}
},
}
|
找到 rw_sem
结构体信息,这正是我们想要的。然后依次往下找即可。btw, 我忘记链表是循环链表了,所以我傻乎乎地手工查了半个小时最后才发现自己重复了……
所以问题来了,crash 里面有快捷的方法可以挨个链表元素查找么……