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 71 Oops消息在哪? 72 --------------- 73 74 通常,Oops文本由klogd从内核缓冲区读取,然后交给 ``syslogd`` ,后者将其写入 75 syslog文件,通常是 ``/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 106 gdb 107 ^^^^ 108 109 GNU 调试器(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 184 objdump 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 259 ML),抄送驱动程序维护者(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 302 klogd至少会提供产生保护故障的模块的名称。如果可加载模块的开发人员选择从模块 303 导出符号信息,则可能会有其他可用的符号信息。 304 305 由于内核模块环境可以是动态的,因此当模块环境发生变化时,必须有一种通知 306 ``klogd`` 守护进程的机制。有一些可用的命令行选项允许klogd向当前正在执行的守 307 护进程发出信号示意应该刷新符号信息。有关更多信息,请参阅 ``klogd`` 手册页。 308 309 sysklogd发行版附带了一个补丁,它修改了 ``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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.