首页 文章 精选 留言 我的

精选列表

搜索[ebpf],共420篇文章
优秀的个人博客,低调大师

Polycube —— 基于 eBPF/XDP 的软件框架

Polycube 是一个为云、边和物联网构建高性能安全网络的开放框架。 一个 "cube" 包含一个独立的网络功能,或者一个可复用的网络功能的模块,多个 "cube" 可以被链接或构建成栈,来创建一个更强大的网络。 包括桥接、路由、NAT、防火墙等网络功能已经得以实现,更多的功能正在被开发。 一个具有一键部署特性的 k8s CNI 插件已经准备好,它使得 Polycube-CNI 成为可用于容器集群的最易用的 "networking provider"。 目前 Polycube 还包含 Polycube-CNI 和 PCN-iptables 等项目。

优秀的个人博客,低调大师

eBPF监控工具bcc系列七开发脚本

bcc开发脚本有两种方式,一种是基于python接口,另一种是基于ruby接口,我们看的是基于python接口的。 本篇的前置条件是系统中已经安装好了bcc。 1. Hello world 输入代码如下: #!/usr/bin/env python from bcc import BPF BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print() 执行后只要有进程执行就会输出Hello,World!字符串。 主要代码其实就是一句 BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print() 如果是x64系统上4.17 内核版本,,可能需要将kprobe__sys_clone 替换成kprobe____x64_sys_clone。 我们看下语法: text=’’表示定义了一个BPF内联程序,程序用C实现。 kprobe__sys_clone是通过kprobes的内核动态跟踪,如果代码中以kprobe__ 开始,后面紧接着的是需要跟踪的内核函数例如sys_clone() void *ctx,可以有参数 bpf_trace_printk()是一个内核的printf函数。不过参数有限最多3个,只能输出字符串,全局共享输出冲突,最好使用BPF_PERF_OUTPUT() return 0,最后返回0. .trace_print()是bcc的程序,读取trace_pipe中数据并输出。 这个就是使用python接口实现bcc工具的最简单程序。 可以将kprobe__sys_open改成其他的系统调用例如:kprobe__sys_sync、kprobe__sys_close等等,你想监控的系统调用。是不是很方便? 2. trace_fields() 使用trace_fields可以格式化输出,其结果来自bpf_trace_printk()函数输出,示例代码如下: #!/usr/bin/env python from bcc import BPF # define BPF program prog = """ int hello(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; } """ # load BPF program b = BPF(text=prog) b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) 同hello world示例相似,不过此处将C代码prog定义为变量,其中有函数hello(),变量方式在有字符串参数的时候很有用。 本篇中使用attach_kprobe来创建sys_clone的kprobe,当触发时候运行hello程序。可以调用多次attach_kprobe来附加C程序给多个内核函数。 最后通过trace_fields来返回来自trace_pipe的一组域。当然trace_print适合调试,真正的工具应该使用BPF_PERF_OUTPUT()。 3. 磁盘处理 跟踪磁盘需要相关内核函数,所以对磁盘处理内核函数要有了解,不然无法定义去跟踪那个函数。源码如下,定义了C函数trace_start、trace_complete,分别附加到内核函数blk_start_request和blk_complete_request。注意的是,blk_start_requst中的函数就是所追踪函数的参数。参数是request结构体指针,用该指针作为hash表的健,可以有效保证唯一性,此外还有进程ID。 #!/usr/bin/python from __future__ import print_function from bcc import BPF REQ_WRITE = 1 # from include/linux/blk_types.h # load BPF program b = BPF(text=""" #include <uapi/linux/ptrace.h> #include <linux/blkdev.h> BPF_HASH(start, struct request *); void trace_start(struct pt_regs *ctx, struct request *req) { // stash start timestamp by request ptr u64 ts = bpf_ktime_get_ns(); start.update(&req, &ts); } void trace_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp, delta; tsp = start.lookup(&req); if (tsp != 0) { delta = bpf_ktime_get_ns() - *tsp; bpf_trace_printk("%d %x %d\\n", req->__data_len, req->cmd_flags, delta / 1000); start.delete(&req); } } """) b.attach_kprobe(event="blk_start_request", fn_name="trace_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_start") b.attach_kprobe(event="blk_account_io_completion", fn_name="trace_completion") # header print("%-18s %-2s %-7s %8s" % ("TIME(s)", "T", "BYTES", "LAT(ms)")) # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (bytes_s, bflags_s, us_s) = msg.split() if int(bflags_s, 16) & REQ_WRITE: type_s = "W" elif bytes_s == "0": # see blk_fill_rwbs() for logic type_s = "M" else: type_s = "R" ms = float(int(us_s, 10)) / 1000 print("%-18.9f %-2s %-7s %8.2f" % (ts, type_s, bytes_s, ms)) 可以执行每个请求的处理时间。 4. 直方图 直方图实现示例如下,结束后会将IO请求的大小画成直方图: #!/usr/bin/python from bcc import BPF from time import sleep # load BPF program b = BPF(text=""" #include <uapi/linux/ptrace.h> #include <linux/blkdev.h> BPF_HISTOGRAM(dist); int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req) { dist.increment(bpf_log2l(req->__data_len / 1024)); return 0; } """) # header print("Tracing... Hit Ctrl-C to end.") # trace until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: print # output b["dist"].print_log2_hist("kbytes") 其中,BPF_HISTOGRAM(dist)定义BPF 直方图映射对象,名字叫做dist。 dist.increment()函数会增加直方图中各个值,值由参数指定。 bpf_log2l()将值变成log-2模式。 print_log2_hist(“kbytes”)打印dist直方图,列单位为kbytes。内核到用户层只传输直方图变量数量,保证高效。 5. TRACEPOINT tracepoint比较稳定,如果可以都建议来替代kprobes。可以使用perf list来列出可用的tracepoints。将BPF程序附加到tracepoints需要内核版本大于4.7。 TRACEPOINT_PROBE(random,urandom_read)是内核的tracepoint random:urandom_read。其格式位于 /sys/kernel/debug/tracing/events/random/urandom_read/format 跟踪随机读源码: #!/usr/bin/python from __future__ import print_function from bcc import BPF # load BPF program b = BPF(text=""" TRACEPOINT_PROBE(random, urandom_read) { // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format bpf_trace_printk("%d\\n", args->got_bits); return 0; } """) # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "GOTBITS")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) 6. 跟踪用户层函数 跟踪用户层函数使用uprobe,对应的bpf函数是attach_uprobe。 例如:b.attach_uprobe(name="c", sym="strlen", fn_name="count") 附加到C库,函数为strlen,对应的处理函数为count。 #!/usr/bin/python from __future__ import print_function from bcc import BPF from time import sleep # load BPF program b = BPF(text=""" #include <uapi/linux/ptrace.h> struct key_t { char c[80]; }; BPF_HASH(counts, struct key_t); int count(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; struct key_t key = {}; u64 zero = 0, *val; bpf_probe_read(&key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx)); val = counts.lookup_or_init(&key, &zero); (*val)++; return 0; }; """) b.attach_uprobe(name="c", sym="strlen", fn_name="count") # header print("Tracing strlen()... Hit Ctrl-C to end.") # sleep until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: pass # print output print("%10s %s" % ("COUNT", "STRING")) counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): print("%10d \"%s\"" % (v.value, k.c.encode('string-escape'))) 7. 使用USDT USDT在python中有支持。 USDT(pid=int(pid))初始化指定进程的USDT. u.enable_probe(probe="http__server__request", fn_name="do_trace") 绑定BPF的C函数到http__server__request的USDT probe。 BPF(text=bpf_text, usdt_contexts=[u]) 传递USDT对象到BPF中。 8. 相关bpf接口函数 bpf_ktime_get_ns()返回纳秒时间。 BPF_HASH(last)创建BPF映射对象,叫做last。如果没有指定任何参数,所以健值都是无符号64位。 bpf_trace_print输出字符串,类似printf ,在调试中使用个,工具中使用BPF_PERF_OUTPUT(). bpf_get_current_pid_tgid()函数获得pid进程,其中低32位是进程ID,高32位是组id。 BPF_PERF_OUTPUT(events)命名输出频道名字为events. bpf_get_current_common()函数用当前进程名字填充第一个参数地址。 events.perf_submit()通过ring buffer将事件提交到用户层。 9. 参考: https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md bcc Python Developer Tutorial

