linux内核调试和性能优化

1.更改printk打印级别
1.1.查看当前控制台的打印级别
cat /proc/sys/kernel/printk

# (1) 控制台日志级别:优先级高于该值的消息将被打印至控制台。
# (2) 缺省的消息日志级别:将用该值来打印没有优先级的消息。
# (3) 最低的控制台日志级别:控制台日志级别可能被设置的最小值。
# (4) 缺省的控制台:控制台日志级别的缺省值。

2.修改printk的打印级别
echo "新的打印级别 4 1 7" > /proc/sys/kernel/printk

2.linux内核打开pr_debug、dev_dbg调试信息
当前代码的makefile中添加:

EXTRA_CFLAGS += -DDEBUG

3.ftrace内核空间
ftrace是一个直接内置在Linux内核中的跟踪工具
参考链接:
https://mp.weixin.qq.com/s/bYwEi9gjYED1vZ5jQeLDuw
https://linux.cn/article-9838-1.html
宋宝华:https://mp.weixin.qq.com/s/aFpXGrQ7sHaZguL66QF-tA

#查看内核支持的跟踪器列表
cat /sys/kernel/debug/tracing/available_tracers

#使能function_graph跟踪器
echo function_graph > /sys/kernel/debug/tracing/current_tracer

#查看当前的跟踪器
cat /sys/kernel/debug/tracing/current_tracer

#使能ftrace 功能
echo 1 > /sys/kernel/debug/tracing/tracing_on

#查看ftrace输出
cat trace | head -40

4.perf-tools工具
https://gitee.com/mirrors/perf-tools

./funcgraph test_proc_show

5.strace用户空间
参考:https://www.cnblogs.com/machangwei-8/p/10388883.html

#示例1,-e trace=file 输出只显示和文件访问有关的内容
strace -tt -T -v -f -e trace=file -o /data/log/strace.log -s 1024 -p 23489

#示例2
strace -o strace.log -tt -p 24298

-tt 在每行输出的前面,显示毫秒级别的时间
-T 显示每次系统调用所花费的时间
-v 对于某些相关调用,把完整的环境变量,文件stat结构等打出来。
-f 跟踪目标进程,以及目标进程创建的所有子进程
-e 控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称
-o 把strace的输出单独写到指定的文件
-s 当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节
-p 指定要跟踪的进程pid, 要同时跟踪多个pid, 重复多次-p选项即可。

-e trace=file 跟踪和文件访问相关的调用(参数中有文件名)
-e trace=process 和进程管理相关的调用,比如fork/exec/exit_group
-e trace=network 和网络通信相关的调用,比如socket/sendto/connect
-e trace=signal 信号发送和处理相关,比如kill/sigaction
-e trace=desc 和文件描述符相关,比如write/read/select/epoll等
-e trace=ipc 进程见同学相关,比如shmget等

6.TRACE_EVENT添加一个新的跟踪点
6.1.定义:
TRACE_EVENT(sched_stat_minvruntime,
TP_PROTO(struct task_struct *tsk, u64 minvruntime),
TP_ARGS(tsk, minvruntime),
TP_STRUCT__entry(
__array( char, comm, TASK_COMM_LEN)
__field( pid_t, pid )
__field( u64, vruntime)
),
TP_fast_assign(
memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
__entry->pid = tsk->pid;
__entry->vruntime = minvruntime;
),
TP_printk("comm=%s pid=%d vruntime=%Lu [ns]", __entry->comm, __entry->pid,(unsigned long long)__entry->vruntime)
);

6.2.使用:
trace_sched_stat_minvruntime(curtask, cfs_rq->min_vruntime);

6.3.查看
ls /sys/kernel/debug/tracing/events/sched/sched_stat_minvruntime

7.使用trace_marker来跟踪程序
# trace_marker 是一个文件节点,允许用户程序写入字符串,frace 会记录写入动作的时间戳

echo nop > /sys/kernel/debug/tracing/current_tracer # 设置function 跟踪器是不能捕捉到示踪标志
echo 1 > /sys/kernel/debug/tracing/tracing_on # 打开ftrace 才能捕捉到示踪标志
echo 0 > /sys/kernel/debug/tracing/tracing_on # 关闭ftrace

