文章目录
printk动态打印
OOPS引发oops查看调用栈ksymoopskallsyms
sysrqprocfs/sysfs/debugfs/relayfsprocfs收集系统信息特定进程子目录总览status常用字段stat常用字段sched常用字段io字段
内核数据/proc/net中的网络信息
其他文件夹
sysfsdebugfsrelayfs
Kprobe/kretprobe/Uprobe/Uretprobestrace/ftrace/Event Tracing/Tracefs/fprobeftrace
kdb/kgdb内核配置kdbkgdb/gdbkdb/kgdb切换从gdb运行kdb命令内核启动参数同时进行调试和打印查看使用python脚本增强gdb调试gdb/kgdb和kdb的使用差异注意事项
参考
printk
通过proc/sys/kernel/printk查看打印等级
$ cat /proc/sys/kernel/printk
1 4 1 3
四个数字分别对应:
console_loglevel:控制台使用的日志级别; default_message_loglevel:调用 printk() 未指定日志级别时使用的日志级别; minimum_console_loglevel:允许设置的控制台日志级别(console_loglevel)最小值; default_console_loglevel:系统启动时使用的日志级别。
修改等级
# echo 1 4 1 7 > /proc/sys/kernel/printk
# cat /proc/sys/kernel/printk
1 4 1 7
如果修改不了,可以使用dmesg命令修改
pi@link:~$ sudo dmesg -n debug
pi@link:~$ cat /proc/sys/kernel/printk
8 4 1 3
动态打印
printk挺好用,但是每次开关都需要重新编译内核,很不方便。因此可以使用动态的打印,在运行时开启或关闭打印开关 需要开启内核编译选项:
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DYNAMIC_DEBUG_CORE=y
控制开关的文件位于/sys/kernel/debug/dynamic_debug/control 可以按文件名 模块 函数等来控制打印的开关
OOPS
引发oops
BUG()和BUG_ON() WARN(x) 和 WARN_ON(x) panic() dump_stack()
查看调用栈
ksymoops
未配置CONFIG_KALLSYMS,使用ksymoops+System.map解码调用栈
kallsyms
配置了CONFIG_KALLSYMS,打印的调用栈是解码的,即可以看到函数
sysrq
需要在内核配置才能启用
CONFIG_MAGIC_SYSRQ=y
然后在shell下使用,比如查看task
echo 1 >/proc/sys/kernel/sysrq
echo t > /proc/sysrq-trigger
如果看不到打印,看下dmesg里是否有,调整下控制台打印等级 在通用控制台,键盘可以使用sysrq键+alt+command触发 但是串口需要使用BREAK + command来触发,可能和配置了CONFIG_KGDB_SERIAL_CONSOLE=y有关 另外需要注意,非控制台无法通过以上两种按键触发,必须通过命令触发 在crt中可以映射ctrl+shif+b为“Telnet Function”的“TN_BREAK”
procfs/sysfs/debugfs/relayfs
参考 Filesystems in the Linux kernel Linux /proc和/sys说明
procfs
The /proc Filesystem 根据目录,其功能如下: Chapter 1: 收集系统信息 Chapter 2: 修改系统参数 Chapter 3: 每进程参数 Chapter 4: 配置 procfs Chapter 5: 文件系统特性
收集系统信息
此部分为/proc的只读部分
特定进程子目录
/proc目录下包含了所有运行在系统上进程的进程号命名的子目录。即/proc/xxx/。还有一个比较特殊的是self,是正在读取文件系统的进程本身。 其实现位于fs/proc/base.c,可以参照内核代码看 也可以使用man proc来查看 以下有些信息可能需要安装内核模块才能使用,安装内核模块时可以按照代码目录寻找对应的ko
总览
这里挑一些常用的
文件内容cmdline命令行参数environ当前进程环境变量,可以使用cat /proc/pid/environ | tr '\000' '\n'查看fd句柄信息maps可执行文件和库文件的内存映射mem内存信息stat进程状态,不同系统可能字段不一致statm进程内存状态信息status更有可读性的进程状态。wchan开启CONFIG_KALLSYMS=y后,显示任务被阻塞时内核的执行函数或者没阻塞时返回0.pagemap页表stackCONFIG_STACKTRACE启用后显示完整调用栈smaps基于maps显示更多的扩展信息,显示每个映射的内存消耗以及与之关联的标志smaps_rollupsmaps的汇总信息numa_maps基于maps的扩展,显示每个映射的内存位置和绑定策略以及内存使用情况(以页为单位)。syscall当前进程正在执行的系统调用latency显示哪些代码造成的延时比较大,执行echo 1 > /proc/sys/kernel/latencytop启用limits显示当前进程的资源限制,Soft Limit表示kernel设置给资源的值,Hard Limit表示Soft Limit的上限,而Units则为计量单元。schedstat字段分别为 在cpu花费的时间,在运行队列等待花费的时间,在cpu上运行的时间片数oom_score进程oom得分oom_adj进程oom值oom_scor_adj进程oom值和oom得分coredump_filter指定哪些进程空间内存可以保存到core文件ioio信息sched调度相关信息
注:1.maps的输出中stack:[tid]是线程的堆栈信息,对应于/proc/[pid]/task/[tid]/路径 2.syscall的输出中,第一个值是系统调用号(7代表poll),后面跟着6个系统调用的参数值(位于寄存器中),最后两个值依次是堆栈指针和指令计数器的值。如果当前进程虽然阻塞,但阻塞函数并不是系统调用,则系统调用号的值为-1,后面只有堆栈指针和指令计数器的值。如果进程没有阻塞,则这个文件只有一个“running”的字符串。
status常用字段
字段内容pid进程号PPid父进程号Threads线程数TracerPid正在追踪此进程的进程Cpus_allowed_list此进程可能运行的cpuvoluntary_ctxt_switches主动的上下文切换次数nonvoluntary_ctxt_switches被动的上下文切换次数SigQ队列数量/最大数量SigPnd待处理的信号SigBlk屏蔽的信号SigIgn忽略的信号SigCgt捕捉的信号
其中关于信号的字段,其掩码可以展开为2进制位,哪个位置1就代表哪个信号被设置,比如SigIgn为0x2,则代表信号2,即SIGINT被忽略,其他都没被忽略
stat常用字段
这个字段比较难找,可以参考do_task_stat函数里的实现: 可以使用awk来查看特定字段
序号字段内容1pid进程号2tcomm3state4ppid省略4个字段9flags任务标志,直接按10进制打的省略4个字段14utime该任务在用户态运行的时间(单位为jiffies)15stime该任务在核心态运行的时间(单位为jiffies)16cutime与子进程一起的用户态运行的时间(单位为jiffies)17cstime与子进程一起的核心态运行时间(单位为jiffies)18priority优先级等级19nicenice等级20num_threads线程数量省略5个字段26start_code代码段起始地址27end_code代码段结束地址28start_stack主进程栈的起始地址29espsp堆栈指针当前值30eipip指令指针当前值31pending待处理信号的位图32blocked阻塞信号的位图33sigign忽略信号的位图34sigcatch捕获信号的位图忽略3个字段38exit_signal退出时发给父进程的信号39task_cpu任务被调度在哪个CPU上40rt_priority实时优先级41policy调度策略 0:非实时进程 1:FIFO实时进程 2:RR实时进程省略3个字段45start_datadata+bss段起始地址46end_datadata+bss段结束地址47start_brk堆内存起始地址48~52省略5个字段
比如打印进程信号相关信息:
$cat /proc/1/stat|awk '{print "pending:",$31,"\nblocked:",$32,"\nsigign:",$33,"\nsigcatch:",$34}'
打印进程优先级信息:
$cat /proc/1/stat|awk '{print "pri:",$18,"\nnice:",$19,"\nrtpri:",$40,"\npolicy:",$41}'
上面的东西几乎都可以通过ps来显示出来,可以通过-o来定制输出的列
sched常用字段
需要启用CONFIG_SCHED_DEBUG: -> Kernel hacking-> Scheduler Debugging-> Collect scheduler debugging info (SCHED_DEBUG [=y])
实现在proc_sched_show_task,可以对照查看 下面是top运行30s抓取结果
top (46894, #threads: 1)
-------------------------------------------------------------------
se.exec_start : 196010947.219876 #此进程最近被调度到的开始执行时刻(这个值是每次update_curr都进行更新)。基于sched_clock()计算,返回自系统启动以来的纳秒数(10的-9次方秒)。如果没有提供本地实现,系统节拍计数器将被用作sched_clock()
se.vruntime : 233.950033 #虚拟运行时间,cfs调度用
se.sum_exec_runtime : 300.145663 #累计运行的物理世界时间
se.nr_migrations : 2 #需要迁移当前进程到其他cpu时累加此字段
sum_sleep_runtime : 27177.092009 #累计睡眠时间
sum_block_runtime : 0.000000 #累计阻塞时间
wait_start : 0.000000 #最近一次当前进程被入队的时刻
sleep_start : 196010947.219876 #此进程最近一次被从队列里取出,并被置S状态的时刻
block_start : 0.000000 #此进程最近一次被从队列里取出,并被置D状态的时刻
sleep_max : 3003.006988 #最长处于S状态时间
block_max : 0.000000 #最长处于D状态时间
exec_max : 4.031979 #最长单次执行时间
slice_max : 5.275208 #经获得时间片的最长时间 (当cpu load过高时开始计算)
wait_max : 0.487865 #最长在就绪队列里的等待时间
wait_sum : 16.583297 #累计在就绪队列里的等待时间
wait_count : 262 #累计等待次数 (被出队的次数) cfs使用,当被选中做下一个待运行进程时(set_next_entity),等待结束
iowait_sum : 0.000000 #io等待时间
iowait_count : 0 #io等待次数 io_schedule调用次数
nr_migrations_cold : 0 #
nr_failed_migrations_affine : 0 #进程设置了cpu亲和,进程迁移时检查失败的次数
nr_failed_migrations_running : 14 #当前进程出入R,不运行迁移的次数
nr_failed_migrations_hot : 12 #当前进程因为是cache hot导致迁移失败的次数
nr_forced_migrations : 2 #在当前进程cache hot下,由于负载均衡尝试多次失败,强行进行迁移的次数
nr_wakeups : 10 #被唤醒的累计次数(从不可运行到可运行)
nr_wakeups_sync : 0 #同步唤醒次数,即a唤醒b,a立刻睡眠,b被唤醒的次数
nr_wakeups_migrate : 0 #被唤醒得到调度的当前cpu,不是之前睡眠的cpu的次数
nr_wakeups_local : 10 #被本地唤醒的次数唤醒后在当前cpu上执行)
nr_wakeups_remote : 0 #非本地唤醒累计次数
nr_wakeups_affine : 0 #考虑了任务的cache亲和性的唤醒次数
nr_wakeups_affine_attempts : 0 #考虑了任务的cache亲和性的唤醒次数
nr_wakeups_passive : 0 #
nr_wakeups_idle : 0 #
avg_atom : 1.145594 #本进程平均切换耗时
avg_per_cpu : 150.072831 #如果本进程曾经被推或者拉到其他cpu上,则计算每个cpu上的平均耗时
nr_switches : 262 #主动切换和被动切换的累计次数
nr_voluntary_switches : 11 #主动切换次数(由于prev->state为不可运行状态引起的切换)
nr_involuntary_switches : 251 #被动切换次数
se.load.weight : 1048576 #该se的load。权重,和负载均衡有关
se.avg.load_sum : 11252 #
se.avg.runnable_sum : 11525008 #
se.avg.util_sum : 11525008 #
se.avg.load_avg : 245 #
se.avg.runnable_avg : 245 #
se.avg.util_avg : 245 #
se.avg.last_update_time : 196010928415744 #
se.avg.util_est.ewma : 266 #
se.avg.util_est.enqueued : 245 #
policy : 0 #进程属性,0为非实时
prio : 120 #优先级
clock-delta : 208 #
io字段
rchar: 323934931 // 读出的总字节数,read或者pread()中的长度参数总和(pagecache中统计而来,不代表实际磁盘的读入)
wchar: 323929600 // 写入的总字节数,write或者pwrite中的长度参数总和
syscr: 632687 // read()或者pread()总的调用次数
syscw: 632675 // write()或者pwrite()总的调用次数
read_bytes: 0 // 实际从磁盘中读取的字节总数 (这里if=/dev/zero 所以没有实际的读入字节数)
write_bytes: 323932160 // 实际写入到磁盘中的字节总数
cancelled_write_bytes: 0 // 由于截断pagecache导致应该发生而没有发生的写入字节数(可能为负数)
内核数据
与进程条目类似,内核数据文件提供了有关正在运行的内核的信息。 下面依旧挑一些常用的
字段内容cmdline内核启动参数,包括bootloader传递的和内嵌的cpuinfocpu信息mtd(Memory Technology Device)子系统,可能需要手动安装模块,显示flash等设备的分区interrupts中断使用iomem内存映射ioportsI/o口使用情况kcore系统的物理内存,并以ELF核心文件格式存储kmsg内核消息ksyms内核符号表loadavg1,5,15分钟负载locks内核锁meminfo内存信息misc杂项modules已加载模块列表mounts挂载的文件系统net网络信息softirqs软中断使用stat概览sys参见第二章sysvipcSysVIPC资源ttytty驱动信息uptime启动以来的墙上时钟时间以及所有cpu的空闲时间version内核版本buddyinfo内核内存分配信息devices块设备和字符设备filesystems支持的文件系统partitions系统分区表
/proc/net中的网络信息
仅列举ipv4的
字段内容protocols各类协议proto结构,socket数量,提供的c接口dev由网卡维护的网卡层面的发送,接收的包数量,丢弃数量,错误数量等softnet_stat在线CPU的每CPU变量传入数据包队列统计,每行代表一个cpu,网络设备子系统处理包,丢弃包数量统计ptype网络设备子系统收包回调,比如ip,arp,也包括tcpdump注册的sockstatsocket统计信息,包括所有使用的sock,TCP,UDP等使用的socknetstat网络统计信息,基本也是取的snmp信息snmpsnmp统计信息,比如ip,icmp,tcp,udp收发包统计等arp内核arp表项dev_mcast设备正在监听的2层组播组(接口索引、标签、引用数量、绑定地址数量)dev_stat网络设备状态rawraw设备统计route内核路由表fib_triefib trie树fib_triestatfib trie树统计信息rt_cache路由缓存tcptcp套接字,有源目的ip地址和端口号,sock状态udpudp套接字unixunxi域套接字wireless无线接口数据igmp主机加入的ip组播地址psched全局报文调度参数netlinknetlink socket列表ip_mr_vifs组播虚拟接口列表ip_mr_cache组播路由缓存表
其他文件夹
task 对应线程的信息 map_files 包含内存映射文件 fdinfo 包含了句柄信息 ns 包含了进程的命名空间信息。每一个子目录可以通过 setns 操作. net 进程的网络相关数据,里面会按照协议分类展示出数据,需要注意的是,有些协议是全局的,但是也会在这里有对应的数据 attr 安全相关
sysfs
sysfs - The filesystem for exporting kernel objects
sysfs是一个基于RAM的文件系统,最初基于ramfs。它提供了一种导出内核数据结构、其属性以及它们之间链接到用户空间的方法。
具体文档参见Documentation/ABI/
debugfs
DebugFS 在内核中开启或者通过mount -t debugfs none /sys/kernel/debug挂载
Debugfs是内核开发人员向用户空间提供信息的简单方法。
relayfs
relay interface (formerly relayfs) relayfs为内核应用程序提供了一种手段,通过用户定义的“中继通道”有效地将大量数据从内核传输到用户空间。
Kprobe/kretprobe/Uprobe/Uretprobes
动态 tracing 机制,能够动态的注入到内核的任意函数中的任意地方,采集调试信息和性能信息,并且不影响内核的运行。
初步理解,使用插桩的方式在函数执行过程中或执行完毕后跳转到自己编写的内核模块采集调试信息和性能信息。
trace/ftrace/Event Tracing/Tracefs/fprobe
静态的 tracing 机制,开发者在内核的代码里的固定位置声明了一些 Hook 点,通过这些 hook 点实现相应的追踪代码插入,一个Hook 点被称为一个 tracepoint。
ftrace
如果先使用了func再使用func graph,必须先使用 echo > set_ftrace_filter 清除已指定的函数,否则无法进行 function_graph 绘制。使用echo 1 > options/funcgraph-tail 可以增加函数尾部注释。
kdb/kgdb
内核配置
这步是在编译内核之后进行的,因为有些特性需要在内核编译时就指定,比如kgdb。可以使用menuconfig把kernel debugging和KGDB选项打开,建议的配置
# CONFIG_STRICT_KERNEL_RWX is not set
# CONFIG_RANDOMIZE_BASE is not set
CONFIG_FRAME_POINTER=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_KDB=y
CONFIG_KDB_KEYBOARD=y
CONFIG_DEBUG_INFO=y
CONFIG_GDB_SCRIPTS=y
使用make scripts_gdb显式编译gdb脚本
kdb
使用实例:
#echo g > /proc/sysrq-trigger
[255170.882750] sysrq: HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) show-blocked-tasks(w)
#echo ttyAMA0 > /sys/module/kgdboc/parameters/kgdboc
[255630.457371] KGDB: Registered I/O driver kgdboc
#echo g > /proc/sysrq-trigger
[255639.294716] sysrq: DEBUG
Entering kdb (current=0xffff00001dba4e00, pid 10020) on processor 0 due to Keyboard Entry
[0]kdb> btc
btc: cpu status: Currently on cpu 0
Available cpus: 0-1
Stack traceback for pid 10020
0xffff00001dba4e00 10020 10019 1 0 R 0xffff00001dba5700 *sh
CPU: 0 PID: 10020 Comm: sh Tainted: P O 5.10.0 #8
Hardware name: Netfactory soc928 EVB (DT)
Call trace:
dump_backtrace+0x0/0x1b0
show_stack+0x14/0x30
dump_stack+0xc4/0xfc
...
如果不想每次都设置kgdboc,可以在内核启动参数中添加kgdboc。 常用参数
m$ 操作内存系列
go 继续运行
r$ 操作寄存器系列
b$ 操作栈系列
env 查看环境变量
set 设置环境变量//比如 set LINES 10000设置行数
cpu 切换cpu
ps 查看的任务
pid 切换到任务
b$ 操作断点系列
ss 单步操作
dump$ dump信息
kgdb/gdb
kgdb相对于kdb来说是源码级调试器 输入kgdb,并等待命令。 可以使用串口连接,或者使用socat实现远程gdb连接kgdb,另外还有agent-proxy小工具可用。 之前尝试过这两种工具发现连上都输入不了,后来发现可能是mac上的ch340驱动有问题,直接echo “xxx”>/dev/tty.wchusbserial220,会直接卡住。但是在wsl2上尝试全部成功。 目前远程方案全部尝试失败,先使用本地 1.接入串口线,使用usbipd实现共享到wsl2 2.在host设备上提前设置好kgdboc,并进入kgdb等待连接 3.使用gdb加载vmlinux,连接串口 但是实际使用时报错
(gdb) set serial baud 115200
(gdb) target remote /dev/ttyUSB0
Remote debugging using /dev/ttyUSB0
warning: multi-threaded target stopped without sending a thread-id, using first non-exited thread
Remote 'g' packet reply is too long (expected 312 bytes, got 788 bytes): 00206a09c0ffffff0100000000000000000000000000000040236a09c0ffffff102a173b80ffffff102a173b80ffffff0000000000000000d8464d09c0ffffffffefffff000000004c751808c0ffffffd8464d09c0ffffff200720072007200720072007200720077fb9e509c0ffffff000000000000000077b9e589c0ffffff00000000000000000000000000000000ffffffffffffffff670000000000000000c04709c0ffffff00d04509c0ffffff03000000000000000000000000000000000000000000000018fcc308c0ffffff00000000000000000000000000000000c01eee0180ffffff80bce509c0ffffff4c751808c0ffffff80bce509c0ffffffa4741808c0ffffff0500006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
报错,查看stackoverflow,说要改gdb源码重编的,还说使用set arch i386:x86-64命令的,这里猜测他们可能都用的qemu,所以host是x86_64,但我这里的host是真实的嵌入式设备,猜测可能需要编build,host=x86-64,target=aarch64的gdb 下载gdb源码包编译,配置tartget为arm64进行编译:
~ aarch64-linux-gnu-gdb --config
This GDB was configured as follows:
configure --host=x86_64-pc-linux-gnu --target=aarch64-linux-gnu
--with-auto-load-dir=$debugdir:$datadir/auto-load
--with-auto-load-safe-path=$debugdir:$datadir/auto-load
--with-expat
--with-gdb-datadir=/home/wsl/project/gdb-10.2/build_x86_64/../out_x86_64/share/gdb (relocatable)
--with-jit-reader-dir=/home/wsl/project/gdb-10.2/build_x86_64/../out_x86_64/lib/gdb (relocatable)
--without-libunwind-ia64
--without-lzma
--without-babeltrace
--without-intel-pt
--without-mpfr
--without-xxhash
--with-python=/usr
--with-python-libdir=/usr/lib
--without-debuginfod
--without-guile
--disable-source-highlight
--with-separate-debug-dir=/home/wsl/project/gdb-10.2/build_x86_64/../out_x86_64/lib/debug (relocatable)
("Relocatable" means the directory can be moved with the GDB installation
tree, and GDB will still find it.)
之后果然可以了,下面是使用agent_proxy来做的分流,可以一边gdb调试,一边看打印 一个注意点是,不管用socat还是agent-proxy做了串口转网络后,如果连接上之后发现输入没反应,可能是之前触发了KGDB导致的,所以是不接受任何输入的,可以尝试输入$3#33或$D#44+回到输入界面。因此最佳使用方式也很明确了,在把串口连接后,一个端口用telnet登录,用来触发kgdb,另一个端口在gdb中用来连接kgdb,完全不用先串口触发完,再退出去,再进gdb,来回折腾了。 其实本来是想使用socat的,但是现在看agent-proxy完全满足了需求,而且这个工具依赖很少很好编,只有两个c文件,太强了。 使用串口转网络后,可以实现远程串口,目前是这样的拓扑 上面是把树莓派串口连接到PC上,然后用agent-proxy分流,做调试和查看,既然使用了5550和5555,那么Mac也能用,PC上的串口情况:
ss -anp|grep 555
tcp LISTEN 0 1 0.0.0.0:5550 0.0.0.0:*
tcp LISTEN 0 1 0.0.0.0:5551 0.0.0.0:*
理论上在Mac上连接了5550和5551,使用完全和PC一样的,只要网络能通,因为这里Mac没装GDB,就不实验了。
kdb/kgdb切换
$3#33 切换回kdb $D#44+ 继续运行,退出kgdb
从gdb运行kdb命令
可以使用monitor help命令查看可以在gdb使用的kdb命令
内核启动参数
kgdboc 配置用来调试的串口 kgdboc_earlycon kgdb将使用bootconsole直到使用kgdboc kgdbwait 在启动内核时等待调试器连接 kgdbcon 在gdb连接到内核时在gdb中看到printk()消息。注意,不能在系统控制台同时使用kgdboc和kgdbcon。当在系统运行时通过echo 1 > /sys/module/kgdb/parameters/kgdb_use_con配置了此功能,必须在下次i/o重配置时才能生效 kgdbreboot 更改调试器处理重新启动通知的方式 nokaslr 禁用随机化
同时进行调试和打印查看
可参考基于A33下linux内核的GDB+KGDB内核调试环境搭建的agent-proxy方法,或者尝试使用socat,比如linux云服务器之间利用socat进行虚拟串口通信采用一对串口的方式,或者使用socat使用网络转接
使用python脚本增强gdb调试
指定CONFIG_GDB_SCRIPTS选项后根目录无vmlinux-gdb.py脚本文件,参考stack overflow,使用make scripts_gdb后生成脚本文件 加载报错:
(gdb) source vmlinux-gdb.py
Traceback (most recent call last):
File "vmlinux-gdb.py", line 25, in
import linux.constants
File "/home/wsl/project/raspberry_pi/linux/scripts/gdb/linux/constants.py", line 5
LX_SB_RDONLY = ((((1UL))) << (0))
^
SyntaxError: invalid decimal literal
打这个补丁可以修改好,应该是6.4之后的版本是正常的,我是6.1,怎么这么倒霉。。 修改完毕重新生成脚本就可以了 需要先file vmlinux之后再使用,直接source好像会报错 常用命令 lx-symbols 加载/重加载内核和当前加载的模块 提前下未加载模块函数的断点 lx-dmesg 打印dmesg… p $lx_current().pid 打印当前任务结构体,目前只支持x86_64和arm64 可以使用命令查看相关帮助
(gdb) apropos lx
function lx_clk_core_lookup -- Find struct clk_core by name
function lx_current -- Return current task.
function lx_device_find_by_bus_name -- Find struct device by bus and name (both strings)
function lx_device_find_by_class_name -- Find struct device by class and name (both strings)
function lx_module -- Find module by name and return the module variable.
function lx_per_cpu -- Return per-cpu variable.
function lx_rb_first -- Lookup and return a node from an RBTree
function lx_rb_last -- Lookup and return a node from an RBTree.
function lx_rb_next -- Lookup and return a node from an RBTree.
function lx_rb_prev -- Lookup and return a node from an RBTree.
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable.
function lx_thread_info -- Calculate Linux thread_info from task variable.
function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid
lx-clk-summary -- Print clk tree summary
lx-cmdline -- Report the Linux Commandline used in the current kernel.
lx-configdump -- Output kernel config to the filename specified as the command
lx-cpus -- List CPU status arrays
lx-device-list-bus -- Print devices on a bus (or all buses if not specified)
lx-device-list-class -- Print devices in a class (or all classes if not specified)
lx-device-list-tree -- Print a device and its children recursively
lx-dmesg -- Print Linux kernel log buffer.
lx-fdtdump -- Output Flattened Device Tree header and dump FDT blob to the filename
lx-genpd-summary -- Print genpd summary
lx-iomem -- Identify the IO memory resource locations defined by the kernel
lx-ioports -- Identify the IO port resource locations defined by the kernel
lx-list-check -- Verify a list consistency
lx-lsmod -- List currently loaded modules.
lx-mounts -- Report the VFS mounts of the current process namespace.
lx-ps -- Dump Linux tasks.
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules.
lx-timerlist -- Print /proc/timer_list
lx-version -- Report the Linux Version of the current kernel.
gdb/kgdb和kdb的使用差异
这张图应该可以很明显的看出,gdb/kgdb的调试信息比kdb丰富的多,其他还有待探索
注意事项
内核多线程
在用 gdb 来调试内核的时候,由于内核在初始化的时候,会创建很多子线程。而默认 gdb会接管所有的线程,如果你从一个线程切换到另外一个线程, gdb 会马上把原先的线程暂停。但是这样很容易导致 kernel 死掉,所以需要设置一下 gdb 。一般用 gdb 进行多线程调试,需要注意两个参数: follow-fork-mode 和detach-on-fork。 即kgdb调试时启用set detach-on-fork on 先开启set detach-on-fork on,在跟踪到__arm64_sys_execve时,不要使用finish,否则容易出错,应该使用next,后续也一样
kgdb断点问题 如果在调试arm64设备时,使用next有问题,如下
Thread 106 hit Breakpoint 1, ip_rcv (skb=0xffffff80060d2000,
dev=0xffffff8003600000, pt=0xffffffc0094f36a0
orig_dev=0xffffff8003600000) at net/ipv4/ip_input.c:562
562 {
(gdb) n
el1_interrupt (regs=0xffffffc00800bc70 <_text+48240>,
handler=0xffffffc008010050
at arch/arm64/kernel/entry-common.c:486
486
可以参照此patch解决
参考
内核通用调试方法 Development tools for the kernel linux内核调试方法 内核调试跟踪 brendangregg的主页 Linux内核的经典调试方式
printk Linux kernel:修改内核 printk 日志等级
动态printk Linux 内核动态打印调试(dev_info、 dev_dbg ) Linux printk和动态输出 Linux Kernel 编程-你不知道的printk(2)
sysrq Linux:内核调试之内核魔术键sysrq Linux sysrq使用 How to send SysRq command over ssh connection? Remapping ⌘-. (command-period) to BREAK in iTerm2
追踪机制 Linux Tracing Technologies 深入理解Linux 内核追踪机制
通用知识 linux 内核启动Initramfs与initrd 及其挂载 Ramfs, rootfs and initramfs 关于Linux内核vmlinuz、initrd.img和System.map 关于内核镜像文件vmlinux-vmlinuz-vmlinux.bin-zimage-bzimage-uImage 之间的差异 RaspberryPi
远程调试内核 Using kgdb, kdb and the kernel debugger internals Debugging kernel and modules via gdb vscode+kgdb+qemu调试linux内核 使用gdb调试内核
gdb脚本报错 GNU调试器 GDB 8.3 发布及安装更新,支持RISC-V与IPv6连接等 玩转C++调试之Python的GDB库增强 GDB解coredump文件报Python异常解决办法 内核调试- vmlinux-gdb.py无法在gdb上运行
gdb报错 Remote ‘g’ packet reply is too long Remote ‘g’ packet reply is too long问题的解决
小工具 socat网络工具 agent-proxy小工具——用一个串口,既当控制台又当kgdb调试通道 linux云服务器之间利用socat进行虚拟串口通信
proc文件系统 Linux /proc/$pid部分内容详解 linux概念之/proc与/sys linux调度器(十)——调度器/proc信息解读 深度剖析Linux进程的内部机制:一探/proc/pid的奥秘 查询进程的IO