优秀的个人博客,低调大师

eBPF监控工具bcc系列九bcc Python

接下来看下python方面的知识。 1. 初始化 1.1 BPF 语法: BPF({text=BPF_program | src_file=filename} [, usdt_contexts=[USDT_object, ...]]) 创建一个BPF对象,能通过交互来产生输出。 1.2 USDT 语法:USDT({pid=pid | path=path}) 创建对象来使用USDT,可以指定进程ID,路径。 2. 事件 2.1 attach_kprobe 语法:BPF.attach_kprobe(event="event", fn_name="name") 使用函数入口的内核动态跟踪,关联C函数name和内核函数event()。 2.2 attach_kretprobe 语法:BPF.attach_kretprobe(event="event", fn_name="name") 关联C函数name和内核函数event,在内核函数返回的时候调用函数name. 2.3 attach_tracepoint 语法:BPF.attach_tracepoint(tp="tracepoint", fn_name="name") 关联C语言定义的BPF函数和内核的tracepoint。也可以使用TRACEPOINT_PROBE宏,使用该宏可以使用高级的自申明的args结构体包含了tracepoint参数。如果,使用attach_tracepoint,参数需要在BPF程序中声明。 2.4 attach_uprobe 语法:BPF.attach_uprobe(name="location", sym="symbol", fn_name="name") 将在location中的函数事件symbol,关联到C定义的函数。当symbol调用时候回调用name函数。 例如: b.attach_uprobe(name="c", sym="strlen", fn_name="count") 2.5 attach_uretprobe 语法:BPF.attach_uretprobe(name="location", sym="symbol", fn_name="name") 同attach_uprobe,不过是在函数返回时候调用name函数。 2.6 USDT.enable_probe 语法:USDT.enable_probe(probe=probe, fn_name=name) 将BPF的C函数附加到USDT探针上。 例如: u = USDT(pid=int(pid)) u.enable_probe(probe="http__server__request", fn_name="do_trace") 查看二进制文件是否有USDT探针,可以使用如下命令检测stap调试段: #readelf –n binary 3. 调试输出 3.1 trace_print 语法:BPF.trace_print(fmt="fields") 持续读取全局共享的/sys/kernel/debug/tracing/trace_pipe文件并输出。这个文件可以被BPF 和bpf_trace_printk()函数写入。 例如: # print trace_pipe output as-is: b.trace_print() # print PID and message: b.trace_print(fmt="{1} {5}") 3.2 trace_fields 语法:BPF.trace_fields(nonblocking=False) 从全局共享文件/sys/kernel/debug/tracing/trace_pipe文件中读取一行并返回域。参数表示在等待写入的时候是否blocking. 4. 输出 4.1 perf_buffer_poll 语法:BPF.perf_buffer_poll() 从perf ring buffers等待数据,有数据会调用open_perf_buffer指定的回调函数。 例如: # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: b.perf_buffer_poll() 5. 映射 5.1 get_table 返回表对象。此函数已经淘汰,因为BFP可以将表作为items来读取,例如BFP[name]. 5.2 open_perf_buffer 语法:table.open_perf_buffers(callback, page_cnt=N, lost_cb=None) 当perf ring buffer中数据可用的时候,调用callback函数。其中table是定义在BPF的。这个是从内核到用户层传递perf event数据的建议方式。 perf ring buffer的大小由page_cnt参数制定,建议是页的偶数倍,默认是8。如果callback处理的不够快,有些数据会丢失掉。当有丢失数据到时候会调用lost_cb。如果lost_cb定义为none,那么会打印一行信息到stderr。 5.3 items 返回表中keys的数组。可以通过BPF_HASH来获取,迭代。 5.4 values 返回表中values数组。 5.5 clear 清除表。 5.6 print_log2_hist 语法:table.print_log2_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None) 使用ASCII以log2直方图打印表。表必须用log2方式存储,这个可以通过bpf_log2()。 val_type可选的,表示列头。 section_header:如果直方图有第二个键,多个表会被打印,section_header会被作为头描述。 如果section_print_fn不是none,传递bucket值。 5.7 print_linear_hist 语法:table.print_linear_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None) 以ASCII方式打印表的线性直方图。 val_type参数可选的,列的头 section_header:如果直方图有第二个键,多个图会打印,section_header会被作为头描述符。 section_print_fn:如果该参数不会NONE,会被传递bucket值。 6. 帮助 6.1 ksym 语法:BPF.ksym(addr) 将一个内核内存地址转成一个内核函数名字。 6.2 ksymname 语法:BPF.ksymname(name) 将一个内核名字转换成一个地址,是ksym的逆向函数。 6.3 sym 语法:BPF.sym(addr, pid, show_module=False, show_offset=False) 为进程转换一个内存地址位函数名字。 参数show_module,show_offset参数控制是否显示symbol符号所在的模块,符号偏移。 6.4 num_open_kprobes 返回打开的k[ret]probes数量。在event_re场景中有用。 7. 关于BPF Errors BPF中所有内存读取都要通过bpf_probe_read()函数将内存复制到BPF栈。如果直接读取内存,会出现Invalid mem access。 参考文件: 内核中Documentation/networking/filter.txt

