Debug Hacks

启动内核转储(core dump)

ulimit -c     // 查看内核转储功能是否有效
ulimit -c unlimited    // 不限制大小
ulimit -c 1073741824    // 设置1G大小

对于转储文件 core*可以用GDB进行调试

未设置时,转出文件都在运行目录下,可以设置到固定位置

# cat /etc/systol.conf
kernel.core_pattern = /var/core/%t-%e-%p-%c.core
kernel.core_uses_pid = 0
# sysctl -p

将会把文件放到 /var/core/下,文件名为%t-%e-%p-%c.core。含义如表2-1所示。

对于kernel.core_uses_pid设置成1,那么文件名末尾就会加 .PID。

 

 自动压缩内核转储文件

// 可以设置进行压缩
kernel.core_pattern = |/usr/local/sbin/core_helper %t %e %p %c  

// 看看core_helper到底是啥
# cat /usr/local/sbin/core_helper 

#!/bin/bash
exec gzip - > /var/core/$1-$2-$3-$4.core.gz // 直接压缩了

有时进程多,全都转储会有一定压力,可以通过掩码屏蔽一些类型。如表2-2

# cat /proc/<PID>/coredump_filter
00000003                // 转储所有匿名内存区段,若改为1则跳过共享内存区段

GDB基本使用方法 一

gdb 文件名    // 启动调试该文件
b 断点(函数名、行号、文件名:行号、文件名:函数名、+偏移量、-偏移量、*地址) // 设置断点,直接b则在下一行设置断点
info break      // 查看设置断点处
run -a        // 开始运行,运行到断点后暂停。
backtrace/bt        // 显示栈帧,
p 变量        // 显示值
p/格式  变量      // 如表2-3所示,按照格式显示值
info reg        // 显示寄存器
x/格式 地址      // 可以显示地址下的数据,若格式为i则显示机器语言的语句
x/NFU ADDR     // ADDR为显示的地址, N为连续显示的个数,F为2-3的格式,U为2-4的单位
next(n)        // 执行源代码的一行,nexti则会执行汇编语言的一行
step(p)        // 对于函数调用,可以进入到函数内部,stepi则会执行汇编语言的一行
continue(c) 次数    // 继续运行程序,遇到断点暂停
watch <表达式>      // 监视点,表达式则是常量或者变量。表达式变化时暂停
awatch <表达式>     //  表达式被访问、改变时暂停
rwatch <表达式>      // 表达式被访问时暂停
delete <编号>       //删除断点或监视点
set variable <变量>=<表达式>  // 修改变量的值
generate-core-file      // 生成内核转储文件

 

 GDB基本使用方法二

attach到进程:用于调试守护进程等已经启动的进程。

attach pid  // attach到pid进程上(在gdb里面进行attach)
ps aux | grep sleep   // 可以查看sleep命令
info proc  // 可以用来查看进程信息
break 断点 if 条件 // 只有条件为真,断点才暂停
clear    // 删除断点
disable   // 禁用断点
enable    // 开启disable禁用的断点
comand 断点编号 // 断点命令 在遇到该断点编号后自动执行,可以用来显示一些变量
命令...
end

 

 

 

 

 

 GDB基本使用方法三

 

p argc // 可以显示argc的值,如表2-6可以显示历史输出的值。
set $i = 0 // 可以随意定义变量,并设置值
show history // 可以显示历史命令,默认保存在./.gdb_history文件
define 命令名  // 可以自定义命令
 命令...
end
命令名      // 通过命令名可以直接运行
document 命令名  // 可以为定义的命令添加说明
 说明...
end
help 命令名  // 可以查看命令名的说明

 

 

 

 字节序:低位在低地址就是小端(Little Endian),否则是大端。

 ESP:栈指针

CS:代码段。DS:数据段。SS:堆栈段。ES/FS/GS:数据段。

EAX:操作数,结果。EBX:DS段的指针。ECX:计数器。EDX:输入输出指针。

ESI:DS下的数据指针,作为源。EDI:ES下的数据指针,作为目的。ESP:栈指针(栈顶)。EBP:栈上数据的指针(栈底)

地址:平坦模式,如图2-9,内存是线性空间。LInux采用该方式。

   分段模式,如图2-10,先寻找段,再找段内偏移。

------------------------------TODO 函数调用前后栈的状态 P72-------------------------

 使用GDB操作栈帧

bt        // 查看栈
frame     // 选择栈帧
up/down   // 选择上一层或者下一层栈帧
i frame 1     // 查看详细的栈帧信息
i proc mapping // 查看attach的进程的内存映射信息,包含栈的空间
                        // 对于内核转储的文件,可以info /proc/<PID>/maps主动增加该文件
b func      // 调试函数

---------------------------------- TODO 函数调试 P79-------------------------

 

crash可以进行反汇编

crash /boot/vmlinux-2.6.19  // 首先进入crash(常用于分析vmcore)
crash> dis 文件名    // 反汇编该文件
crash> bt        // 打印函数栈
crash> ps       // 查看崩溃时,运行的所有进程

----------------------------------TODO oops 105~108--------------------

netconsole:可以让printk信息通过网络(UDP)发送到远端主机。

      如下图,发送端为10.1.1.100,接收端为10.1.0.200

文件 /etc/sysconfig/syslog中

将 SYSLOGD_OPTIONS="-m 0" 修改为 "-m 0 -r"  可用于接收主机日志。

 

