调试篇-综合运用各种工具分析程序状态

目录

gdb 调试利器

更强大的工具cgdb

分析线程栈

lsof命令查看进程打开的资源

IPC资源管理

pstack跟踪栈空间

 strace分析系统调用

nm列出目标文件的符号清单

objdump

readelf

size查看程序内存占用

file文件类型查询

strings查询二进制文件中的文本信息

fuser显示文件使用者

ldd 查看程序依赖库


gdb 调试利器

运行

  • run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令。
  • continue (简写c ):继续执行,到下一个断点处(或运行结束)
  • next:(简写 n),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同 step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。
  • step (简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的
  • until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
  • until+行号: 运行至某行,不仅仅用来跳出循环
  • finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
  • call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
  • quit:简记为 q ,退出gdb

设置断点

  • break n (简写b n):在第n行处设置断点

    (可以带上代码路径和代码名称: b OAGUPDATE.cpp:578)

  • b fn1 if a>b:条件断点设置
  • break func(break缩写为b):在函数func()的入口处设置断点,如:break cb_button
  • delete 断点号n:删除第n个断点
  • disable 断点号n:暂停第n个断点
  • enable 断点号n:开启第n个断点
  • clear 行号n:清除第n行的断点
  • info b (info breakpoints) :显示当前程序的断点设置情况
  • delete breakpoints:清除所有断点:

查看源代码

  • list :简记为 l ,其作用就是列出程序的源代码,默认每次显示10行。
  • list 行号:将显示当前文件以“行号”为中心的前后10行代码,如:list 12
  • list 函数名:将显示“函数名”所在函数的源代码,如:list main
  • list :不带参数,将接着上一次 list 命令的,输出下边的内容。

打印表达式

  • print 表达式:简记为 p ,其中“表达式”可以是任何当前正在被测试程序的有效表达式,比如当前正在调试C语言的程序,那么“表达式”可以是任何C语言的有效表达式,包括数字,变量甚至是函数调用。

  • print a:将显示整数 a 的值
  • print ++a:将把 a 中的值加1,并显示出来
  • print name:将显示字符串 name 的值
  • print gdb_test(22):将以整数22作为参数调用 gdb_test() 函数
  • print gdb_test(a):将以变量 a 作为参数调用 gdb_test() 函数
  • display 表达式:在单步运行时将非常有用,使用display命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。如: display a
  • watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如: watch a
  • whatis :查询变量或函数
  • info function: 查询函数
  • 扩展info locals: 显示当前堆栈页的所有变量

查询运行信息

  • where/bt :当前运行的堆栈列表;

  • bt backtrace 显示当前调用堆栈
  • up/down 改变堆栈显示的深度
  • set args 参数:指定运行时的参数
  • show args:查看设置好的参数
  • info program: 来查看程序的是否在运行,进程号,被暂停的原因。

分割窗口

  • layout:用于分割窗口,可以一边查看代码,一边测试:
  • layout src:显示源代码窗口
  • layout asm:显示反汇编窗口
  • layout regs:显示源代码/反汇编和CPU寄存器窗口
  • layout split:显示源代码和反汇编窗口
  • Ctrl + L:刷新窗口

更强大的工具cgdb

cgdb可以看作gdb的界面增强版,用来替代gdb的 gdb -tui。cgdb主要功能是在调试时进行代码的同步显示,这无疑增加了调试的方便性,提高了调试效率。界面类似vi,符合unix/linux下开发人员习惯;如果熟悉gdb和vi,几乎可以立即使用cgdb。

分析线程栈

使用命令pmap,来输出进程内存的状况,可以用来分析线程堆栈;

$pmap PID
eg:
gm@Inspiron:~/test$ pgrep -l bash
28740
gm@Inspiron:~/test$ pmap  28740
28740:   bash
0000000000400000    976K r-x-- bash
00000000006f3000      4K r---- bash
00000000006f4000     36K rw--- bash
00000000006fd000     24K rw---   [ anon ]
0000000000fe7000   1988K rw---   [ anon ]
00007fc34e036000     44K r-x-- libnss_files-2.23.so
00007fc34e041000   2044K ----- libnss_files-2.23.so
00007fc34e240000      4K r---- libnss_files-2.23.so
00007fc34e241000      4K rw--- libnss_files-2.23.so

lsof命令查看进程打开的资源

lsof(list open files)是一个列出当前系统打开文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等; 在查询网络端口时,经常会用到这个工具。lsof打开的文件可以是:

  1. 普通文件
  2. 目录
  3. 网络文件系统的文件
  4. 字符或设备文件
  5. (函数)共享库
  6. 管道,命名管道
  7. 符号链接
  8. 网络文件(例如:NFS file、网络socket,unix域名socket)
  9. 还有其它类型的文件,等等

查询7902端口现在运行什么程序:

#分为两步
#第一步,查询使用该端口的进程的PID;
    $lsof -i:7902
    COMMAND   PID   USER   FD   TYPE    DEVICE SIZE NODE NAME
    WSL     30294 tuapp    4u  IPv4 447684086       TCP 10.6.50.37:tnos-dp (LISTEN)

#查到30294
#使用ps工具查询进程详情:
$ps -fe | grep 30294
tdev5  30294 26160  0 Sep10 ?        01:10:50 tdesl -k 43476
root     22781 22698  0 00:54 pts/20   00:00:00 grep 11554

IPC资源管理

查看系统使用的IPC资源:

gm@Inspiron:~/test$ ipcs 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 3801088    gm         600        67108864   2          dest             
0x00000000 5308430    gm         600        393216     2          dest         
0x00000000 7143439    gm         600        2097152    3          dest         
0x00000000 11370512   gm         600        524288     2          dest         
0x00000000 13041681   gm         600        4194304    2          dest         

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
$ipcs -m  //查看系统使用的IPC共享内存资源
$ipcs -q  //查看系统使用的IPC队列资源
$ipcs -s  //查看系统使用的IPC信号量资源

用示例:查看IPC资源被谁占用,有个IPCKEY:51036 ,需要查询其是否被占用;

1.首先通过计算器将其转为十六进制:

   51036 -> c75c

2.如果知道是被共享内存占用:

 $ipcs -m | grep c75c
 0x0000c75c 40403197   tdea3    666        536870912  2

3.如果不确定,则直接查找:

 $ipcs | grep c75c
 0x0000c75c 40403197   tdea3    666        536870912  2
 0x0000c75c 5079070    tdea3    666        4

pstack跟踪栈空间

pstack是一个脚本工具,可显示每个进程的栈跟踪。pstack 命令必须由相应进程的属主或 root 运行。其核心实现就是使用了gdb以及thread apply all bt命令;

 strace分析系统调用

strace常用来跟踪进程执行时的系统调用和所接收的信号。在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。

完整程序: 

gm@Inspiron:~/test$ sudo strace -o output.txt -T -tt -e trace=all -p 31626
strace: Process 31626 attached
^Cstrace: Process 31626 detached
gm@Inspiron:~/test$ cat output.txt 
16:26:58.070234 restart_syscall(<... resuming interrupted nanosleep ...>) = 0 <0.953816>
16:26:59.024480 write(1, "0x400594\n", 9) = 9 <0.000136>
16:26:59.024863 nanosleep({1, 0}, 0x7ffcc803dc40) = 0 <1.000169>
16:27:00.025260 write(1, "0x400594\n", 9) = 9 <0.000093>
16:27:00.025585 nanosleep({1, 0}, 0x7ffcc803dc40) = 0 <1.000192>
16:27:01.025984 write(1, "0x400594\n", 9) = 9 <0.000063>
16:27:01.026188 nanosleep({1, 0}, 0x7ffcc803dc40) = 0 <1.000172>
16:27:02.026532 write(1, "0x400594\n", 9) = 9 <0.000065>

 跟踪31626进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(以可视化的时分秒格式显示),最后将记录结果存在output.txt文件里面。

查看进程正在做什么(实时输出进程执行系统调用的情况):

gm@Inspiron:~/test$ sudo strace -p 31626
strace: Process 31626 attached
restart_syscall(<... resuming interrupted nanosleep ...>) = 0
write(1, "0x400594\n", 9)               = 9
nanosleep({1, 0}, 0x7ffcc803dc40)       = 0
write(1, "0x400594\n", 9)               = 9
nanosleep({1, 0}, 0x7ffcc803dc40)       = 0
write(1, "0x400594\n", 9)               = 9
nanosleep({1, 0}, 0x7ffcc803dc40)       = 0

nm列出目标文件的符号清单

  • -a或–debug-syms:显示所有的符号,包括debugger-only symbols。
  • -B:等同于–format=bsd,用来兼容MIPS的nm。
  • -C或–demangle:将低级符号名解析(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
  • –no-demangle:默认的选项,不需要将低级符号名解析成用户级名。
  • -D或–dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
  • -f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。
  • -g或–extern-only:仅显示外部符号。
  • -n、-v或–numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。
  • -p或–no-sort:按目标文件中遇到的符号顺序显示,不排序。
  • -P或–portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
  • -s或–print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。
  • -r或–reverse-sort:反转排序的顺序(例如,升序变为降序)。
  • –size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
  • –target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。
  • -u或–undefined-only:仅显示没有定义的符号(那些外部符号)。
  • –defined-only:仅显示定义的符号。
  • -l或–line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。
  • -V或–version:显示nm的版本号。
  • –help:显示nm的选项。
0000000000400594 T main
                 U printf@@GLIBC_2.2.5
00000000004004e0 t register_tm_clones
                 U sleep@@GLIBC_2.2.5
0000000000400470 T _start
0000000000400566 T test
0000000000601040 D __TMC_END__

常见的各种编码包括:

  • A 表示绝对 (absolute),这意味着不能将该值更改为其他的连接;
  • B 表示 BSS 段中的符号;
  • C 表示引用未初始化的数据的一般符号

有关这些类型的数据的详细信息,可以阅读 UNIX 中 nm 的 man 页面。nm 程序可用于列举符号及其类型和值,

objdump

ogjdump工具用来显示二进制文件的信息,显示elf和object格式文件,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。

  • -f 显示文件头信息
  • -D 反汇编所有section (-d反汇编特定section)
  • -h 显示目标文件各个section的头部摘要信息
  • -x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。
  • -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
  • -r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
  • -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些共享库。
  • -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
  • -t 显示文件的符号表入口。类似于nm -s提供的信息
gm@Inspiron:~/test$ objdump  -d hello
hello:     file format elf64-x86-64
Disassembly of section .init:
0000000000400400 <_init>:
  400400:	48 83 ec 08          	sub    $0x8,%rsp
  400404:	48 8b 05 ed 0b 20 00 	mov    0x200bed(%rip),%rax        # 600ff8 <_DYNAMIC+0x1d0>
  40040b:	48 85 c0             	test   %rax,%rax
  40040e:	74 05                	je     400415 <_init+0x15>
  400410:	e8 4b 00 00 00       	callq  400460 <sleep@plt+0x10>
  400415:	48 83 c4 08          	add    $0x8,%rsp
  400419:	c3                   	retq   

这是一个功能非常强大的工具,可用于研究编译器和汇编器的输出。

readelf

这个工具和objdump命令提供的功能类似,但是它显示的信息更为具体

  • -a –all 全部 Equivalent to: -h -l -S -s -r -d -V -A -I
  • -h –file-header 文件头 Display the ELF file header
  • -l –program-headers 程序 Display the program headers
  • –segments An alias for –program-headers
  • -S –section-headers 段头 Display the sections’ header
  • -e –headers 全部头 Equivalent to: -h -l -S
  • -s –syms 符号表 Display the symbol table
  • -r –relocs 重定位 Display the relocations (if present)-n –notes 内核注释 Display the core notes (if present)
  • -u –unwind Display the unwind info (if present)
  • -d –dynamic 动态段 Display the dynamic segment (if present)
  • -V –version-info 版本 Display the version sections (if present)
  • -A –arch-specific CPU构架 Display architecture specific information (if any).
  • -D –use-dynamic 动态段 Use the dynamic section info when displaying symbols
  • -x –hex-dump=<number> 显示 段内内容Dump the contents of section <number>
  • -w[liaprmfFso] or
  • -I –histogram Display histogram of bucket list lengths
  • -W –wide 宽行输出 Allow output width to exceed 80 characters
  • -H –help Display this information
  • -v –version Display the version number of readelf
gm@Inspiron:~/test$ readelf  -h hello
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400470
  Start of program headers:          64 (bytes into file)
  Start of section headers:          7528 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 33

ELF Header 为该文件中所有段入口显示了详细的摘要。在列举出这些 Header 中的内容之前,可以看到 Header 的具体数目。在研究一个较大的目标文件时,该信息可能非常有用。

除了所有这些段之外,编译器可以将调试信息放入到目标文件中,并且还可以显示这些信息。输入下面的命令,仔细分析编译器的输出:

$readelf --debug-dump hello | more

size查看程序内存占用

size这个工具用来查看程序运行时各个段的实际内存占用:

 

gm@Inspiron:~/test$ 
gm@Inspiron:~/test$ size hello
   text	   data	    bss	    dec	    hex	filename
   1336	    560	      8	   1904	    770	hello

file文件类型查询

这个工具用于查看文件的类型;比如在64位机器上发现了一个32位的库,链接不上,这就有问题了:

gm@Inspiron:~/test$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=cf3094a2200463e0ae79e1d3b7f16fbbee0d605d, not stripped

也可以查看Core文件是由哪个程序生成:

$file core.22355

strings查询二进制文件中的文本信息

一个文件中包含二进制数据和文本数据,如果只需要查看其文本信息,使用这个命令就很方便;过滤掉非字符数据,将文本信息输出:

gm@Inspiron:~/test$ strings  hello
/lib64/ld-linux-x86-64.so.2
libc.so.6
printf
sleep
__libc_start_main
__gmon_start__
GLIBC_2.2.5
UH-@

fuser显示文件使用者

显示所有正在使用着指定的file, file system 或者 sockets的进程信息;

gm@Inspiron:~/test$ fuser hello
/home/gm/test/hello: 31626e

使用了-m和-u选项,用来查找所有正在使用redis-server的所有进程的PID以及该进程的OWNER;

fuser通常被用在诊断系统的”resource busy”问题。如果你希望kill所有正在使用某一指定的file, file system or sockets的进程的时候,你可以使用-k选项。

ldd 查看程序依赖库

ldd

作用:用来查看程式运行所需的共享库,常用来解决程式因缺少某个库文件而不能运行的一些问题。

示例:查看hello程序运行所依赖的库:

gm@Inspiron:~/test$ ldd hello
	linux-vdso.so.1 =>  (0x00007fffebff2000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff8e8110000)
	/lib64/ld-linux-x86-64.so.2 (0x0000560996b54000)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值