Python 性能剖析利器:DTrace 与 SystemTap 深度指南
在 Python 开发过程中,深入了解程序的运行时行为对于优化性能、排查问题至关重要。本文聚焦于 DTrace 和 SystemTap 这两款强大的监控工具,详细介绍它们在 CPython 中的应用,包括启用静态标记、编写 DTrace 和 SystemTap 脚本、利用可用的静态标记和 Tapsets 等内容,帮助开发者精准剖析 CPython 程序,提升开发效率和程序质量。
目录
一、DTrace 和 SystemTap 简介
二、启用静态标记
(一)macOS 系统
(二)Linux 系统
三、静态 DTrace 探针
四、静态 SystemTap 标记
(一)直接使用静态标记
(二)可用的静态标记
(三)SystemTap Tapsets
五、总结
相关学习资源
一、DTrace 和 SystemTap 简介
DTrace 和 SystemTap 都是功能强大的监控工具,为用户提供了检查计算机系统上进程的有效方式。它们通过特定领域的语言,允许用户编写脚本实现进程监视过滤、数据收集以及生成数据报告等功能。从 Python 3.6 开始,CPython 支持构建带有嵌入式 “标记”(探测器)的版本,借助 DTrace 或 SystemTap 脚本,开发者能够更轻松地监视系统上 CPython 进程的运行状态 。不过需要注意的是,DTrace 标记属于 CPython 解释器的实现细节,在不同 CPython 版本之间,探针兼容性无法得到保证,版本变更时 DTrace 脚本可能会失效。
二、启用静态标记
(一)macOS 系统
macOS 系统内置了对 DTrace 的支持。在 macOS 上,用户可以通过在后台运行 Python 进程,然后使用以下命令列出 Python 程序提供的所有探测器:
$ python3.6 -q& $ sudo dtrace -l -P python$! # 或者:dtrace -l -m python3.6
(二)Linux 系统
在 Linux 系统中,若要使用 SystemTap 的嵌入式标记构建 CPython,首先需要安装 SystemTap 开发工具。可以通过以下命令进行安装:
# 使用yum安装 $ yum install systemtap-sdt-devel # 使用apt-get安装 $ sudo apt-get install systemtap-sdt-dev
安装完成后,在构建 CPython 时需要配置--with-dtrace选项,例如:
checking for --with-dtrace... yes
构建完成后,可以通过查看二进制文件是否包含.note.stapsdt部分来验证 SystemTap 静态标记是否存在:
$ readelf -S./python|grep.note.stapsdt
如果 Python 被编译为共享库(使用--enable-shared配置选项),则需要在共享库内部进行查看,如:
$ readelf -S libpython3.3dm.so.1.0|grep.note.stapsdt
部分较新版本的readelf命令还可以打印元数据,通过这些元数据能够详细了解 SystemTap 的相关信息,包括如何修补机器码指令以启用跟踪钩子 。
三、静态 DTrace 探针
下面通过一个 DTrace 脚本示例,展示如何显示 Python 脚本的调用 / 返回层次结构,并且仅在调用名为start的函数内进行跟踪(即导入时的函数调用不会被列出)。
self int indent;
python$target:::function-entry
/copyinstr(arg1) == "start"/
{self->trace = 1;
}
python$target:::function-entry
/self->trace/
{printf("%d\t%*s:", timestamp, 15, probename);printf("%*s", self->indent, "");printf("%s:%s:%d\n", basename(copyinstr(arg0)), copyinstr(arg1), arg2);self->indent++;
}
python$target:::function-return
/self->trace/
{self->indent--;printf("%d\t%*s:", timestamp, 15, probename);printf("%*s", self->indent, "");printf("%s:%s:%d\n", basename(copyinstr(arg0)), copyinstr(arg1), arg2);
}
python$target:::function-return
/copyinstr(arg1) == "start"/
{self->trace = 0;
}
运行该脚本的命令如下:
$ sudo dtrace -q -s call_stack.d -c "python3.6 script.py"
执行后,输出结果类似如下形式:
156641360502280 function-entry:call_stack.py:start:23 156641360518804 function-entry: call_stack.py:function_1:1 156641360532797 function-entry: call_stack.py:function_3:9 156641360546807 function-return: call_stack.py:function_3:10 156641360563367 function-return: call_stack.py:function_1:2 156641360578365 function-entry: call_stack.py:function_2:5 156641360591757 function-entry: call_stack.py:function_1:1 156641360605556 function-entry: call_stack.py:function_3:9 156641360617482 function-return: call_stack.py:function_3:10 156641360629814 function-return: call_stack.py:function_1:2 156641360642285 function-return: call_stack.py:function_2:6 156641360656770 function-entry: call_stack.py:function_3:9 156641360669707 function-return: call_stack.py:function_3:10 156641360687853 function-entry: call_stack.py:function_4:13 156641360700719 function-return: call_stack.py:function_4:14 156641360719640 function-entry: call_stack.py:function_5:18 156641360732567 function-return: call_stack.py:function_5:21 156641360747370 function-return:call_stack.py:start:28
通过上述脚本和命令,我们可以清晰地看到函数的调用和返回顺序,以及对应的文件名、函数名和行号,方便开发者分析程序的执行流程 。
四、静态 SystemTap 标记
(一)直接使用静态标记
直接使用静态标记时,需要明确指定包含标记的二进制文件。例如,以下 SystemTap 脚本用于显示 Python 脚本的调用 / 返回层次结构:
probe process("python").mark("function__entry") {filename = user_string($arg1);funcname = user_string($arg2);lineno = $arg3;
printf("%s => %s in %s:%d\\n",thread_indent(1), funcname, filename, lineno);
}
probe process("python").mark("function__return") {filename = user_string($arg1);funcname = user_string($arg2);lineno = $arg3;
printf("%s <= %s in %s:%d\\n",thread_indent(-1), funcname, filename, lineno);
}
运行该脚本的命令为:
$ stap show-call-hierarchy.stp -c "./python test.py"
输出结果如下:
11408 python(8274): => __contains__ in Lib/_abcoll.py:362 11414 python(8274): => __getitem__ in Lib/os.py:425 11418 python(8274): => encode in Lib/os.py:490 11424 python(8274): <= encode in Lib/os.py:493 11428 python(8274): <= __getitem__ in Lib/os.py:426 11433 python(8274): <= __contains__ in Lib/_abcoll.py:366
输出结果中的列分别表示脚本开始后经过的微秒数、可执行文件的名字、进程的 PID 以及脚本执行时的调用 / 返回层次结构。
如果使用的是 CPython 的--enable-shared编译版,由于标记包含在libpython共享库内部,probe 的加点路径需要相应调整。例如,上述脚本中的probe process("python").mark("function__entry") {应改为probe process("python").library("libpython3.6dm.so.1.0").mark("function__entry") {(假定为 CPython 3.6 的调试编译版) 。
(二)可用的静态标记
CPython 提供了多个可用的静态标记,方便开发者从不同角度监控程序运行:
| 标记名称 | 触发时机 | 参数说明 | 用途 |
|---|---|---|---|
function__entry(str filename, str funcname, int lineno) | Python 函数执行开始(仅针对纯 Python 字节码函数) | $arg1:文件名(使用user_string($arg1)访问) $arg2:函数名(使用user_string($arg2)访问) $arg3:行号 | 用于跟踪函数的调用,分析函数执行的起始位置和相关信息 |
function__return(str filename, str funcname, int lineno) | Python 函数执行结束(通过return或异常,仅针对纯 Python 字节码函数) | 与function__entry参数相同 | 用于跟踪函数的返回,分析函数执行的结束位置和相关信息 |
line(str filename, str funcname, int lineno) | Python 行即将被执行(不会在 C 函数中触发) | 与function__entry参数相同 | 类似于 Python 分析器逐行追踪,可用于细粒度的代码执行分析 |
gc__start(int generation) | Python 解释器启动垃圾回收循环时 | arg0:要扫描的代(与gc.collect()中的参数含义相同) | 用于监控垃圾回收机制的启动,分析垃圾回收操作的触发时机和相关参数 |
gc__done(long collected) | Python 解释器完成垃圾回收循环时 | arg0:收集到的对象数量 | 用于监控垃圾回收机制的结束,分析垃圾回收的效果和效率 |
import__find__load__start(str modulename) | importlib试图查找并加载模块之前 | arg0:模块名称 | 用于跟踪模块导入的开始阶段,分析模块导入的触发原因和相关模块信息 |
import__find__load__done(str modulename, int found) | importlib的find_and_load函数被调用后 | arg0:模块名称,arg1:表示模块是否成功加载 | 用于跟踪模块导入的结束阶段,分析模块导入的结果和相关模块信息 |
audit(str event, void *tuple) | sys.audit()或PySys_Audit()被调用时 | arg0:事件名称的 C 字符串,arg1:指向元组对象的PyObject指针 | 用于监控系统审计相关的操作,分析程序运行过程中的安全相关事件 |
(三)SystemTap Tapsets
SystemTap 的 Tapsets 是一种更高层次的集成方式,它类似于库,能够隐藏静态标记的一些底层细节,使开发者使用起来更加便捷。例如,以下是一个基于 CPython 非共享构建的 tapset 文件示例:
/*提供对 function__entry 和 function__return 标记的高级封装*/
probe python.function.entry = process("python").mark("function__entry")
{filename = user_string($arg1);funcname = user_string($arg2);lineno = $arg3;frameptr = $arg4
}
probe python.function.return = process("python").mark("function__return")
{filename = user_string($arg1);funcname = user_string($arg2);lineno = $arg3;frameptr = $arg4
}
如果将这个文件安装在 SystemTap 的 tapset 目录下(如/usr/share/systemtap/tapset),就会新增两个可用的探测点:
-
python.function.entry(str filename, str funcname, int lineno, frameptr):表示一个 Python 函数的执行已经开始,仅针对纯 Python 字节码函数触发。 -
python.function.return(str filename, str funcname, int lineno, frameptr):表示一个 Python 函数的执行已经结束(通过return或异常),仅针对纯 Python 字节码函数触发。
基于上述 tapset,我们可以编写更简洁的 SystemTap 脚本。例如,以下脚本使用该 tapset 实现跟踪 Python 函数调用层次结构:
probe python.function.entry
{printf("%s => %s in %s:%d\n",thread_indent(1), funcname, filename, lineno);
}
probe python.function.return
{printf("%s <= %s in %s:%d\n",thread_indent(-1), funcname, filename, lineno);
}
还有另一个脚本,使用该 tapset 提供所有运行中的 CPython 代码的类似top的视图,显示整个系统中每一秒内前 20 个最频繁进入的字节码帧:
global fn_calls;probe python.function.entry
{fn_calls[pid(), filename, funcname, lineno] += 1;
}probe timer.ms(1000) {printf("\033[2J\033[1;1H") /* clear screen */printf("%6s %80s %6s %30s %6s\n","PID", "FILENAME", "LINE", "FUNCTION", "CALLS")foreach ([pid, filename, funcname, lineno] in fn_calls -limit 20) {printf("%6d %80s %6d %30s %6d\n",pid, filename, lineno, funcname,fn_calls[pid, filename, funcname, lineno]);}delete fn_calls;
}
五、总结
本文详细介绍了如何使用 DTrace 和 SystemTap 对 CPython 进行检测。通过启用静态标记,开发者可以在不同系统上为 CPython 添加监控能力;利用静态 DTrace 探针和 SystemTap 标记,能够实现对 CPython 程序的函数调用、垃圾回收、模块导入等关键行为的跟踪;而 SystemTap Tapsets 则提供了更高级、更便捷的方式来编写监控脚本。掌握这些工具和技术,开发者可以更深入地了解 CPython 程序的运行时行为,为性能优化、问题排查提供有力支持。
TAG: Python;DTrace;SystemTap;CPython;性能剖析;程序监控
相关学习资源
-
Python 官方文档:使用 DTrace 和 SystemTap 检测 CPython,提供了基础的使用方法和概念。
-
DTrace 官方文档:DTrace 的官方文档,深入了解 DTrace 的语法和功能,有助于编写更复杂的 DTrace 脚本 。
-
SystemTap 官方文档:SystemTap 的官方文档,详细介绍了 SystemTap 的使用方法和特性,是学习 SystemTap 的重要资源 。
相关文章:
Python 性能剖析利器:DTrace 与 SystemTap 深度指南
在 Python 开发过程中,深入了解程序的运行时行为对于优化性能、排查问题至关重要。本文聚焦于 DTrace 和 SystemTap 这两款强大的监控工具,详细介绍它们在 CPython 中的应用,包括启用静态标记、编写 DTrace 和 SystemTap 脚本、利用可用的静态…...
unity学习47:寻路和导航,unity2022后版本如何使用 Navmesh 和 bake
目录 1 寻路和导航对移动的不同 1.1 基础的移动功能 1.1.1 基础移动 1.1.2 智能导航寻路 1.1.3 智能导航寻路还可以 2 如何实现这个效果? 2.1 通过地图网格的形式 2.1.1 警告信息 the static value has been deprecated的对应搜索 2.1.2 新的navigation ba…...
工作-绩效笔记
文章目录 销售项目经理研发项目管理人天拆分抓手评估人天如何拆的细而且有理有据管理等 对这个一直不感兴趣,干好活就行了,但是公司肯定是出于量化的指标,而且不同角色指标不一样,记录下也科普下自己。 销售 销售额 确收、回款 …...
GPT-SoVITS更新V3 win整合包
GPT-SoVITS 是由社区开发者联合打造的开源语音生成框架,其创新性地融合了GPT语言模型与SoVITS(Singing Voice Inference and Timbre Synthesis)语音合成技术,实现了仅需5秒语音样本即可生成高保真目标音色的突破。该项目凭借其开箱…...
WPF的页面设计和实用功能实现
目录 一、TextBlock和TextBox 1. 在TextBlock中实时显示当前时间 二、ListView 1.ListView显示数据 三、ComboBox 1. ComboBox和CheckBox组合实现下拉框多选 四、Button 1. 设计Button按钮的边框为圆角,并对指针悬停时的颜色进行设置 一、TextBlock和TextBox…...
Python项目源码34:网页内容提取工具1.0(Tkinter+requests+html2text)
------★Python练手项目源码★------- Python项目32:订单销售额管理系统1.0(TkinterCSV) Python项目31:初学者也能看懂的聊天机器人1.0源码(命令行界面Re正则表达式) Python项目源码30:待办事…...
javaSE学习笔记22-线程(thread)-线程通信、线程池
线程通信 应用场景:生产者和消费者问题 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,…...
vue单据打印 一维码、二维码实现
编码规则与 JavaScript 代码实现 编码规则数组:定义了 Code 128 条形码编码规则数组 BARS,其中每个数字对应一种条形码的线条组合模式。 const BARS [212222,222122,222221,121223,121322,131222,122213,122312,132212,221213,221312,231212,112232,12…...
远程控制macOS一直卡在100%,能连接上了却只显示了壁纸?
前言 前段时间有个朋友过来咨询关于Windows使用第三方远程软件(向日葵、Todesk等)远程连接控制macOS系统,但出现了一些奇奇怪怪的问题。 比如在连接的时候,一直卡在100%连接,对方的电脑却已经显示已经被控制的状态。…...
Spring Boot定时任务原理
Spring Boot定时任务原理 在现代应用中,定时任务的调度是实现周期性操作的关键机制。Spring Boot 提供了强大的定时任务支持,通过注解驱动的方式,开发者可以轻松地为方法添加定时任务功能。本文将深入探讨 Spring Boot 中定时任务的实现原理…...
C#初级教程(7)——初级期末检测
练习 1:计算圆的周长和面积 改编题目:编写一个 C# 程序,让用户输入圆的半径,然后计算并输出该圆的周长和面积,结果保留两位小数。 using System;class CircleCalculation {static void Main(){const double pi 3.14…...
原生稀疏注意力机制(NSA):硬件对齐且可原生训练的稀疏注意力机制-论文阅读
摘要 长上下文建模对于下一代语言模型至关重要,但标准注意力机制的高计算成本带来了巨大的计算挑战。稀疏注意力提供了一种在保持模型能力的同时提高效率的有前途的方向。本文提出了一种名为 NSA(原生可训练稀疏注意力机制) 的方法ÿ…...
Apache Struts RCE (CVE-2024-53677)
前言 对目前的Apache Struts RCE (CVE-2024-53677)的poc进行总结,由于只能单个ip验证,所以自己更改一下代码,实现:多线程读取url验证并保存,更改为中文解释 免责声明 请勿利用文章内的相关技术从事非法测试…...
GIS地图、轨道交通与智能驾驶UI设计:未来交通的智能化探索
随着科技的飞速发展,我们正迎来一个高度智能化的未来。在这个时代背景下,GIS(地理信息系统)、轨道交通以及智能驾驶UI设计正逐步成为推动交通行业变革的重要力量。本文将深入探讨这三者之间的内在联系及其在未来交通系统中的应用前…...
OpenResty
文章目录 OpenResty执行原理getting-started 核心模块: lua-nginx-module (ngx_lua)常用指令配置指令的执行顺序 API OpenResty 官方文档: http://openresty.org/ 官方文档完全不明所以, 除了getting-started完全不知道下一步该干啥 (都不知道ngx是什么它就开始用了), 找不到架…...
如何将公钥正确添加到服务器的 authorized_keys 文件中以实现免密码 SSH 登录
1. 下载密钥文件 2. RSA 解析 将 id_ed25519 类型的私钥转换为 RSA 类型,要将 ED25519 私钥转换为 RSA 私钥,需要重新生成一个新的 RSA 密钥对。 步骤: 生成新的 RSA 密钥对 使用 ssh-keygen 来生成一个新的 RSA 密钥对。比如,执…...
SQLMesh 系列教程7- 详解 seed 模型
SQLMesh 是一个强大的数据建模和管道管理工具,允许用户通过 SQL 语句定义数据模型并进行版本控制。Seed 模型是 SQLMesh 中的一种特殊模型,主要用于初始化和填充基础数据集。它通常包含静态数据,如参考数据和配置数据,旨在为后续的…...
Git常见命令--助力开发
git常见命令: 创建初始化仓库: git 将文件提交到暂存区 git add 文件名 将文件提交到工作区 git commit -m "注释(例如这是发行的版本1)" 文件名 查看状态 如果暂存区没有文件被提交显示: $ git status On…...
学习整理安装php的uuid扩展以及uuid调用方法
学习整理安装php的uuid扩展以及uuid调用方法 1、安装uuid依赖库2、下载并安装3、ini中添加扩展4、re2c版本报错5、uuid调用方法 1、安装uuid依赖库 yum -y install uuid uuid-devel e2fsprogs-devel libuuid-devel2、下载并安装 点我下载uuid安装包 wget http://pecl.php.ne…...
算法系列之贪心算法
在算法中,贪心算法(Greedy Algorithm)是一种常见的解决优化问题的算法。贪心算法的核心思想是:在每一步选择中都采取当前状态下最优的选择,即贪心的做出局部最优的决策,从而希望最终能够得到全局最优解。尽…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
