refactor(compare-alert): 抽 _last_segment、StuckPoint 加 dwell_ms 字段
末段扫描+时长算法抽成 _last_segment 供三处复用;StuckPoint 新增 dwell_ms(默认 None、承载末帧所在屏停留),T5 行为不变。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-11
@@ -47,6 +47,7 @@ class StuckPoint:
|
||||
frames: int # 末段连续困住的帧数(上限 max_tail)
|
||||
stuck_ms: int | None = None # 末段连续卡住的时长(ms);无 timestamp 时 None
|
||||
detected_page: str | None = None # 末帧所在页面(pricebot detected_page 原值);无则 None
|
||||
dwell_ms: int | None = None # 末帧所在屏停留时长(ms);末帧路径(failed/兜底)用,无 ts → None
|
||||
|
||||
def label(self) -> str:
|
||||
p = PLATFORM_LABELS.get(self.platform, self.platform)
|
||||
@@ -90,10 +91,15 @@ def _read_head(path: Path, nbytes: int = 4096) -> tuple[str | None, str | None,
|
||||
return (ps.group(1) if ps else None, pg.group(1) if pg else None, ts.group(1) if ts else None)
|
||||
|
||||
|
||||
def _platform_stuck(
|
||||
platform: str, step_files: list[Path], threshold: int, max_tail: int
|
||||
) -> StuckPoint | None:
|
||||
"""末帧往前数连续同 (pipeline_step, detected_page) 的帧数 ≥threshold → 卡死。"""
|
||||
def _last_segment(
|
||||
step_files: list[Path], max_tail: int
|
||||
) -> tuple[str, str | None, int, int | None] | None:
|
||||
"""末帧往前数连续同 (pipeline_step, detected_page) 的一段。
|
||||
|
||||
返回 (pipeline_step, detected_page, count, dwell_ms);末帧 pipeline_step 抠不出 → None。
|
||||
dwell_ms = 段末帧ts − 段首帧ts(ms);两端 ts 不全可解析、或负(帧钟回退) → None。
|
||||
这是「末段停留」的唯一算法,卡死判据(≥threshold)与末帧停留(无门槛)都复用它。
|
||||
"""
|
||||
tail = step_files[-max_tail:]
|
||||
heads = [_read_head(p) for p in tail] # [(ps, pg, ts), ...]
|
||||
last_ps, last_pg, _ = heads[-1]
|
||||
@@ -105,15 +111,25 @@ def _platform_stuck(
|
||||
seg_ts.append(ts)
|
||||
else:
|
||||
break
|
||||
count = len(seg_ts)
|
||||
t_last, t_first = _parse_ts(seg_ts[0]), _parse_ts(seg_ts[-1])
|
||||
dwell_ms = round((t_last - t_first).total_seconds() * 1000) if t_last and t_first else None
|
||||
if dwell_ms is not None and dwell_ms < 0:
|
||||
dwell_ms = None # 时钟不单调(帧 timestamp 回退)→ 降级为不显示时长
|
||||
return last_ps, last_pg, len(seg_ts), dwell_ms
|
||||
|
||||
|
||||
def _platform_stuck(
|
||||
platform: str, step_files: list[Path], threshold: int, max_tail: int
|
||||
) -> StuckPoint | None:
|
||||
"""末帧往前数连续同 (pipeline_step, detected_page) 的帧数 ≥threshold → 卡死。"""
|
||||
seg = _last_segment(step_files, max_tail)
|
||||
if seg is None:
|
||||
return None
|
||||
ps, pg, count, dwell_ms = seg
|
||||
if count < threshold:
|
||||
return None
|
||||
# seg_ts[0]=末帧, seg_ts[-1]=段首帧;两端都能解析才算时长
|
||||
t_last, t_first = _parse_ts(seg_ts[0]), _parse_ts(seg_ts[-1])
|
||||
stuck_ms = round((t_last - t_first).total_seconds() * 1000) if t_last and t_first else None
|
||||
if stuck_ms is not None and stuck_ms < 0:
|
||||
stuck_ms = None # 时钟不单调(帧 timestamp 回退)→ 降级为不显示时长
|
||||
return StuckPoint(platform, last_ps, count, stuck_ms, detected_page=last_pg)
|
||||
# 卡死:frames=末段帧数、stuck_ms=末段时长(同段自洽);dwell_ms 字段留默认 None(T5 用 stuck_ms)
|
||||
return StuckPoint(platform, ps, count, dwell_ms, detected_page=pg)
|
||||
|
||||
|
||||
def read_stuck_points(trace_dir: Path, *, threshold: int, max_tail: int) -> StuckResult:
|
||||
|
||||
Reference in New Issue
Block a user