本文希望整理一下在调试和Debug程序过程中可能用到的工具和命令的使用方式,如Tcpdump
,netstat
,gdb
等。
1、网络抓包
1.1 Tcpdump
tcpdump 是一个运行在命令行下的网络嗅探工具。它允许用户拦截和显示发送或收到过网络连接到该计算机的TCP/IP和其他数据包。
使用方式:
命令行下使用 man tcpdump
查看使用指南
tcpdump的功能非常强大,这里只介绍基本的使用方式,用来日常抓包调试应该足够了。
1.1.1 tcpdump输出内容结构:
直接使用tcpdump会将抓取所有经过第一个网卡上的数据包,一般是eth0
tcpdump的输出内容主要分为以下几个部分:
21:01:09.721855 IP 172.16.27.1.64048 > 121.15.15.170.sun-sr-https: Flags [P.], seq 353:545, ack 169, win 65535, options [nop,nop,TS val 2478626342 ecr 1770673271], length 192
- 时间 21:01:09.721855
- 网络协议 IP
- 源和目的ip 172.16.27.1.64048 > 121.15.15.170
- 数据包内容,包括Flags标识符,seq号,ack号,win 窗口,数据长度length,其中 [P.] 表示 PUSH 标志位为 1
seq是报文中的数据报文的序号, ack是下次期望的序号,window是接收缓存的窗口大小,urgent表明报文中是否有紧急指针。 Options是选项。
Flag标识符 -- 使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种:
[S]
: SYN(开始连接)[P]
: PSH(推送数据)[F]
: FIN (结束连接)[R]
: RST(重置连接)[.]
: 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK 也有可能是 URG)
1.1.2 tcpdump 过滤规则
1、指定网卡(可以使用ifconfig查看有哪些网络)
-i
: 指定要过滤的网卡接口, 如果要查看所有网卡, 可以 -i any
tcpdump -i en0
2、指定主机host(可以是src也可以是dst)
tcpdump host 192.168.10.100
3、进一步指定网卡,源/目的ip(多个条件使用and连接)
tcpdump -i en0 src 172.16.27.1 and dst 121.15.15.170
4、指定端口号(不区分源/目的)/指定源,目的端口
tcpdump port 8080
tcpdump src port 80 and dst port 8080
tcpdump port 80 or port 8088
指定一个port范围
tcpdump portrange 8000-8080
对于一些常见协议的默认端口,可以直接使用协议名,而不用具体的端口号
比如http == 80,https == 443 等
tcpdump tcp port http
5、指定网络协议
tcpdump icmp
1.1.3 可选参数
-n
不把ip转化成域名,直接显示 ip,避免执行 DNS lookups 的过程,速度会快很多-nn
不把协议和端口号转化成名字,速度也会快很多
sudo tcpdump port 64048 -nn
-v
:产生详细的输出。比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。-vv
:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。-vvv
:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) 抓包得到的数据都是展示在命令行,不好分析,一般会保存到文件中
tcpdump port 64048 -nn -v
-x
:以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部)-xx
:以16进制的形式打印每个包的头部数据(包括数据链路层的头部)-X
:以16进制和 ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。-XX
:以16进制和 ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。
tcpdump -Qdir=out -s 100 -vvv -XX
-s
: tcpdump 默认只会截取前96
字节的内容,要想截取所有的报文内容,可以使用-s number
,number
就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。-e
所截取的每个包都显示链路层报头:源MAC地址>目的MAC地址,以太类型 IPV4 (0X0800), 包数据长度。-Q
: 选择是入方向还是出方向的数据包,可选项有:in, out, inout(mac上需要使用-Qdir=out)
sudo tcpdump -Qdir=out src 172.16.27.1
-
-c
: 捕获 count 个包 tcpdump 就退出 -
-w
保存到文件 -
-r
从文件中读取
tcpdump icmp -w icmp.pcap
tcpdump icmp -r icmp.pcap
1.1.4 使用过滤规则组合
tcpdump可以使用下面的关键字来组合规则
- and:所有的条件都需要满足,也可以表示为
&&
- or:只要有一个条件满足就可以,也可以表示为
||
- not:取反,也可以使用
!
=
:判断二者相等==
:判断二者相等!=
:判断二者不相等 使用这些符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如- if:表示网卡接口名、
- proc:表示进程名
- pid:表示进程 id
- svc:表示 service class
- dir:表示方向,in 和 out
- eproc:表示 effective process name
- epid:表示 effective process ID
比如要过滤来自进程名为 nc
发出的流经 en0 网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写
tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)"
tcpdump还有很多更复杂的使用,但个人觉得使用可视化的软件wireshark可能效率会更高;wireshark是一款抓包软件,功能和tcpdump基本类似,规则语法也类似。
1.2 netstat
netstat
命令用于显示系统的网络状态。利用netstat
指令可让你得知整个系统的网络情况。
同样的,使用man netstat
可以查看命令基本用法。
1.2.1 netstat 基本输出内容结构
- Proto:表示协议名(tcp协议还是udp协议)
- recv-Q:接收队列,这表示收到的数据已经在本地接收缓冲,但是还有多少没有被进程取走,recv()会从中取消息
- send-Q:发送队列,对方还没有收到的数据或者说对方还没有没有发送Ack的。如果发送队列Send-Q不能很快的清零,可能是有应用向外发送数据包过快,或者是对方接收数据包不够快。
接受和发送队列这两个值通常应该为0,如果不为0可能是有问题的。packets在两个队列里都不应该有堆积状态。可接受短暂的非0情况。
- local Address 和 foreign address表示发送和接受的ip地址
- state:链路状态,共有12种状态
LISTEN:
监听连接中--The socket is listening for incoming connections.SYN_SENT:
三次握手时客户端tcp发送一个SYN以请求建立一个连接.之后状态置为SYN_SENT -- The socket is actively attempting to establish a connection.SYN_RECV:
服务端应发出ACK确认客户端的SYN,同时自己向客户端发送一个SYN.之后状态置为SYN_RECV-- A connection request has been received from the network.ESTABLISHED:
表示已建立连接 -- The socket has an established connection.FIN_WAIT1:
主动关闭端应用程序调用close,于是其TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT1状态 -- The socket is closed, and the connection is shutting down.CLOSE_WAIT:
被动关闭端TCP接到FIN后,就发出ACK以回应FIN请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT.-- The remote end has shut down, waiting for the socket to close.FIN_WAIT2:
主动关闭端接到ACK后,就进入了 FIN-WAIT-2 -- Connection is closed, and the socket is waiting for a shutdown from the remote end.LAST_ACK:
被动关闭端一段时间后,接收到文件结束符的应用程序将调用CLOSE关闭连接。这导致它的TCP也发送一个 FIN,等待对方的ACK.就进入了LAST-ACK .-- The remote end has shut down, and the socket is closed. Waiting for acknowledgement.TIME_WAIT:
在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态。/* The socket is waiting after close to handle packets still in the network.CLOSING:
比较少见.Both sockets are shut down but we still don’t have all our data sent.CLOSED:
被动关闭端在接收到ACK包后,就进入了closed的状态。连接结束.The socket is not being used.UNKNOWN:
未知的Socket状态 The state of the socket is unknown.
mac上使用 netstat -v
还会打印pid等相关信息
1.2.2 netstat 可选参数
1、列出所有连接
netstat -a
2、过滤指定协议(tcp/udp)
netstat -at 或者
netstat -au
3、取消域名解析
netstat -n
4、显示每种协议的详细统计
netstat -s
5、现实pid等信息
mac上需要使用 netstat -v
linux应该需要使用 netstat -p --没有测试
6、显示路由信息
netstat -r
路由表
7、需要更具体的信息可以结合grep进行过滤
1.3 lsof
lsof -- lists openfiles,是一个系统管理工具。使用它来获得你系统上设备的信息,你能通过它了解到指定的用户在指定的地点正在碰什么东西,或者甚至是一个进程正在使用什么文件或网络连接。
lsof [参数][文件]
1.3.1 lsof 输出内容结构
COMMAND
:进程的名称PID
:进程标识符PPID
:父进程标识符(需要指定-R参数)USER
:进程所有者PGID
:进程所属组FD
:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等TYPE
:文件类型,如DIR、REG等,常见的文件类型DEVICE
:指定磁盘的名称SIZE
:文件的大小NODE
:索引节点(文件在磁盘上的标识)NAME
:打开文件的确切名称
1.3.2 lsof可选参数
1、查看谁正在使用某个文件 例如查看谁正在使用bash文件
lsof /bin/bash
2、列出某个用户打开的文件信息
lsof -u username
3、列出某个进程打开的文件信息
lsof -c programName
4、列出某个进程打开的文件信息
lsof -p Pid
5、列出所有网络连接
lsof -i
6、列出谁正在使用某个端口
lsof -i :3306 或
lsof -i tcp:3306
2 进程&调试
2.1 top
top
命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。
2.1.1 top 展示信息结构
top展示信息的第一部分显示的是系统的概况(linux和mac 展示的内容会有所区别,但都是这些信息)
-
第一行展示了当前系统的进程数,运行的进程数,阻塞的进程数,睡眠的进程数和线程数以及当前系统时间
-
第二行展示了负载情况
- Load Avg: 分别是1分钟、5分钟、15分钟的负载情况 ; 而系统平均负载被定义为在特定时间间隔内运行队列中的平均进程树,可以这样认为,即正在运行的进程 + 准备好等待运行的进程在特定时间内(1分钟,5分钟,10分钟)的平均进程数
- Cpu Usage:cpu使用率,分为user-用户态占用时间,sys-内核态占用时间,idle-空闲时间
- SharedLibs:共享库,300M resident 常驻空间大小、62M data 数据段的大小、14M linkedit 链接编辑器的使用情况
- MemRegions:内存区域的总空间和大小.常驻,私有,共享
- PhysMem:物理内存使用情况
- 剩下的是磁盘的使用率和vm虚拟机的信息(这部分暂时不太了解)
-
第三部分开始就是具体的进程和使用情况
- Pid:进程号
- COMMAND:进程所对应的命令行名称也就是启动的程序
- %CPU:进程使用的cpu时间比例(top命令是按CPU总使用率来显示的,4核理论上最高可达400%)
- Time:自进程启动到目前为止的cpu时间总量
- #TH 线程数量
- #WQ
- #PORT:占用端口数量 -- 这点不确定
- PPID:父進程ID
- Mem: 占用内存
mac上的展示信息和linux不太一致,部分字段还需要进一步确定含义
常用操作
top //每隔5秒显式所有进程的资源占用情况
top -d 2 //每隔2秒显式所有进程的资源占用情况
top -c //每隔5秒显式进程的资源占用情况,并显示进程的命令行参数(默认只有进程名)
top -p 12345 -p 6789//每隔5秒显示pid是12345和pid是6789的两个进程的资源占用情况
top -d 2 -c -p 123456 //每隔2秒显示pid是12345的进程的资源使用情况,并显式该进程启动的命令行参数
在top模式下
按?
,打开提示界面
按o
可以输入模式,填入cpu按cpu排序,填入mem按内存排序
2.2 gdb
用C++的同学肯定都有听过或用过gdb,用来打断点调试或者查bug。
在程序运行过程中,如果出现了段错误等一系列情况而crash了,Linux会将崩溃前进程的内存状态保存在core文件里,就像保存了案发现场的照片,可以帮助开发人员找到事故原因,修复程序。
需要开启系统core dump才可以,用命令ulimit -a
可以查看是否开启,core file size 为0 则没有开启
使用ulimit -c unlimited
设置为无限制
这时如果程序崩溃了则会产生core文件,可以利用gdb来对core文件进行调试,查看问题。但是要注意编译的时候需要设置参数加入调试信息,strip删除符号信息也会对调试带来一些不便。
其实Go
也是支持使用gdb调试的,怎么安装Go,配置环境这里就不说了,下面讲讲怎么使用gdb对go程序进行调试(mac 使用brew install gdb安装gdb)
使用gdb调试:
go build 将程序编译成二进制文件后,使用gdb对二进制可执行文件进行debug
( go build -gcflags "-N -l" main.go) 关闭内联优化
gdb main
gdb有以下参数:
-
r
:run,执行程序 -
n
:next,下一步,不进入函数 -
s
:step,下一步,会进入函数 -
b
:breakponit,设置断点 -
l
:list,查看源码 -
c
:continue,继续执行到下一断点 -
bt
:backtrace,查看当前调用栈 -
p
:print,打印查看变量 -
q
:quit,退出 GDB -
whatis
:查看对象类型 -
info breakpoints
:查看所有的断点 -
info locals
:查看局部变量 -
info args
:查看函数的参数值及要返回的变量值 -
info frame
:堆栈帧信息 -
info goroutines
:查看 goroutines 信息。在使用前 ,需要注意先执行 source /usr/local/go/src/runtime/runtime-gdb.py -
goroutine 1 bt
:查看指定序号的 goroutine 调用堆栈 -
回车:重复执行上一次操作
用list 查看代码
set listsize <count> :可以设置一次展示多少行
list <first>, <last> :显示从first行到last行之间的源代码。
list <linenum> 显示程序第linenum行的周围的源程序。
list <function> 显示函数名为function的函数的源程序。
list - :显示当前行前面的源程序。
用b
打断点,可以是行号可以是函数名
delete N可以删除n号断点
delete删除所有断点
bt 可以查看当前堆栈信息
set 可以设置变量值
return 可以强制返回结果
call <expr>可以执行一个函数
2.3 other
还有一些其他命令如
strace
可以追踪系统调用
ldd
可以打印程序依赖的库列表
objdump
可以进行反汇编
nm
进行符号分析