作者:邓欢,爱可生DMP团队成员,主要负责DMP的开发。本文来源:原创投稿*爱可生开源社区出品,原创内容未经授权不得使用,转载请联系小编并注明出处。之前社区有一篇文章《如何使用 bcc 工具观测 MySQL 延迟》,介绍了什么是bcc,以及如何用bcc项目提供的工具观察MySQL。bcc项目https://github.com/iovisor/bcc但是如果bcc项目提供的MySQL观察工具不能满足我们的需求怎么办?然后自己写一个MySQL观察工具。本文将通过实例介绍如何编写自己的MySQL动态观察工具:观察MySQLMGR数据包的处理线程数。环境准备准备一台Linux机器,安装Python和内核开发包。(注意:内核开发包的版本一定要和内核的版本一致)通过dbdeployer安装一组组复制架构的数据库。bcc观察原理bcc是eBPF的前端,依赖ePBF实现数据采集。整个过程可以分为三个部分:生成BPF字节码,将BPF字节码加载到内核中执行,通过perf事件或异步方式将数据从内核拷贝到用户空间。bcc程序是用Python编写的,它会嵌入一段C代码。在执行过程中,c代码被编译成字节码,加载到内核中运行。而Python代码可以通过perfevent读取数据并显示出来。了解了原理之后,我们就可以开始写自己的观察工具了。在组复制插件中发现MySQLMGR数据包的处理函数是apply_data_packet,所以我们的目标是观察是哪个线程执行了这个函数。开始开发我们将源码文件命名为mgr_apply_data_packet.py,因为我们使用Python进行开发,所以在第一行指定Python解释器:#!/usr/bin/python接下来是需要导入的包,我们只使用bcc包的BPF对象,所以你只需要导入这一个对象。这个对象可以把我们的观察代码嵌入观察点执行,同时采集观察点数据。frombccimportBPF接下来我们需要编写我们的观察代码。bpf_text="""#include#includestructdata_t{u32pid;u32tgid;u64ts;};BPF_PERF_OUTPUT(events);intdo_apply_data_packet(structpt_regs*ctx){structtask_struct*t=(structtask_struct*)bpf_get_current_task();structdata_tdata={};//线程iddata.pid=t->pid;//线程组id(用户空间中的pid)数据。tgid=t->tgid;//bpf_ktime_get_ns返回u64纳秒数。在系统启动时启动,但在挂起期间停止。data.ts=bpf_ktime_get_ns();events.perf_submit(ctx,&data,sizeof(data));return0;}"""观察代码是一段c代码,但是是通过BPF对象执行的,所以这里放在bpf_text对象中。这段代码主要有四个部分:头文件和。结构体data_t保存着我们每次观察的结果。BPF_PERF_OUTPUT定义了一个名为events的表。观察代码可以将观察数据写入事件表,Python代码可以从该表中读取观察数据。do_apply_data_packet函数收集观察数据。我们通过bpf_get_current_task获取到MySQL进程对应的结构体task_struct,然后从中获取pid和tgid。这里要注意,task_struct中的pid其实对应的是线程id,tgid对应的是线程组id(即用户空间中的进程id)。时间ts通过bpf_ktime_get_ns函数获取。这个时间不是时钟时间,而是系统启动的时间。最后通过events表的perf_submit方法将观察到的数据提交到表中。接下来要做的就是将观察代码与MySQL中需要观察的函数关联起来。#获取mysql函数名并跟踪它。path="/root/sandboxes/mysql_base/8.0.18/lib/plugin/group_replication.so"regex="\\w+apply_data_packet\\w+"symbols=BPF.get_user_functions_and_addresses(path,regex)iflen(symbols)==0:print("Can'tfindfunction'apply_data_packet'in%s"%(path))exit(1)(mysql_func_name,addr)=symbols[0]b=BPF(text=bpf_text)b.attach_uprobe(名称=path,sym=mysql_func_name,fn_name="do_apply_data_packet")因为MySQL组复制是通过组复制插件实现的,所以我们需要在插件的代码中找到需要观察的函数apply_data_packet。查找是由BPF对象的get_user_functions_and_addresses函数实现的,该函数从插件代码的符号表中查找与正则表达式regex匹配的所有符号。这里我们只需要从所有符号中获取第一个符号的函数名即可。然后将之前的观察代码作为参数创建一个BPF对象,然后通过attach_uprobe将mysql程序中的apply_data_packet函数与观察函数do_apply_data_packet关联起来。值得一提的是,attach_uprobe函数与用户空间函数和观察函数相关联。如果需要观察Linux内核中的函数,需要使用attach_kprobe。attach_kprobehttps://github.com/iovisor/bc...最后输出观察结果。#输出跟踪result.print("TracingMySQLservermgrapply_data_packetfunction")print("%-14s%-6s%-6s"%("SINCE_UP_TIME(s)","PID","THREAD"))defprint_event(cpu,数据,大小):event=b["events"].event(data)print("%-14s%-6s%-6s"%(event.ts/1000000000,event.tgid,event.pid))b["events"].open_perf_buffer(print_event)while1:try:b.perf_buffer_poll()exceptKeyboardInterrupt:exit()输出的头两行信息。然后是输出单次观测数据的回调函数,open_perf_buffer将这个回调函数注册到events表中。最后就是不断从perlbuffer中获取检测数据,然后通过回调函数输出结果。结果演示了在另一个终端运行该工具并向MySQL写入数据。可以观察工具输出:root@ubuntu:/tmp#pythonmgr_apply_data_packet.pyTracingMySQLservermgrapply_data_packetfunctionSINCE_UP_TIME(s)PIDTHREAD2165924263872706021659242581027044216592参考1.http://www.brendangregg.com/e...2.https://github.com/iovisor/bc...3。https://github.com/iovisor/bc...4。https://github.com/iovisor/bc…