Linux内核实时调试,它是如何完成的以及使用了什么工具?

时间:2011-02-09 10:53:02

标签: linux debugging linux-kernel kernel

用于在Linux内核上进行实时调试的最常见和不常见的方法和工具是什么?我知道Linus比如说。是against这种针对Linux内核的调试还是最少的,因此在那些年里没有做过那么多的事情,但老实说,自2000年以来已经过了很多时间,如果这种心态有这种心态,我感兴趣有关Linux项目的改变以及当前用于在Linux内核上进行实时调试的当前方法(本地或远程)?

欢迎参考有关所提及的技术和工具的演练和教程。

11 个答案:

答案 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中获得有关此过程的更多详细信息:

  

Debugging Linux systems using GDB and QEMU

答案 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)

根据wikikgdb已合并到2.6.26内核,这是在过去几年内。 kgdbremote debugger,因此您activate it in your kernel然后以某种方式将gdb附加到其中。我以某种方式说,似乎有很多选择 - 见connecting gdb。鉴于kgdb现在位于源代码树中,我会说这是您想要使用的内容。

所以看起来Linus放弃了。但是,我会强调他的论点 - 你应该知道你在做什么并且很了解系统。这是核心土地。如果出现任何问题,你就不会得到segfault,从后来的一个模糊问题到整个系统都会出现问题。这里是龙。小心谨慎,你已受到警告。

答案 3 :(得分:14)

“实时”调试的另一个好工具是kprobes / dynamic probes。

这使您可以动态构建在执行某些地址时运行的微小模块 - 有点像断点。

他们的最大优势是:

  1. 它们不影响系统 - 即当一个位置被击中时 - 它只是代码 - 它不会停止整个内核。
  2. 您不需要两个不同的系统互连(目标和调试),如kgdb
  3. 最好做一些事情,比如点击一个断点,看看有什么数据值,或检查是否有事情被改变/覆盖等等。如果你想“逐步执行代码” - 它不会这样做。

    增加 - 2018年:

    另一个非常强大的方法是一个简单称为“perf”的程序,它可以汇总许多工具(如动态探测器),并替换/删除其他工具(如oprofile)。

    特别是,perf probe命令可用于轻松创建/添加动态探测到系统,之后perf record可以在探测器被命中时对系统进行采样并报告信息(和回溯)通过perf report(或perf script)进行报告。如果你在内核中有很好的调试符号,你甚至可以在没有内核的情况下从系统中获得出色的知识。请访问man perf(在Google或您的系统上)以获取有关此工具的更多信息,或查看其中的精彩页面:

    http://www.brendangregg.com/perf.html

答案 4 :(得分:5)

KGDB + QEMU一步一步

KGDB是一个内核子系统,允许您从主机GDB逐步调试内核。

我的QEMU + Buildroot示例是一种在没有真正硬件的情况下体验它的好方法:https://github.com/cirosantilli/linux-kernel-module-cheat/tree/1969cd6f8d30dace81d9848c6bacbb8bad9dacd8#kgdb

优点和缺点与其他方法:

  • 对QEMU的优势:
    • 您经常没有为您的设备进行软件模拟,因为硬件供应商不希望为其设备发布准确的软件模型
    • 真正的硬件方式比QEMU快
  • 与JTAG的优势:无需额外的JTAG硬件,更易于设置
  • 与QEMU和JTAG相比的缺点:可见度更低,侵入性更强。 KGDB依赖于内核的某些部分来工作以便能够与主机通信。所以例如它在恐慌中崩溃,你无法查看启动顺序。

主要步骤是:

  1. 使用以下代码编译内核:

    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
    

    其中大部分都不是强制性的,但这是我测试过的。

  2. 添加到您的QEMU命令:

    -append 'kgdbwait kgdboc=ttyS0,115200' \
    -serial tcp::1234,server,nowait
    
  3. 使用以下命令从Linux内核源代码树的根目录运行GDB:

    gdb -ex 'file vmlinux' -ex 'target remote localhost:1234'
    
  4. 在GDB中:

    (gdb) c
    

    并且靴子应该完成。

  5. 在QEMU:

    echo g > /proc/sysrq-trigger
    

    GDB应该破解。

  6. 现在我们完成了,您可以像往常一样使用GDB:

    b sys_write
    c
    
  7. 在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中放置断点,暂停程序执行,列出源并继续。

    但是,有时当我在nextsys_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。如果您需要,请考虑:

然后在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

相关主题:

另见:

已知限制:

答案 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的计数,并且可以按预期看到源。

优点:

  • 完全包含在Linux内核主线树
  • 比QEMU的完整系统仿真更轻量级

缺点:

另请参阅: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内核环境,缺乏合适的调试器是很烦人的,因为有维护的垃圾代码。