修改/etc/sysctl.conf

将 kernel.sysrq = 0修改为 1。并执行sysctl -p可以用于启动SysRq

 

 SysRq:用于调试内核。SysRq使用了中断,即使无法登陆,按键无响应也可以使用,但内核禁止中断时无法使用。

make menuconfig

sysctl -w kernel.sysrq=1  // 1代表所有命令键都可使用,如表3-1为其他配置。48则是允许Sync和重新挂载。

SysRq : show memory // 显示内存占用

SysRq : show State      // 显示状态

 

 

disdump:为发生kernel panic 获取转出文件功能。

/etc/sysconfig/diskdump加入:

DEVICE = /dev/sda3       // 需要准备一个分区,分区要大于内存大小。

service disdump initialformat   // 准备启动diskdump
chkconfig diskdump on
service diskdump start
cat /proc/diskdump    // 查看diskdump是否生效

 

 kdump:获取内核崩溃转储

makedumpfile:缩小转储文件

 

crash常用命令

// 启动crash

crash vmlinux vmcore   // vmlinux未压缩的镜像,vmcore为转储文件。

crash vmlinux  // 查看当前实时系统。
// crash内部命令

set 命令     // 指定进程,显示其上下文。不指定则为当前运行进程。
ascii  命令    // 将16进制转为字符串。
h 命令      // 显示输入过的命令历史
bt 命令      // 输出backtrace
dev命令      // 显示字符设备列表
dis 命令      // 输出反汇编
files 命令      // 显示进程打开的文件
irq 命令       // 显示打开的中断
kmem 命令     // 显示内存信息
list 命令      // 按顺序显示地址
mod 命令      // 加载模块信息,符号信息,调试信息。
net 命令      // 列出网络设备
ps 命令      // 显示进程信息
rd 命令      // 读取内存地址
runq 命令     // 显示进程调度的运行队列
.....

------------------------- TODO IPMI NMI获取崩溃转储---------------------------

 

内核独有汇编指令:

ud2   // CPU引发的异常,内核执行ud2指令,使CPU接收该异常。
cli      // 禁止中断
sli      // 允许中断

 

current 宏:获取当前运行进程的task_struct

task_struct和 thread_finfo  :管理进程task_struct 的结构位于SLAB中。

             task_struct和thread_info都有对方指针。如图3-6.

 

 

发生SIGSEGV:应用程序非法访问了内存。

        NULL指针访问。指针被破坏,导致非法地址访问。栈溢出,访问超出了分配的空间。

数据非法访问导致内存破坏:缓冲区溢出后,bt不会显示符号名

             运行地址的改变有:指定地址并调用。指定了内存区域,保存了跳转地址、ret命令返回时的栈指针被破坏。

             由于第一类使用的地址都被保存在只读空间,所以通常是后两类。 

             确定破坏跳转地址值的位置(栈被破坏),如:对于复制字符串的目的地址空间过小,缓冲区溢出。原本是保存返回地址的,保存成了错误地址。如图4-4所示。

 

监视点:对于越界的地址可以用监视点,看什么时候访问了该错误的地址。

malloc和free:双重释放、非法区域释放可能导致问题。

应用程序停止响应(死锁):对于停止响应,如果用ps看到状态是R,说明还在运行,可能是失控。对于S,说明睡眠,可能是死锁。

             attach到该进程。i thr(可以看有哪些线程)。thr 2(跳转到2线程)

应用程序停止响应(死循环):通常有大量相同信息,可以通过top命令、vmstat命令查看cpu利用率。

 

kernel panic(空指针引用):首先可以通过crash下的disas进行反汇编,并加载符号需要的模块。

kernel panic(链表被破坏):在删除时,prev和next有了错误数据。

 -------------------------------------------------TODO 遇到的各种BUG---------------------------

strace:跟踪进程使用的系统调用,并显示内容。

strace 可执行文件:可以看到,下图2处,由于没有打开文件权限,导致失败。

strace -i 可执行文件:可以看到哪里执行了系统调用,有利于GDB调试去设置断点。

strace -p ·pidof st2· :(pidof st2就获得了进程的pid),attach到正在运行的pid进程上。

strace -o output.log command :可以将内容输出到文件

 

 

objdump:可以处理带有调试信息的二进制文件。

 

valgrind:用于检测内存的非法使用。

     如图所示代码,malloc 20的空间没有释放,HEAP SUMMARY内部显示20字节,分配1次,释放0次。

    除此之外,还能检测:非法内存地址的访问,读取未初始化区域,访问已释放区域,内存双重释放,非法栈操作

    valgrind对于检测非法使用内存很有效果,但不是万能的。

 

 

 

valigrind:内存泄漏会根据时间增长,内存使用(VIRT和RES)逐渐增长。VIRT和RES的区别,VIRT包含了被交换(swap)的空间,而RES不包括。

 

kprobes:动态插入侦测器获取内核内部信息。

     无需重启内核,就可以执行printk在内的各种调试工作。

jprobes:将侦测器插入到内核函数的开头,获取内核内部信息。

KAHO:获取被编译器优化掉的变量的值

 systemtap:调试运行中的内核。

/proc/<PID>/mem:读取进程的内存内容

OOM(Out of Memory) Killer:内存和交换区用尽时的强制结束信号。

 

 

参考:Debug Hacks 中文版

posted @ 2020-04-27 10:40  注册以后还能改吧  阅读(262)  评论(0编辑  收藏  举报