debug

本文最后更新于:2025年4月28日 下午

debug

调试工具

fadd2line

Binutils

addr2line

nm

symbols

内核符号表

strip

strings

  • 在当前目录下递归查找so文件中的目标字符串

    1
    find . -type f -name "*.so*" -exec sh -c 'strings "$0" | grep "target_string" && echo "$0"' {} \;

    1
    2
    3
    4
    5
    6
    7
    这个命令会在当前目录下递归查找所有后缀名为 .so 或者 .so.* 的文件,并使用 strings 命令提取其中的字符串,然后使用 grep 命令在字符串中查找目标字符串,如果目标字符串出现了,就输出包含目标字符串的文件名。

    具体来说,该命令使用 find 命令在当前目录下查找所有类型为文件(-type f)且文件名匹配 *.so* 的文件,然后使用 -exec 选项执行后面的命令。

    后面的命令使用 sh -c 执行,将每个匹配到的文件名({})传递给了 $0 变量。在命令中,先使用 strings 命令提取文件中的字符串,然后使用 grep 命令查找目标字符串是否存在,如果存在,就使用 echo 命令输出包含目标字符串的文件名。

    需要注意的是,命令中使用了单引号包围命令,以避免 Shell 解析命令中的 $0 变量和 {} 字符。同时,在命令的末尾需要使用 \; 来表示命令的结束,而不是使用分号 ;,因为分号 ; 是 Shell 的保留字符。

  • linux中的strings命令简介

objdump

  • objdump反汇编用法示例

  • 反汇编代码格式

  • objdump 反汇编代码带行号:

    1
    objdump -d -l -S your_binary_file

    这个命令中的参数含义如下:

    -d:表示进行反汇编。

    -l:表示生成包含行号信息的输出。

    -S:表示同时输出源代码。

    -C:如果二进制文件包含 C++ 符号,使用 C++ 符号名进行显示,而不是使用原始符号名。

    将 your_binary_file 替换为你要反汇编的二进制文件的路径。

    这将生成一个包含反汇编代码和源代码行号信息的输出。你可以查看这个输出来分析程序的汇编代码。

    • 注意事项:cmake-objdump
      • cmake中使用参数-DCMAKE_BUILD_TYPE=Release编译出来的版本在coredump时生成的dmesg报错中的ip地址与使用-DCMAKE_BUILD_TYPE=Debug编出来的版本再使用objdump反汇编得到的汇编代码对不上。
      • cmkke中-DCMAKE_BUILD_TYPE=Release编译出来的版本在coredump时生成的dmesg报错中的ip地址与在CMakeLists.txt中额外添加set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")编译选项后再使用objdump反汇编得到的汇编代码对应的上。
      • cmake中-DCMAKE_BUILD_TYPE=Debug编译出来的版本后用strip得到的不含调试包的版本在coredump时生成的dmesg报错中的ip地址与使用-DCMAKE_BUILD_TYPE=Debug编出来的版本再使用objdump反汇编得到的汇编代码对应的上。

gdb

gef

continue

ptype

查看某个结构体或变量的类型定义:

1
ptype filep

查看某个结构体或变量的类型定义且包含偏移量信息:

1
ptype /o struct file

print

打印内存值

C++对象布局

list

多进程

gdb tui

gdb assembly

gdb设置源码路径

gdb打印qt数据类型

gdb print errno

gdb远程调试

gcc

libtool

vscode gdb

vscode调试linux内核

串口

  • Linux下常用的串口助手 —— minicom、putty、cutecom

    主机上执行:

    1
    sudo minicom -s -D /dev/ttyUSB0

    ls -l /dev/ttyUSB0找不到设备时可以尝试手动添加:

    1
    lsusb

    ls -l /dev/ttyUSB0

    1
    echo '067b 23a3' > /sys/bus/usb-serial/drivers/generic/new_id

    被调式机上执行:

    1
    sudo cutecom

  • 查询串口波特率:

    1
    stty -F /dev/ttyS0

    其中 /dev/ttyS0 是串口设备文件的路径。你可以将路径更改为你所关心的特定串口。

    这将返回串口的配置,包括波特率、数据位、校验位、停止位等。通常,波特率会显示在配置输出中,如下所示:

    1
    speed 9600 baud; line = 0;

  • 设置串口波特率

    1
    stty -F /dev/ttyS0 9600

    在上面的命令中:

    /dev/ttyS0 是串口设备文件的路径,你可以根据需要更改为你使用的串口。

    9600 是所需的波特率。你可以将其替换为你想要的任何波特率。

    运行此命令后,串口 /dev/ttyS0 的波特率将设置为 9600。确保串口没有被其他程序占用,以便成功设置波特率。