8.trace-cmd和kernelshark
sudo apt install trace-cmd kernelshark

1.记录trace数据:
trace-cmd record -e 'sched_wakeup*' -e sched_switch -e 'sched_migrate*'
# -p plugin:指定一个跟踪器,可以通过 trace-cmd list 来获取系统支持的跟踪器。常见的跟踪器有 function_graph、function、nop 等。
# –e event:指定一个跟踪事件。
# –f filter:指定一个过滤器,这个参数必须紧跟着“-e”参数。
# –P pid:指定一个进程进行跟踪。
# –l func:指定跟踪的函数,可以是一个或多个。
# –n func:不跟踪某个函数。

2.图形化界面分析trace数据
kernelshark trace.dat

9.调试oops问题
9.1.有问题源码的调试
#首先定位在汇编中出错的位置
KBUILD_CFLAGS +=-g
aarch64-none-linux-gnu-objdump -Sd oops.o

#然后定位在C代码中出错的位置
aarch64-linux-gnu-gdb oops.o
list *create_oops+0x2c

9.2.无问题源码的调试工具scripts/decodecode
# 首先把内核出错日志的打印复制并保存到一个.txt 文件中
vim opps.txt

# 然后使用scripts/decodecode工具定位在汇编出错的位置
export ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu-
./scripts/decodecode < opps.txt

10.perf内核自带性能分析工具
(1).编译perf
cd tools/perf
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu-

#编译生成的文件:
tools/perf/perf

(2).使用
1).查看当前软硬件平台支持的性能事件列表,性能事件的属性:
perf list

# u: 仅统计用户空间程序触发的性能事件
# k: 仅统计内核触发的性能事件
# h: 仅统计 Hypervisor 触发的性能事件
# G: 在 KVM 虚拟机中,仅统计 Guest 系统触发的性能事件
# H: 仅统计 Host 系统触发的性能事件
# p: 精度级别

2).分析性能:
# 进程级别统计
perf stat -p $pid -d
# 系统整体统计
perf stat -a -d sleep 5
#分析进程调用系统调用的情形
perf stat -p $pid -e 'syscalls:sys_enter' sleep 10

3).实时显示系统/进程的性能统计信息, 默认性能事件为 cycles ( CPU 周期数 ):
# 进程级别
perf top -p $pid -g
# 系统整体
perf top -g

sudo perf top -e sched:sched_switch -s pid

4).记录一段时间内系统/进程的性能事件, 默认性能事件为 cycles ( CPU 周期数 ):
perf record -e cpu-clock -g command
perf report > log.txt

#进程采样
perf record -p $pid -g -e cycles -e cs
#系统整体采样
perf record -a -g -e cycles -e cs

5).统计系统中进入 S 状态的进程的睡眠时长及原因
echo 1 >/proc/sys/kernel/sched_schedstats
perf record -e sched:sched_stat_sleep -e sched:sched_switch -a -g -o perf.data.raw sleep 1
perf inject -s -v -i perf.data.raw -o perf.data
perf script -i perf.data.raw -F comm,pid,tid,cpu,time,period,event,ip,sym,dso,trace
perf script -i perf.data -F comm,pid,tid,cpu,time,period,event,ip,sym,dso,trace

6).使用perf annotate分析源码
使用perf annotate深入到指令级分析:

perf record ./noploop 5
perf annotate -d ./noploop

使用perf annotate分析内核:

perf record --vmlinux vmlinux
perf report -k vmlinux
perf annotate -k vmlinux -d symbol

11.火焰图的生成和使用
1).下载火焰图工具
git clone https://gitee.com/mirrors/FlameGraph.git

2).生成火焰图
sudo perf record -e cpu-clock -g ./test
sudo chmod 777 perf.data
perf script > out.perf
FlameGraph/stackcollapse-perf.pl out.perf > out.folded
FlameGraph/flamegraph.pl out.folded > kernel.svg

12.检查内存泄漏之slub_debug
slub_debug可以检测内存越界(out-of-bounds)和访问已经释放的内存(use-after-free)等问题,仅仅针对从slub分配器分配的内存,不可以检测从栈中或者数据区分配内存。

