这些 Linux 程序调试工具,可以看到程序内部的很多小秘密
来源:https://urlify.cn/VF7V7v
01 总览
编译阶段
nm 获取二进制文件包含的符号信息
strings 获取二进制文件包含的字符串常量
strip 去除二进制文件包含的符号
readelf 显示目标文件详细信息
objdump 尽可能反汇编出源代码
addr2line 根据地址查找代码行
运行阶段
gdb 强大的调试工具
ldd 显示程序需要使用的动态库和实际使用的动态库
strace 跟踪程序当前的系统调用
ltrace 跟踪程序当前的库函数
time 查看程序执行时间、用户态时间、内核态时间
gprof 显示用户态各函数执行时间
valgrind 检查内存错误
mtrace 检查内存错误
其他
proc文件系统
系统日志
02 编译阶段
nm(获取二进制文件里面包含的符号)
-C 把C++函数签名转为可读形式 -A 列出符号名的时候同时显示来自于哪个文件。 -a 列出所有符号(这将会把调试符号也列出来。默认状态下调试符号不会被列出) -l 列出符号在源代码中对应的行号(指定这个参数后,nm将利用调试信息找出文件名以及符号的行号。对于一个已定义符号,将会找出这个符号定义的行号,对于未定义符号,显示为空) -n 根据符号的地址来排序(默认是按符号名称的字母顺序排序的) -u 只列出未定义符号
strings(获取二进制文件里面的字符串常量)
eg:strings | grep '^.\{16\}$'
查找-a 不只是扫描目标文件初始化和装载段, 而是扫描整个文件。 -f 在显示字符串之前先显示文件名。 -n min-len打印至少min-len字符长的字符串.默认的是4。
#strings /lib/tls/libc.so.6 | grep GLIBC
GLIBC_2.0
GLIBC_2.1
GLIBC_2.1.1
……
strip(去除二进制文件里面包含的符号)
readelf(显示目标文件详细信息)
readelf
-a 显示所有ELF文件的信息 -h 显示ELF文件的文件头 -l 显示程序头(program-header)和程序段(segment)和段下面的节 -S 显示较为详细的节信息(section) -s 显示符号信息, -n 显示标识信息(如果有) -r 显示重定位信息(如果有) -u 显示展开函数信息(如果有) -d 显示动态节信息,一般是动态库的信息
objdump –S
addr2line(根据地址查找代码行)
eg:Mar 31 11:34:28 l02 kernel: failing address: 0
eg:addr2line –e exe addr
该可执行程序用-g编译,使之带调试信息。 如果crash在一个so里面,那addr2line不能直接给出代码行。
-a 在显示函数名或文件行号前显示地址 -b 指定二进制文件格式 -C 解析C++符号为用户级的名称,可指定解析样式 -e 指定二进制文件 -f 同时显示函数名称 -s 仅显示文件的基本名,而不是完整路径 -i 展开内联函数 -j 读取相对于指定节的偏移而不是绝对地址 -p 每个位置都在一行显示
03 运行阶段
ldd(显示程序需要使用的动态库和实际使用的动态库)
# ldd /bin/ls
linux-gate.so.1 => (0xbfffe000)
librt.so.1 => /lib/librt.so.1 (0xb7f0a000)
libacl.so.1 => /lib/libacl.so.1 (0xb7f04000)
libc.so.6 => /lib/libc.so.6 (0xb7dc3000)
libpthread.so.0 => /lib/libpthread.so.0 (0xb7dab000)
/lib/ld-linux.so.2 (0xb7f1d000)
libattr.so.1 => /lib/libattr.so.1 (0xb7da6000)
strace(跟踪当前系统调用)
-p
attach到一个进程-c 最后统计各个system call的调用情况 -T 打印system call的调用时间 -t/-tt/-ttt 时间格式 -f/-F 跟踪由fork/vfork调用所产生的子进程 -o
,将strace的输出定向到file中。
strace -f -o ~/
-e expr 指定一个表达式,用来控制如何跟踪,格式如下: -e open等价于-e trace=open,表示只跟踪open调用
strace –e open ./prg
来看程序使用了哪些配置文件或日志文件,很方便。-e trace=
只跟踪指定的系统调用
-e trace=open,close,rean,write
表示只跟踪这四个系统调用.-e trace=file只跟踪有关文件操作的系统调用 -e trace=process只跟踪有关进程控制的系统调用 -e trace=network跟踪与网络有关的所有系统调用 -e strace=signal 跟踪所有与系统信号有关的系统调用 -e trace=ipc跟踪所有与进程通讯有关的系统调用
ltrace(跟踪当前库函数)
time(查看程序执行时间、用户态时间、内核态时间)
# time ps aux | grep 'hi'
1020 21804 0.0 0.0 1888 664 pts/6 S+ 17:46 0:00 grep hi
real 0m0.009s
user 0m0.000s
sys 0m0.004s
gprof(显示用户态各函数执行时间)
在编译和链接程序的时候(使用 -pg 编译和链接选项),gcc在你应用程序的每个函数中都加入了一个名为mcount(or“_mcount”, or“__mcount”)的函数,也就是说-pg编译的应用程序里的每一个函数都会调用mcount, 而mcount会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址。这张调用图也保存了所有与函数相关的调用时间,调用次数等等的所有信息。
gcc -pg -o exec exec.c
gcc -lc_p -gp -o exec exec.c
gprof exec gmon.out > profile.txt
gdb core exec
(gdb查看core文件) 准备生成core:ulimit -c unlimited
,设置core文件不限制大小。(相反,ulimit -c 0
,可以阻止生成core文件)/proc/sys/kernel/core_uses_pid
可以控制产生的core文件的文件名中是否添加pid作为扩展,1为扩展,否则为0。proc/sys/kernel/core_pattern
可以设置格式化的core文件保存位置或文件名,比如原来文件内容是core,可以修改为:echo "/data/core/core-%e-%p-%t" > core_pattern
%p - insert pid into filename 添加pid %u - insert current uid into filename 添加当前uid %g - insert current gid into filename 添加当前gid %s - insert signal that caused the coredump into the filename 添加导致产生core的信号 %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间 %h - insert hostname where the coredump happened into filename 添加主机名 %e - insert coredumping executable name into filename 添加命令名
gdb
opprofile (查看CPU耗在哪)
opcontrol --no-vmlinux : 指示oprofile启动检测后,不记录内核模块、内核代码相关统计数据 opcontrol --init : 加载oprofile模块、oprofile驱动程序
opcontrol --start : 指示oprofile启动检测 opcontrol --dump : 指示将oprofile检测到的数据写入文件 opcontrol --reset : 清空之前检测的数据记录 opcontrol -h : 关闭oprofile进程
opreport : 以镜像(image)的角度显示检测结果,进程、动态库、内核模块属于镜像范畴 opreport -l : 以函数的角度显示检测结果 opreport -l test : 以函数的角度,针对test进程显示检测结果 opannotate -s test : 以代码的角度,针对test进程显示检测结果 opannotate -s /lib64/libc-2.4.so : 以代码的角度,针对libc-2.4.so库显示检测结果
linux # opreport
CPU: Core 2, speed 2128.07 MHz (estimated) Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 100000CPU_CLK_UNHAL
T.........| samples | %| ------------------------ 31645719 87.6453 no-vmlinux 4361113 10.3592 libend.so 7683 0.1367 libpython2.4.so.1.0 7046 0.1253 op_test
valgrind(检查内存错误)
#include
void f(void)
{
int* x = malloc(10 * sizeof(int));
x[10] = 0; // problem 1: heap block overrun
} // problem 2: memory leak -- x not freed
int main(void)
{
f();
return 0;
}
gcc -Wall -g -o test test.c
valgrind --tool=memcheck --leak-check=full ./test
–fstack-protector-all
和 -D_FORTIFY_SOURCE=2
来检测。Stack-protector-all
会在每个函数里加上堆栈保护的代码,并在堆栈上留上指纹。(记录下,没用过)-fmudflap –lmudflap
来检测。(记录下,没用过)mtrace(检查内存错误)
#include
#include
int main(void)
{
int *p;
int i;
#ifdef DEBUG
setenv("MALLOC_TRACE", "./memleak.log", 1);
mtrace();
#endif
p=(int *)malloc(1000);
return 0;
}
gcc -g -DDEBUG -o test1 test1.c
./test1
,在目录里会发现./memleak.log
。mtrace memleak.log
查看信息。# mtrace test1 memleak.log
- 0x0804a008 Free 3 was never alloc'd 0xb7e31cbe
- 0x0804a100 Free 4 was never alloc'd 0xb7ec3e3f
- 0x0804a120 Free 5 was never alloc'd 0xb7ec3e47
Memory not freed:
-----------------
Address Size Caller
0x0804a4a8 0x3e8 at /home/illidanliu/test1.c:14
04 其他
proc文件系统
cmdline 内核命令行 cpuinfo 关于Cpu信息 devices 可以用到的设备(块设备/字符设备) filesystems 支持的文件系统 interrupts 中断的使用 ioports I/O端口的使用 kcore 内核核心映像 kmsg 内核消息 meminfo 内存信息 mounts 加载的文件系统 stat 全面统计状态表 swaps 对换空间的利用情况 version 内核版本 uptime 系统正常运行时间 net 网络信息 sys 可写,可以通过它来访问或修改内核的参数
proc//
目录结构(部分):cmdline 命令行参数 environ 环境变量值 fd 一个包含所有文件描述符的目录 mem 进程的内存被利用情况 stat 进程状态 status Process status in human readable form cwd 当前工作目录的链接 exe Link to the executable of this process maps 内存映像 statm 进程内存状态信息 root 链接此进程的root目录
系统日志
/var/log/
下的日志文件:/var/log/messages 整体系统信息,其中也包含系统启动期间的日志。此外,mail、cron、daemon、kern和auth等内容也记录在var/log/messages日志中。 /var/log/auth.log 系统授权信息,包括用户登录和使用的权限机制等。 /var/log/boot.log 系统启动时的日志。 /var/log/daemon.log 各种系统后台守护进程日志信息。 /var/log/lastlog 记录所有用户的最近信息。这不是一个ASCII文件,因此需要用lastlog命令查看内容。 /var/log/user.log 记录所有等级用户信息的日志。 /var/log/cron 每当cron进程开始一个工作时,就会将相关信息记录在这个文件中。 /var/log/wtmp或utmp 登录信息。 /var/log/faillog 用户登录失败信息。此外,错误登录命令也会记录在本文件中。
有收获,点个在看
评论