kgdb

kgdboe

ptrace

core文件

coredumpctl

dmesg

dump_stack

sysctl

oops

panic

更改 sysctl 中的某些内核参数可能导致系统不稳定,甚至触发 panic。以下是一些可能会导致系统问题的内核参数,慎重修改:

  • kernel.panic

    作用: 设置系统 panic 的延迟时间。

    潜在风险: 如果将其设置得太低,系统可能会在不必要的情况下触发 panic。

    1
    sysctl -w kernel.panic=10

    内核编译选项:CONFIG_PANIC_TIMEOUT=0

  • kernel.panic_on_oops

    作用: 控制在发生内核 oops(可修复的内核错误)时是否触发 panic。

    潜在风险: 如果启用,系统在 oops 时会触发 panic。

    1
    sysctl -w kernel.panic_on_oops=1

    内核编译选项:PANIC_ON_OOPS=y

  • kernel.hung_task_panic

    作用: 控制在系统检测到“挂起”任务(可能是由于死锁)时是否触发 panic。

    潜在风险: 如果启用,系统在检测到挂起任务时会触发 panic。

    1
    sysctl -w kernel.hung_task_panic=1

    内核编译选项:CONFIG_DETECT_HUNG_TASK=y CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y

  • kernel.hung_task_timeout_secs:

    作用: 该参数用于设置系统在检测到任务挂起(hanging task)的超时时间。如果系统中的某个任务在指定的时间内没有恢复正常运行,内核将记录信息并采取相应的措施。

    默认值: 通常情况下,kernel.hung_task_timeout_secs 的默认值是 120 秒(2 分钟)。

    潜在风险: 如果将其设置得太低,系统可能会对一些正常但需要一定时间来完成的任务误报为挂起状态。

    1
    sysctl -w kernel.hung_task_timeout_secs=60

    内核编译选项:CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120

  • vm.panic_on_oom

    作用: 控制在发生内存耗尽(OOM)时是否触发 panic。

    潜在风险: 如果启用,系统在内存不足时会触发 panic。

    1
    sysctl -w vm.panic_on_oom=1

  • kernel.softlockup_panic

    作用: 软死锁是指内核中的一个任务(线程)由于长时间未能释放CPU而导致系统失去响应时是否触发 panic。

    潜在风险: 如果启用,系统在检测到软锁定时会触发 panic。

    1
    sysctl -w kernel.softlockup_panic=1

    内核编译选项:CONFIG_SOFTLOCKUP_DETECTOR=y BOOTPARAM_SOFTLOCKUP_PANIC=y

  • kernel.panic_on_warn

    作用: 这个参数控制内核在遇到某些非致命性错误(例如警告)时是否触发 panic。

    0: 禁用自动重启。内核在遇到警告时不会触发系统重启,而是继续正常运行。

    1: 启用自动重启。当内核发生某些警告时,系统会自动重启。

    1
    sysctl -w kernel.panic_on_warn=1

    在调试或特殊情况下,禁用自动重启可能有助于保留内核警告的信息以进行故障排除。在生产环境中,通常将其设置为非0,以确保系统在遇到某些严重问题时能够自动重启,尽早恢复正常运行状态。

sysctl -w kernel.hung_task_panic=1echo 1 > /proc/sys/kernel/hung_task_panic这两种方法都是在运行时直接生效的,而且都是暂时性的修改。

如果你希望修改是持久的,可以将相应的配置写入 /etc/sysctl.conf 文件,并使用 sysctl -p 命令使其生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
sudo cat << EOF >> /etc/sysctl.conf