slub内存分配图:

red_left_pad object_size red_zone FP alloc/free track padding
1).打开功能
CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y

2).slabinfo工具
#源码位于tools/vm目录,当系统开机之后,就可以运行slaninfo –v命令触发SLUB allocator检测所有的object,并将log信息输出到syslog
aarch64-linux-gnu-gcc -o slabinfo slabinfo.c

3).检测的时机
第一种:slabinfo –v命令触发
第二种:执行kfree后

13.检查内存泄漏之kmemleak
kmemleak的使用

1).内核配置
①.在uboot的bootarg中加入"kmemleak=on"
②.CONFIG_DEBUG_KMEMLEAK 在Kernel hacking中被使能,一个内核线程每10分钟(默认值)扫描内存,并打印发现新的未引用的对象的数量
Kernel hacking --->
Memory Debugging --->
[*] Kernel memory leak detector

2).检测时机
第一种:等待系统的memleak检测线程调度(最长10min)
第二种:执行如下的命令强制系统去检测内存泄露:
#注:当你强制触发检测的时候,需要留意的是第一次触发检测的时候,会先sleep出去1min,以保证系统完全的bring up。
echo scan > /sys/kernel/debug/kmemleak

3).使用方法
# 挂载debugfs文件系统,非必须
mount -t debugfs nodev /sys/kernel/debug/

# 开启内核自动检测线程
echo scan > /sys/kernel/debug/kmemleak

# 查看内存泄露
cat /sys/kernel/debug/kmemleak

# 清除内核检测报告
echo clear > /sys/kernel/debug/kmemleak

# 常用参数
off 禁用kmemleak(不可逆)
stack=on 启用任务堆栈扫描(default)
stack=off 禁用任务堆栈扫描
scan=on 启动自动记忆扫描线程(default)
scan=off 停止自动记忆扫描线程
scan=<secs> 设置n秒内自动记忆扫描,默认600s
scan 开启内核扫描
clear 清除内存泄露报告
dump=<addr> 转存信息对象在<addr>

4).使用范围
1.只能检查kmalloc、vmalloc、memblock方式分配的内存泄露问题,而alloc_pages/__get_free_pages/dma_alloc_coherent并不能检查;
2.如果把虚拟地址转换成物理地址保存,kmemleak会报内存泄露.

5).手动修改memleak的检测线程间隔时间
mm/Kmemleak.c
#define SECS_SCAN_WAIT 600 /* subsequent auto scanning delay */

6).当发现内存泄露后,可以将泄露的内存释放还给系统
echo scan=off > /sys/kernel/debug/kmemleak
echo off > /sys/kernel/debug/kmemleak

14.检查内存泄漏之kasan
KASAN实现原理
Kasan-Linux 内核的内存检测工具

1).内核配置
CONFIG_SLUB_DEBUG=y
CONFIG_KASAN=y

2).使用
KASAN检测到bug后会自动输出log。


15.检查内存泄漏之valgrind(用户空间)
valgrind详解,安装,使用,示例
valgrind简介与使用

1).安装
sudo apt install valgrind

2).使用
用法: valgrind [options]prog-and-args [options]:常用选项,适用于所有Valgrind工具:
1. -tool=<name>最常用的选项。运行valgrind中名为toolname的工具。默认memcheck。
2. h –help显示帮助信息。
3. -version显示valgrind内核的版本,每个工具都有各自的版本。
4. q –quiet安静地运行,只打印错误信息。
5. v –verbose更详细的信息,增加错误数统计。
6. -trace-children=no|yes跟踪子线程? [no]
7. -track-fds=no|yes跟踪打开的文件描述?[no]
8. -time-stamp=no|yes增加时间戳到LOG信息? [no]
9. -log-fd=<number>输出LOG到描述符文件 [2=stderr]
10. -log-file=<file>将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID
11. -log-file-exactly=<file>输出LOG信息到 file
12. -log-file-qualifier=<VAR>取得环境变量的值来做为输出信息的文件名。 [none]
13. -log-socket=ipaddr:port输出LOG到socket ,ipaddr:port

