网站格式有哪些,爱站seo,如何用电脑做网站服务器,网络美工是做什么的在 Python 开发过程中#xff0c;深入了解程序的运行时行为对于优化性能、排查问题至关重要。本文聚焦于 DTrace 和 SystemTap 这两款强大的监控工具#xff0c;详细介绍它们在 CPython 中的应用#xff0c;包括启用静态标记、编写 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: PythonDTraceSystemTapCPython性能剖析程序监控
相关学习资源 Python 官方文档使用 DTrace 和 SystemTap 检测 CPython提供了基础的使用方法和概念。 DTrace 官方文档DTrace 的官方文档深入了解 DTrace 的语法和功能有助于编写更复杂的 DTrace 脚本 。 SystemTap 官方文档SystemTap 的官方文档详细介绍了 SystemTap 的使用方法和特性是学习 SystemTap 的重要资源 。