Linux内核实时debugging,如何完成以及使用哪些工具?
什么是最常见的,为什么不常用的方法和工具在Linux内核上进行实时debugging? 我知道Linus例如。 反对 Linux内核的这种debugging,或者至less是在那个年代没有做过那么多的事情,但是从2000年开始,老实说已经有很长的一段时间了,而且我对Linux的心态如何变化感兴趣项目和当前使用哪些当前方法在Linux内核上进行实时debugging(本地或远程)?
欢迎参考有关所提及技术和工具的演练和教程。
另一种select是使用ICE / JTAG控制器和GDB。 这种“硬件”解决scheme特别适用于embedded式系统,
但是例如Qemu提供了类似的function:
-
用gdb'remote'存根启动qemu,在'localhost:1234'上侦听:
qemu -s ...
, -
然后用GDB打开用debugging信息编译的内核文件
vmlinux
(你可以看看这个邮件列表线程讨论内核未优化的地方)。 -
连接GDB和Qemu:
target remote localhost:1234
-
看你是活的内核:
(gdb) where #0 cpu_v7_do_idle () at arch/arm/mm/proc-v7.S:77 #1 0xc0029728 in arch_idle () atarm/mach-realview/include/mach/system.h:36 #2 default_idle () at arm/kernel/process.c:166 #3 0xc00298a8 in cpu_idle () at arch/arm/kernel/process.c:199 #4 0xc00089c0 in start_kernel () at init/main.c:713
不幸的是,到目前为止,用户空间debugging在GDB中是不可能的(没有任务列表信息,没有MMU重新编程来查看不同的进程上下文,…),但是如果你停留在内核空间,这是非常方便的。
-
info threads
会给你不同的CPU的列表和状态
编辑:
您可以在本PDF中获得有关此过程的更多详细信息:
使用GDB和QEMUdebuggingLinux系统 。
根据wiki , kgdb
在2.6.26
被合并到内核中,这是在过去的几年里。 kgdb
是一个远程debugging器 ,所以你可以在你的内核中激活它,然后以某种方式将gdb附加到它。 我说不知何故,似乎有很多选项 – 请参阅连接gdb 。 鉴于kgdb
现在在源码树中,我会说,这是你想要使用的。
所以看起来就像Linus所说的那样。但是我会强调他的观点 – 你应该知道自己在做什么,并且很好地了解系统。 这是核心土地。 如果出了什么问题,你就不会出现段错误,从后来的一些不起眼的问题中,你可以得到任何东西,直到整个系统崩溃。 这里是龙。 谨慎行事,你已经受到警告。
在debuggingLinux内核时,我们可以使用多种工具,例如debugging器(KDB,KGDB),崩溃时转储(LKCD),跟踪工具包(LTT,LTTV,LTTng),定制内核工具(dprobes,kprobes)。 在下面的部分我试图总结大部分,希望这些将有所帮助。
LKCD (Linux Kernel Crash Dump)工具允许Linux系统在发生崩溃时写入其内存的内容。 这些日志可以进一步分析崩溃的根本原因。 有关LKCD的资源
- http://www-01.ibm.com/support/knowledgecenter/linuxonibm/liaax/lkcd.pdf
- https://www.novell.com/coolsolutions/feature/15284.html
- https://www.novell.com/support/kb/doc.php?id=3044267
当内核检测到问题时,Oops会打印一个Oops消息。 这样的消息由error handling程序(arch / * / kernel / traps.c)中的printk语句生成。 printk语句使用内核中的专用环形缓冲区。 Oops包含诸如Oops发生的CPU,CPU寄存器的内容,Oops的数量,描述,堆栈返回跟踪等信息。 关于内核糟糕的资源
- https://www.kernel.org/doc/Documentation/oops-tracing.txt
- http://madwifi-project.org/wiki/DevDocs/KernelOops
- https://wiki.ubuntu.com/DebuggingKernelOops
Dynamic Probes是IBM开发的Linuxdebugging工具之一。 这个工具允许在用户和内核空间的几乎任何地方放置一个“探测器”。 探测包含一些代码(用专门的,面向堆栈的语言编写),当控制点到达给定点时执行。 有关dynamic探针的资源如下所列
- http://www-01.ibm.com/support/knowledgecenter/linuxonibm/liaax/dprobesltt.pdf
- http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.107.6212&rep=rep1&type=pdf
Linux跟踪工具包是一个内核补丁和一组允许在内核中跟踪事件的相关实用程序。 跟踪包括时间信息,并可以创build一个相当完整的图像,在一段时间内发生的事情。 LTT,LTT Viewer和LTT Next Generation的资源
- http://elinux.org/Linux_Trace_Toolkit
- http://www.linuxjournal.com/article/3829
- http://multivax.blogspot.com/2010/11/introduction-to-linux-tracing-toolkit.html
MEMWATCH是一个开源的内存错误检测工具。 它通过在gcc语句中定义MEMWATCH并向我们的代码添加一个头文件来工作。 通过这个我们可以跟踪内存泄漏和内存损坏。 有关MEMWATCH的资源
ftrace是Linux内核的一个很好的跟踪框架。 ftrace跟踪内核的内部操作。 这个工具包含在2.6.27的Linux内核中。 通过各种跟踪插件,ftrace可以针对不同的静态跟踪点,如调度事件,中断,内存映射I / O,CPU电源状态转换以及与文件系统和虚拟化相关的操作。 此外,内核函数调用的dynamic跟踪是可用的,可选地通过使用globs可限制到函数的子集,并且可以生成调用图并提供堆栈使用。 你可以在https://events.linuxfoundation.org/slides/2010/linuxcon_japan/linuxcon_jp2010_rostedt.pdffind一个很好的ftrace教程。;
ltrace是Linux中的一个debugging工具,用于显示用户空间应用程序对共享库的调用。 这个工具可以用来跟踪任何dynamic的库函数调用。 它拦截并logging被执行进程调用的dynamic库调用以及该进程接收到的信号。 它也可以拦截和打印程序执行的系统调用。
- http://www.ellexus.com/getting-started-with-ltrace-how-does-it-do-that/?doing_wp_cron=1425295977.1327838897705078125000
- http://developerblog.redhat.com/2014/07/10/ltrace-for-rhel-6-and-7/
KDB是Linux内核的内核debugging器。 KDB遵循简单的壳式界面。 我们可以用它来检查内存,registry,进程列表,dmesg,甚至设置断点停在某个位置。 通过KDB,我们可以设置断点并执行一些基本的内核运行控制( 尽pipeKDB不是源代码级的debugging器 )。 关于KDB的几个方便的资源
- http://www.drdobbs.com/open-source/linux-kernel-debugging/184406318
- http://elinux.org/KDB
- http://dev.man-online.org/man1/kdb/
- https://www.kernel.org/pub/linux/kernel/people/jwessel/kdb/usingKDB.html
KGDB旨在用作Linux内核的源代码级debugging器。 它和gdb一起用来debugging一个Linux内核。 使用kgdb需要两台机器。 其中一台机器是开发机器,另一台是目标机器。 要debugging的内核在目标机器上运行。 期望的是,gdb可以用来“打入”到内核来检查内存,variables,并查看调用堆栈信息,类似于应用程序开发人员使用gdbdebugging应用程序的方式。 可以在内核代码中放置断点,并执行一些有限的执行步骤。 关于KGDB的几个方便的资源
另一个用于“实时”debugging的好工具是kprobes /dynamic探测器。
这可以让你dynamic地构build一些微小的模块,这些模块在某些地址被执行时运行 – 就像断点一样。
他们的巨大优势是:
- 它们不会影响系统 – 即当某个位置被击中时 – 它只是执行代码 – 它不会停止整个内核。
- 与kgdb不同,您不需要两个不同的系统互相连接(目标和debugging)
最好做一些事情,比如打断点,查看数据值是什么,检查是否已经改变/覆盖等等。如果你想“逐步执行代码”,那么不会这样做。
实际上这个笑话是Linux自从2.2.12版本以来就有了一个内核debugging器,但是只有powerpc
架构(实际上它是ppc
)。
这不是一个源代码级别的debugging器,它几乎完全没有logging,但仍然是。
http://lxr.linux.no/linux-old+v2.2.12/arch/ppc/xmon/xmon.c#L119
作为编写内核代码的人,我不得不说我从来没有使用kgdb,而且很less使用kprobes等。
投入一些战略printks
仍然是最好的方法。 在更新的内核中, trace_printk
是一个很好的方法,不会发送dmesg垃圾邮件。
在Ubuntu 16.10主机上testingQEMU + GDB的分步过程
为了快速入门,我已经在https://github.com/cirosantilli/linux-kernel-module-cheat上做了一个最小化的完全自动化的QEMU + Buildroot示例。主要步骤如下。
首先得到rootfs.cpio.gz
文件系统rootfs.cpio.gz
。 如果您需要,请考虑:
- 一个最小的
init
唯一可执行映像: https : //unix.stackexchange.com/questions/122717/custom-linux-distro-that-runs-just-one-program-nothing-else/238579#238579 - Busybox交互式系统: https : //unix.stackexchange.com/questions/2692/what-is-the-smallest-possible-linux-implementation/203902#203902
然后在Linux内核上:
git checkout v4.9 make mrproper make x86_64_defconfig cat <<EOF >.config-fragment CONFIG_DEBUG_INFO=y CONFIG_DEBUG_KERNEL=y CONFIG_GDB_SCRIPTS=y EOF ./scripts/kconfig/merge_config.sh .config .config-fragment make -j"$(nproc)" qemu-system-x86_64 -kernel arch/x86/boot/bzImage \ -initrd rootfs.cpio.gz -S -s
在另一个terminal上,假设你想从start_kernel
开始debugging:
gdb \ -ex "add-auto-load-safe-path $(pwd)" \ -ex "file vmlinux" \ -ex 'set arch i386:x86-64:intel' \ -ex 'target remote localhost:1234' \ -ex 'break start_kernel' \ -ex 'continue' \ -ex 'disconnect' \ -ex 'set arch i386:x86-64' \ -ex 'target remote localhost:1234'
我们完成了!
有关内核模块,请参阅: 如何使用QEMUdebuggingLinux内核模块?
对于Ubuntu 14.04,GDB 7.7.1,需要hbreak
, break
软件断点被忽略。 在16.10中不再是这种情况。 另见: https : //bugs.launchpad.net/ubuntu/+source/qemu-kvm/+bug/901944
凌乱的disconnect
,然后是解决错误:
Remote 'g' packet reply is too long:
相关线程:
- https://sourceware.org/bugzilla/show_bug.cgi?id=13984可能是一个GDB错误;
- 远程“g”分组回复太长
- http://wiki.osdev.org/QEMU_and_GDB_in_long_mode osdev.org像往常一样,这些问题的真棒源
- https://lists.nongnu.org/archive/html/qemu-discuss/2014-10/msg00069.html
也可以看看:
- https://github.com/torvalds/linux/blob/v4.9/Documentation/dev-tools/gdb-kernel-debugging.rst官方Linux内核“文档”;
- 如何用GDB和QEMUdebuggingLinux内核?
已知的限制:
- 如果使用
-O0
,Linux内核不支持(甚至无需编译补丁): 如何使用-O0
来优化Linux内核并编译它? - 即使在
max-completions
修正之后,GDB 7.11也会在某些types的选项卡完成上留下内存: 大型二进制文件的选项卡完成中断有可能是该补丁没有涉及的一些特殊情况。 所以一个ulimit -Sv 500000
是一个明智的行动前debugging。 当我为sys_execve
的filename
参数选项卡完成的file<tab>
时,特意sys_execve
: https :sys_execve
用户模式Linux(UML)
https://en.wikipedia.org/wiki/User-mode_Linux
另一种虚拟化的另一种方法是允许步骤debugging内核代码。
UML非常巧妙:它像x86
一样被实现为一个ARCH
,而不是使用低级指令,而是用userland系统调用来实现ARCH
函数。
结果就是你可以在Linux主机上运行Linux内核代码作为用户态进程!
首先创build一个rootfs并运行它,如下所示: https : //unix.stackexchange.com/questions/73203/how-to-create-rootfs-for-user-mode-linux-on-fedora-18/372207#372207
um
defconfig默认设置CONFIG_DEBUG_INFO=y
(yup,这是一个开发的东西),所以我们很好。
客人:
i=0 while true; do echo $i; i=$(($i+1)); done
在另一个shell的主机上:
ps aux | grep ./linux gdb -pid "$pid"
在GDB中:
break sys_write continue continue
现在你正在控制GDB的数量,并可以看到预期的来源。
优点:
- 完全包含在Linux内核主线树中
- 比QEMU的全系统仿真更轻量
缺点:
-
非常有创意,因为它改变了内核本身的编译方式。
但
ARCH
细节之外的更高级别的API应该保持不变。 -
可以说是不是非常活跃: 用户模式Linux(UML)项目停止?
另见: https : //unix.stackexchange.com/questions/127829/why-would-someone-want-to-run-usermode-linux-uml
KGDB + QEMU一步一步
KGDB是一个内核子系统,允许你从主机GDB中debugging内核本身。
我的QEMU + Buildroot的例子是一个很好的方式来获得它没有真正的硬件的味道: https : //github.com/cirosantilli/linux-kernel-module-cheat/tree/1969cd6f8d30dace81d9848c6bacbb8bad9dacd8#kgdb
优点和缺点vs其他方法:
- 优势与QEMU:
- 由于硬件供应商不喜欢为其设备发布精确的软件模型,因此您的设备通常不具备软件仿真function
- 真正的硬件方式比QEMU更快
- 优势与JTAG:无需额外的JTAG硬件,更容易设置
- 与QEMU和JTAG相比的缺点:更less的可见性和更多的侵入性。 KGDB依靠内核工作的某些部分能够与主机通信。 所以,例如它在恐慌中崩溃,你无法查看启动顺序。
主要步骤是:
-
编译内核:
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO=y CONFIG_CONSOLE_POLL=y CONFIG_KDB_CONTINUE_CATASTROPHIC=0 CONFIG_KDB_DEFAULT_ENABLE=0x1 CONFIG_KDB_KEYBOARD=y CONFIG_KGDB=y CONFIG_KGDB_KDB=y CONFIG_KGDB_LOW_LEVEL_TRAP=y CONFIG_KGDB_SERIAL_CONSOLE=y CONFIG_KGDB_TESTS=y CONFIG_KGDB_TESTS_ON_BOOT=n CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 CONFIG_SERIAL_KGDB_NMI=n
其中大部分都不是强制性的,但这是我testing过的。
-
添加到您的QEMU命令:
-append 'kgdbwait kgdboc=ttyS0,115200' \ -serial tcp::1234,server,nowait
-
从Linux内核源代码树的根目录下运行GDB:
gdb -ex 'file vmlinux' -ex 'target remote localhost:1234'
-
在GDB中:
(gdb) c
和开机应该完成。
-
在QEMU:
echo g > /proc/sysrq-trigger
GDB应该打破。
-
现在我们完成了,你可以照常使用GDB:
b sys_write c
在Ubuntu 14.04testing。
KGDB +树莓派
与上面完全相同的设置几乎在树莓派2,Raspbian Jessie 2016-05-27。
你只需要学习在Pi上执行QEMU步骤,这很容易实现:
-
添加configuration选项,并按照https://www.raspberrypi.org/documentation/linux/kernel/building.md中的说明重新编译内核。不幸的是,缺省的内核版本缺less选项,特别是没有debugging符号,所以重新编译是需要。;
-
编辑启动分区的
cmdline.txt
并添加:kgdbwait kgdboc=ttyAMA0,115200
-
将
gdb
连接到串口:arm-linux-gnueabihf-gdb -ex 'file vmlinux' -ex 'target remote /dev/ttyUSB0'
如果您对序列不熟悉,请查看: https : //www.youtube.com/watch?v=da5Q7xL_OTo所有您需要的是像这样的廉价适配器。 确保在试用KGDB之前,可以通过串口获得一个shell来确保它正在工作。
-
做:
echo g | sudo tee /proc/sysrq-trigger
从SSH会话中,因为串行已经被GDB占用了。
通过这个设置,我可以在sys_write
放置一个断点,暂停程序执行,列出源并继续。
但是,有时候我在sys_write
next
做的sys_write
刚刚挂了sys_write
GDB,并且打印了这个错误信息:
Ignoring packet error, continuing...
所以我不确定我的设置是否出了问题,或者是因为在更复杂的Raspbian图像中进行了一些后台处理而导致这种情况。
我也被告知尝试使用Linux启动选项禁用多处理,但是我还没有尝试过。
kgdb和gdb对于debugging内核几乎是无用的,因为代码是如此优化的,与原始源无关,许多variables都被优化了。 这使得步进,因此通过源是不可能的,检查variables是不可能的,因此是几乎点。
其实它比无用的更糟糕,它实际上给你错误infoprmation如此分离是你正在眺望的实际运行代码的代码。
不,你不能closures内核的优化,它不会编译。
我不得不说,来自Windows内核环境,缺乏体面的debugging器是厌烦的,因为有垃圾代码需要维护。