feat(hotspot): 热点日志在进程退出时也落盘(atexit + 幂等守卫)
此前 --hotspot-sites 仅由 SetConsoleCtrlHandler 在 Ctrl+C 时落盘,导致正常退出与 fail-fast(VLogger.error 走 sys.exit(1),底层是 extern exit() 即 libc exit)都拿不到热点数据。 现把两个 handler 主体抽为 void() 落盘函数(__fhs_dump_sites/__fhs_dump_time),新增 __fhs_dump_all 与每模块 internal i32 __fhs_dumped 幂等守卫,并在 ctor/main 入口同时注册 SetConsoleCtrlHandler 与 atexit;Ctrl+C 处理器仍返回 FALSE(0) 保持原有终止行为。同模块只落盘一次,不产生重复段。 覆盖:正常 return、sys.exit(1) fail-fast、Ctrl+C。不覆盖(已在报告中列明):_exit、abort、崩溃信号、被强杀。不启用开关时 IR 逐字节不变;两个输出文件格式与排序不变。
This commit is contained in:
1 parent
84d91d53e5
commit
136e9bc33e
1 file changed
+165
-93
+165
-93
@@ -1130,7 +1130,7 @@ class BaseGenMixin:
|
||||
inc 里,因而会计入父帧的 **self**(见下方说明)。
|
||||
|
||||
返回记录列表 (自身耗时全局, 包含式耗时全局, 进入次数全局, 函数名),顺序即
|
||||
N 的编号顺序。落盘由 _CreateHotspotTimeHandler 完成(按 self 降序)。
|
||||
N 的编号顺序。落盘由 _CreateHotspotTimeDump 完成(按 self 降序)。
|
||||
|
||||
固有性质(务必据此解读数值):**未插桩的被调方**(间接调用、无定义体的外部
|
||||
函数如 strcmp/memcpy、名字以 __fhs_ 开头的辅助函数、深度溢出而跳过的调用)
|
||||
@@ -1232,13 +1232,13 @@ class BaseGenMixin:
|
||||
records.append((self_gv, inc_gv, enter_gv, func.name))
|
||||
return records
|
||||
|
||||
def _CreateHotspotSitesHandler(
|
||||
def _CreateHotspotSitesDump(
|
||||
self,
|
||||
path: str,
|
||||
records: list[tuple[ir.GlobalVariable, str, str, str, int]],
|
||||
module_id: str | None = None,
|
||||
) -> ir.Function:
|
||||
"""生成 Win32 控制台 Ctrl+C 处理器:按调用次数降序把各调用点追加写入 <path>。
|
||||
"""生成 void() 落盘函数:按调用次数降序把各调用点追加写入 <path>。
|
||||
|
||||
落盘格式(追加):
|
||||
# sites <module_sha1>\\n
|
||||
@@ -1246,14 +1246,13 @@ class BaseGenMixin:
|
||||
module_id 为空时省略行首 sha1 与头部 sha1(留空,不伪造)。
|
||||
|
||||
- 排序:落盘前用选择排序(两层运行期循环,IR 规模 O(N)、运行期 O(N^2) 仅
|
||||
在 Ctrl+C 触发一次)按计数降序输出,只输出 count>0 的调用点;计数相同时
|
||||
在落盘时触发一次)按计数降序输出,只输出 count>0 的调用点;计数相同时
|
||||
按计数器编号 K 升序(稳定、可复现)。
|
||||
- 源码位置:作为可选尾字段,格式 "<file>:<line>"(含前置单个空格);该位置
|
||||
来自生成期挂在 call 指令对象上的 _src_file/_src_line(见 _HotspotSiteBuilder)。
|
||||
生成期无法关联到 AST 的合成调用点取不到位置,则此字段整体省略(不伪造、不填默认值)。
|
||||
- 处理器签名为 Win32 PHANDLER_ROUTINE(i32 (i32) -> BOOL),用 CRT
|
||||
fopen("a")/fprintf/fclose 落盘(沿用既有做法),返回 FALSE(0) 让默认
|
||||
处理继续,使 Ctrl+C 仍能终止程序。
|
||||
- 函数签名为 void(),由 __fhs_dump_all(见 _CreateHotspotDumpAll)统一带守卫
|
||||
调用,本函数自身不检查守卫。用 CRT fopen("a")/fprintf/fclose 落盘(沿用既有做法)。
|
||||
"""
|
||||
n: int = len(records)
|
||||
i8: ir.IntType = ir.IntType(8)
|
||||
@@ -1270,16 +1269,16 @@ class BaseGenMixin:
|
||||
fprintf: ir.Function = self._DeclareExternalFunc(
|
||||
'fprintf', ir.FunctionType(i32, [i8p, i8p], var_arg=True))
|
||||
fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p]))
|
||||
handler: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(i32, [i32]),
|
||||
name=self._UniqueGlobalName('__fhs_sites_handler'))
|
||||
handler.linkage = 'internal'
|
||||
b: ir.IRBuilder = ir.IRBuilder(handler.append_basic_block(name='entry'))
|
||||
b.position_at_start(handler.entry_basic_block)
|
||||
# 无匹配调用点:生成空处理器(不落盘、不建数组),仅占位以保持注册链路一致
|
||||
dump_fn: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(ir.VoidType(), []),
|
||||
name=self._UniqueGlobalName('__fhs_dump_sites'))
|
||||
dump_fn.linkage = 'internal'
|
||||
b: ir.IRBuilder = ir.IRBuilder(dump_fn.append_basic_block(name='entry'))
|
||||
b.position_at_start(dump_fn.entry_basic_block)
|
||||
# 无匹配调用点:生成空落盘函数(不落盘、不建数组),保持调用链一致
|
||||
if n == 0:
|
||||
b.ret(ir.Constant(i32, 0))
|
||||
return handler
|
||||
b.ret_void()
|
||||
return dump_fn
|
||||
path_ptr: ir.Constant = self._create_string_global(
|
||||
path, name=self._UniqueGlobalName('__fhs_sites_path'))
|
||||
mode_ptr: ir.Constant = self._create_string_global(
|
||||
@@ -1348,16 +1347,16 @@ class BaseGenMixin:
|
||||
i_ptr: ir.Value = b.alloca(i64, name='fhs_sites_i')
|
||||
b.store(c0_64, k_ptr)
|
||||
# 选择排序:外层 k 走 0..n-1,每轮在未输出项里选计数最大者(计数相同取 K 小者)
|
||||
loop_k: ir.Block = handler.append_basic_block(name='fhs_sites_sort_k')
|
||||
body_k: ir.Block = handler.append_basic_block(name='fhs_sites_sort_k_body')
|
||||
loop_i: ir.Block = handler.append_basic_block(name='fhs_sites_sort_i')
|
||||
body_i: ir.Block = handler.append_basic_block(name='fhs_sites_sort_i_body')
|
||||
scan_i: ir.Block = handler.append_basic_block(name='fhs_sites_sort_i_scan')
|
||||
upd_i: ir.Block = handler.append_basic_block(name='fhs_sites_sort_i_upd')
|
||||
next_i: ir.Block = handler.append_basic_block(name='fhs_sites_sort_i_next')
|
||||
end_i: ir.Block = handler.append_basic_block(name='fhs_sites_sort_i_end')
|
||||
print_k: ir.Block = handler.append_basic_block(name='fhs_sites_print_k')
|
||||
after_k: ir.Block = handler.append_basic_block(name='fhs_sites_sort_end')
|
||||
loop_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_k')
|
||||
body_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_k_body')
|
||||
loop_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i')
|
||||
body_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_body')
|
||||
scan_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_scan')
|
||||
upd_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_upd')
|
||||
next_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_next')
|
||||
end_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_end')
|
||||
print_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_print_k')
|
||||
after_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_end')
|
||||
b.branch(loop_k)
|
||||
# loop_k: if k < n -> body_k else after_k
|
||||
b.position_at_end(loop_k)
|
||||
@@ -1412,19 +1411,19 @@ class BaseGenMixin:
|
||||
name='fhs_sites_write')
|
||||
b.store(b.add(b.load(k_ptr), c1_64), k_ptr)
|
||||
b.branch(loop_k)
|
||||
# after_k: fclose; return 0
|
||||
# after_k: fclose; ret_void
|
||||
b.position_at_end(after_k)
|
||||
b.call(fclose, [fp])
|
||||
b.ret(ir.Constant(i32, 0))
|
||||
return handler
|
||||
b.ret_void()
|
||||
return dump_fn
|
||||
|
||||
def _CreateHotspotTimeHandler(
|
||||
def _CreateHotspotTimeDump(
|
||||
self,
|
||||
path: str,
|
||||
records: list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]],
|
||||
module_id: str | None = None,
|
||||
) -> ir.Function:
|
||||
"""生成 Win32 控制台 Ctrl+C 处理器:按**自身耗时**降序写入每函数计时文件。
|
||||
"""生成 void() 落盘函数:按**自身耗时**降序写入每函数计时文件。
|
||||
|
||||
落盘格式(追加):
|
||||
# time <module_sha1>\\n
|
||||
@@ -1438,9 +1437,8 @@ class BaseGenMixin:
|
||||
|
||||
- 排序:与调用点计数同法,落盘前用选择排序按 self_tick 降序输出,只输出
|
||||
进入次数 >0 的函数;self_tick 相同时按编号 N 升序(稳定、可复现)。
|
||||
- 处理器签名为 Win32 PHANDLER_ROUTINE(i32 (i32) -> BOOL),用 CRT
|
||||
fopen("a")/fprintf/fclose 落盘(沿用既有做法),返回 FALSE(0) 让默认
|
||||
处理继续,使 Ctrl+C 仍能终止程序。
|
||||
- 函数签名为 void(),由 __fhs_dump_all(见 _CreateHotspotDumpAll)统一带守卫
|
||||
调用,本函数自身不检查守卫。用 CRT fopen("a")/fprintf/fclose 落盘(沿用既有做法)。
|
||||
"""
|
||||
n: int = len(records)
|
||||
i8: ir.IntType = ir.IntType(8)
|
||||
@@ -1457,16 +1455,16 @@ class BaseGenMixin:
|
||||
fprintf: ir.Function = self._DeclareExternalFunc(
|
||||
'fprintf', ir.FunctionType(i32, [i8p, i8p], var_arg=True))
|
||||
fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p]))
|
||||
handler: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(i32, [i32]),
|
||||
name=self._UniqueGlobalName('__fhs_ftime_handler'))
|
||||
handler.linkage = 'internal'
|
||||
b: ir.IRBuilder = ir.IRBuilder(handler.append_basic_block(name='entry'))
|
||||
b.position_at_start(handler.entry_basic_block)
|
||||
# 无计时对象(模块内无用户函数定义):生成空处理器占位,保持注册链路一致
|
||||
dump_fn: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(ir.VoidType(), []),
|
||||
name=self._UniqueGlobalName('__fhs_dump_time'))
|
||||
dump_fn.linkage = 'internal'
|
||||
b: ir.IRBuilder = ir.IRBuilder(dump_fn.append_basic_block(name='entry'))
|
||||
b.position_at_start(dump_fn.entry_basic_block)
|
||||
# 无计时对象(模块内无用户函数定义):生成空落盘函数占位,保持调用链一致
|
||||
if n == 0:
|
||||
b.ret(ir.Constant(i32, 0))
|
||||
return handler
|
||||
b.ret_void()
|
||||
return dump_fn
|
||||
path_ptr: ir.Constant = self._create_string_global(
|
||||
path, name=self._UniqueGlobalName('__fhs_ftime_path'))
|
||||
mode_ptr: ir.Constant = self._create_string_global(
|
||||
@@ -1525,16 +1523,16 @@ class BaseGenMixin:
|
||||
i_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_i')
|
||||
b.store(c0_64, k_ptr)
|
||||
# 选择排序:外层 k 走 0..n-1,每轮在未输出项里选自身耗时(self)最大者(相同取 N 小者)
|
||||
loop_k: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_k')
|
||||
body_k: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_k_body')
|
||||
loop_i: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_i')
|
||||
body_i: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_i_body')
|
||||
scan_i: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_i_scan')
|
||||
upd_i: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_i_upd')
|
||||
next_i: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_i_next')
|
||||
end_i: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_i_end')
|
||||
print_k: ir.Block = handler.append_basic_block(name='fhs_ftime_print_k')
|
||||
after_k: ir.Block = handler.append_basic_block(name='fhs_ftime_sort_end')
|
||||
loop_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_k')
|
||||
body_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_k_body')
|
||||
loop_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i')
|
||||
body_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_body')
|
||||
scan_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_scan')
|
||||
upd_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_upd')
|
||||
next_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_next')
|
||||
end_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_end')
|
||||
print_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_print_k')
|
||||
after_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_end')
|
||||
b.branch(loop_k)
|
||||
b.position_at_end(loop_k)
|
||||
k_val: ir.Value = b.load(k_ptr)
|
||||
@@ -1581,36 +1579,112 @@ class BaseGenMixin:
|
||||
name='fhs_ftime_write')
|
||||
b.store(b.add(b.load(k_ptr), c1_64), k_ptr)
|
||||
b.branch(loop_k)
|
||||
# after_k: fclose; return 0
|
||||
# after_k: fclose; ret_void
|
||||
b.position_at_end(after_k)
|
||||
b.call(fclose, [fp])
|
||||
b.ret(ir.Constant(i32, 0))
|
||||
return handler
|
||||
b.ret_void()
|
||||
return dump_fn
|
||||
|
||||
def _RegisterHotspotSitesViaCtor(self, handlers: list[ir.Function]) -> None:
|
||||
"""用 @llvm.global_ctors 在进程启动期为本模块注册全部热点处理器。
|
||||
def _CreateHotspotDumpAll(
|
||||
self,
|
||||
sites_dump: ir.Function,
|
||||
time_dump: ir.Function,
|
||||
) -> tuple[ir.Function, ir.Function]:
|
||||
"""生成模块级"总落盘"链路:幂等守卫 + void() 总落盘函数 + Ctrl+C 处理器。
|
||||
|
||||
生成 internal void @__fhs_sites_ctor()(内部逐个调用 SetConsoleCtrlHandler
|
||||
注册 handlers:调用点计数处理器与每函数计时处理器),并写入 appending
|
||||
linkage 的 @llvm.global_ctors —— 每个模块一份,链接器按 appending 语义把
|
||||
各模块的构造器拼接进 .init_array,进程启动时各执行一次。相比"在 main 入口
|
||||
注册",本方案不依赖 main 所在模块,适配 Projectrans 的"每源文件独立翻译
|
||||
单元"模型。
|
||||
同一模块**只落盘一次**:Ctrl+C 与 atexit 可能先后都触发(例如处理器返回后
|
||||
进程继续退出),故用每模块一份的 internal i32 @__fhs_dumped(初值 0)作幂等
|
||||
守卫:
|
||||
- void @__fhs_dump_all():先读守卫,非 0 直接返回;否则置 1 后依次调用
|
||||
sites_dump(调用点计数)与 time_dump(每函数计时)——两个落盘函数本身
|
||||
不检查守卫;
|
||||
- i32 @__fhs_ctl_handler(i32):直接调用 __fhs_dump_all() 后返回 FALSE(0),
|
||||
供 SetConsoleCtrlHandler 注册(保持"仍让进程终止"的既有行为)。
|
||||
|
||||
atexit 与 Ctrl+C 共用同一守卫,先到者落盘、后到者直接返回,避免重复段/行。
|
||||
返回 (Ctrl+C 处理器, 总落盘函数)。
|
||||
"""
|
||||
i32: ir.IntType = ir.IntType(32)
|
||||
guard: ir.GlobalVariable = ir.GlobalVariable(
|
||||
self.module, i32, name=self._UniqueGlobalName('__fhs_dumped'))
|
||||
guard.linkage = 'internal'
|
||||
guard.initializer = ir.Constant(i32, 0)
|
||||
dump_all: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(ir.VoidType(), []),
|
||||
name=self._UniqueGlobalName('__fhs_dump_all'))
|
||||
dump_all.linkage = 'internal'
|
||||
b: ir.IRBuilder = ir.IRBuilder(dump_all.append_basic_block(name='entry'))
|
||||
do_bb: ir.Block = dump_all.append_basic_block(name='fhs_dump_do')
|
||||
done_bb: ir.Block = dump_all.append_basic_block(name='fhs_dump_done')
|
||||
b.position_at_start(dump_all.entry_basic_block)
|
||||
cur: ir.Value = b.load(guard, name='fhs_dumped_load')
|
||||
b.cbranch(b.icmp_signed('!=', cur, ir.Constant(i32, 0)), done_bb, do_bb)
|
||||
b.position_at_end(do_bb)
|
||||
b.store(ir.Constant(i32, 1), guard)
|
||||
b.call(sites_dump, [])
|
||||
b.call(time_dump, [])
|
||||
b.branch(done_bb)
|
||||
b.position_at_end(done_bb)
|
||||
b.ret_void()
|
||||
ctl_handler: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(i32, [i32]),
|
||||
name=self._UniqueGlobalName('__fhs_ctl_handler'))
|
||||
ctl_handler.linkage = 'internal'
|
||||
cb: ir.IRBuilder = ir.IRBuilder(ctl_handler.append_basic_block(name='entry'))
|
||||
cb.call(dump_all, [])
|
||||
cb.ret(ir.Constant(i32, 0))
|
||||
return ctl_handler, dump_all
|
||||
|
||||
def _EmitHotspotDumpRegistration(
|
||||
self,
|
||||
builder: ir.IRBuilder,
|
||||
ctl_handler: ir.Function,
|
||||
dump_all: ir.Function,
|
||||
) -> None:
|
||||
"""向给定构建器发射两处落盘触发注册:
|
||||
|
||||
- SetConsoleCtrlHandler(bitcast @__fhs_ctl_handler, i8*), TRUE):Ctrl+C 路径;
|
||||
- atexit(bitcast @__fhs_dump_all, i8*)):正常返回 / exit() 退出路径。
|
||||
|
||||
二者共用 __fhs_dumped 幂等守卫(见 _CreateHotspotDumpAll),任一先触发即落盘,
|
||||
后触发者直接返回,不重复写段/行。atexit 声明沿用本文件既有外部函数写法(见
|
||||
fopen/SetConsoleCtrlHandler:ir.FunctionType + ir.PointerType(ir.IntType(8)));
|
||||
本仓库 IR 为 typed pointer(函数指针统一以 i8* 传递),故以 bitcast 取函数地址。
|
||||
返回值为 int,此处有意忽略(拿不到 atexit 会在链接期报错,不静默降级)。
|
||||
|
||||
覆盖 libc exit()(含正常 return 与 sys.exit 的 fail-fast);不覆盖 _exit()/
|
||||
abort()/TerminateProcess/崩溃信号等不经过 exit() 的终止路径。
|
||||
"""
|
||||
i32: ir.IntType = ir.IntType(32)
|
||||
i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
|
||||
set_handler: ir.Function = self._DeclareExternalFunc(
|
||||
'SetConsoleCtrlHandler', ir.FunctionType(i32, [i8p, i32]))
|
||||
atexit_fn: ir.Function = self._DeclareExternalFunc(
|
||||
'atexit', ir.FunctionType(i32, [i8p]))
|
||||
builder.call(set_handler, [builder.bitcast(ctl_handler, i8p), ir.Constant(i32, 1)])
|
||||
builder.call(atexit_fn, [builder.bitcast(dump_all, i8p)])
|
||||
|
||||
def _RegisterHotspotSitesViaCtor(self, ctl_handler: ir.Function, dump_all: ir.Function) -> None:
|
||||
"""用 @llvm.global_ctors 在进程启动期为本模块注册热点落盘触发。
|
||||
|
||||
生成 internal void @__fhs_sites_ctor()(内部调用 SetConsoleCtrlHandler 注册
|
||||
Ctrl+C 处理器 __fhs_ctl_handler,并调用 atexit 注册退出处理器 __fhs_dump_all),
|
||||
并写入 appending linkage 的 @llvm.global_ctors —— 每个模块一份,链接器按
|
||||
appending 语义把各模块的构造器拼接进 .init_array,进程启动时各执行一次。相比
|
||||
"在 main 入口注册",本方案不依赖 main 所在模块,适配 Projectrans 的"每源文件
|
||||
独立翻译单元"模型。
|
||||
|
||||
模块内若已存在 @llvm.global_ctors 视为冲突,显式报错(不静默降级)。
|
||||
"""
|
||||
i32: ir.IntType = ir.IntType(32)
|
||||
i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
|
||||
void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), []))
|
||||
set_handler: ir.Function = self._DeclareExternalFunc(
|
||||
'SetConsoleCtrlHandler', ir.FunctionType(i32, [i8p, i32]))
|
||||
ctor: ir.Function = ir.Function(
|
||||
self.module, ir.FunctionType(ir.VoidType(), []),
|
||||
name=self._UniqueGlobalName('__fhs_sites_ctor'))
|
||||
ctor.linkage = 'internal'
|
||||
cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry'))
|
||||
for h in handlers:
|
||||
cb.call(set_handler, [cb.bitcast(h, i8p), ir.Constant(i32, 1)])
|
||||
self._EmitHotspotDumpRegistration(cb, ctl_handler, dump_all)
|
||||
cb.ret_void()
|
||||
if 'llvm.global_ctors' in self.module.globals:
|
||||
raise RuntimeError(
|
||||
@@ -1633,48 +1707,45 @@ class BaseGenMixin:
|
||||
|
||||
顺序:先装调用点计数(其扫描只针对用户代码,不会把随后插入的计时原语
|
||||
调用计入),再装每函数计时;随后在 main 入口用 "w" 各清空一次调用点计数
|
||||
文件与计时文件,并注册全部 Ctrl+C 处理器(程序启动期各执行一次)。仅在
|
||||
hotspot_sites_file 非 None(用户传入 --hotspot-sites)时被调用;未启用时
|
||||
该方法不会执行,IR 输出与既有行为一致(零影响)。
|
||||
文件与计时文件,并注册落盘触发(Ctrl+C 处理器 SetConsoleCtrlHandler +
|
||||
退出处理器 atexit,程序启动期各执行一次)。仅在 hotspot_sites_file 非 None
|
||||
(用户传入 --hotspot-sites)时被调用;未启用时该方法不会执行,IR 输出与
|
||||
既有行为一致(零影响)。
|
||||
"""
|
||||
path: str | None = self.hotspot_sites_file
|
||||
if not path:
|
||||
raise RuntimeError('--hotspot-sites: 需要提供调用点计数输出文件路径')
|
||||
i32: ir.IntType = ir.IntType(32)
|
||||
i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
|
||||
module_id: str | None = self.module_sha1 or None
|
||||
defs, records = self._EmitHotspotSiteCounters()
|
||||
time_records = self._EmitHotspotTimeCounters()
|
||||
time_path: str = self._HotspotTimePath(path)
|
||||
handlers: list[ir.Function] = [
|
||||
self._CreateHotspotSitesHandler(path, records, module_id=module_id),
|
||||
self._CreateHotspotTimeHandler(time_path, time_records, module_id=module_id),
|
||||
]
|
||||
sites_dump: ir.Function = self._CreateHotspotSitesDump(
|
||||
path, records, module_id=module_id)
|
||||
time_dump: ir.Function = self._CreateHotspotTimeDump(
|
||||
time_path, time_records, module_id=module_id)
|
||||
ctl_handler, dump_all = self._CreateHotspotDumpAll(sites_dump, time_dump)
|
||||
# main 入口各清空一次历史文件(require_main=True → 无 main 显式报错)
|
||||
main_func: ir.Function | None = self._InstallHotspotTruncate(
|
||||
defs, path, require_main=True, flag='--hotspot-sites')
|
||||
if main_func is None:
|
||||
raise RuntimeError(
|
||||
'--hotspot-sites: 找不到 main 函数定义,无法注册 Ctrl+C 处理器')
|
||||
'--hotspot-sites: 找不到 main 函数定义,无法注册热点落盘触发')
|
||||
self._InstallHotspotTruncate(
|
||||
defs, time_path, require_main=True, flag='--hotspot-sites')
|
||||
# 在 main 入口注册全部处理器(清空/截断之后、程序 body 之前执行)
|
||||
# 在 main 入口注册落盘触发(清空/截断之后、程序 body 之前执行)
|
||||
mb: ir.IRBuilder = ir.IRBuilder(main_func.entry_basic_block)
|
||||
mb.position_at_start(main_func.entry_basic_block)
|
||||
set_handler: ir.Function = self._DeclareExternalFunc(
|
||||
'SetConsoleCtrlHandler', ir.FunctionType(i32, [i8p, i32]))
|
||||
for h in handlers:
|
||||
mb.call(set_handler, [mb.bitcast(h, i8p), ir.Constant(i32, 1)])
|
||||
self._EmitHotspotDumpRegistration(mb, ctl_handler, dump_all)
|
||||
|
||||
def _InstallHotspotSitesModuleLocal(self) -> None:
|
||||
"""安装调用点热点统计(--hotspot-sites):Projectrans 多模块路径。
|
||||
|
||||
每模块自带 internal 调用点计数器 + internal 每函数计时器 + internal 处理器
|
||||
每模块自带 internal 调用点计数器 + internal 每函数计时器 + internal 落盘函数
|
||||
(调用点计数追加写最终 <path>,计时追加写同目录 hotspot_time.txt),并各自
|
||||
通过 @llvm.global_ctors 在进程启动期注册 SetConsoleCtrlHandler
|
||||
(见 _RegisterHotspotSitesViaCtor)。仅含 main 的模块额外在 main 入口以 "w"
|
||||
各清空一次文件,保证"一次运行一份结果"。未启用(hotspot_sites_file 为 None)
|
||||
时本方法不会被调用。
|
||||
通过 @llvm.global_ctors 在进程启动期注册落盘触发:Ctrl+C 处理器
|
||||
(SetConsoleCtrlHandler) 与退出处理器 (atexit)(见 _RegisterHotspotSitesViaCtor)。
|
||||
仅含 main 的模块额外在 main 入口以 "w" 各清空一次文件,保证"一次运行一份结果"。
|
||||
未启用(hotspot_sites_file 为 None)时本方法不会被调用。
|
||||
"""
|
||||
path: str | None = self.hotspot_sites_file
|
||||
if not path:
|
||||
@@ -1683,11 +1754,12 @@ class BaseGenMixin:
|
||||
defs, records = self._EmitHotspotSiteCounters()
|
||||
time_records = self._EmitHotspotTimeCounters()
|
||||
time_path: str = self._HotspotTimePath(path)
|
||||
handlers: list[ir.Function] = [
|
||||
self._CreateHotspotSitesHandler(path, records, module_id=module_id or None),
|
||||
self._CreateHotspotTimeHandler(time_path, time_records, module_id=module_id or None),
|
||||
]
|
||||
self._RegisterHotspotSitesViaCtor(handlers)
|
||||
sites_dump: ir.Function = self._CreateHotspotSitesDump(
|
||||
path, records, module_id=module_id or None)
|
||||
time_dump: ir.Function = self._CreateHotspotTimeDump(
|
||||
time_path, time_records, module_id=module_id or None)
|
||||
ctl_handler, dump_all = self._CreateHotspotDumpAll(sites_dump, time_dump)
|
||||
self._RegisterHotspotSitesViaCtor(ctl_handler, dump_all)
|
||||
# 非 main 模块无 main、不需要截断(返回 None 不报错):其它模块的写入
|
||||
# 依赖含 main 模块在启动期清空,不会互相覆盖。
|
||||
self._InstallHotspotTruncate(defs, path, flag='--hotspot-sites')
|
||||
|
||||
Reference in new issue
Block a user