feat(protocol): 全量 opt 打点按帧分组,定位每个模型消费的位数
This commit is contained in:
1 parent
d95e35e5c9
commit
b8651b4a8a
2 files changed
+116
-31
No files matched your search
@@ -0,0 +1,86 @@
|
||||
# analyze_opt_trace.py - 分析客户端「全量 opt 读取」打点,按帧还原每个模型消费了几位
|
||||
#
|
||||
# 数据来源:镜像站 module_24635.js 的 Ht.t7h 补丁。每条记录:
|
||||
# rid = 位读取器实例 id(同一帧共享 → 同 rid 即同帧)
|
||||
# pos = 读位前的绝对位索引;bit = 读到的位
|
||||
# slots = 内层读器槽位名(模型字段);elemKeys = 列表元素读器槽位名
|
||||
# 槽位名全局唯一,所以能用 names/codec_fields.json 的 Slots 反查出具体模型。
|
||||
#
|
||||
# 用法: python tools/analyze_opt_trace.py [--names names] [--only bs,Tu,tu]
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
Root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def Arg(Name, Def):
|
||||
if '--' + Name in sys.argv:
|
||||
I = sys.argv.index('--' + Name)
|
||||
if I + 1 < len(sys.argv):
|
||||
return sys.argv[I + 1]
|
||||
return Def
|
||||
|
||||
|
||||
NamesDir = os.path.abspath(Arg('names', os.path.join(Root, 'names')))
|
||||
TracePath = os.path.join(NamesDir, 'opt_trace.json')
|
||||
FieldsPath = os.path.join(NamesDir, 'codec_fields.json')
|
||||
|
||||
|
||||
def LoadSlotIndex():
|
||||
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(S.get('Slot') for S in (C.get('Slots') or [])))
|
||||
if Slots and Slots not in Index:
|
||||
Index[Slots] = Key
|
||||
return Index
|
||||
|
||||
|
||||
def Main():
|
||||
if not os.path.exists(TracePath):
|
||||
print('缺少 ' + TracePath)
|
||||
print('请先用镜像站进一局战斗(Ht.t7h 已打全量 opt 打点)。')
|
||||
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]
|
||||
|
||||
Frames = defaultdict(list)
|
||||
for R in Rows:
|
||||
Frames[R.get('rid')].append(R)
|
||||
|
||||
print(' 帧数:%d' % len(Frames))
|
||||
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):
|
||||
continue
|
||||
Shown += 1
|
||||
if Shown > 12:
|
||||
continue
|
||||
print(' rid=%s(%d 位):%s' % (Rid, len(Seq), ' '.join(Items)))
|
||||
if Shown > 12:
|
||||
print(' …还有 %d 帧匹配' % (Shown - 12))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(Main())
|
||||
@@ -413,26 +413,29 @@
|
||||
null == n ? t.f7h_1.b7h(!0) : (t.f7h_1.b7h(!1), this.r7h_1.s7h(t, n));
|
||||
}),
|
||||
(z(Ht).t7h = function (t) {
|
||||
// [Hornet 调试] 可选读器的位痕迹采样:只对「可选 + 列表」这类罕见的组合记录
|
||||
// (判据:内层读器带列表槽位 z7h_1),把位值、内层读器类名、调用栈节流 POST 到
|
||||
// /__dump__/nullmap_trace.json,用于确认 opt<list<…>> 在真实数据里到底占不占位。
|
||||
// 位只读一次:采样失败也只是少一条日志,绝不改变解码结果。
|
||||
// [Hornet 调试] 可选读器打点(全量 opt,按帧分组):记录
|
||||
// rid = 位读取器实例 id(一帧内所有记录共享同一个位读取器 → 同一 rid 即同一帧)
|
||||
// pos = 读位前的绝对位索引;bit = 读到的位
|
||||
// slots = 内层读器的槽位名(模型字段 → 槽位名全局唯一,可离线反查是哪个模型)
|
||||
// 目的:直接看客户端对「某个模型」到底消费了几个 opt 位,从而定位我们少算的那一位。
|
||||
// 位只读一次;打点异常不影响解码。
|
||||
var Inner = this.r7h_1;
|
||||
if (Inner && Inner.z7h_1 !== undefined) {
|
||||
var B0 = t.f7h_1.xy();
|
||||
var BR = t && t.f7h_1;
|
||||
if (BR && typeof BR.xy === "function" && BR.y7g_1 !== undefined) {
|
||||
var B0 = BR.xy();
|
||||
try {
|
||||
if (typeof window !== "undefined") {
|
||||
var T = (window.__HornetBitTrace__ = window.__HornetBitTrace__ || {
|
||||
rows: [], sent: 0, timer: 0,
|
||||
var T0 = (window.__HornetOptTrace__ = window.__HornetOptTrace__ || {
|
||||
rows: [], timer: 0, nextRid: 1,
|
||||
push: function (Row) {
|
||||
if (this.rows.length >= 4000) return;
|
||||
if (this.rows.length >= 8000) return;
|
||||
this.rows.push(Row);
|
||||
if (!this.timer) {
|
||||
var Me = this;
|
||||
this.timer = setTimeout(function () {
|
||||
Me.timer = 0;
|
||||
try {
|
||||
fetch("/__dump__/nullmap_trace.json", {
|
||||
fetch("/__dump__/opt_trace.json", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ at: new Date().toISOString(), count: Me.rows.length, trace: Me.rows }),
|
||||
});
|
||||
@@ -441,30 +444,26 @@
|
||||
}
|
||||
},
|
||||
});
|
||||
var St = "";
|
||||
try {
|
||||
St = String(new Error().stack || "").split("\n").slice(1, 4).join(" | ");
|
||||
} catch (E3) {}
|
||||
// hid:给每个可选包装器实例分配稳定 id(同一字段的多次读取共享 id);
|
||||
// elem/elemKeys:内层列表的元素 codec 类名与槽位名——槽位名全局唯一,可离线唯一定位字段。
|
||||
var Elem = Inner.z7h_1;
|
||||
if (!Inner.__hornetHid) {
|
||||
Inner.__hornetHid = T.nextId || 1;
|
||||
T.nextId = Inner.__hornetHid + 1;
|
||||
}
|
||||
var ElemName = '';
|
||||
var Slots = [];
|
||||
var ElemKeys = [];
|
||||
try {
|
||||
ElemName = (Elem && Elem.constructor && Elem.constructor.name) || '';
|
||||
ElemKeys = Elem ? Object.keys(Elem) : [];
|
||||
Slots = Inner ? Object.keys(Inner) : [];
|
||||
ElemKeys = Inner && Inner.z7h_1 ? Object.keys(Inner.z7h_1) : [];
|
||||
} catch (E5) {}
|
||||
T.push({
|
||||
hid: Inner.__hornetHid, bit: B0, inner: Inner.constructor && Inner.constructor.name,
|
||||
elem: ElemName, elemKeys: ElemKeys,
|
||||
// pos = 客户端位读取器的绝对游标(Et.y7g_1,读之前)。用来和我们离线解的位游标对比
|
||||
pos: (t.f7h_1 && typeof t.f7h_1.y7g_1 === "number") ? t.f7h_1.y7g_1 : -1,
|
||||
stack: St,
|
||||
});
|
||||
// 只记「能唯一定位」的:模型槽位 或 列表元素槽位;基础类型 opt 太碎且无指纹,跳过
|
||||
if (Slots.length > 0 || ElemKeys.length > 0) {
|
||||
if (!BR.__hornetRid) {
|
||||
BR.__hornetRid = T0.nextRid || 1;
|
||||
T0.nextRid = BR.__hornetRid + 1;
|
||||
}
|
||||
T0.push({
|
||||
rid: BR.__hornetRid,
|
||||
pos: BR.y7g_1 - 1,
|
||||
bit: B0,
|
||||
slots: Slots,
|
||||
elemKeys: ElemKeys,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (E4) {}
|
||||
return B0 ? null : Inner.t7h(t);
|
||||
|
||||
Reference in new issue
Block a user