# sysrq

# CONFIG_MAGIC_SYSRQ=y
kernel.sysrq=1

# panic

# CONFIG_PANIC_TIMEOUT=0
kernel.panic=10

# CONFIG_PANIC_ON_OOPS=y
kernel.panic_on_oops=1

# CONFIG_DETECT_HUNG_TASK=y CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
kernel.hung_task_panic=1

# CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
kernel.hung_task_timeout_secs=60

vm.panic_on_oom=1

# CONFIG_SOFTLOCKUP_DETECTOR=y BOOTPARAM_SOFTLOCKUP_PANIC=y
kernel.softlockup_panic=1

kernel.panic_on_warn=1
EOF
1
sysctl -p

任务挂起和睡眠的区别?

任务挂起(hanging task)和睡眠(sleep)是两个概念,涉及到系统中运行的进程和线程的状态。

任务挂起(Hanging Task):

  • 定义: 任务挂起指的是系统中的某个任务(通常是一个进程或线程)由于某种原因而无法继续正常执行,长时间处于一种不响应的状态。

  • 原因: 挂起可能是由于死锁、资源争用、错误的程序行为或其他系统问题引起的。

  • 检测: 内核通过监视任务的执行状态和超时机制来检测是否有任务挂起。

  • 处理: 一旦检测到任务挂起,系统可能会采取相应的措施,例如记录相关信息、触发系统 panic,以及尝试恢复任务的正常执行。

睡眠(Sleep):

  • 定义: 睡眠指的是一个任务主动放弃 CPU 并进入一种等待状态,等待某个事件的发生。在睡眠期间,任务通常不占用 CPU 时间,并允许其他任务执行。

  • 原因: 任务可能进入睡眠状态以等待外部事件,例如等待 I/O 操作完成、等待定时器触发、等待信号量或锁的释放等。

  • 检测: 睡眠通常是由任务自己通过系统调用(如 sleep、wait)或内核操作引起的,不同于挂起的 passivity(被动性)。

  • 处理: 睡眠是一种正常的任务状态,系统不会像在检测到挂起时那样采取紧急措施。任务会在等待的事件发生时被唤醒,继续执行。

总的来说,任务挂起通常是一种异常状态,可能导致系统不稳定,而睡眠是一种正常的、被控制的状态,允许任务在需要时主动放弃 CPU 并等待特定条件的发生。

watchdog

  • 禁用watchdog方法汇总

  • Linux watchdog配置

  • Linux禁用watchdog

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    nmi_watchdog=   [KNL,BUGS=X86] Debugging features for SMP kernels
    Format: [panic,][nopanic,][num]
    Valid num: 0 or 1
    0 - turn hardlockup detector in nmi_watchdog off
    1 - turn hardlockup detector in nmi_watchdog on
    When panic is specified, panic when an NMI watchdog
    timeout occurs (or 'nopanic' to not panic on an NMI
    watchdog, if CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is set)
    To disable both hard and soft lockup detectors,
    please see 'nowatchdog'.
    This is useful when you use a panic=... timeout and
    need the box quickly up again.

    These settings can be accessed at runtime via
    the nmi_watchdog and hardlockup_panic sysctls.

kdump

carsh

crash 是一个用于分析 Linux 内核转储文件(core dump)的工具,允许在不中断系统运行的情况下进行诊断。以下是 crash 的一些基本原理:

  • 核心文件: crash 的核心原理是基于核心文件的分析。内核转储文件(core dump)记录了系统在发生关键错误时的内存和寄存器状态。crash 使用这些核心文件进行分析。

  • 调试信息: 为了正确解析核心文件,crash 需要访问内核二进制文件(通常是 vmlinux)。这个文件包含有关内核符号、数据结构和函数的调试信息。在使用 crash 之前,你需要确保有匹配正在运行内核的 vmlinux 文件。

  • 在线调试: crash 允许在线调试运行中的内核,而无需停止系统。它通过访问 /proc/kcore 文件获取正在运行内核的内存映像,结合核心文件和调试信息,提供了对内核状态的深入分析。

  • 命令式界面: crash 提供了一个交互式的命令行界面,用户可以在其中运行各种命令以查看内核状态、进程信息、调度器信息等。这样用户可以动态地检查系统状态。

  • 插件系统: crash 具有可扩展的插件系统,可以通过插件添加额外的命令和功能。这使得用户可以根据需要定制分析环境。

  • 安全性和谨慎使用: 尽管 crash 在不中断系统的情况下进行诊断,但仍需注意,对于生产环境,谨慎使用是很重要的。某些 crash 命令可能会对系统产生影响,因此建议在测试环境中使用。

