内存分析工具主要包含日常常用工具、systemtap和bpftrace工具等。其中常用工具主要包括:vmstat、wapon、sar、slabtop、numastat、ps、top、pmap等。
内存分析常用工具
vmstat
vmstat是一个虚拟内存统计工具,提供了一个上层视角的内存监控状态,包含空闲内存和换页统计;其中cpu相关的参数在之前已经讲解过。
~# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 574936 155544 6819876 0 0 0 1 0 0 0 0 100 0 0
0 0 0 574888 155544 6819964 0 0 0 0 102 100 0 0 100 0 0
0 0 0 595600 155544 6819964 0 0 0 0 185 161 0 1 99 0 0
0 0 0 596136 155544 6819964 0 0 0 0 116 101 0 0 100 0 0
0 0 0 596192 155552 6819964 0 0 0 56 155 217 0 0 100 0 0
0 0 0 596160 155552 6819964 0 0 0 0 114 109 0 0 100 0 0
0 0 0 596192 155552 6819964 0 0 0 0 101 89 0 0 100 0 0
命令输出的第一行不是系统从启动到现在的数据,而是当前时刻的数据。以下列的默认值是kilobytes:
- swpd:换出内存的总量
- free:空闲内存的总量
- buff:buffer缓存的内存总量
- cache:页缓存的内存总量
- si:换入内存总量(换页paging)
- so:换出内存总量(换页paging)
如果感觉输出的格式后面比较乱,对不齐,可以使用-S参数进行格式输出(M代表1024进制MB,m代表1000进制MB),如下所示。
1024进制
~# vmstat -SM 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 582 151 6660 0 0 0 1 0 0 0 0 100 0 0
0 0 0 582 151 6660 0 0 0 0 86 73 0 0 100 0 0
0 0 0 582 151 6660 0 0 0 0 54 66 0 0 100 0 0
0 0 0 582 151 6660 0 0 0 0 74 81 0 0 100 0 0
0 0 0 582 151 6660 0 0 0 0 51 61 0 0 100 0 0
0 0 0 582 151 6660 0 0 0 0 42 45 0 0 100 0 0
1000进制
~# vmstat -Sm 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 610 159 6983 0 0 0 1 0 0 0 0 100 0 0
0 0 0 610 159 6983 0 0 0 0 90 96 0 0 100 0 0
0 0 0 610 159 6983 0 0 0 0 70 71 0 0 100 0 0
0 0 0 610 159 6983 0 0 0 0 51 61 0 0 100 0 0
0 0 0 610 159 6983 0 0 0 0 101 111 0 0 100 0 0
0 0 0 610 159 6983 0 0 0 0 77 87 0 0 100 0 0
可以使用-a参数显示页缓存中活跃或非活跃的内存。
~# vmstat -a -SM 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free inact active si so bi bo in cs us sy id wa st
1 0 0 582 4172 2133 0 0 0 1 0 0 0 0 100 0 0
0 0 0 582 4172 2133 0 0 0 0 84 83 0 0 100 0 0
0 0 0 582 4172 2133 0 0 0 0 73 81 0 0 100 0 0
0 0 0 582 4172 2133 0 0 0 0 89 108 0 0 100 0 0
0 0 0 582 4172 2133 0 0 0 0 82 85 0 0 100 0 0
0 0 0 582 4172 2133 0 0 0 0 71 79 0 0 100 0 0
可以使用-s参数以列表形式打印内存使用统计。
~# vmstat -s
8147216 K total memory
574224 K used memory
2184904 K active memory
4272516 K inactive memory
597264 K free memory
155608 K buffer memory
6820120 K swap cache
0 K total swap
0 K used swap
0 K free swap
988896 non-nice user cpu ticks
160614 nice user cpu ticks
871224 system cpu ticks
9520067017 idle cpu ticks
164229 IO-wait cpu ticks
0 IRQ cpu ticks
243435 softirq cpu ticks
555064 stolen cpu ticks
3918760 pages paged in
64968211 pages paged out
0 pages swapped in
0 pages swapped out
1195009341 interrupts
1140494763 CPU context switches
1644926996 boot time
980686 forks
PSI
在4.20以后的内核版本中引入了PSI(pressure stall information),包含内存满载度的统计。不仅能显示内存压力,还能显示压力在过去5分钟的变化状态。
~# cat /proc/pressure/memory
some avg10=2.84 avg60=1.23 avg300=0.32 total=1468344
full avg10=1.85 avg60=0.66 avg300=0.16 total=702578
从结果中可以看出,内存的压力在逐渐增大。过去10秒的均值大于过去300秒的均值。值代表任务暂停在内存上面的时间占比。some一行显示某些被影响的进程(线程),full一行显示的是所有被影响的进程(线程)。
swapon
swapon命令可以用来查看交换设备是否被配置,如果被配置,则其输出结果会出现在vmstat命令中的si和so列,如果没有配置则命令没有任何输出。
没有配置
~# swapon
~#
有配置
~# swapon
NAME TYPE SIZE USED PRIO
/dev/dm-2 partition 980M 611.6M -2
/swap1 file 30G 10.9M -3
free
free命令输出系统内存使用的整体结果,如下所示。
~# free
total used free shared buff/cache available
Mem: 8147216 574724 596612 7952 6975880 7260968
Swap: 0 0 0
低版本的centos可能输出的结果不太一样,但是大致是相同的,我们以centos7以后的结果为例讲解:
- total:系统总的物理内存和交换空间
- used:系统已经使用的物理内核和交换空间
- free:系统剩余物理内存和交换空间
- shard:被共享使用的物理内存大小,可以包含共享库和/dev/shm下的内容
- buff/cache:被buff和cache使用的内存大小
- available:还可以被系统使用的内存大小,是一个预测值,并非立即可用大小
可以使用-w参数将buff和cache分开显示,如下所示。
~# free -w
total used free shared buffers cache available
Mem: 8147216 575416 595828 7952 155744 6820228 7260288
Swap: 0 0 0
ps
ps命令可以显示进程使用的内存,如下所示。
~# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 172556 14572 ? Ss Feb15 4:09 /sbin/init
root 2 0.0 0.0 0 0 ? S Feb15 0:03 [kthreadd]
root 3 0.0 0.0 0 0 ? I< Feb15 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< Feb15 0:00 [rcu_par_gp]
root 6 0.0 0.0 0 0 ? I< Feb15 0:00 [kworker/0:0H-kblockd]
root 9 0.0 0.0 0 0 ? I< Feb15 0:00 [mm_percpu_wq]
root 10 0.0 0.0 0 0 ? S Feb15 0:01 [ksoftirqd/0]
root 11 0.0 0.0 0 0 ? I Feb15 107:52 [rcu_sched]
其中需要注意的有以下3列:
- %MEM:进程使用系统物理内存的百分比
- VSZ:虚拟内存大小
- RSS:进程正在使用的所有物理内存
ps命令可以通过参数指定输出以上几列,更多信息可以查看/proc/
~# ps -eo pid,pmem,vsz,rss | head -10
PID %MEM VSZ RSS
1 0.1 172556 14572
2 0.0 0 0
3 0.0 0 0
4 0.0 0 0
6 0.0 0 0
9 0.0 0 0
10 0.0 0 0
11 0.0 0 0
12 0.0 0 0
pmap
pmap可以通过地址空间段来展示进程内存使用,例如。
~# pmap -x 980436
980436: (sd-pam)
Address Kbytes RSS Dirty Mode Mapping
000055e10f8b0000 200 0 0 r---- systemd
000055e10f8e2000 760 128 0 r-x-- systemd
000055e10f9a0000 344 64 0 r---- systemd
000055e10f9f6000 280 260 252 r---- systemd
000055e10fa3c000 4 4 4 rw--- systemd
000055e10fc18000 6300 4192 4192 rw--- [ anon ]
00007fb640000000 132 12 12 rw--- [ anon ]
00007fb640021000 65404 0 0 ----- [ anon ]
00007fb648000000 132 12 12 rw--- [ anon ]
00007fb648021000 65404 0 0 ----- [ anon ]
00007fb64e15d000 8 0 0 r---- pam_gnome_keyring.so
00007fb64e15f000 24 0 0 r-x-- pam_gnome_keyring.so
00007fb64e165000 12 0 0 r---- pam_gnome_keyring.so
00007fb64e168000 4 4 4 r---- pam_gnome_keyring.so
00007fb64e169000 4 4 4 rw--- pam_gnome_keyring.so
00007fb64e16a000 4 0 0 r---- libpam_misc.so.0.82.1
00007fb64e16b000 4 0 0 r-x-- libpam_misc.so.0.82.1
00007fb64e16c000 4 0 0 r---- libpam_misc.so.0.82.1
-x参数可以增加Dirty一列,将展示已经被修改但是还没写回的页。
slabtop
slabtop命令可以打印出内核slab分配器缓存的使用情况,与top类似,实时输出,例如。
~# slabtop -sc
Active / Total Objects (% used) : 3682298 / 4152088 (88.7%)
Active / Total Objects (% used) : 3682337 / 4152127 (88.7%)
Active / Total Slabs (% used) : 116386 / 116386 (100.0%)
Active / Total Caches (% used) : 104 / 157 (66.2%)
Active / Total Size (% used) : 845034.22K / 961201.15K (87.9%)
Minimum / Average / Maximum Object : 0.01K / 0.23K / 8.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
327265 286556 87% 1.07K 11285 29 361120K ext4_inode_cache
1484886 1370300 92% 0.10K 38074 39 152296K buffer_head
645351 438645 67% 0.19K 30731 21 122924K dentry
136668 114266 83% 0.57K 4881 28 78096K radix_tree_node
59228 57655 97% 0.59K 2278 26 36448K inode_cache
169080 168393 99% 0.13K 5636 30 22544K kernfs_node_cache
4176 4149 99% 4.00K 522 8 16704K kmalloc-4k
77493 76762 99% 0.20K 1987 39 15896K vm_area_struct
361794 328979 90% 0.04K 3547 102 14188K ext4_extent_status
199808 193039 96% 0.06K 3122 64 12488K vmap_area
47424 45219 95% 0.25K 1482 32 11856K filp
10336 10006 96% 1.00K 323 32 10336K kmalloc-1k
-sc参数是指定使用cache size进行排序,使用最多的排在上面。
slab统计信息来自/proc/slabinfo,也可以通过vmstat -m命令打印。
numastat
numastat可以打印在NUMA架构内存访问,以下是有2颗CPU的输出结果。
$ numastat
node0 node1
numa_hit 22803297317096 16241914532514
numa_miss 216025857428 7470170746474
numa_foreign 7470170746376 216025857426
interleave_hit 100698246 143800065
local_node 22803189089828 16242714785561
other_node 216134084696 7469370493427
numastat命令可以通过以下命令安装yum install numactl
。关键信息如下:
- numa_hit:内存分配在local节点
- numa_miss+numa_foregin:内存分配在非优先选择的节点
other_node:内存分配在这个节点,但是进程运行在另外一个节点。
top
top命令会显示正在运行的程序和程序使用的内存信息。如下所示。
~# top -o %MEM top - 17:28:45 up 1262 days, 5:55, 1 user, load average: 98.05, 91.25, 93.78 Tasks: 2364 total, 6 running, 2356 sleeping, 0 stopped, 2 zombie %Cpu(s): 71.8 us, 8.0 sy, 0.0 ni, 11.6 id, 7.6 wa, 0.0 hi, 1.0 si, 0.0 st KiB Mem : 19780201+total, 4733848 free, 14077816+used, 52290016 buff/cache KiB Swap: 0 total, 0 free, 0 used. 48779544 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 10893 yarn 20 0 12.590g 0.010t 14008 S 420.0 5.5 68:55.24 java 154889 yarn 20 0 14.960g 0.010t 15396 S 625.0 5.3 18847:13 java 186790 yarn 20 0 14.960g 0.010t 15396 R 10.0 5.3 0:00.02 java 156061 yarn 20 0 10.996g 9.143g 13600 S 5.0 4.8 787:21.09 java 117171 yarn 20 0 10.399g 8.717g 13564 S 0.0 4.6 655:34.94 java 193316 yarn 20 0 10.840g 8.668g 13508 S 0.0 4.6 33:14.55 java
输出结果中,通过-o %MEM参数对输出结果按照内存使用量大小进行了逆序排序。
sar
sar(system activity reporter),可以用来观测当前系统的活动。Linux版本用来进行内存观测的参数如下:
-B:换页统计
- -H:大页统计
- -r:内存使用率
- -S:交换分区统计
- -W:swapping统计
各项统计信息说明:
选项 | 统计 | 描述 | 单位 |
---|---|---|---|
-B | pgpgin/s | 换入 | Kbytes/s |
pgpgout/s | 换出 | Kbytes/s | |
fault/s | major fault和minor fault | Count/s | |
majflt/s | major fault | Count/s | |
pgfree/s | 加到free list的页数量 | Count/s | |
pgscank/s | kwapd进程后台扫描的页数量 | Count/s | |
pgscand/s | 直接扫描的页数量 | Count/s | |
pgsteal/s | 页和交换缓存回收数 | Count/s | |
%vmeff | pg steal/pg scan的占比,可以反应页回收的效率 | Percent | |
-H | kbhugfree | 空闲大页内存 | Kbytes |
kbhugused | 大页使用量 | Kbytes | |
%hugused | 大页使用比例 | Percent | |
-r | kbmemfree | 空闲内存 | Kbytes |
kbmemused | 内存使用大小(不包含内核) | Kbytes | |
%memused | 内存使用率 | Percent | |
kbbuffers | Buffer cache大小 | Kbytes | |
kbcached | Page cache大小 | Kbytes | |
kbcommit | |||
%commit | |||
kbactive | Active list内存大小 | Kbytes | |
kbinact | Inactive list内存大小 | Kbytes | |
kbdirty | 需要写回磁盘的脏内存大小 | Kbytes | |
-S | kbswpfree | 空闲swap空间 | Kbytes |
kbswpused | 使用swap空间 | Kbytes | |
%swpused | 使用swap比例 | Percent | |
kbswpcad | cache的swap空间 | Kbytes | |
%swpcad | cache的swap空间占已使用的比例 | Percent | |
-W | pswpin/s | swap换入页数量 | Pages/s |
pswpout/s | swap换出页数量 | Pages/s |
sar -B
# sar -B 1
Linux 3.10.0-693.5.2.el7.x86_64 (xxxxxxx) 07/04/2022 _x86_64_ (56 CPU)
07:44:31 PM pgpgin/s pgpgout/s fault/s majflt/s pgfree/s pgscank/s pgscand/s pgsteal/s %vmeff
07:44:32 PM 8232.00 0.00 448092.00 0.00 35553.00 0.00 0.00 0.00 0.00
07:44:33 PM 24068.00 208.00 508789.00 0.00 35398.00 0.00 0.00 0.00 0.00
07:44:34 PM 140120.00 43032.00 838535.00 0.00 52782.00 0.00 0.00 0.00 0.00
07:44:35 PM 182600.00 8600.00 780663.00 0.00 69181.00 0.00 0.00 0.00 0.00
07:44:36 PM 96316.00 224.00 538031.00 0.00 41329.00 0.00 0.00 0.00 0.00
07:44:37 PM 8276.00 732.00 575471.00 0.00 38922.00 0.00 0.00 0.00 0.00
07:44:38 PM 16172.00 24128.00 450544.00 0.00 49388.00 0.00 0.00 0.00 0.00
07:44:39 PM 8336.00 96.00 716726.00 0.00 36242.00 0.00 0.00 0.00 0.00
07:44:40 PM 8340.00 76.00 637336.00 0.00 60962.00 0.00 0.00 0.00 0.00
^C
07:44:41 PM 49796.92 467.69 591041.54 0.00 42863.08 0.00 0.00 0.00 0.00
Average: 54386.32 8020.73 609156.89 0.00 46385.28 0.00 0.00 0.00 0.00
sar -H
# sar -H 1
Linux 3.10.0-693.5.2.el7.x86_64 (xxxxxxx) 07/04/2022 _x86_64_ (56 CPU)
07:45:22 PM kbhugfree kbhugused %hugused
07:45:23 PM 0 0 0.00
07:45:24 PM 0 0 0.00
07:45:25 PM 0 0 0.00
07:45:26 PM 0 0 0.00
^C
07:45:27 PM 0 0 0.00
Average: 0 0 0.00
sar -r
# sar -r 1
Linux 3.10.0-693.5.2.el7.x86_64 (xxxxxxx) 07/04/2022 _x86_64_ (56 CPU)
07:45:57 PM kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty
07:45:58 PM 13626744 184165092 93.11 65460 60734772 136710732 69.12 131185828 48408768 38588
07:45:59 PM 13560152 184231684 93.14 65460 60790900 136710332 69.12 131189220 48465552 34664
07:46:00 PM 13518588 184273248 93.17 65460 60805216 136753604 69.14 131207576 48479612 48208
07:46:01 PM 13416536 184375300 93.22 65468 60813200 139314084 70.43 131274552 48486932 48676
07:46:02 PM 12874604 184917232 93.49 65476 60857060 169497468 85.69 131738296 48534912 31128
07:46:03 PM 12867388 184924448 93.49 65476 60854772 139385412 70.47 131804308 48533416 30860
07:46:04 PM 12623376 185168460 93.62 65476 60844268 141125696 71.35 132056892 48523128 32892
^C
07:46:04 PM 12651424 185140412 93.60 65476 60845112 141102516 71.34 132028816 48523196 32988
Average: 13142352 184649484 93.36 65469 60818162 142574980 72.08 131560686 48494440 37250
sar -S
# sar -S 1
Linux 3.10.0-693.5.2.el7.x86_64 (xxxxxxx) 07/04/2022 _x86_64_ (56 CPU)
07:46:46 PM kbswpfree kbswpused %swpused kbswpcad %swpcad
07:46:47 PM 0 0 0.00 0 0.00
07:46:48 PM 0 0 0.00 0 0.00
07:46:49 PM 0 0 0.00 0 0.00
07:46:50 PM 0 0 0.00 0 0.00
07:46:51 PM 0 0 0.00 0 0.00
07:46:52 PM 0 0 0.00 0 0.00
^C
07:46:52 PM 0 0 0.00 0 0.00
Average: 0 0 0.00 0 0.00
sar -W
# sar -W 1
Linux 3.10.0-693.5.2.el7.x86_64 (xxxxxxx) 07/04/2022 _x86_64_ (56 CPU)
07:46:55 PM pswpin/s pswpout/s
07:46:56 PM 0.00 0.00
07:46:57 PM 0.00 0.00
07:46:58 PM 0.00 0.00
07:46:59 PM 0.00 0.00
07:47:00 PM 0.00 0.00
^C
07:47:01 PM 0.00 0.00
Average: 0.00 0.00
perf
perf是Linux下面一个非常强大的工具,我们之前在CPU的章节中已经介绍过perf有关的用法,本次我们只讲解perf在内存方面的使用。
perf 单行命令
记录10s 缺页异常的事件数据
perf record -e page-faults -a -g -- sleep 10 [ perf record: Woken up 114 times to write data ] [ perf record: Captured and wrote 38.086 MB perf.data (178308 samples) ]
记录PID为23912进程10秒缺页异常,连带栈信息
# perf record -e page-faults -c 1 -p 23912 -g -- sleep 10 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.287 MB perf.data (129 samples) ] # perf script Failed to open /tmp/perf-23912.map, continuing without symbols java 52494 109151486.674671: page-faults: 654e4b CollectedHeap::common_mem_allocate_init (/usr/java/jdk1.8.0_102/jre/lib/amd64/server/libjvm.so) 6433cc InstanceKlass::allocate_instance (/usr/java/jdk1.8.0_102/jre/lib/amd64/server/libjvm.so) 9b949e OptoRuntime::new_instance_C (/usr/java/jdk1.8.0_102/jre/lib/amd64/server/libjvm.so) 7f83b05fb685 [unknown] (/tmp/perf-23912.map)
记录brk系统调用
# perf record -e syscalls:sys_enter_brk -a -g -- sleep 60
追踪kwapd进程唤醒事件,使用
Ctrl+C
结束# perf record -e vmscan:mm_vmscan_wakeup_kswapd -ag ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.694 MB perf.data ]
使用perf工具生成火焰图
# perf record -e page-faults -a -g -- sleep 10 # perf script --header > out.sshd.stacks # git clone https://github.com/brendangregg/FlameGraph # ./FlameGraph/stackcollapse-perf.pl < out.sshd.stacks | ./FlameGraph/flamegraph.pl --hash --bgcolor=green --count=pages --title="SSHD Page Fault Flame Graph" > sshd.out.svg
BPF工具
BPF和bpftrace工具有非常多的内存分析工具,下面我们一一介绍。
oomkill
追踪OOM Killer事件信息
BCC 版本# oomkill-bpfcc Tracing OOM kills... Ctrl-C to stop. 12:35:36 Triggered by PID 987694 ("python3"), OOM kill of PID 987694 ("python3"), 2036804 pages, loadavg: 0.17 0.04 0.01 5/435 987722
测试OOM程序
~# python3 Python 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> KB=1024 >>> MB=1024*KB >>> GB=1024*MB >>> "aaaaaaa"*GB Killed ~# dmesg -T | grep Out [Mon Jul 4 12:32:29 2022] Out of memory: Killed process 987694 (python3) total-vm:7384756kB, anon-rss:7308836kB, file-rss:848kB, shmem-rss:0kB, UID:0 pgtables:14388kB oom_score_adj:0
bpftrace版本 ```bash
oomkill.bt
Attaching 2 probes… Tracing oom_kill_process()… Hit Ctrl-C to end. 12:53:05 Triggered by PID 987810 (“systemd-udevd”), OOM kill of PID 0 (“”), 1280070982 pages, loadavg: 0.52 0.11 0.04 5/410 987811
测试OOM程序同BCC版本的测试程序<br />bpftrace版本代码
```bash
#include <linux/oom.h>
BEGIN
{
printf("Tracing oom_kill_process()... Hit Ctrl-C to end.\n");
}
kprobe:oom_kill_process
{
$oc = (struct oom_control *)arg1;
time("%H:%M:%S ");
printf("Triggered by PID %d (\"%s\"), ", pid, comm);
printf("OOM kill of PID %d (\"%s\"), %d pages, loadavg: ",
$oc->chosen->pid, $oc->chosen->comm, $oc->totalpages);
cat("/proc/loadavg");
}
memleak
memleak是一个BCC程序,当前还没有bpftrace版本。
展示内存分配和释放信息
~# memleak-bpfcc -p 2582811
Attaching to pid 2582811, Ctrl+C to quit.
[14:21:43] Top 10 stacks with outstanding allocations:
[14:21:48] Top 10 stacks with outstanding allocations:
[14:21:53] Top 10 stacks with outstanding allocations:
[14:21:58] Top 10 stacks with outstanding allocations:
命令行语法
memleak [options] [-p PID] [-c COMAND] [interval [count]]
命令行选项
- -s RATE:采样频率,使用RATE分之一的采样频率来降低额外消耗
- -o OLDER:忽略哪些存活时间小于OLDER毫秒的内存分配
内存分配,尤其是用户态的内存分配可能会是非常频繁的,这个命令可能会导致性能下降至原来的十分之一以下
brkstack
brk系统调用可以通过BCC的stackcount进行监听和统计,stackcount是BCC版本的trace,当然这个系统调用也是可以通过perf获取的。
~# stackcount-bpfcc -PU t:syscalls:sys_enter_brk
Tracing 1 functions for "t:syscalls:sys_enter_brk"... Hit Ctrl-C to end.
^C
b'[unknown]'
b'[unknown]'
b'python3' [987856]
1
b'[unknown]'
b'[unknown]'
b'command-not-fou' [987855]
bpftrace版本主要代码
t:syscalls:sys_enter_brk { @[ustack, comm] = count(); }
可以通过如下方式执行
~# bpftrace -e 't:syscalls:sys_enter_brk { @[ustack, comm] = count(); }'
Attaching 1 probe...
^C
@[
0x7fabfce12f3b
0x41e589480000b264
, python3]: 1
@[
0x7fabfcd042bb
, python3]: 12
shmsnoop
跟踪System V的共享内存系统调用:shmget、shmat、shmdt以及shmctl
命令使用语法
shmsnoop [options]
命令行选项
- -T:包含时间戳信息
- -p PID:仅关注给定的进程
shmsnoop抓取shmget和shmat调用
~# shmsnoop-bpfcc -T
TIME(s) PID COMM SYS RET ARGs
0.000000000 988552 shm_test SHMGET 9 key: 0x0, size: 1024, shmflg: 0x180 (0600)
0.000142000 988552 shm_test SHMAT 7fe7322ea000 shmid: 0x9, shmaddr: 0x0, shmflg: 0x0
1.000484000 988553 shm_test SHMAT 7fe7322ea000 shmid: 0x9, shmaddr: 0x0, shmflg: 0x0
8.432580000 988556 shm_test SHMGET a key: 0x0, size: 1024, shmflg: 0x180 (0600)
8.432749000 988556 shm_test SHMAT 7f1bda58c000 shmid: 0xa, shmaddr: 0x0, shmflg: 0x0
9.433112000 988557 shm_test SHMAT 7f1bda58c000 shmid: 0xa, shmaddr: 0x0, shmflg: 0x0
调用shmget和shmat的C代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/wait.h>
#define PERM (S_IRUSR | S_IWUSR)
int main ( int argc, char **argv ) {
int shmid;
char *p_addr, *c_addr;
if ( argc != 2 ) {
fprintf ( stderr, "Usage: %s\n", argv[0] );
exit ( 1 );
}
if ( ( shmid = shmget ( IPC_PRIVATE, 1024, PERM ) ) == -1 ) { /* 创建共享内存 */
fprintf ( stderr, "Create Share Memory Error: %s\n", strerror ( errno ) );
exit ( 1 );
}
if ( fork() ) { /* 父进程写 */
p_addr = ( char * ) shmat ( shmid, NULL, 0 );
memset ( p_addr, '\0', 1024 );
strncpy ( p_addr, argv[1], 1024 );
wait ( NULL );
exit ( 0 );
} else { /* 子进程读 */
sleep ( 1 ); /* 暂停1秒 */
c_addr = ( char * ) shmat ( shmid, NULL, 0 );
printf ( "Client get %s\n", c_addr );
exit ( 0 );
}
}
编译&运行C代码
# ./shm_test abcd
Client get abcd
# ./shm_test abcd
Client get abcd
faults
缺页异常和对应的调用栈信息,可以用BCC的stackcount工具对用户态和内核台的缺页异常进行统计。
用户态统计
~# stackcount-bpfcc -PU t:exceptions:page_fault_user
Tracing 1 functions for "t:exceptions:page_fault_user"... Hit Ctrl-C to end.
^C
b'[unknown]'
b'[unknown]'
b'stackcount-bpfc' [988657]
1
Detaching...
内核态统计
~# stackcount-bpfcc t:exceptions:page_fault_kernel
Tracing 1 functions for "t:exceptions:page_fault_kernel"... Hit Ctrl-C to end.
^C
b'do_page_fault'
b'do_page_fault'
b'do_async_page_fault'
b'async_page_fault'
b'__clear_user'
b'clear_user'
b'load_elf_binary'
b'search_binary_handler'
b'load_script'
b'search_binary_handler'
b'__do_execve_file.isra.0'
b'__x64_sys_execve'
b'do_syscall_64'
b'entry_SYSCALL_64_after_hwframe'
b'[unknown]'
1
缺页异常生成火焰图
~# stackcount-bpfcc -f -PU t:exceptions:page_fault_user > out.pagefault.txt
~# cat out.pagefault.txt
stackcount-bpfc;[unknown];[unknown] 1
gsd-color;[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];[unknown];g_time_zone_new 4
~# ./FlameGraph/flamegraph.pl --hash --width=800 --title="Page Fault Flame Page" --colors=java --bgcolor=green < out.pagefault.txt >out.pagefault.svg
ffaults
ffaults是一个bpftrace程序,使用该程序可以根据文件名来跟踪缺页异常,代码如下(有bug尚未修复):
#!/usr/bin/bpftrace
#include <linux/mm.h>
kprobe:handle_mm_fault
{
$vma = (struct vm_area_struct *)arg0;
$file = $vma->vm_file->f_path.dentry->d_name.name;
@[str($file)] = count();
}
执行结果如下
./ffaults.bt
Attaching 1 probe...
^C
@[]: 4701
drsnoop
用来跟踪内存释放过程中的直接回收部分
~# drsnoop-bpfcc -T
TIME(s) COMM PID LAT(ms) PAGES
命令行语法
drsnoop [options]
命令行选项
- -T:包括时间戳信息
- -p PID:仅关注给定的进程
llcstat-bpfcc
按进程显示末级缓存的命中率~# llcstat-bpfcc Running for 10 seconds or hit Ctrl-C to end. PID NAME CPU REFERENCE MISS HIT% 0 swapper/4 4 100 200 0.00% 0 swapper/1 1 200 200 0.00% 0 swapper/6 6 100 200 0.00% 9769 llcstat-bpfcc 7 200 300 0.00% 0 swapper/0 0 200 200 0.00% 0 swapper/5 5 100 200 0.00% 0 swapper/3 3 100 200 0.00% 0 swapper/2 2 100 200 0.00% Total References: 1100 Total Misses: 1700 Hit Rate: -54.55%
BPF单行程序
BCC
- 根据用户态调用栈信息统计进程堆内存扩展(brk())
stackcount -U t:syscalls:sys_enter_brk
- 根据用户态调用栈信息统计缺页错误
stackcount -U t:exceptions:page_fault_user
- 通过跟踪点来统计vmscan操作
funccount 't:vmscan:*'
- 按进程展示hugepage_madvise()调用:
trace hugepage_madvise
- 统计页迁移事件
funccount t:migrate:mm_migrate_pages
- 统计页压缩事件
tarce t:compaction:mm_compaction_begin
bpftrace
- 根据用户态调用栈信息统计进程堆内存扩展(brk())
bpftrace -e 't:syscalls:sys_enter_brk { @[ustack, comm] = count(); }'
- 按进程统计缺页错误
bpftrace -e 'software:page-faults:1 { @[comm, pid] = count(); }'
- 根据用户态调用栈信息统计缺页错误
bpftrace -e 't:exceptions:page_fault_user { @[ustack, comm] = count(); }'
- 通过跟踪点来统计vmscan操作
bpftrace -e 't:vmscan:* { @[probe] = count(); }'
- 按进程展示hugepage_madvise()调用:
bpftrace -e 'k:hugepage_madvise { printf ("%s by PID %d\n", probe, pid); }'
- 统计页迁移事件
bpftrace -e 't:migrate:mm_migrate_pages { @ = count(); }'
- 统计页压缩事件
bpftrace -e 't:compaction:mm_compaction_begin { time(); }'
Systemtap工具
systemtap工具无法与BPF工具一一对应,中途需要不少的开发工作,本次我们挑选的是systemtap默认自带的example来做讲解。
glibc-malloc
该程序对glibc malloc函数的内部实现做统计,通过-x/-c指定被统计的进程,如下所示。
~# stap glibc-malloc.stp -c "ls -l"
total 168
-rw-r--r--. 1 root root 381 Oct 14 2018 glibc-malloc.meta
-rw-r--r--. 1 root root 2491 Oct 14 2018 glibc-malloc.stp
[...]
-rwxr-xr-x. 1 root root 419 Oct 14 2018 vm.tracepoints.stp
-rw-r--r--. 1 root root 557 Oct 14 2018 vm.tracepoints.txt
malloc information for pid 16243
Contention:
Active arenas:
Allocated heaps:
Total sbrk: 135168 bytes
Mmap threshold in the end: 128 kb
pfaults
> stap pfaults.stp
2972446:8994:140735039790248d:w:minor:10
2972569:18551:93958907995264d:w:minor:14
2972582:18551:93958888569296d:r:minor:6
2972592:18551:93958888510448d:r:minor:1
2972637:18551:93958888554608d:r:minor:2
2972659:18551:93958888468416d:r:minor:2