linux内核调试和性能优化

1.更改printk打印级别

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

# (1) 控制台日志级别:优先级高于该值的消息将被打印至控制台。
# (2) 缺省的消息日志级别:将用该值来打印没有优先级的消息。
# (3) 最低的控制台日志级别:控制台日志级别可能被设置的最小值。
# (4) 缺省的控制台:控制台日志级别的缺省值。
  • 1.2.修改printk的打印级别
echo "新的打印级别  4    1    7" > /proc/sys/kernel/printk

2.linux内核打开pr_debug、dev_dbg调试信息

  • 2.1、独立于内核的驱动模块,在当前代码的makefile中添加:
EXTRA_CFLAGS += -DDEBUG
  • 2.2、在编译内核时,打开所有的调试信息
make KCFLAGS=-DDEBUG
  • 2.3、打开指定内核模块的调试信息
# 1、要调试的模块的Makefile中加入
ccflags-y += -DDEBUG

# 2、内核启动后,如果没有显示调试信息,改变控制台debug消息显示级别,就可以打印debug信息
echo "8 4 1 7" > /proc/sys/kernel/printk

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_padobject_sizered_zoneFPalloc/free trackpadding
  • 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崩溃转存
如何在Ubuntu18.04下安装和配置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

    ubuntu(x86平台)kdump环境搭建
    ubuntu 20.04 启用kdump服务及下载vmlinux
    在Ubuntu系统上安装Kdump来处理系统崩溃

  • (5.1).x86_64下基于ubuntu20.04配置kdump,先安装kdump工具:

    sudo apt install crash
    sudo apt install kdump-tools
    sudo apt install kexec-tools
    sudo apt install makedumpfile
    

    上述操作可以使用一条安装替代:

    sudo apt-get install linux-crashdump
    
  • (5.2).参数配置检查
    ① 检查grub文件/boot/grub/grub.cfg,会发生引导内核命令后多了一个参数(一共2处):crashkernel=384M-:128M

    inux  /boot/vmlinuz-3.13.0-24-generic root=UUID=273c313e-f524-4b65-b9e3-9412d836485b ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet crashkernel=384M-:128M
    

    ② 修改kdump配置文件(/etc/default/kdump-tools)

    修改其中的USE_KDUMP=0为USE_KDUMP=1
    

    ③ 查看kdump相关服务是否开启
    sudo service --status-all
    正常情况如下:

    ……
     [ - ]  kdump-tools
     [ + ]  kerneloops
     [ ? ]  kexec
     [ ? ]  kexec-load
    ……
    

    ④ 启动kdump

    sudo /etc/init.d/kdump-tools start
    
  • (5.3).功能检查
    完成上一步后,先关机重启,使参数生效。
    ① 查看crashkernel内存分配的地址空间

    ~$ cat /proc/iomem | grep -i crash
    2d000000-34ffffff : Crash kernel
    

    ② 查看crashkernel内存分配的大小

    ~$ cat /sys/kernel/kexec_crash_size
    134217728
    

    ③ 查看crashkernel大小

    ~$ sudo dmesg | grep crash
    [    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-3.13.0-24-generic root=UUID=273c313e-f524-4b65-b9e3-9412d836485b ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet crashkernel=384M-:128M
    [    0.000000] Reserving 128MB of memory at 720MB for crashkernel (System RAM: 3567MB)
    [    0.000000] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-3.13.0-24-generic root=UUID=273c313e-f524-4b65-b9e3-9412d836485b ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet crashkernel=384M-:128M
    

    ④ 查看kdump相关服务的状态

    ~$ sudo kdump-config show
    DUMP_MODE:        kdump
    USE_KDUMP:        1
    KDUMP_SYSCTL:     kernel.panic_on_oops=1
    KDUMP_COREDIR:    /var/crash
    crashkernel addr: 0x2d000000
    current state:    ready to kdump
    
    kexec command:
      /sbin/kexec -p --command-line="BOOT_IMAGE=/boot/vmlinuz-3.13.0-24-generic root=UUID=273c313e-f524-4b65-b9e3-9412d836485b ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet irqpoll maxcpus=1 nousb" --initrd=/boot/initrd.img-3.13.0-24-generic /boot/vmlinuz-3.13.0-24-generic
    
  • (5.4).如果上述操作都正常,参数都有相应的值的话。说明kdump配置成功。
    测试生成vmcore,手动触发系统崩溃:

    su - root
    echo c > /proc/sysrq-trigger
    
  • (5.5).下载vmlinux

    可以从这里Index of /pool/main/l/linux下载对应版本的,下载完成后dpkg -i来安装。
    安装完成后,可以在/usr/lib/debug/目录下找到,vmlinux位置在/usr/lib/debug/boot/vmlinux-xxxxxxxxx。
    crash内核:

    crash /usr/lib/debug/boot/vmlinux-xxxxx-generic /var/crash/xxxxx/dump.xxxxx
    

17.后续

  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值