curl https://repos.baslab.org/bpftools.repo --output /etc/yum.repos.d/bpftools.repo
yum install bpftrace bpftrace-tools bpftrace-doc bcc-static bcc-tools bpftool
# error GPG key retrieval failed: [Errno 14] HTTP Error 404: Not Fou
# 修改 vim /etc/yum.repos.d/bpftools.repo
# gpgcheck=0
bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n",comm,str(args->filename)); }'
bpftrace -l 'tracepoint:syscalls:sys_enter_open*'
bpftrace -e 'tracepoint:syscalls:sys_enter_open*{@[probe]=count();}'
bpftool prog show
bpftool prog dump xlated id 36
bpftool prog dump xlated id 37 opcodes
bpftrace -v /usr/share/bpftrace/tools/biolatency.bt
bpftrace -e 'kprobe:vfs_* {@[probe]=count();}'
perf top
find / -name 'libc-2.17.so'
# /usr/lib64/libc-2.17.so
bpftrace -l 'uprobe:/usr/lib64/libc-2.17.so:gethost*'
bpftrace -e 'uprobe:/usr/lib64/libc-2.17.so:gethost*{@[probe]=count();}'
RCU(Read-Copy Update)机制:首先将需要修改的内容复制出一份副本,然后在副本上进行修改操作。在写者进行修改操作的过程中,旧数据没有做任何更新,不会产生读写竞争,因此依然可以被读者并行访问。当写者修改完成后,写者直接将新数据内存地址替换掉旧数据的内存地址,由于内存地址替换操作是原子的,因此可以保证读写不会产生冲突。内存地址替换后,原有读者访问旧数据,新的读者将访问新数据。当原有读者访问完旧数据,进入静默期后,旧数据将被写者删除回收。当然,通常写者只进行更新、删除指针操作,旧数据内存的回收由另一个线程完成。
bpftrace -e 'tracepoint:sched:sched_process_exec {printf("exec by %s\n",comm);}'
模式
计数:PMC能够跟踪事件发生的频率。
溢出采样:PMC在所监控的事件发生到一定次数时通知内核,这样内核可以获得额外的状态。监控的事件可能会以每秒百万、亿级别的频率发生,如果对每次事件都进行中断会导致系统性能下降到不可用。解决方案就是利用一个可编程的计数器进行采样,具体来说,是当计数器溢出时就向内核发送信号(比如每10000次LLC缓存未命中事件,或者每百万次阻塞的指令时钟周期)
。vfsstat
bpftrace -e 'kprobe:vfs_read {@[comm] = count();}'
Linux60秒分析
uptime # 平均负载
dmesg|tail # 系统日志
vmstat 1 # r CPU正在执行和等待执行的进程数量。不包括I/O
mpstat -P ALL 1 # 将每个CPU分解到各个状态下的时间打印出来。
pidstat 1 # 按每个进程展示CPU的使用情况
iostat -xz 1 # 显示存储设备的I/O指标
free -m
sar -n DEV 1 # 网络设备指标
sar -n TCP,ETCP 1 # TCP指标和TCP错误信息。active/s 本地发起的TCP连接数/s。passive/s 远端发起的TCP连接数/s。 retrans/s TCP重传数/s
top
execsnoop # 跟踪execve系统调用
opensnoop # 跟踪open系统调用,包括打开文件的路径、打开操作是否成功
ext4slower(或者brtfs*、xfs*、zfs*) # 跟踪ext4文件系统的常见操作
biolatency # 跟踪磁盘I/O延迟
biosnoop # 将每一次磁盘I/O请求打印出来,包含延迟之类的细节信息。
cachestat # 展示文件系统缓存的统计信息。
tcpconnect
tcpaccept
tcpretrans
runqlat # 线程等待CPU运行的时间进行统计
profile # CPU剖析器,可以用来理解那些代码路径消耗了CPU资源。周期性的对调用栈进行采样,然后将消重后的调用栈连同出现的次数一起打印出来。
funccount:对事件--特别是函数调用--进行计数
funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] [-D] pattern
Count all malloc() calls in libc:
# funccount c:malloc
展示每秒块I/O事件的数量
# funccount -i 1 't:block:*'
展示每秒libc中getaddrinfo()(域名解析)函数的调用次数
# funccount -i 1 c:getaddrinfo
对libgo中全部的“os.*”调用进行计数
# funccount 'go:os.*'
u:lib:name 对lib库中名为name的USDT探针进行插桩
path:name 对位于path路径下文件中的用户态函数name()进行插桩
stackcount:对导致某事件发生的函数调用栈进行计数
。stackcount可以回答如下问题:
火焰图
stackcount -fP -D 10 ktime_get>out.stackcount01.txt
git clone https://github.com/brendangregg/FlameGraph.git
cd FlameGraph
/Users/junmo/www/FlameGraph/flamegraph.pl --hash --bgcolors=grey < out.stackcount01.txt > out.stackcount01.svg
对创建块I/O的函数调用栈进行计数
stackcount t:block:block_rq_insert
对发送IP数据包的调用栈进行计数
stackcount ip_output
对导致线程阻塞并且导致脱离CPU的调用栈进行计数
stackcount t:sched:sched_switch
对导致系统调用read()的调用栈进行计数
stackcount t:syscalls:sys_enter_read
# arg2是do_sys_open()函数的第二个参数,打印文件名
trace 'do_sys_open "%s",arg2'
# 跟踪内核函数do_sys_open(),并打印返回值
trace 'r::do_sys_open "ret: %d", retval'
# 跟踪do_nanosleep(),并且打印用户态的调用栈
trace -U 'do_nanosleep "mode: %d",arg2'
# 跟踪通过pam库进行身份鉴别的请求
trace 'pam:pam_start "%s: %s",arg1,arg2'
# bcc使用系统头文件和内核头文件来获取结构体信息
trace 'do_nanosleep(struct hrtimer_sleeper *t) "task: %x",t->task'
trace -I 'net/sock.h' 'udpv6_sendmsg(struct sock *sk) (sk->sk_dport == 13568)'
argdist -H 'r::__tcp_select_window():int:$retval'
# 将内核函数vfs_read的返回值以直方图的形式打印出来
argdist -H 'r::vfs_read()'
# 以直方图对pid为1005的进程的用户态调用libc的read()函数的返回值(size)进行统计输出
argdist -p 1005 -H 'r:c:read()'
# Aggregate interrupts by interrupt request (IRQ)
argdist -C 't:irq:irq_handler_entry():int:args->irq'
bpftrace -e 'tracepoint:syscalls:sys_enter_execve {printf("%s -> %s\n",comm,str(args->filename))}'
# 展示新进程的创建,以及参数信息
bpftrace -e 'tracepoint:syscalls:sys_enter_execve {join(args->argv)}'
# 展示进程的磁盘I/O尺寸
bpftrace -e 'tracepoint:block:block_rq_issue {printf("%d %s %d\n",pid,comm,args->bytes)}'
#!/usr/bin/bpftrace
BEGIN
{
printf("Hello world!\n");
}
END
{
printf("Game over!\n");
}
kprobe:vfs_read
{
@start[tid] = nsecs;
}
kretprobe:vfs_read
/@start[tid]/ // 过滤器
{
$duration_us = (nsecs - @start[tid]) / 1000;
@us[pid,comm] = hist($duration_us);
delete(@start[tid]);
}
变量
探针类型
bpftrace -lv tracepoint:syscalls:sys_enter_read
bpftrace控制流
bpftrace内置变量
bpftrace函数
bpftrace映射表的操作函数
bpftrace -e 'k:vfs_* {@[probe] = count();} END {print(@,5);clear(@);}'
# perf.data
perf record -F 99 -a -g -o cycle_0526.perf -- sleep 30
# 用perf script工具对cycle_0526.perf进行解析
perf script -i cycle_0526.perf &> perf.unfold
# 将perf.unfold中的符号进行折叠:
./stackcollapse-perf.pl perf.unfold &> perf.folded
# svg
./flamegraph.pl perf.folded > perf.svg
execsnoop 跟踪全系统中新进程执行信息的工具
。利用这个工具可以找到消耗大量CPU的短进程
,并且可以用来分析软件执行过程,包括启动脚本等。# bpftrace版本实现
bpftrace -e 't:syscalls:sys_enter_execve {printf ("%-10u %-5d ",elapsed/1000000,pid);join(args->argv);}'
exitsnoop 跟踪进程退出事件,打印出进程的总运行时长和退出原因。可以帮助调试短时进程。
runqlat: CPU调度延迟分析工具。
在需求超过供给,CPU资源处于饱和状态时,这个工具可以用来识别和量化问题的严重性。runqlat利用对CPU调度器的线程唤醒事件和线程上下文切换事件的跟踪来计算线程从唤醒到运行之间的时间间隔。runqlat 10 1 # 运行10s,只打印1次。nsecs单位为微秒
# -m 以毫秒为单位输出
# -P 给每个进程打印一个直方图
# --pidnss 给每个PID空间打印一个直方图
# -p PID 指定进程
# -T 输出包含时间戳
runqlen 采样CPU运行队列的长度信息,可以统计有多少线程正在等待运行,并以直方图的方式输出。
runqlen -C 10 1
# -C 每个CPU输出一个直方图
# -O 运行队列占有率信息,运行队列不为0的时长百分比
# -T 输出时间戳信息
runqslower 可以列出运行队列中等待延迟超过阈值的线程名字,可以输出受延迟影响的进程名和对应的延时时长。
cpudist 用来展示每次线程唤醒之后在CPU上执行的时长分布。
在内部跟踪CPU调度器的上下文切换事件,在繁忙的生产环境中发生的频率很高,额外消耗显著,使用时多小心。
profile:定时采样调用栈信息并且汇报调用栈出现频率信息
。默认以49Hz的频率同时采样所有的CPU的用户态和内核态的调用栈。
profile -af 30 > out.stacks01
./flamegraph.pl --color=java < ../out.stacks01 > out.svg
# 核心实现等同于如下bpftrace
bpftrace -e 'profile:hz:49 /pid/ { @samples[ustack,kstack,comm]=count();}'
offcputime 用于统计线程阻塞和脱离CPU运行的时间,同时输出调用栈信息
,以便理解阻塞原因。这个工具正好是profile工具的对立面;这两个工具结合起来覆盖了线程的全部生命周期:profile覆盖在CPU之上运行的分析,而offcputime则分析脱离CPU运行的时间
# 内核调用栈5秒的火焰图
offcputime -fKu 5 > out.offcuptime01.txt
./flamegraph.pl --hash --bgcolors=blue --title="OFF-CPU Time Flame Graph" \
< out.offcputime01.txt > out.offcputime01.svg
syscount 统计系统中的系统调用数量
./usr/share/bcc/tools/syscount -LP
bpftrace -e 't:syscalls:sys_enter_*{@[probe]=count();}'
argdist和trace:针对每个事件自定义处理方法、
# 获取参数名字
$ tplist -v syscalls:sys_enter_read
syscalls:sys_enter_read
int __syscall_nr;
unsigned int fd;
char * buf;
size_t count;
# count read调用缓存的大小
argdist -H 't:syscalls:sys_enter_read():int:args->count'
argdist -H 't:syscalls:sys_exit_read():int:args->ret'
# 等价bpftrace程序如下
bpftrace -e 't:syscalls:sys_enter_read {@ = hist(args->count);}'
bpftrace -e 't:syscalls:sys_exit_read {@ = hist(args->ret);}'
# bpftrace针对负值有一个单独的统计区间([...,0]),read的返回值如果是负数就说明出现了错误
# 统计出现的错误频率
bpftrace -e 't:syscalls:sys_exit_read /args->ret<0/ {@ = lhist(- args->ret,0,100,1);}'
trace 't:syscalls:sys_enter_execve "-> %s", args->filename'
funccount 可以统计事件和函数调用频率。此工具是根据函数动态跟踪来统计的:对内核态函数使用kprobes,对用户态函数使用uprobes
funccount 'tcp_*' # 统计内核以tcp_开头的所有函数
funccount -i 1 get_page_from_freelist
bpftrace -e 'k:tcp_* {@[probe] = count();} interval:s:1{print(@);clear(@);}'
softirqs 显示系统中软中断消耗的CPU时间。
bpftrace -e 't:irq:softirq_entry {@[args->vec] = count();}'
hardirqs 显示系统处理硬中断的时间。
cpuwalk.bt 采样每个CPU上运行的进程名,并且以线性直方图的方式输出。
内存页的生命周期:
进程所使用的全部物理内存数量称为常驻集大小(RSS)。
用户态内存分配 usdt:/usr/lib64/libc-2.28.so:*
内核态内存分配 t:kmem:*
堆内存扩展
共享内存函数
缺页错误 kprobes、软件事件,以及exception跟踪点
页迁移 t:migration:*'
页压缩 t:compaction:*
VM扫描器 t:vmscan:*
内存访问周期 PMC
bpftrace -l 'usdt:/usr/lib64/libc-2.28.so:*'
内存分析策略
传统工具
oomkill:用来跟踪OOM Killer事件的信息,以及打印出平均负载等详细信息。
memleak:用来跟踪内存分配和释放事件对应的调用栈信息。随着时间的推移,这个工具可以显示长期不被释放的内存。根据内存分配频繁程度,性能会下降,只能用来调试。
mmapsnoop:跟踪全系统的mmap系统调用并打印出映射请求的详细信息,这对内存映射调试来说是很有用的
。
bpftrace -e 't:syscalls:sys_enter_mmap {@[comm] = count();}'
brkstack:跟踪brk调用
trace -U t:syscalls:sys_enter_brk
stackcount -PU t:syscalls:sys_enter_brk
bpftrace -e 't:syscalls:sys_enter_brk {@[ustack,comm]=count();}'
shmsnoop:可以跟踪System V的共享内存系统调用
:shmget()、shmat()、shmdt、以及shmctl()。faults:跟踪缺页错误和对应的调用栈信息
,截取首次使用该内存触发缺页错误的代码路径。缺页错误会直接导致RSS的增长,所以这里截取的调用栈信息可以用来解释进程内存用量的增长。stackcount -U t:exceptions:page_fault_user
stackcount t:exceptions:page_fault_kernel
火焰图
stackcount -f -PU t:exceptions:page_fault_user > out.pagefaults01.txt
flamegraph.pl --hash --width=800 --title="Page Fault Flame Graph" \
--color=java --bgcolor=green < out.pagefaults01.txt >out.pagefaults01.svg
bpftrace -e 's:page-faults:1 {@[ustack,comm]=count();}'
ffaults根据文件名来跟踪缺页错误。
find / -name "mm.h"
bpftrace --include "/usr/src/kernels/4.18.0-193.28.1.el8_2.x86_64/include/linux/mm.h" -e 'k:handle_mm_fault{$vma=(struct vm_area_struct *)arg0; $file=$vma->vm_file->f_path.dentry->d_name.name; @[str($file)]=count();}'
vmscan:使用vmscan跟踪点来观察页换出守护进程(kswapd)的操作,该进程在系统内存压力上升时负责释放内存以便重用
。funccount 't:vmscan:*'
drsnoop:用来跟踪内存释放过程中直接回收部分,可以显示受到影响的进程,以及对应的延迟
:直接回收所需的时间。可以用来分析内存受限的系统中应用程序的性能影响。swapin:展示了那个进程正在从换页设备中换入页,前提是系统有正在使用的换页设备。
bpftrace -e 'k:swap_readpage{@[comm,pid]=count();} interval:s:1{time();print(@);clear(@)}'
hfaults:通过跟踪巨页相关的缺页错误信息,按进程展示详细信息。
bpftrace -e 'k:hugetlb_fault{@[pid,comm]=count();}'
strace -tttT cksum /usr/bin/cksum
perf trace cksum /usr/bin/cksum
perf stat -e 'ext4:*' -a
perf record -e ext4:ext4_da_write_begin -a // 由于perf.data是写入文件系统的,如果跟踪的是文件系统的写事件,那么就会产生一个自反馈循环
bpftrace -e 't:ext4:ext4_da_write_begin{@ = hist(args->len);}'
#!/usr/bin/bpftrace
#include
kprobe:do_mmap{
$file = (struct file *)arg0;
$name = $file->f_path.dentry;
$dir1 = $name->d_parent;
$dir2 = $dir1->d_parent;
@[str($dir2->d_name.name), str($dir1->d_name.name),str($name->d_name.name)] = count();
}
#!/usr/bin/bpftrace
#include
#include
#include
t:syscalls:sys_enter_read{
$task = (struct task_struct *)curtask;
$file = (struct file *)*($task->files->fdt->fd + args->fd); // 运行失败
@filename[str($file->f_path.dentry->d_name.name)] = count();
}
#!/usr/bin/bpftrace
#include
kprobe:filemap_fault{
$vf = (struct vm_fault *)arg0;
$file = $vf->vma->vm_file->f_path.dentry->d_name.name;
@[comm, str($file)] = count();
}
funccount 'vfs_*'
bpftrace -e 'kprobe:vfs_* {@[func] = count();}'
磁盘I/O性能:
整体分析策略
传统工具
iostat:按磁盘分别输出I/O统计信息,可以提供IOPS、吞吐量、I/O请求时长,以及使用率的信息。
perf:
# 跟踪进入队列的请求、发往存储设备的请求,以及完成的请求
perf record -e block:block_rq_insert,block:block_rq_issue,block:block_rq_complete -a
perf script
df -h
blktrace /dev/vda1 # 后台统计
blkparse vda1.blktrace.0 # 解析后台统计
btrace /dev/vda1 # 前台展示
sysctl -w dev.scsi.logging_level=0x1b6db6db
echo 0x1b6db6db > /proc/sys/dev/scsi/logging_level
dmesg
biolatency:以直方图方式统计块I/O设备的延迟信息。这里的设备延迟指的是向设备发出请求到请求完成的全部时间,这包括在操作系统内部排队的时间。单位微秒
biosnoop:可以针对每个磁盘I/O打印一行信息
biotop: 块设备I/O top版
-C 不刷新屏幕
bitesize:展示磁盘I/O的尺寸。使用block:block_rq_issue
biostacks.bt:可以跟踪完整的I/O延迟(从进入操作系统队列到设备完成I/O请求),同时显示初始化该I/O请求的调用栈信息。
mdflush:跟踪来自md的缓冲清空请求。md是多重设备驱动程序,用在某些系统上实现软RAID。
iosched:跟踪I/O请求在I/O调度器中排队的时间,并且按照调度器名称分组显示。【脚本执行报错】
#!/usr/bin/bpftrace
#include
BEGIN{printf("Tracing block I/O schedulers. Hit Ctrl-C to end.\n");}
kprobe:__elv_add_request{@start[arg1] = nsecs;}
kprobe:blk_start_request,kprobe:blk_mq_start_request/@start[arg0]/{
$r = (struct request*)arg0;
@usecs[$r->q->elevator->type->elevator_name] = hist((nsecs - @start[arg0])/1000);
delete(@start[arg0]);
}
END{clear(@start);}
队列管理器:一个可选的网络层,可用于流量分类(tc)、调度、数据包修改、流量过滤,以及流量整形等
设备驱动程序:驱动程序内部有可能有自己的驱动程序内部队列(网卡的RX-ring和TX-ring,接收环形缓冲区与发送环形缓冲区)
NIC(网络接口卡):包含物理网络端口的设备。也可能是虚拟设备,例如隧道接口、veth虚拟网卡设备,以及回送接口loopback。
内核绕过技术:应用程序可以使用数据层开发套件(DPDK)这样的技术来绕过内核网络软件栈,这样可以提高性能,提高网络包处理能力。这种技术需要应用程序在用户态实现自己的网络软件栈,使用DPDK软件库和内核用户态I/O驱动(UIO)或者虚拟I/O驱动(VFIO)来直接向网卡设备驱动程序发送数据。可以通过直接从网卡内存中读取数据包的技术来避免数据的多次复制。由于绕过了整个内核网络栈,导致无法使用传统工具跟踪功能和性能指标。
高速数据路径技术(XDP)为网络数据包提供了另外一条通道:一个可以使用扩展BPF编程的快速处理通道,与现有的内核软件栈可以直接集成,无需绕过。由于这种技术使用网卡驱动程序中内置的BPF钩子直接访问原始网络帧数据,因而可以避免TCP/IP软件栈处理的额外消耗,而直接告诉网卡是应该传递还是丢弃数据包。当有需要时,这种技术还可以回退到正常的网络栈处理过程。这种技术的应用场景包括快速DDos缓解,以及软件定义路由SDR等场景。
TCP积压队列:当内核收到一个TCP SYN包时,就会开启一个新的被动TCP连接。
测量延迟
TCP首字节延迟:也称为TTFB,指的是从连接建立到客户端收到第一个字节的时间。这个时间包括各主机上CPU调度和应用程序的工作时间,所以这个指标衡量的更多的是应用程序的性能和当前负载,而不是TCP连接延迟
分析策略
传统工具
ss -tiepm # i 显示tcp内部信息 e 显示扩展套接字信息 p 进程 m 内存用量
ESTAB 0 0 172.17.142.151:https 222.217.132.169:37986
users:(("nginx",pid=28735,fd=241)) ino:496282700 sk:1916645 <->
skmem:(r0,rb369280,t0,tb188928,f0,w0,o0,bl0,d0) ts sack cubic wscale:5,7 rto:288 rtt:77.815/2.358
ato:40 mss:1388 pmtu:1500 rcvmss:536 advmss:1448 cwnd:62 ssthresh:57 bytes_acked:222613 bytes_received:2104
segs_out:167 segs_in:79 data_segs_out:166 data_segs_in:5 send 8.8Mbps lastsnd:13611 lastrcv:13871
lastack:13531 pacing_rate 10.6Mbps delivery_rate 7.8Mbps app_limited busy:702ms retrans:0/1 rcv_space:28960
rcv_ssthresh:33248 minrtt:60.914
rto:288:TCP重传超时时间288ms
rtt:77.815/2.358:平均往返时间是77.815ms,2.358ms为平均误差
mss:1388:最大段尺寸为1388
cwnd:62:拥塞窗口尺寸为62*1388
bytes_acked:222613:成功传输了222KB
pacing_rate 10.6Mbps:节奏控制率控制在10.6Mbps
ip 管理路由、网络设备、接口以及隧道的工具
nstat 打印内核维护的各种网络指标,运行一次可以清空计数,使用-s选项避免清空
netstat
sar 系统活动报表工具
nicstat 网络接口统计信息
ethtool 可以利用-i和-k选项检查网络接口的静态配置信息,也可以使用-S来打印驱动程序的统计信息
tcpdump 网络包嗅探器
/proc/net
sockstat 打印套接字统计信息
# 按进程统计
bpftrace -e 'kprobe:sock_sendmsg{ @[comm] =count();}'
#!/usr/bin/bpftrace
BEGIN
{
printf("Tracing sock statistics, Output every 1 second.\n");
}
tracepoint:syscalls:sys_enter_accept*,
t:syscalls:sys_enter_connect,
t:syscalls:sys_enter_bind,
t:syscalls:sys_enter_socket*,
k:sock_recvmsg,
kprobe:sock_sendmsg
{
@[probe] = count();
}
interval:s:1
{
time();
print(@);
clear(@);
}
#!/usr/bin/bpftrace
#include
BEGIN
{
printf("Tracing socket connect/accepts. Ctrl-C to end.\n");
// from linux/socket.h
@fam2str[AF_UNSPEC] = "AF_UNSPEC";
@fam2str[AF_UNIX] = "AF_UNIX";
@fam2str[AF_INET] = "AF_INET";
@fam2str[AF_INET6] = "AF_INET6";
}
t:syscalls:sys_enter_connect
{
@connect[comm,args->uservaddr->sa_family,@fam2str[args->uservaddr->sa_family]] = count();
}
tracepoint:syscalls:sys_enter_accept,
tracepoint:syscalls:sys_enter_accept4
{
@sockaddr[tid] = args->upeer_sockaddr;
}
tracepoint:syscalls:sys_exit_accept,
tracepoint:syscalls:sys_exit_accept4
/@sockaddr[tid]/
{
if (args->ret >0){
$sa = (struct sockaddr *)@sockaddr[tid];
@accept[comm,$sa->sa_family,@fam2str[$sa->sa_family]] = count();
}
delete(@sockaddr[tid]);
}
END
{
clear(@sockaddr);clear(@fam2str);
}
#!/usr/bin/bpftrace
#include
BEGIN
{
printf("Tracing socket connect/accepts. Ctrl-C to end.\n");
// from linux/socket.h
@prot2str[IPPROTO_IP] = "IPPROTO_IP";
@prot2str[IPPROTO_ICMP] = "IPPROTO_ICMP";
@prot2str[IPPROTO_TCP] = "IPPROTO_TCP";
@prot2str[IPPROTO_UDP] = "IPPROTO_UDP";
}
kprobe:security_socket_accept,
kprobe:security_socket_connect
{
$sock = (struct socket *)arg0;
$protocol = $sock->sk->sk_protocol & 0xff;
@connect[comm,$protocol,@prot2str[$protocol],$sock->sk->__sk_common.skc_prot->name] = count();
}
END
{
clear(@prot2str);
}
@read_bytes[nginx]:
[0] 672 |@@@@@@@@@@@@@@@@@@@@@@@ |
[1] 695 |@@@@@@@@@@@@@@@@@@@@@@@@ |
[2, 4) 0 | |
[4, 8) 3 | |
[8, 16) 0 | |
[16, 32) 128 |@@@@ |
[32, 64) 209 |@@@@@@@ |
[64, 128) 407 |@@@@@@@@@@@@@@ |
[128, 256) 547 |@@@@@@@@@@@@@@@@@@@ |
[256, 512) 675 |@@@@@@@@@@@@@@@@@@@@@@@ |
[512, 1K) 1088 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[1K, 2K) 187 |@@@@@@ |
[2K, 4K) 162 |@@@@@ |
[4K, 8K) 1474 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[8K, 16K) 1232 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[16K, 32K) 50 |@ |
[32K, 64K) 122 |@@@@ |
# I/O数量 平均字节数 总吞吐量字节数
@write_bytes[nginx]: count 7135, average 9346, total 66689498
@rmem_alloc:
[0] 3191 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[1] 0 | |
...
[1K, 2K) 287 |@@@ |
[2K, 4K) 4235 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[4K, 8K) 806 |@@@@@@@@@ |
[8K, 16K) 1042 |@@@@@@@@@@@@ |
[16K, 32K) 607 |@@@@@@@ |
[32K, 64K) 558 |@@@@@@ |
[64K, 128K) 629 |@@@@@@@ |
[128K, 256K) 914 |@@@@@@@@@@@ |
[256K, 512K) 140 |@ |
@rmem_limit:
[128K, 256K) 2186 |@@@@@@@@@@@@@@@@ |
[256K, 512K) 6721 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[512K, 1M) 0 | |
[1M, 2M) 2834 |@@@@@@@@@@@@@@@@@@@@@ |
[2M, 4M) 59 | |
[4M, 8M) 1119 |@@@@@@@@ |
[8M, 16M) 0 | |
[16M, 32M) 2 | |
w # 查看当前的终端
ttysnoop 1
从ELF符号表中得到函数符号 readelf -s /bin/ls
tplist 可以列出某个进程当前使用的USDT探针 tplist -p PID