feat(hotspot): 新增 --hotspot-sites 逐调用点计数并带源码 file:line
为匹配白名单的直接 call 点插桩 internal i64 __fhs_site_<K>(call 前 +1),计数天然包含循环内放大的真实执行次数,可把热点定位到具体调用点。 新增 --hotspot-callees 覆盖白名单、--hotspot-sites-all 全量;按去 16 位 sha1 前缀后的基名匹配。Ctrl+C 落盘独立文件 hotspot_sites.txt(# sites 分段,含调用方/被调方/计数/源码位置),按计数降序;不破坏既有 hotspot.txt 格式。 在 AST->IR 生成期由 _HotspotSiteBuilder 为每条新生成的 call 指令轻量挂载 _src_file/_src_line,插桩时作为可选尾字段输出;取不到则留空不伪造。开关关闭时改用原生 ir.IRBuilder,零影响。
This commit is contained in:
1 parent
0ffaadc95e
commit
2021c0edb7
11 files changed
+567
-25
No files matched your search
@@ -150,6 +150,9 @@ def main() -> None:
|
||||
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
|
||||
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
|
||||
parser.add_argument('--func-hotspot', help='函数热点统计:为每个模块的函数定义插入计数器,运行时 Ctrl+C 各模块以追加方式把计数写入 <path>(含 main 的模块在启动时清空一次,保证每次运行一份结果)(可用 python Projectrans.py --project ... --func-hotspot <path>)')
|
||||
parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt;可独立于 --func-hotspot 使用)')
|
||||
parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat')
|
||||
parser.add_argument('--hotspot-sites-all', action='store_true', help='--hotspot-sites 对全部直接 call 点插桩(忽略白名单;体积/开销大,用于深度排查)')
|
||||
|
||||
args: argparse.Namespace = parser.parse_args()
|
||||
|
||||
@@ -199,6 +202,18 @@ def main() -> None:
|
||||
func_hotspot_file: str | None = args.func_hotspot
|
||||
if func_hotspot_file:
|
||||
func_hotspot_file = os.path.abspath(func_hotspot_file)
|
||||
# 调用点热点统计(--hotspot-sites):None=未启用;''=启用但未给路径(默认
|
||||
# hotspot_sites.txt);其余为显式路径。一律绝对化,保证运行时(exe 工作目录为
|
||||
# output_dir)各模块处理器追加写入的路径与预期一致。
|
||||
hotspot_sites_file: str | None = args.hotspot_sites
|
||||
if hotspot_sites_file is not None:
|
||||
if hotspot_sites_file == '':
|
||||
hotspot_sites_file = 'hotspot_sites.txt'
|
||||
hotspot_sites_file = os.path.abspath(hotspot_sites_file)
|
||||
# 白名单覆盖:逗号分隔 → 去空白后的基名列表(None=用 BaseGen 默认白名单)
|
||||
hotspot_callees: list[str] | None = None
|
||||
if args.hotspot_callees:
|
||||
hotspot_callees = [c.strip() for c in args.hotspot_callees.split(',') if c.strip()]
|
||||
|
||||
if isinstance(src, list):
|
||||
if len(src) == 1 and os.path.isdir(src[0]):
|
||||
@@ -217,6 +232,8 @@ def main() -> None:
|
||||
_vlog().info(f"数据布局: {target_datalayout}")
|
||||
if func_hotspot_file:
|
||||
_vlog().info(f"函数热点统计: {func_hotspot_file}(各模块运行时以追加方式写入,含 main 的模块启动时清空一次)")
|
||||
if hotspot_sites_file:
|
||||
_vlog().info(f"调用点热点统计: {hotspot_sites_file}(按真实调用次数降序落盘,含 main 的模块启动时清空一次)")
|
||||
|
||||
# 每次编译自动清理子进程性能剖析输出(PROF 目录 *.prof,硬编码路径)
|
||||
from lib.Projectrans.Phase2Translator import _CleanProfFiles, PROF_DIR
|
||||
@@ -324,6 +341,10 @@ def main() -> None:
|
||||
trans.slice_level = slice_level
|
||||
# 函数热点统计(--func-hotspot):None=未启用(零影响)
|
||||
trans.func_hotspot_file = func_hotspot_file
|
||||
# 调用点热点统计(--hotspot-sites):None=未启用(零影响)
|
||||
trans.hotspot_sites_file = hotspot_sites_file
|
||||
trans.hotspot_sites_all = bool(args.hotspot_sites_all)
|
||||
trans.hotspot_callees = hotspot_callees
|
||||
trans.run()
|
||||
|
||||
if args.run and linker_output and not linker_output.endswith('.bin'):
|
||||
|
||||
@@ -162,7 +162,10 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
|
||||
stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int,
|
||||
shared_sym_pickle_path: str, struct_sha1_map: dict[str, str],
|
||||
prebuilt_ModuleSha1Map: dict[str, str] | None = None,
|
||||
func_hotspot_file: str | None = None) -> tuple[str, str, str]:
|
||||
func_hotspot_file: str | None = None,
|
||||
hotspot_sites_file: str | None = None,
|
||||
hotspot_sites_all: bool = False,
|
||||
hotspot_callees: list[str] | None = None) -> tuple[str, str, str]:
|
||||
# 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中
|
||||
# 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的
|
||||
# 符号污染后文件 → 跨模块 struct 类型前缀错位(如 7cadfe07e983fd90.CompilerFactory
|
||||
@@ -183,6 +186,10 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
|
||||
# 函数热点统计(--func-hotspot):worker 子进程无父进程属性,须显式透传
|
||||
# (None=未启用,零影响);生成 IR 时各模块处理器统一追加写最终 <path>
|
||||
trans.func_hotspot_file = func_hotspot_file
|
||||
# 调用点热点统计(--hotspot-sites):同样须显式透传(None=未启用,零影响)
|
||||
trans.hotspot_sites_file = hotspot_sites_file
|
||||
trans.hotspot_sites_all = hotspot_sites_all
|
||||
trans.hotspot_callees = hotspot_callees
|
||||
|
||||
if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path):
|
||||
# 使用 pickle.load(f) 流式加载,避免 pickle.loads(f.read()) 一次性
|
||||
@@ -227,7 +234,10 @@ def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_
|
||||
sha1_map: dict[str, str], sig_files: dict[str, str],
|
||||
stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int,
|
||||
shared_sym_pickle_path: str, struct_sha1_map: dict[str, str],
|
||||
func_hotspot_file: str | None = None) -> tuple[list, list]:
|
||||
func_hotspot_file: str | None = None,
|
||||
hotspot_sites_file: str | None = None,
|
||||
hotspot_sites_all: bool = False,
|
||||
hotspot_callees: list[str] | None = None) -> tuple[list, list]:
|
||||
"""批次子任务:worker 内创建一个 Phase2Translator + pickle.load 一次,顺序翻译批内全部文件。
|
||||
|
||||
batch 每项: (i, src_path, out_path, sha1, prebuilt_ModuleSha1Map)
|
||||
@@ -250,6 +260,10 @@ def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_
|
||||
trans.struct_sha1_map = struct_sha1_map
|
||||
# 函数热点统计(--func-hotspot):批内每文件的处理器统一追加写最终 <path>
|
||||
trans.func_hotspot_file = func_hotspot_file
|
||||
# 调用点热点统计(--hotspot-sites):批内每文件同样透传
|
||||
trans.hotspot_sites_file = hotspot_sites_file
|
||||
trans.hotspot_sites_all = hotspot_sites_all
|
||||
trans.hotspot_callees = hotspot_callees
|
||||
|
||||
symbol_table_bytes: bytes | None = None
|
||||
if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path):
|
||||
@@ -651,7 +665,14 @@ class Phase2Translator:
|
||||
# 函数热点统计(--func-hotspot):None=未启用(零影响);str=计数器输出
|
||||
# 文件路径。各模块运行时自带 Ctrl+C 处理器、统一以追加方式写该路径;
|
||||
# 含 main 的模块在 main 入口清空一次 → 每次运行一份结果(无构建后合并步骤)。
|
||||
self.func_hotspot_file: str | None = None
|
||||
self.func_hotspot_file: str | None = None
|
||||
# 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数
|
||||
# 落盘文件路径。各模块运行时自带 Ctrl+C 处理器、统一以追加方式写该路径;
|
||||
# 含 main 的模块在 main 入口清空一次 → 每次运行一份结果。
|
||||
self.hotspot_sites_file: str | None = None
|
||||
# 是否对全部直接 call 点插桩(忽略白名单);白名单被调函数基名列表(None=默认)
|
||||
self.hotspot_sites_all: bool = False
|
||||
self.hotspot_callees: list[str] | None = None
|
||||
self.sha1_map: dict[str, str] = {}
|
||||
self.sig_files: dict[str, str] = {}
|
||||
self.stub_files: dict[str, str] = {}
|
||||
@@ -855,10 +876,11 @@ class Phase2Translator:
|
||||
self.struct_sha1_map = self._build_struct_sha1_map()
|
||||
|
||||
need_translate = []
|
||||
# --func-hotspot:缓存 .ll / .stub.ll+.text.ll 可能不含热点计数 IR,启用时
|
||||
# 一律重译,保证每个模块都嵌入 internal 计数器与 @llvm.global_ctors 自注册。
|
||||
# 未启用时 hotspot_on=False,缓存与重组分支行为与改动前完全一致(零影响)。
|
||||
hotspot_on: bool = bool(self.func_hotspot_file)
|
||||
# --func-hotspot / --hotspot-sites:缓存 .ll / .stub.ll+.text.ll 可能不含
|
||||
# 热点计数 IR,启用任一功能时一律重译,保证每个模块都嵌入 internal 计数器
|
||||
# 与 @llvm.global_ctors 自注册。未启用时 hotspot_on=False,缓存与重组分支
|
||||
# 行为与改动前完全一致(零影响)。
|
||||
hotspot_on: bool = bool(self.func_hotspot_file or self.hotspot_sites_file)
|
||||
for i, src_path in enumerate(py_files, 1):
|
||||
rel = os.path.relpath(src_path, self.src_root)
|
||||
# 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过)
|
||||
@@ -1018,6 +1040,9 @@ class Phase2Translator:
|
||||
self.include_py_map, self.slice_level,
|
||||
shared_pickle_path, self.struct_sha1_map,
|
||||
func_hotspot_file=self.func_hotspot_file,
|
||||
hotspot_sites_file=self.hotspot_sites_file,
|
||||
hotspot_sites_all=self.hotspot_sites_all,
|
||||
hotspot_callees=self.hotspot_callees,
|
||||
)
|
||||
futures[future] = (bidx, rel_first, len(batch))
|
||||
|
||||
@@ -1565,6 +1590,14 @@ class Phase2Translator:
|
||||
if self.func_hotspot_file:
|
||||
trans.translator.FuncHotspotFile = self.func_hotspot_file
|
||||
trans.translator.FuncHotspotModuleLocal = True
|
||||
# 调用点热点统计(--hotspot-sites,Projectrans 多模块):同上,注册策略为
|
||||
# 本模块 ctor 自注册(见 BaseGen._InstallHotspotSitesModuleLocal);
|
||||
# self.hotspot_sites_file 为 None 时不设置任何属性 → 与未启用时逐字节一致。
|
||||
if self.hotspot_sites_file:
|
||||
trans.translator.HotspotSitesFile = self.hotspot_sites_file
|
||||
trans.translator.HotspotSitesAll = self.hotspot_sites_all
|
||||
trans.translator.HotspotCallees = self.hotspot_callees
|
||||
trans.translator.HotspotSitesModuleLocal = True
|
||||
|
||||
if self._shared_symbol_table is not None:
|
||||
trans.translator.SymbolTable = self._shared_symbol_table
|
||||
@@ -2845,7 +2878,10 @@ class Phase2Translator:
|
||||
self.include_py_map, self.slice_level,
|
||||
shared_pickle_path, self.struct_sha1_map,
|
||||
include_ModuleSha1Map,
|
||||
func_hotspot_file=self.func_hotspot_file
|
||||
func_hotspot_file=self.func_hotspot_file,
|
||||
hotspot_sites_file=self.hotspot_sites_file,
|
||||
hotspot_sites_all=self.hotspot_sites_all,
|
||||
hotspot_callees=self.hotspot_callees
|
||||
)
|
||||
in_progress[future] = item
|
||||
|
||||
@@ -2862,7 +2898,10 @@ class Phase2Translator:
|
||||
self.include_py_map, self.slice_level,
|
||||
shared_pickle_path, self.struct_sha1_map,
|
||||
include_ModuleSha1Map,
|
||||
func_hotspot_file=self.func_hotspot_file
|
||||
func_hotspot_file=self.func_hotspot_file,
|
||||
hotspot_sites_file=self.hotspot_sites_file,
|
||||
hotspot_sites_all=self.hotspot_sites_all,
|
||||
hotspot_callees=self.hotspot_callees
|
||||
)
|
||||
in_progress[future] = item
|
||||
remaining.clear()
|
||||
|
||||
Reference in new issue
Block a user