总的来说,crash 提供了一种非常强大的工具,用于在不影响系统运行的情况下进行 Linux 内核的调试和分析。

1
2
3
4
5
git clone https://github.com/crash-utility/crash.git
cd crash/
git checkout -b 8.0.4 8.0.4
make -j16
sudo make install

fedora

仅为一个命令启用存储库:

1
sudo dnf --enablerepo=fedora-debuginfo,updates-debuginfo install kernel-debuginfo

永久启用存储库(可能不是您想要的):

1
sudo dnf config-manager --set-enabled fedora-debuginfo updates-debuginfo

禁用存储库:

1
sudo dnf config-manager --set-disabled fedora-debuginfo updates-debuginfo

推荐使用sudo dnf debuginfo-install kernel安装内核调试包:

1
sudo dnf debuginfo-install kernel

eval命令

有时我们想知道一个数字的那些位是1,那么需要在数字前面使用-b参数:

1
2
3
4
5
6
7
crash> cpumask 0xffff88b48aa23f20 -x
struct cpumask {
bits = {0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90004000, 0x0, 0x1, 0xffffc9000d20c000, 0x420804000000002, 0x0, 0x0, 0x3700000000, 0x4, 0xfffb706a, 0xffff88ea8be9c000, 0x3700000037, 0x7800000000, 0x7800000078, 0x0, 0xffffffff82260540, 0x100000, 0x400000, 0x100000, 0x1, 0x0, 0x0, 0xffff88b48aa240b0, 0xffff88b48aa240b0, 0x0, 0xd4da92e6, 0x26554, 0xffffffffff3b4a54, 0x1fe63, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff88b492fe9a00, 0x0, 0x0, 0x0, 0x0, 0xd4da9000, 0x1a, 0x1a, 0x2a400006be6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff88b48aa24280, 0xffff88b48aa24280, 0x0, 0x0, 0x64, 0x0, 0x0, 0xffff88b492fe9bc0, 0x0, 0xffffffff83149580, 0xffff88b48aa242d0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
}
crash> eval -b 0x3 //vcpu0的SMT调度域包含vcpu0和vcpu1一对超线程
bits set: 1 0
crash>

kmem

查看内存的使用统计信息:

1
kmem -i

查看slab的信息:

1
kmem -s > kmem_s.log

按slabs降序:

1
2
3
4
5
6
7
8
9
10
11
12
cat kmem_s.log | sort -k5,5nr | head -n10
CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME
ffff8003ff689900 304 3563564 7039393 282756 16k kmemleak_object
ffff8003ad115800 192 1597195 1601956 51676 16k dentry(664:aTrustDaemon.service)
ffff80037d372700 1072 409123 409620 17826 32k ext4_inode_cache(904:session-1.scope)
ffff80037d375800 192 307034 310121 10007 16k dentry(904:session-1.scope)
ffff8003e87c8b00 128 332086 332185 9491 16k kernfs_node_cache
ffff80037d375480 144 147286 172328 5213 16k buffer_head(904:session-1.scope)
ffff8003ff688400 128 86102 86450 3458 16k kmalloc-128
ffff80037d375b80 192 63328 63365 2051 16k vm_area_struct(904:session-1.scope)
ffff80037d374680 64 37358 37400 1873 8k anon_vma_chain(904:session-1.scope)
ffff8003954a1580 40 34932 34944 1664 8k ext4_extent_status

percpu变量

查看percpu变量在每个cpu上的基地址

1
2
3
4
5
6
7
8
9
10
crash> kmem -o
PER-CPU OFFSET VALUES:
CPU 0: ffff88807f600000
CPU 1: ffff88807fa00000
CPU 2: ffff88813d600000
CPU 3: ffff88813da00000
CPU 4: ffff8881bd600000
CPU 5: ffff8881bda00000
CPU 6: ffff88823d600000
CPU 7: ffff88823da00000

查看一个全局的percpu变量的具体值

以下面这个全局percpu变量call_single_queue为例:

1
static DEFINE_PER_CPU_SHARED_ALIGNED(struct llist_head, call_single_queue);
1
2
3
4
5
6
7
8
9
10
11
12
crash> p call_single_queue
PER-CPU DATA TYPE:
struct llist_head call_single_queue;
PER-CPU ADDRESSES:
[0]: ffff88807f800340
[1]: ffff88807fc00340
[2]: ffff88813d800340
[3]: ffff88813dc00340
[4]: ffff8881bd800340
[5]: ffff8881bdc00340
[6]: ffff88823d800340
[7]: ffff88823dc00340

查看其在某些CPU上的具体内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
crash> p call_single_queue:0,2,5-7
per_cpu(call_single_queue, 0) = $5 = {
first = 0x0
}
per_cpu(call_single_queue, 2) = $6 = {
first = 0x0
}
per_cpu(call_single_queue, 5) = $7 = {
first = 0x0
}
per_cpu(call_single_queue, 6) = $8 = {
first = 0x0
}
per_cpu(call_single_queue, 7) = $9 = {
first = 0x0
}

根据结构体中percpu变量的偏移地址得到绝对地址

1
2
3
4
5
6
7
8
struct kmem_cache {
struct array_cache __percpu *cpu_cache;

/* 1) Cache tunables. Protected by slab_mutex */
unsigned int batchcount;
unsigned int limit;
unsigned int shared;
...

以16进制方式查看 kmem_cache 的 per-CPU 缓存指针(cpu_slab)的偏移量:

1
2
crash> struct kmem_cache.cpu_slab -x ffff893751f60800
cpu_slab = 0x5fc135c77b40,

上面cpu_slab的偏移量是0x5fc135c77b40

假如想知道这个成员在cpu10上的地址,下面有2种方法:

