一,背景
通常一些偷流量、端口占用、网络负载高、socket创建释放异常等Android网络相关问题,可以通过使用tcpdump抓tcp/ip报文,来定位。但是tcpdump无进程信息,也没有APK包名信息,无法确认异常的报文来自哪些Apk或者native进程。本题解决tcpdump报文无法关联到哪个进程/APK的问题。
二,tcpdump
tcpdump是该类问题基本的工具。在此仅作简单介绍。
tcpdump tcp -i eth1 -t -s 0 -c 100 and dst port ! 22 and src net 192.168.0.1/24 -C 60 -W 15 -w /data/....
tupdump使用参数说明
tcp
ip icmp arp rarp 和 tcp、udp、icmp这些选项等都要放到第一个参数的位置,用来过滤数据报的类型。如果都看,则删掉这个字段。
-i eth1
只抓经过接口eth1的包。可以换成其他网卡,比如rmnet0。有些手机产品,可能传统数据在rmnet0, IMS数据在rmnet8(举例)。为避免遗漏可以 -i any 表示抓取所有网卡的tcpdump。
-t
不显示时间戳
-s 0
抓取数据包时默认抓取长度为68字节。加上-S 0 后可以抓到完整的数据包。默认不要这个参数,除非是为了导出完整应用层报文。
-c 100
只抓取100个数据包
dst port ! 22
不抓取目标端口是22的数据包
src net 192.168.0.1/24
数据包的源网络地址为192.168.0.1/24
-w /data/netdebug/tcpdump.pcap
保存到指定位置。注意位置的selinux权限,注意不要放在/mnt等重启后会丢的位置。
-C 60 -W 15
-C 表示count。 tcpdump将在接受到count个数据包后退出。-W与-C 选项配合使用, 这将限制可打开的文件数目, 并且当文件数据超过这里设置的限制时, 依次循环替代之前的文件, 这相当于一个拥有filecount 个文件的文件缓冲池. 同时, 该选项会使得每个文件名的开头会出现足够多并用来占位的0, 这可以方便这些文件被正确的排序。
其他可以参考 :
tcpdump详解及抓取安卓数据包使用介绍 - Curtain_dusk - 博客园 (cnblogs.com)
三,strace
strace命令可以用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
针对网络问题,开机阶段在adb shell中敲如下命令:
adb root
adb shell
#mkdir /data/netdebug
#strace -p `pidof zygote64` -f -tt -e trace=network -o /data/netdebug/strace.txt
由于zygote是所有的java世界APP的父进程,参数:
-p `pidof zygote64` -f
用来跟踪所有zygote孵化出来的应用的系统调用信息。
-tt
用来打印时间戳
-e trace=network
用来筛选network相关的打印,避免打印太多。
抓到的trace.txt,可以通过关键字筛选你需要的LOG,如:
4895 10:59:30.755422 sendto(71, "\32\34 \0056\r2.18.24.01971\v\"t\237\17X<L\\", 28, 0, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("221.181.97.233")}, 16) = 28
4895 10:59:33.788692 sendto(71, "\32\34 \0056\r2.18.24.01971\v\"t\237\17X<L\\", 28, 0, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("221.181.97.233")}, 16) = 28
4895 10:59:34.803777 sendto(71, "\32\34 \0056\r2.18.24.01971\v\"t\237\17X<L\\", 28, 0, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("221.181.97.233")}, 16) = 28
4905 10:59:34.830972 recvfrom(71, "\32\34 \0066\0\v,6\016223.160.207.64B\0\0\357K", 4096, 0, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("221.181.97.233")}, [16]) = 29
4895 10:59:36.833750 sendto(71, "\32\34 \0056\r2.18.24.01971\v\"t\237\17X<L\\", 28, 0, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("221.181.97.233")}, 16) = 28
4895 10:59:38.611536 sendto(71, "&\0\0\1'\1\0\0\0\0\0\0\0\0\0\0\n\2t\237\17X\21\351\246,0\1LR\0\\"..., 295, 0, {sa_family=AF_INET, sin_port=htons(30013), sin_addr=inet_addr("112.53.47.99")}, 16) = 295
4905 10:59:38.700206 recvfrom(71, "&\0\0\1\20\1\0\351\246\0\0\0\0\0\0\0\n\f\21\351\246,0\1L\\b\0\7\367\357|"..., 4096, 0, {sa_family=AF_INET, sin_port=htons(30013), sin_addr=inet_addr("112.53.47.99")}, [16]) = 272
4895 10:59:39.920103 sendto(71, "\32\34 \0056\r2.18.24.01971\v\"t\237\17X<L\\", 28, 0, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("221.181.97.233")}, 16) = 28
时间戳左边的是进程ID,右边表示发送或接收、对方的端口以及对方的ip地址等信息。
更多关于strace的介绍,参考下面:
Linux strace命令 - ggjucheng - 博客园 (cnblogs.com)
四,netstat
strace打印用来定位有两个缺点。一个是如果系统网络吞吐率比较高,打印会比较多,甚至爆掉。另一个就是看不到应用创建socket在本地的端口。对于有些短时间创建了大量socket却并未能正常联网导致端口被占用的问题,定位不方便。strace的缺点=可以通过使用netstat命令来弥补:
adb root
adb shell
#netstat -atpln
打印的信息如下:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program Name作为服务端
tcp 0 0 127.0.0.1:60001 0.0.0.0:* LISTEN 3805/com.fiill.app.map
tcp 0 0 193.18.2.101:60001 0.0.0.0:* LISTEN 491/name-server作为client
tcp 0 0 193.18.2.101:60207 193.18.2.100:60034 ESTABLISHED 2793/com.fiill.car.parking
tcp 0 0 193.18.2.101:58211 193.18.2.100:60027 ESTABLISHED 1521/com.android.car
端口、IP、包名、PID、当前进程状态都很清晰。缺点手动netstat才会有。
五,trace脚本化
1,通过周期性保存netstat打印
针对netstat不是周期性打印的缺陷,可以通过新建脚本放到系统中运行解决。不多说,直接上代码:
netstat_dump.sh
#! /system/bin/sh#
#adb root
#adb shell "mkdir -p /data/netdebug"
#adb push netstat_dump.sh /data/
#adb shell chmod 777 /data/netstat_dump.sh
#./data/netstat_dump.sh
#global GPATH="/data/netdebug"prepare_path(){mkdir -p $GPATH
}prepare_pathwhile true
doDUMP_FILE=$GPATH/netstat_atpln_$(date '+%F-%H-%M-%S')echo "******BEGIN******" >> $DUMP_FILEdate '+%F-%H-%M-%S' >> $DUMP_FILEnetstat -atpln >> $DUMP_FILEdate '+%F-%H-%M-%S' >> $DUMP_FILEecho "******END******" >> $DUMP_FILEsleep 30
done
上述脚本是每30秒执行一次netstat, 并保存在/data/netdebug/目录下以时间戳命令的文件中。
2,strace创建开机运行脚本
类似地,也可以给strace脚本编译到系统中,通过init.rc启动,实现自动化。
创建一个starce_network.sh脚本,编译到/system/bin目录。
#! /system/bin/sh#
#adb root
#adb shell "mkdir -p /data/netdebug"
#global GPATH="/data/netdebug"prepare_path(){mkdir -p $GPATH
}prepare_pathstrace -p `pidof zygote64` -f -tt -e trace=network -o /data/netdebug/strace.txt
在init.rc里面声明:
service starce_network /system/bin/starce_network.shclass late_startuser rootgroup rootdisabledoneshot
六,其他
1,类似地,可以增加更多的脚本,比如开机启动tcpdump。不在冗述。
2,针对native世界,则 strace -p `pidof zygote64` -f 覆盖不到。 可以创建多个实例,通过如
-p `pidof surfaceflinger`
穷举的方式来dump。