LOG信息输出:
1. -xml=yes将信息以xml格式输出,只有memcheck可用
2. -num-callers=<number> show <number> callersin stack traces [12]
3. -error-limit=no|yes如果太多错误,则停止显示新错误? [yes]
4. -error-exitcode=<number>如果发现错误则返回错误代码 [0=disable]
5. -db-attach=no|yes当出现错误,valgrind会自动启动调试器gdb。[no]
6. -db-command=<command>启动调试器的命令行选项[gdb -nw %f %p]

适用于Memcheck工具的相关选项:
1. -leak-check=no|summary|full要求对leak给出详细信息? [summary]
2. -leak-resolution=low|med|high how much bt merging in leakcheck [low]
-show-reachable=no|yesshow reachable blocks in leak check? [no]

3).示例
#内存越界和泄漏
gcc -g valgrind_test.c -o valgrind_test
valgrind --leak-check=yes ./valgrind_test

#线程死锁
gcc -g valgrind_test_helgrand.c -o valgrind_test_helgrand -lpthread
valgrind --tool=helgrind ./valgrind_test_helgrand

16.kdump的使用
Linux内核调试之kdump
使用Qemu虚拟ARM64平台演示kdump崩溃转存

1).kdump流程
当系统崩溃时,kdump 使用 kexec 启动到第二个内核。第二个内核通常叫做捕获内核,以很小内存启动以捕获转储镜像。第一个内核启动时会保留一段内存给kdump用。
2).kdump的配置
在kernel command line中加入如下参数:crashkernel=size[@offset]
3).安装 kdump-tools 相关的软件包
sudo apt install kdump-tools crash kexec-tools makedumpfile systemd -y

4).qemu使用kdump
(4.1).方法一(注意内核版本不能太新,亲测linux5.2.0可以用):
①.内核配置
CONFIG_KEXEC=y
CONFIG_SYSFS=y
CONFIG_DEBUG_INFO=y
CONFIG_CRASH_DUMP=y
CONFIG_PROC_VMCORE=y

②.qemu启动脚本配置
-append "noinintrd sched_debug root=/dev/vda rootfstype=ext4 rw loglevel=8 crashkernel=256M"

③.执行kexec命令,使kdump进入ready状态
sudo kexec -p --command-line="noinintrd sched_debug root=/dev/vda rw nr_cpus=2 nr_cpus=1" ./Image

④.检测配置是否成功
系统启动后查看预留内存是否成功
sudo cat /proc/iomem

开启kdump-tools服务
sudo systemctl start kdump-tools.service
sudo systemctl status kdump-tools

查看是否启动
sudo kdump-config show

⑤.进行触发崩溃的操作
su
echo c > /proc/sysrq-trigger

⑥.使用crash分析内核崩溃转储文件
在内核奔溃后,如果部署了kdump, 会在/var/crash目录中找到vmcore转储文件,vmcore文件可以配合crash工具进行分析:
crash [vmcore] [vmlinux]

(4.2).方法二:(实测不成功,待改进)
①.首先先将qemu的panic重启关闭,防止coredump的时候发生了reboot
echo 0 > /proc/sys/kernel/panic

②.触发kernel panic
echo c > /proc/sysrq-trigger

③.使得qemu进入monitor模式
ctrl+a,c

④.进入monitor模式后,进行coredump
dump-guest-memory -z xxx-vmcore

⑤.使用crash分析内核崩溃转储文件
在内核奔溃后,如果部署了kdump, 会在/var/crash目录中找到vmcore转储文件,vmcore文件可以配合crash工具进行分析:
crash xxx-vmcore vmlinux

5).x86_64使用kdump
————————————————
版权声明:本文为CSDN博主「zhang-ge」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40837318/article/details/117899001

本公司成本价甩卖工控主板,欢迎大家选购:
PCIE总线转八串口卡,PCIE总线转IO卡,瑞芯微3568板卡,寒武纪CE3226摄像头板卡,龙芯3A4000工控板卡,龙芯3A5000工控板卡,海光3250工控板卡,飞腾FT-2000/4板卡

联系方式:
电话、微信:15918785568 罗生
Email:13279654@qq.com
公众号:开发之美

 

posted @ 2022-05-18 11:47  DMCF  阅读(557)  评论(0编辑  收藏  举报