用于在Linux内核上进行实时调试的最常见和不常见的方法和工具是什么?我知道Linus比如说。是against这种针对Linux内核的调试还是最少的,因此在那些年里没有做过那么多的事情,但老实说,自2000年以来已经过了很多时间,如果这种心态有这种心态,我感兴趣有关Linux项目的改变以及当前用于在Linux内核上进行实时调试的当前方法(本地或远程)?
欢迎参考有关所提及的技术和工具的演练和教程。
答案 0 :(得分:28)
另一种选择是使用ICE / JTAG控制器和GDB。这种“硬件”解决方案特别适用于嵌入式系统,
但是例如Qemu提供了类似的功能:
使用gdb“remote”存根启动qemu,该存根侦听“localhost:1234”:qemu -s ...
,
然后使用GDB打开使用调试信息编译的内核文件vmlinux
(您可以查看一个this邮件列表线程,讨论内核的优化问题。)
连接GDB和Qemu:target remote localhost:1234
查看 live 内核:
(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
遗憾的是,到目前为止,使用GDB无法进行用户空间调试(没有任务列表信息,没有MMU重新编程以查看不同的进程上下文,...),但是如果你留在内核空间,这非常方便。 / p>
info threads
将为您提供不同 CPU的列表和状态 修改强>
您可以在此PDF中获得有关此过程的更多详细信息:
答案 1 :(得分:21)
在调试Linux内核时,我们可以使用多种工具,例如,调试器(KDB,KGDB),崩溃时转储(LKCD),跟踪工具包(LTT,LTTV,LTTng),自定义内核工具(dprobes,kprobes)。在下一节中,我试图总结其中的大多数,希望这些将有所帮助。
LKCD (Linux Kernel Crash Dump)工具允许Linux系统在发生崩溃时写入其内存的内容。可以进一步分析这些日志以查找崩溃的根本原因。有关LKCD的资源
糟糕当内核检测到问题时,会打印一条Oops消息。这样的消息是由错误处理程序(arch / * / kernel / traps.c)中的printk语句生成的。内核中的专用环形缓冲区由printk语句使用。 Oops包含诸如发生Oops的CPU,CPU寄存器的内容,Oops的数量,描述,堆栈跟踪跟踪等信息。关于内核Oops的资源
Dynamic Probes是由IBM开发的流行的Linux调试工具之一。此工具允许在用户和内核空间中的系统中的几乎任何位置放置“探针”。探针由一些代码(用专门的,面向堆栈的语言编写)组成,当控制命中给定点时执行。有关下面列出的动态探测器的资源
Linux Trace Toolkit是一个内核补丁和一组相关的实用程序,允许跟踪内核中的事件。该跟踪包括定时信息,并且可以创建在给定时间段内发生的事情的相当完整的图像。 LTT,LTT Viewer和LTT Next Generation的资源
MEMWATCH是一个开源内存错误检测工具。它的工作原理是在gcc语句中定义MEMWATCH,并在代码中添加头文件。通过这个,我们可以跟踪内存泄漏和内存损坏。有关MEMWATCH的资源
ftrace是一个很好的Linux内核跟踪框架。 ftrace跟踪内核的内部操作。该工具包含在2.6.27的Linux内核中。通过其各种跟踪器插件,ftrace可以针对不同的静态跟踪点,例如调度事件,中断,内存映射I / O,CPU电源状态转换以及与文件系统和虚拟化相关的操作。此外,可以使用动态跟踪内核函数调用,可选地通过使用glob限制函数子集,并且可以生成调用图并提供堆栈使用。你可以在https://events.linuxfoundation.org/slides/2010/linuxcon_japan/linuxcon_jp2010_rostedt.pdf
找到一个很好的ftrace教程ltrace是Linux中的一个调试实用程序,用于显示用户空间应用程序对共享库的调用。此工具可用于跟踪任何动态库函数调用。它拦截并记录由执行过程调用的动态库调用以及该进程接收的信号。它还可以拦截和打印程序执行的系统调用。
KDB 是Linux内核的内核调试器。 KDB遵循简单的shell风格接口。我们可以用它来检查内存,寄存器,进程列表,dmesg,甚至设置断点以在某个位置停止。通过KDB,我们可以设置断点并执行一些基本的内核运行控制(虽然KDB不是源代码级调试器)。关于KDB的几个方便的资源
KGDB 旨在用作Linux内核的源代码级调试器。它与gdb一起用于调试Linux内核。使用kgdb需要两台机器。其中一台机器是开发机器,另一台是目标机器。要调试的内核在目标计算机上运行。期望gdb可用于“闯入”内核以检查内存,变量并查看调用堆栈信息,类似于应用程序开发人员使用gdb调试应用程序的方式。可以在内核代码中放置断点并执行一些有限的执行步进。关于KGDB的几个方便的资源
答案 2 :(得分:20)
根据wiki,kgdb
已合并到2.6.26
内核,这是在过去几年内。 kgdb
是remote debugger,因此您activate it in your kernel然后以某种方式将gdb附加到其中。我以某种方式说,似乎有很多选择 - 见connecting gdb。鉴于kgdb
现在位于源代码树中,我会说这是您想要使用的内容。
所以看起来Linus放弃了。但是,我会强调他的论点 - 你应该知道你在做什么并且很了解系统。这是核心土地。如果出现任何问题,你就不会得到segfault
,从后来的一个模糊问题到整个系统都会出现问题。这里是龙。小心谨慎,你已受到警告。
答案 3 :(得分:14)
“实时”调试的另一个好工具是kprobes / dynamic probes。
这使您可以动态构建在执行某些地址时运行的微小模块 - 有点像断点。
他们的最大优势是:
最好做一些事情,比如点击一个断点,看看有什么数据值,或检查是否有事情被改变/覆盖等等。如果你想“逐步执行代码” - 它不会这样做。
增加 - 2018年:
另一个非常强大的方法是一个简单称为“perf”的程序,它可以汇总许多工具(如动态探测器),并替换/删除其他工具(如oprofile)。
特别是,perf probe
命令可用于轻松创建/添加动态探测到系统,之后perf record
可以在探测器被命中时对系统进行采样并报告信息(和回溯)通过perf report
(或perf script
)进行报告。如果你在内核中有很好的调试符号,你甚至可以在没有内核的情况下从系统中获得出色的知识。请访问man perf
(在Google或您的系统上)以获取有关此工具的更多信息,或查看其中的精彩页面:
答案 4 :(得分:5)
KGDB + QEMU一步一步
KGDB是一个内核子系统,允许您从主机GDB逐步调试内核。
我的QEMU + Buildroot示例是一种在没有真正硬件的情况下体验它的好方法:https://github.com/cirosantilli/linux-kernel-module-cheat/tree/1969cd6f8d30dace81d9848c6bacbb8bad9dacd8#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
其中大部分都不是强制性的,但这是我测试过的。
添加到您的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.04中测试。
KGDB + Raspberry Pi
完全相同的设置几乎适用于Raspberry Pi 2,Raspbian Jessie 2016-05-27。
你只需要学习在Pi上做QEMU步骤,这很容易 Googlable:
添加配置选项并按照https://www.raspberrypi.org/documentation/linux/kernel/building.md中的说明重新编译内核。遗憾的是,缺省内核版本中缺少选项,特别是没有调试符号,因此需要重新编译。
编辑启动分区的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您只需要一个便宜的适配器like this one。确保您可以通过序列获取shell以确保它在尝试KGDB之前正常工作。
做的:
echo g | sudo tee /proc/sysrq-trigger
来自SSH会话,因为GDB已经采用了该序列。
通过这种设置,我能够在sys_write
中放置断点,暂停程序执行,列出源并继续。
但是,有时当我在next
中sys_write
时,GDB只挂了并多次打印此错误消息:
Ignoring packet error, continuing...
所以我不确定我的设置是否有问题,或者是否因为某些后台进程在更复杂的Raspbian图像中所做的事情而出现这种情况。
我也被告知尝试使用Linux启动选项禁用多处理,但我还没有尝试过。
答案 5 :(得分:4)
实际上,笑话是Linux自2.2.12以来一直有内核调试器xmon
,但仅适用于powerpc
架构(实际上它当时是ppc
)。
它不是源代码级调试器,它几乎完全没有文档,但仍然。
http://lxr.linux.no/linux-old+v2.2.12/arch/ppc/xmon/xmon.c#L119
答案 6 :(得分:3)
作为编写内核代码的人,我不得不说我从未使用过kgdb,而且很少使用kprobes等。
这仍然是投入某些战略printks
的最佳方法。在最近的内核中,trace_printk
是一种很好的方法,可以在不发送垃圾邮件的情况下执行此操作。
答案 7 :(得分:3)
在Ubuntu 16.10主机上测试的QEMU + GDB分步过程
为了快速入门,我已经在https://github.com/cirosantilli/linux-kernel-module-cheat制作了一个最小化的全自动QEMU + Buildroot示例。主要步骤如下所示。
首先获取根文件系统rootfs.cpio.gz
。如果您需要,请考虑:
init
- 仅可执行图片:https://unix.stackexchange.com/questions/122717/custom-linux-distro-that-runs-just-one-program-nothing-else/238579#238579 然后在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
在另一个终端上,假设您要从start_kernel
开始调试:
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'
我们完成了!!
对于内核模块,请参阅:How to debug Linux kernel modules with QEMU?
对于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: 000000000000000017d11000008ef4810120008000000000fdfb8b07000000000d352828000000004040010000000000903fe081ffffffff883fe081ffffffff00000000000e0000ffffffffffe0ffffffffffff07ffffffffffffffff9fffff17d11000008ef4810000000000800000fffffffff8ffffffffff0000ffffffff2ddbf481ffffffff4600000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000
相关主题:
另见:
已知限制:
-O0
:How to de-optimize the Linux kernel to and compile it with -O0? max-completions
修复之后,GDB 7.11也会在某些类型的标签页上完成记忆:Tab completion interrupt for large binaries可能是某个角落的情况,该修补程序中没有涉及。所以ulimit -Sv 500000
在调试之前是一个明智的动作。当我选中file<tab>
filename
sys_execve
List
参数时,我会明确地将其展开,如:https://stackoverflow.com/a/42290593/895245 答案 8 :(得分:1)
用户模式Linux(UML)
https://en.wikipedia.org/wiki/User-mode_Linux
另一种虚拟化方法,允许步骤调试内核代码。
UML非常巧妙:它实现为ARCH
,就像x86
一样,但它不是使用低级指令,而是使用userland系统调用实现ARCH
函数。 / p>
结果是您可以在Linux主机上以Linux用户进程的形式运行Linux内核代码!
首先制作一个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
(是的,这是一个开发的东西),所以我们没事。
客人:
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的计数,并且可以按预期看到源。
优点:
缺点:
非常具有侵略性,因为它改变了内核本身的编译方式。
但ARCH
细节之外的更高级API应保持不变。
另请参阅:https://unix.stackexchange.com/questions/127829/why-would-someone-want-to-run-usermode-linux-uml
答案 9 :(得分:0)
你们错了,kgdb对于最新的内核仍然运行良好,您需要注意分割映像的内核配置,随机优化。
通过串行端口的kgdb没用,因为当今没有计算机在主板串行端口上支持DB9,USB串行端口不支持轮询模式。
新游戏是kgdboe,以下是日志跟踪:
以下是主机,vmlinux是来自目标计算机
root@Thinkpad-T510:~/KGDBOE# gdb vmlinux
Reading symbols from vmlinux...done.
(gdb) target remote udp:192.168.1.22:31337
1077 kernel/debug/debug_core.c: No such file or directory.
(gdb) l oom_kill_process
828 mm/oom_kill.c: No such file or directory.
(gdb) l oom_kill_process
828 in mm/oom_kill.c
(gdb) break oom_kill_process
Breakpoint 1 at 0xffffffff8119e0c0: file mm/oom_kill.c, line 833.
(gdb) c
Continuing.
[New Thread 1779]
[New Thread 1782]
[New Thread 1777]
[New Thread 1778]
[New Thread 1780]
[New Thread 1781]
[Switching to Thread 1779]
Thread 388 hit Breakpoint 1, oom_kill_process (oc=0xffffc90000d93ce8, message=0xffffffff82098fbc "Out of memory")
at mm/oom_kill.c:833
833 in mm/oom_kill.c
(gdb) s
834 in mm/oom_kill.c
(gdb)
在对等目标计算机上,下面是如何使其崩溃并被主机捕获
#swapoff -a
#stress -m 4 --vm-bytes=500m
答案 10 :(得分:-4)
kgdb和gdb对于调试内核几乎没用,因为代码是如此优化的,它与orioginal源无关,许多变量都被优化了。这使得steppijng,因此踩过源是不可能的,检查变量是不可能的,因此是最不重要的。
实际上它比无用更糟糕,它实际上给你错误的信息,所以你看到的代码是实际运行的代码。
不,你不能关闭内核中的优化,它不会编译。
我不得不说,来自Windows内核环境,缺乏合适的调试器是很烦人的,因为有维护的垃圾代码。