  • 绝对值相加

    首先获取percpu变量在cpu10上的基地址:

    1
    2
    crash> kmem -o | grep "CPU 10"
    CPU 10: ffff88debfd00000

    然后相加即可:

    1
    2
    3
    4
    5
    crash> eval ffff88debfd00000 + 0x5fc135c77b40
    hexadecimal: ffffe89ff5977b40
    decimal: 18446718372450630464 (-25701258921152)
    octal: 1777777211776545675500
    binary: 1111111111111111111010001001111111110101100101110111101101000000

    上面将相加后的结果分别按16进制,10进制,8进制以及2进制进行了输出。

  • 使用ptov

    1
    2
    3
    4
    crash> ptov 0x5fc135c77b40:10
    PER-CPU OFFSET: 5fc135c77b40
    CPU VIRTUAL
    [10] ffffe89ff5977b40

    这种方法更加方便。

读取percpu变量的内容

还是以kmem_cache为例:

1
2
crash> struct kmem_cache.cpu_slab -x ffff893751f60800
cpu_slab = 0x5fc135c77b40,

如果想读取cpu_slab的内容,之前的办法是先得到绝对地址,然后再读取(用*代替struct),其实也可以直接读取,下面的例子读取cpu2上的cpu_slab的内容:

1
2
3
4
5
6
7
8
crash> *kmem_cache_cpu 0x5fc135c77b40:2
[2]: ffffe89ff5577b40
struct kmem_cache_cpu {
freelist = 0x0,
tid = 2,
page = 0x0,
partial = 0x0
}

sysrq-trigger

“Alt+PrtSc+C”:手动触发kdump,触发后服务器会自动重启。(正常情况下勿按该组合键。)

pstore

中断

windebug

其他


debug
https://realwujing.github.io/linux/debug/debug/
作者
Wu Jing
发布于
2024年7月23日
许可协议