fix(tools): 修正审计位游标少加形态位的一处偏移
This commit is contained in:
1 parent
fe83456f04
commit
bdca6a8fff
2 files changed
+50
-30
No files matched your search
@@ -1,12 +1,14 @@
|
||||
# analyze_opt_trace.py - 分析客户端「全量 opt 读取」打点,按帧还原每个模型消费了几位
|
||||
# analyze_opt_trace.py - 还原客户端每帧的 opt 位序列,按「模型片段」汇总
|
||||
#
|
||||
# 数据来源:镜像站 module_24635.js 的 Ht.t7h 补丁。每条记录:
|
||||
# rid = 位读取器实例 id(同一帧共享 → 同 rid 即同帧)
|
||||
# pos = 读位前的绝对位索引;bit = 读到的位
|
||||
# slots = 内层读器槽位名(模型字段);elemKeys = 列表元素读器槽位名
|
||||
# 槽位名全局唯一,所以能用 names/codec_fields.json 的 Slots 反查出具体模型。
|
||||
# 数据来源:镜像站 module_24635.js 的 Ht.t7h 打点,每条记录:
|
||||
# rid = 位读取器 id(同一帧共享)、pos = 读位前绝对位索引、bit = 读到的位、
|
||||
# slots/elemKeys = 内层读器(或列表元素)的槽位名(全局唯一,可反查模型)。
|
||||
#
|
||||
# 用法: python tools/analyze_opt_trace.py [--names names] [--only bs,Tu,tu]
|
||||
# 输出:把同一帧里「连续属于同一个模型」的读位合并成片段:
|
||||
# 模型@起始位 × 位数 = 位串
|
||||
# 这样可以直观看出某个模型到底消费了几位(例如 Item3DCC 是 4 位还是 5 位)。
|
||||
#
|
||||
# 用法: python tools/analyze_opt_trace.py [--only bs,Tu,tu] [--frames 5]
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
@@ -28,10 +30,15 @@ TracePath = os.path.join(NamesDir, 'opt_trace.json')
|
||||
FieldsPath = os.path.join(NamesDir, 'codec_fields.json')
|
||||
|
||||
|
||||
def LoadSlotIndex():
|
||||
def LoadIndex():
|
||||
"""槽位集合 → codec 键。用 Fields[].Slot(比 Slots 更全),再补 Slots。"""
|
||||
with open(FieldsPath, 'r', encoding='utf-8') as F:
|
||||
Codecs = json.load(F).get('Codecs') or {}
|
||||
Index = {}
|
||||
for Key, C in Codecs.items():
|
||||
Slots = tuple(sorted({str(F2.get('Slot')) for F2 in (C.get('Fields') or []) if F2.get('Slot')}))
|
||||
if Slots and Slots not in Index:
|
||||
Index[Slots] = Key
|
||||
for Key, C in Codecs.items():
|
||||
Slots = tuple(sorted(S.get('Slot') for S in (C.get('Slots') or [])))
|
||||
if Slots and Slots not in Index:
|
||||
@@ -41,44 +48,55 @@ def LoadSlotIndex():
|
||||
|
||||
def Main():
|
||||
if not os.path.exists(TracePath):
|
||||
print('缺少 ' + TracePath)
|
||||
print('请先用镜像站进一局战斗(Ht.t7h 已打全量 opt 打点)。')
|
||||
print('缺少 ' + TracePath + '(先让镜像站进一局)')
|
||||
return 1
|
||||
with open(TracePath, 'r', encoding='utf-8') as F:
|
||||
Data = json.load(F)
|
||||
Rows = Data.get('trace') or []
|
||||
print('opt 打点:%d 条(采集于 %s)' % (len(Rows), Data.get('at', '?')))
|
||||
|
||||
Index = LoadSlotIndex()
|
||||
Only = [S for S in (Arg('only', '') or '').split(',') if S]
|
||||
Index = LoadIndex()
|
||||
Only = [S for S in (Arg('only', 'Tu,tu,bs') or '').split(',') if S]
|
||||
MaxFrames = int(Arg('frames', '5') or 5)
|
||||
|
||||
Frames = defaultdict(list)
|
||||
for R in Rows:
|
||||
Frames[R.get('rid')].append(R)
|
||||
|
||||
print(' 帧数:%d' % len(Frames))
|
||||
def NameOf(R):
|
||||
Slots = tuple(sorted(R.get('slots') or []))
|
||||
Key = Index.get(Slots)
|
||||
if Key:
|
||||
return Key.split('::')[-1]
|
||||
Elem = tuple(sorted(R.get('elemKeys') or []))
|
||||
if Elem:
|
||||
K2 = Index.get(Elem)
|
||||
return 'list<' + (K2.split('::')[-1] if K2 else '?') + '>'
|
||||
return '?' + ''.join(Slots)
|
||||
|
||||
def Segments(Seq):
|
||||
Out = []
|
||||
for R in Seq:
|
||||
N = NameOf(R)
|
||||
if Out and Out[-1][0] == N and Out[-1][2] == R.get('pos'):
|
||||
Out[-1][1] += str(R.get('bit') and 1 or 0)
|
||||
Out[-1][2] = R.get('pos', 0) + 1
|
||||
else:
|
||||
Out.append([N, str(R.get('bit') and 1 or 0), R.get('pos', 0) + 1, R.get('pos', 0)])
|
||||
return Out
|
||||
|
||||
Shown = 0
|
||||
for Rid in sorted(Frames, key=lambda X: (X is None, X)):
|
||||
Seq = Frames[Rid]
|
||||
Items = []
|
||||
Names = []
|
||||
for R in Seq:
|
||||
Slots = tuple(sorted(R.get('slots') or []))
|
||||
Key = Index.get(Slots)
|
||||
if not Key:
|
||||
ElemKeys = tuple(sorted(R.get('elemKeys') or []))
|
||||
Key = ('list<' + (Index.get(ElemKeys) or '?') + '>') if ElemKeys else ('?' + str(Slots))
|
||||
Short = Key.split('::')[-1]
|
||||
Names.append(Short)
|
||||
Items.append('%s@%s=%s' % (Short, R.get('pos'), R.get('bit')))
|
||||
if Only and not any(N in Only for N in Names):
|
||||
Segs = Segments(Seq)
|
||||
if Only and not any(S[0] in Only for S in Segs):
|
||||
continue
|
||||
Shown += 1
|
||||
if Shown > 12:
|
||||
if Shown > MaxFrames:
|
||||
continue
|
||||
print(' rid=%s(%d 位):%s' % (Rid, len(Seq), ' '.join(Items)))
|
||||
if Shown > 12:
|
||||
print(' …还有 %d 帧匹配' % (Shown - 12))
|
||||
Line = ' '.join('%s@%d×%d=%s' % (S[0], S[3], len(S[1]), S[1]) for S in Segs[:40])
|
||||
print(' rid=%s(%d 位):%s' % (Rid, len(Seq), Line))
|
||||
print(' 匹配帧数:%d' % Shown)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -151,7 +151,9 @@ function AuditPt(Body, Off, File, BitStart, Label) {
|
||||
const Bits = NmSize > 0 && NmSize <= Body.length ? NullmapBits(Body.slice(0, NmSize)) : null;
|
||||
// 位游标由 semantic 的顺序推进给出(BitStart = 本记录形态位之后的绝对位索引),
|
||||
// 不再假设「pt 是帧内第一条记录」——多记录帧里那样会整体错位。
|
||||
if (Bits) Reader.AttachBits(Bits, typeof BitStart === 'number' ? BitStart : 1);
|
||||
// BitStart 是「本记录形态位」的索引(semantic 在消费形态位之前记录),
|
||||
// 负载里的 opt 位从它的下一位开始 —— 少加这个 1 会让整条记录的位游标整体早一位。
|
||||
if (Bits) Reader.AttachBits(Bits, (typeof BitStart === 'number' ? BitStart : 0) + 1);
|
||||
try {
|
||||
for (let I = 0; I < D; I++) {
|
||||
if (At + 8 > Buf.length) return;
|
||||
|
||||
Reference in new issue
Block a user