Python动态追踪函数执行并提取相关代码的实用技巧
一、背景需求
在日常开发中,我们经常需要分析一个程序的执行路径。例如:当执行某个入口函数时,实际调用了哪些自定义函数和类?如何自动提取这些被执行的代码片段?这在代码审计、依赖分析等场景下非常有用。
二、解决方案概述
本方案基于Python标准库中的trace
模块,通过以下步骤实现动态追踪和代码提取:
- 动态追踪:记录函数执行过程中所有被执行代码的位置
- 路径过滤:排除系统库等无关文件(以
/usr
开头的路径) - 代码分析:通过AST解析识别被执行的类和函数
- 代码提取:根据执行记录提取完整的代码块
三、关键技术解析
3.1 动态追踪技术
使用trace.Trace
类进行执行追踪:
tracer = trace.Trace(
ignoredirs=[sys.prefix, sys.exec_prefix, '/usr'], # 排除系统路径
trace=False, # 不生成详细跟踪信息
count=True # 启用行号统计
)
tracer.runfunc(main)
这里的关键参数说明:
ignoredirs
:过滤不需要追踪的目录trace=False
:避免生成冗余的跟踪日志count=True
:统计被执行的行号信息
执行后会生成results
对象,其中包含详细的执行统计信息。
3.2 AST代码解析
抽象语法树(AST)是解析代码结构的利器。我们通过它来精确识别代码中的类和函数定义:
import ast
with open(filename, 'r') as f:
source = f.read()
tree = ast.parse(source) # 生成AST树
遍历AST节点的示例:
for node in tree.body:
if isinstance(node, ast.ClassDef):
# 处理类定义
elif isinstance(node, ast.FunctionDef):
# 处理函数定义
3.3 执行范围判断
通过对比AST节点位置与执行记录,判断代码块是否被执行:
# 假设已收集到被执行行号集合linenos
func_lines = set(range(item.lineno, item.end_lineno + 1))
if func_lines.intersection(linenos):
# 该函数/类需要被提取
这里用range
生成代码块的行号范围,通过与实际执行行号的交集判断是否需要提取。
四、完整实现步骤
def main():
#你的代码
import sys
import os
import trace
import ast
from collections imp