xref: /linux/Documentation/translations/zh_CN/admin-guide/bug-hunting.rst (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1.. include:: ../disclaimer-zh_CN.rst
2
3:Original: :doc:`../../../admin-guide/bug-hunting`
4
5:译者:
6
7 吴想成 Wu XiangCheng <bobwxc@email.cn>
8
9追踪缺陷
10=========
11
12内核错误报告通常附带如下堆栈转储::
13
14	------------[ cut here ]------------
15	WARNING: CPU: 1 PID: 28102 at kernel/module.c:1108 module_put+0x57/0x70
16	Modules linked in: dvb_usb_gp8psk(-) dvb_usb dvb_core nvidia_drm(PO) nvidia_modeset(PO) snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd soundcore nvidia(PO) [last unloaded: rc_core]
17	CPU: 1 PID: 28102 Comm: rmmod Tainted: P        WC O 4.8.4-build.1 #1
18	Hardware name: MSI MS-7309/MS-7309, BIOS V1.12 02/23/2009
19	 00000000 c12ba080 00000000 00000000 c103ed6a c1616014 00000001 00006dc6
20	 c1615862 00000454 c109e8a7 c109e8a7 00000009 ffffffff 00000000 f13f6a10
21	 f5f5a600 c103ee33 00000009 00000000 00000000 c109e8a7 f80ca4d0 c109f617
22	Call Trace:
23	 [<c12ba080>] ? dump_stack+0x44/0x64
24	 [<c103ed6a>] ? __warn+0xfa/0x120
25	 [<c109e8a7>] ? module_put+0x57/0x70
26	 [<c109e8a7>] ? module_put+0x57/0x70
27	 [<c103ee33>] ? warn_slowpath_null+0x23/0x30
28	 [<c109e8a7>] ? module_put+0x57/0x70
29	 [<f80ca4d0>] ? gp8psk_fe_set_frontend+0x460/0x460 [dvb_usb_gp8psk]
30	 [<c109f617>] ? symbol_put_addr+0x27/0x50
31	 [<f80bc9ca>] ? dvb_usb_adapter_frontend_exit+0x3a/0x70 [dvb_usb]
32	 [<f80bb3bf>] ? dvb_usb_exit+0x2f/0xd0 [dvb_usb]
33	 [<c13d03bc>] ? usb_disable_endpoint+0x7c/0xb0
34	 [<f80bb48a>] ? dvb_usb_device_exit+0x2a/0x50 [dvb_usb]
35	 [<c13d2882>] ? usb_unbind_interface+0x62/0x250
36	 [<c136b514>] ? __pm_runtime_idle+0x44/0x70
37	 [<c13620d8>] ? __device_release_driver+0x78/0x120
38	 [<c1362907>] ? driver_detach+0x87/0x90
39	 [<c1361c48>] ? bus_remove_driver+0x38/0x90
40	 [<c13d1c18>] ? usb_deregister+0x58/0xb0
41	 [<c109fbb0>] ? SyS_delete_module+0x130/0x1f0
42	 [<c1055654>] ? task_work_run+0x64/0x80
43	 [<c1000fa5>] ? exit_to_usermode_loop+0x85/0x90
44	 [<c10013f0>] ? do_fast_syscall_32+0x80/0x130
45	 [<c1549f43>] ? sysenter_past_esp+0x40/0x6a
46	---[ end trace 6ebc60ef3981792f ]---
47
48这样的堆栈跟踪提供了足够的信息来识别内核源代码中发生错误的那一行。根据问题的
49严重性,它还可能包含 **“Oops”** 一词,比如::
50
51	BUG: unable to handle kernel NULL pointer dereference at   (null)
52	IP: [<c06969d4>] iret_exc+0x7d0/0xa59
53	*pdpt = 000000002258a001 *pde = 0000000000000000
54	Oops: 0002 [#1] PREEMPT SMP
55	...
56
57尽管有 **Oops** 或其他类型的堆栈跟踪,但通常需要找到出问题的行来识别和处理缺
58陷。在本章中,我们将参考“Oops”来了解需要分析的各种堆栈跟踪。
59
60如果内核是用 ``CONFIG_DEBUG_INFO`` 编译的,那么可以使用文件:
61`scripts/decode_stacktrace.sh` 。
62
63链接的模块
64-----------
65
66受到污染或正在加载/卸载的模块用“(…)”标记,污染标志在
67`Documentation/admin-guide/tainted-kernels.rst` 文件中进行了描述,“正在被加
68载”用“+”标注,“正在被卸载”用“-”标注。
69
70
71Oops消息在哪?
72---------------
73
74通常,Oops文本由klogd从内核缓冲区读取,然后交给 ``syslogd`` ,后者将其写入
75syslog文件,通常是 ``/var/log/messages`` (取决于 ``/etc/syslog.conf`` )。
76在使用systemd的系统上,它也可以由 ``journald`` 守护进程存储,并通过运行
77``journalctl`` 命令进行访问。
78
79有时 ``klogd`` 会挂掉,这种情况下您可以运行 ``dmesg > file`` 从内核缓冲区
80读取数据并保存它。或者您可以 ``cat /proc/kmsg > file`` ,但是您必须适时
81中断以停止传输,因为 ``kmsg`` 是一个“永无止境的文件”。
82
83如果机器严重崩溃,无法输入命令或磁盘不可用,那还有三个选项:
84
85(1) 手动复制屏幕上的文本,并在机器重新启动后输入。很难受,但这是突然崩溃下
86    唯一的选择。或者你可以用数码相机拍下屏幕——虽然不那么好,但总比什么都没
87    有好。如果消息滚动超出控制台顶部,使用更高分辨率(例如 ``vga=791`` )
88    引导启动将允许您阅读更多文本。(警告:这需要 ``vesafb`` ,因此对“早期”
89    的Oppses没有帮助)
90
91(2) 从串口终端启动(参见
92    :ref:`Documentation/admin-guide/serial-console.rst <serial_console>` ),
93    在另一台机器上运行调制解调器然后用你喜欢的通信程序捕获输出。
94    Minicom运行良好。
95
96(3) 使用Kdump(参阅 Documentation/admin-guide/kdump/kdump.rst ),使用
97    Documentation/admin-guide/kdump/gdbmacros.txt 中的dmesg gdbmacro从旧内存
98    中提取内核环形缓冲区。
99
100找到缺陷位置
101-------------
102
103如果你能指出缺陷在内核源代码中的位置,则报告缺陷的效果会非常好。这有两种方法。
104通常来说使用 ``gdb`` 会比较容易,不过内核需要用调试信息来预编译。
105
106gdb
107^^^^
108
109GNU 调试器(GNU debugger, ``gdb`` )是从 ``vmlinux`` 文件中找出OOPS的确切
110文件和行号的最佳方法。
111
112在使用 ``CONFIG_DEBUG_INFO`` 编译的内核上使用gdb效果最好。可通过运行以下命令
113进行设置::
114
115  $ ./scripts/config -d COMPILE_TEST -e DEBUG_KERNEL -e DEBUG_INFO
116
117在用 ``CONFIG_DEBUG_INFO`` 编译的内核上,你可以直接从OOPS复制EIP值::
118
119 EIP:    0060:[<c021e50e>]    Not tainted VLI
120
121并使用GDB来将其翻译成可读形式::
122
123  $ gdb vmlinux
124  (gdb) l *0xc021e50e
125
126如果没有启用 ``CONFIG_DEBUG_INFO`` ,则使用OOPS的函数偏移::
127
128 EIP is at vt_ioctl+0xda8/0x1482
129
130并在启用 ``CONFIG_DEBUG_INFO`` 的情况下重新编译内核::
131
132  $ ./scripts/config -d COMPILE_TEST -e DEBUG_KERNEL -e DEBUG_INFO
133  $ make vmlinux
134  $ gdb vmlinux
135  (gdb) l *vt_ioctl+0xda8
136  0x1888 is in vt_ioctl (drivers/tty/vt/vt_ioctl.c:293).
137  288	{
138  289		struct vc_data *vc = NULL;
139  290		int ret = 0;
140  291
141  292		console_lock();
142  293		if (VT_BUSY(vc_num))
143  294			ret = -EBUSY;
144  295		else if (vc_num)
145  296			vc = vc_deallocate(vc_num);
146  297		console_unlock();
147
148或者若您想要更详细的显示::
149
150  (gdb) p vt_ioctl
151  $1 = {int (struct tty_struct *, unsigned int, unsigned long)} 0xae0 <vt_ioctl>
152  (gdb) l *0xae0+0xda8
153
154您也可以使用对象文件作为替代::
155
156  $ make drivers/tty/
157  $ gdb drivers/tty/vt/vt_ioctl.o
158  (gdb) l *vt_ioctl+0xda8
159
160如果你有调用跟踪,类似::
161
162     Call Trace:
163      [<ffffffff8802c8e9>] :jbd:log_wait_commit+0xa3/0xf5
164      [<ffffffff810482d9>] autoremove_wake_function+0x0/0x2e
165      [<ffffffff8802770b>] :jbd:journal_stop+0x1be/0x1ee
166      ...
167
168这表明问题可能在 :jbd: 模块中。您可以在gdb中加载该模块并列出相关代码::
169
170  $ gdb fs/jbd/jbd.ko
171  (gdb) l *log_wait_commit+0xa3
172
173.. note::
174
175     您还可以对堆栈跟踪处的任何函数调用执行相同的操作,例如::
176
177	 [<f80bc9ca>] ? dvb_usb_adapter_frontend_exit+0x3a/0x70 [dvb_usb]
178
179     上述调用发生的位置可以通过以下方式看到::
180
181	$ gdb drivers/media/usb/dvb-usb/dvb-usb.o
182	(gdb) l *dvb_usb_adapter_frontend_exit+0x3a
183
184objdump
185^^^^^^^^
186
187要调试内核,请使用objdump并从崩溃输出中查找十六进制偏移,以找到有效的代码/汇
188编行。如果没有调试符号,您将看到所示例程的汇编程序代码,但是如果内核有调试
189符号,C代码也将可见(调试符号可以在内核配置菜单的hacking项中启用)。例如::
190
191    $ objdump -r -S -l --disassemble net/dccp/ipv4.o
192
193.. note::
194
195   您需要处于内核树的顶层以便此获得您的C文件。
196
197如果您无法访问源代码,仍然可以使用以下方法调试一些崩溃转储(如Dave Miller的
198示例崩溃转储输出所示)::
199
200     EIP is at 	+0x14/0x4c0
201      ...
202     Code: 44 24 04 e8 6f 05 00 00 e9 e8 fe ff ff 8d 76 00 8d bc 27 00 00
203     00 00 55 57  56 53 81 ec bc 00 00 00 8b ac 24 d0 00 00 00 8b 5d 08
204     <8b> 83 3c 01 00 00 89 44  24 14 8b 45 28 85 c0 89 44 24 18 0f 85
205
206     Put the bytes into a "foo.s" file like this:
207
208            .text
209            .globl foo
210     foo:
211            .byte  .... /* bytes from Code: part of OOPS dump */
212
213     Compile it with "gcc -c -o foo.o foo.s" then look at the output of
214     "objdump --disassemble foo.o".
215
216     Output:
217
218     ip_queue_xmit:
219         push       %ebp
220         push       %edi
221         push       %esi
222         push       %ebx
223         sub        $0xbc, %esp
224         mov        0xd0(%esp), %ebp        ! %ebp = arg0 (skb)
225         mov        0x8(%ebp), %ebx         ! %ebx = skb->sk
226         mov        0x13c(%ebx), %eax       ! %eax = inet_sk(sk)->opt
227
228`scripts/decodecode` 文件可以用来自动完成大部分工作,这取决于正在调试的CPU
229体系结构。
230
231报告缺陷
232---------
233
234一旦你通过定位缺陷找到了其发生的地方,你可以尝试自己修复它或者向上游报告它。
235
236为了向上游报告,您应该找出用于开发受影响代码的邮件列表。这可以使用 ``get_maintainer.pl`` 。
237
238
239例如,您在gspca的sonixj.c文件中发现一个缺陷,则可以通过以下方法找到它的维护者::
240
241	$ ./scripts/get_maintainer.pl -f drivers/media/usb/gspca/sonixj.c
242	Hans Verkuil <hverkuil@xs4all.nl> (odd fixer:GSPCA USB WEBCAM DRIVER,commit_signer:1/1=100%)
243	Mauro Carvalho Chehab <mchehab@kernel.org> (maintainer:MEDIA INPUT INFRASTRUCTURE (V4L/DVB),commit_signer:1/1=100%)
244	Tejun Heo <tj@kernel.org> (commit_signer:1/1=100%)
245	Bhaktipriya Shridhar <bhaktipriya96@gmail.com> (commit_signer:1/1=100%,authored:1/1=100%,added_lines:4/4=100%,removed_lines:9/9=100%)
246	linux-media@vger.kernel.org (open list:GSPCA USB WEBCAM DRIVER)
247	linux-kernel@vger.kernel.org (open list)
248
249请注意它将指出:
250
251- 最后接触源代码的开发人员(如果这是在git树中完成的)。在上面的例子中是Tejun
252  和Bhaktipriya(在这个特定的案例中,没有人真正参与这个文件的开发);
253- 驱动维护人员(Hans Verkuil);
254- 子系统维护人员(Mauro Carvalho Chehab);
255- 驱动程序和/或子系统邮件列表(linux-media@vger.kernel.org);
256- Linux内核邮件列表(linux-kernel@vger.kernel.org)。
257
258通常,修复缺陷的最快方法是将它报告给用于开发相关代码的邮件列表(linux-media
259ML),抄送驱动程序维护者(Hans)。
260
261如果你完全不知道该把报告寄给谁,且 ``get_maintainer.pl`` 也没有提供任何有用
262的信息,请发送到linux-kernel@vger.kernel.org。
263
264感谢您的帮助,这使Linux尽可能稳定:-)
265
266修复缺陷
267---------
268
269如果你懂得编程,你不仅可以通过报告错误来帮助我们,还可以提供一个解决方案。
270毕竟,开源就是分享你的工作,你不想因为你的天才而被认可吗?
271
272如果你决定这样做,请在制定解决方案后将其提交到上游。
273
274请务必阅读
275:ref:`Documentation/process/submitting-patches.rst <submittingpatches>` ,
276以帮助您的代码被接受。
277
278
279---------------------------------------------------------------------------
280
281用 ``klogd`` 进行Oops跟踪的注意事项
282------------------------------------
283
284为了帮助Linus和其他内核开发人员, ``klogd`` 对保护故障的处理提供了大量支持。
285为了完整支持地址解析,至少应该使用 ``sysklogd`` 包的1.3-pl3版本。
286
287当发生保护故障时, ``klogd`` 守护进程会自动将内核日志消息中的重要地址转换为
288它们的等效符号。然后通过 ``klogd`` 使用的任何报告机制来转发这个已翻译的内核
289消息。保护错误消息可以直接从消息文件中剪切出来并转发给内核开发人员。
290
291``klogd`` 执行两种类型的地址解析,静态翻译和动态翻译。静态翻译使用System.map
292文件。为了进行静态转换, ``klogd`` 守护进程必须能够在守护进程初始化时找到系
293统映射文件。有关 ``klogd`` 如何搜索映射文件的信息,请参见klogd手册页。
294
295当使用内核可加载模块时,动态地址转换非常重要。由于内核模块的内存是从内核的
296动态内存池中分配的,因此无论是模块的开头还是模块中的函数和符号都没有固定的
297位置。
298
299内核支持系统调用,允许程序确定加载哪些模块及其在内存中的位置。klogd守护进程
300使用这些系统调用构建了一个符号表,可用于调试可加载内核模块中发生的保护错误。
301
302klogd至少会提供产生保护故障的模块的名称。如果可加载模块的开发人员选择从模块
303导出符号信息,则可能会有其他可用的符号信息。
304
305由于内核模块环境可以是动态的,因此当模块环境发生变化时,必须有一种通知
306``klogd`` 守护进程的机制。有一些可用的命令行选项允许klogd向当前正在执行的守
307护进程发出信号示意应该刷新符号信息。有关更多信息,请参阅 ``klogd`` 手册页。
308
309sysklogd发行版附带了一个补丁,它修改了 ``modules-2.0.0`` 包,以便在加载或
310卸载模块时自动向klogd发送信号。应用此补丁基本上可无缝支持调试内核可加载模块
311发生的保护故障。
312
313以下是 ``klogd`` 处理的可加载模块中的保护故障示例::
314
315	Aug 29 09:51:01 blizard kernel: Unable to handle kernel paging request at virtual address f15e97cc
316	Aug 29 09:51:01 blizard kernel: current->tss.cr3 = 0062d000, %cr3 = 0062d000
317	Aug 29 09:51:01 blizard kernel: *pde = 00000000
318	Aug 29 09:51:01 blizard kernel: Oops: 0002
319	Aug 29 09:51:01 blizard kernel: CPU:    0
320	Aug 29 09:51:01 blizard kernel: EIP:    0010:[oops:_oops+16/3868]
321	Aug 29 09:51:01 blizard kernel: EFLAGS: 00010212
322	Aug 29 09:51:01 blizard kernel: eax: 315e97cc   ebx: 003a6f80   ecx: 001be77b   edx: 00237c0c
323	Aug 29 09:51:01 blizard kernel: esi: 00000000   edi: bffffdb3   ebp: 00589f90   esp: 00589f8c
324	Aug 29 09:51:01 blizard kernel: ds: 0018   es: 0018   fs: 002b   gs: 002b   ss: 0018
325	Aug 29 09:51:01 blizard kernel: Process oops_test (pid: 3374, process nr: 21, stackpage=00589000)
326	Aug 29 09:51:01 blizard kernel: Stack: 315e97cc 00589f98 0100b0b4 bffffed4 0012e38e 00240c64 003a6f80 00000001
327	Aug 29 09:51:01 blizard kernel:        00000000 00237810 bfffff00 0010a7fa 00000003 00000001 00000000 bfffff00
328	Aug 29 09:51:01 blizard kernel:        bffffdb3 bffffed4 ffffffda 0000002b 0007002b 0000002b 0000002b 00000036
329	Aug 29 09:51:01 blizard kernel: Call Trace: [oops:_oops_ioctl+48/80] [_sys_ioctl+254/272] [_system_call+82/128]
330	Aug 29 09:51:01 blizard kernel: Code: c7 00 05 00 00 00 eb 08 90 90 90 90 90 90 90 90 89 ec 5d c3
331
332---------------------------------------------------------------------------
333
334::
335
336  Dr. G.W. Wettstein           Oncology Research Div. Computing Facility
337  Roger Maris Cancer Center    INTERNET: greg@wind.rmcc.com
338  820 4th St. N.
339  Fargo, ND  58122
340  Phone: 701-234-7556
341