优秀的个人博客,低调大师

eBPF监控工具bcc系列六工具查询列表

关于trace,argdist,funccount三个工具已有专门篇章介绍。所有脚本位于bcc/tools文件夹中。 execsnoop跟踪新进程创建,跟踪exec函数。 bashreadline打印系统中所有bash上运行的命令,通过跟踪readline()函数实现。 biolatency跟踪块设备IO,记录IO延时分布并输出直方图。 biosnoop跟踪块设备IO,为每个IO打印一行。 biotop是块IO的top命令,查看哪些进程在使用磁盘IO. bitesize显示各个进程的请求块大小的IO分布。 bpflist用于显示哪个BPF程序在运行,并打印打开的探针。 btrfsdist跟踪btrfs文件系统的读、写、打开和同步,并总结延时的直方图。 btrfsslower跟踪btrfs文件操作:reads,writes,opens,syncs 。衡量操作的花费时间,并打印超过阈值的信息。相比btrfsdist,这个是打印超过阈值的信息。 cachestat显示页高速缓存的命中率和丢失率,包括读写命中率 cachetop是cachestat 的top显示功能 capable跟踪内核负责安全监测的cap_capable函数,并打印调用信息。 cobjnew调用uobjnew,uobjnew统计新对象的分配事件,并打印出分配频率、分配大小的统计信息。帮助诊断导致大量垃圾回收问题的普通分配路径。 cpudist统计任务的on-CPU事件,在被调用前花费了多少CPU.这个提供了有用的信息可以用来指导硬件采购。也可以统计off-CPU事件,就是等待磁盘IO、锁、定时器和其他挂住的情况。 cpuunclaimed函数采样CPU运行队列长度,确定是否有idle的cpu. dbslower跟踪Mysql或Postgresql的查询,然后打印超出延时的,默认延时是1ms。 dbstat跟踪Mysql或PostgreSQL数据库进程查询,并显示查询延时的直方图。 dcsnoop跟踪目录缓存查询,用于dcstat之后的高级查询。 dcstat显示目录缓存静态统计。 deadlock_detector用于检测运行进程潜在的死锁,将uprobes绑定到pthread_mutex_lock和pthread_mutex_unlock。 ext4dist跟踪ext4的reads,writes,opens,fsync,总结延时并得到直方图。 ext4slower显示ext4中reads,writes,opens,fsync中延时超过阈值的操作。 filelife跟踪短命文件,就是那些创建后就被删除的文件,例如gcc等。 fileslower显示文件同步读和写超过阈值的操作。 filetop是文件读和写的top。 funclatency计时内核函数,直到ctrl+c.例如./funclatency do_sys_open funcslower显示内核或用户函数超过阈值。如./funcslower c:open -u 1 gethostlatency跟踪主机名查找调用,如getaddrinfo(),gethostbyname(),gethostbyname2(). hardirqs跟踪硬中断,并保存其消耗时间的统计信息。 inject通过调用链和可选的谓词集,保证指定注入模式的适当错误返回。 Javacalls会调用ucalls,ucalls统计在高级语言的方法调用,例如java,python,ruby,php和系统调用。显示最常调用的方法以及其延时。 javaflow调用uflow,uflow跟踪方法入口和退出事件,并打印流程图,类似跟踪的调试器。可以用来理解高级语言的程序流,这些高级语言提供了USDT探针。 javagc调用ugc,ugc跟踪高级语言的垃圾回收事件。 javaobjnew调用uobjnew函数,统计java新对象分配事件。 javastat会调用ustat,ustat类似top工具,用于监控高级语言的事件。 javathreads会调用uthreads,uthreads跟踪java或raw threads的进程创建事件 killsnoop跟踪kill()系统调用的信号。 llcstat跟踪缓存相关信息,如缓存miss事件。这些事件定义在uapi/linux/perf_event.h中。 mdflush跟踪md驱动层的flush的信息。 memleak跟踪内存分配和回收请求,收集每次分配的调用栈。会将调用分配后没有进行释放的打印出来。 mountsnoop跟踪mount()和umount()系统调用。 mysqld_qslower跟踪Mysql的查询,并打印超过阈值延时的,默认阈值是1ms。 nfsdist跟踪NFS的reads,writes,opens,getattr,并输出其直方图。 nfsslower显示NFS的reads,writes,opens,getattr,延时超过阈值。 nodegc通过ugc跟踪node的垃圾回收。 nodestat通过ustat显示node层的监控事件,类似top工具。 offcputime显示阻塞的调用栈,以及阻塞的事件。 offwaketime显示被阻塞的任务和内核调用栈,以及唤醒他们的线程。 oomkill跟踪linux中OOM,每个显示一行。 opensnoop跟踪系统中的open系统调用. phpcalls会调用ucalls,总结php方法的调用情况。 phpflow调用uflow,跟踪方法的入口和退出并打印其方法流。 phpstat调用ustat,类似top工具来监视php的事件。 pidpersec显示每秒新进程创建的数量。 profile是CPU的profiler,在给定时间间隔中采样堆栈. pythoncalls调用ucalls,ucalls总结python的方法调用情况。 pythonflow调用uflow,uflow跟踪python方法的入口和出口事件,并打印流程图。 pythongc调用ugc,ugc跟踪python的垃圾回收事件。 Pythonstat调用ustat,类似top工具监控python事件。 reset-trace可能永远用不到,如果使用kill -9杀掉bcc工具,会导致bcc工具崩溃,内核跟踪会处于半使能状态。使用reset-trace来清除跟踪状态并重置和禁止激活的跟踪。 rubycalls调用ucalls,ucalls总结ruby的方法调用情况。 rubyflow调用uflow,uflow跟踪ruby方法的入口和出口事件,并打印流程图。 rubygc调用ugc,ugc跟踪ruby的垃圾回收事件。 rubyobjnew调用uobjnew,uobjnew总结新对象分配事件,并打印分配频率。 rubystat调用ustat,类似top工具监控ruby事件。 runqlat总结调度器运行队列延时的直方图,并显示器等待时间。 runqlen总结调度器队列长度的直方图。 slabratetop显示内核中内存分配缓存(SLAB,SLUB)的使用率和总大小,类似top. softirqs跟踪软中断,保存其统计信息。 solisten当一个程序要监听TCP连接的时候,跟踪内核调用 sslsniff跟踪OpenSSL的SSL_READ和SSL_WRITE函数。 stackcount跟踪函数的使用频率和整个调用栈,如./stackcount submit_bio statsnoop跟踪系统的stat()系统调用。 syncsnoop跟踪内核sync的调用。 syscount总结系统或指定进程的系统调用。 tcpaccept跟踪接收tcp套接字链接(accept)的内核函数。 tcpconnect跟踪活动TCP链接(connect)的内核函数 tcpconnlat跟踪活动TCP链接,及链接延时。 tcplife总结跟踪期间打开和关闭的TCP会话。 tcpretrans跟踪TCP重发函数。 tcpstates打印TCP状态改变的信息,包括每个状态的持续时间。 tcpsubnet显示目标子网的吞吐量,只在IPv4工作。可以指定子网。 tcptop类似top功能,根据主机和端口显示吞吐量。 tcptracer跟踪TCP链接(connect,accept)和关闭的内核函数。 tplist显示内核tracepoinst和USDT探针,包括他们的格式。 ttysnoop观测tty或pts设备,并打印设备上的输出。 vfscount通过跟踪vfs_开头的内核函数来记录VFS调用 vfsstat跟踪vfs调用,并输出每秒情况。 Wakeuptime衡量线程锁,显示唤醒操作线程的栈,唤醒和目标程序名字,以及总的阻塞时间。 xfsdist跟踪XFS的reads,writes,opens,fsyncs,并输出直方图。 xfsslower显示XFS的reads,writes,opens,fsyncs中超过阈值的操作。 zfsdist跟踪ZFS的reads,writes,opens,fsyncs,并输出直方图。 zfsslower显示ZFS的reads,writes,opens,fsyncs中超过阈值的操作。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册