feat(protocol): 采集客户端枚举读取打点并加分析脚本

This commit is contained in:
TermiNexus committed 2026-10-21 03:29:56 +08:00
1 parent ce8eeec8d6
commit 1c5dc3afb9
2 files changed
+105

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
# analyze_enum_trace.py - 分析客户端「枚举读取」打点:确认读 enum 时字节游标是否推进
#
# 数据来源:镜像站 module_24635.js 的 Kt.t7h 补丁(枚举读器)。它记录
# {enum: 枚举 kClass 名, pos0/pos1: 读前/读后的字节游标, bit: nullmap 位游标}
# 节流 POST 到 serve.js 的 /__dump__/enum_trace.json。
#
# 判读:
# pos1 - pos0 == 0 → 该枚举不占字节(与源码 EnumCodecInfo/Kt 空实现一致);
# pos1 - pos0 != 0 → 客户端确实为这个枚举读了字节,说明宽度不是常量。
#
# 用法: python tools/analyze_enum_trace.py [--names names]
import json
import os
import sys
from collections import Counter, 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, 'enum_trace.json')
FieldsPath = os.path.join(NamesDir, 'codec_fields.json')
def Main():
if not os.path.exists(TracePath):
print('缺少 ' + TracePath)
print('请先用镜像站进一局战斗(Kt.t7h 已打枚举打点,会自动 POST)。')
return 1
with open(TracePath, 'r', encoding='utf-8') as F:
Data = json.load(F)
Rows = Data.get('trace') or []
print('枚举打点:%d 条(采集于 %s)' % (len(Rows), Data.get('at', '?')))
with open(FieldsPath, 'r', encoding='utf-8') as F:
Codecs = json.load(F).get('Codecs') or {}
# 枚举类型名 → 用到它的 (codec, 字段)
Users = defaultdict(list)
for Key, C in Codecs.items():
for F in C.get('Fields') or []:
T = str(F.get('Type') or '')
if T.startswith('enum<') and T.endswith('>'):
Users[T[5:-1]].append((Key, F.get('Name')))
Delta = Counter()
ByEnum = defaultdict(Counter)
for R in Rows:
D = (R.get('pos1', -1) - R.get('pos0', -1)) if R.get('pos0', -1) >= 0 else -1
Delta[D] += 1
ByEnum[R.get('enum') or '?'][D] += 1
print(' 字节推进量分布(pos1-pos0):%s' % dict(Delta))
print(' 按枚举类型:')
for Name, Cnt in sorted(ByEnum.items(), key=lambda X: -sum(X[1].values()))[:25]:
Where = Users.get(Name) or []
Sample = ('%s.%s' % Where[0]) if Where else '?'
print(' %-4s 次数=%-5d 推进=%s 使用处=%s%s'
% (Name or '?', sum(Cnt.values()), dict(Cnt), Sample, (' 等 %d 处' % len(Where)) if len(Where) > 1 else ''))
return 0
if __name__ == '__main__':
sys.exit(Main())
@@ -510,6 +510,40 @@
(z(Vt).u7h = function (t) {}),
(z(Kt).u7h = function (t) {}),
(z(Kt).t7h = function (t) {
// [Hornet 调试] 枚举读取打点:记录「枚举类型名 + 读前/读后的字节游标」。
// 枚举本身按源码不占字节(这里恒返回 null),打点只为确认客户端在真实数据里
// 读 enum 时字节游标到底动没动,以及是哪个枚举类型(y7h_1 = kClass)。
try {
if (typeof window !== "undefined") {
var S = t.e7h_1;
var P0 = S && typeof S.g2t === "function" ? S.g2t() : -1;
var E = (window.__HornetEnumTrace__ = window.__HornetEnumTrace__ || {
rows: [], timer: 0,
push: function (Row) {
if (this.rows.length >= 3000) return;
this.rows.push(Row);
if (!this.timer) {
var Me = this;
this.timer = setTimeout(function () {
Me.timer = 0;
try {
fetch("/__dump__/enum_trace.json", {
method: "POST",
body: JSON.stringify({ at: new Date().toISOString(), count: Me.rows.length, trace: Me.rows }),
});
} catch (E2) {}
}, 1500);
}
},
});
E.push({
enum: (this.y7h_1 && this.y7h_1.name) || "",
pos0: P0,
pos1: S && typeof S.g2t === "function" ? S.g2t() : -1,
bit: (t.f7h_1 && typeof t.f7h_1.y7g_1 === "number") ? t.f7h_1.y7g_1 : -1,
});
}
} catch (E4) {}
return null;
}),
(z(Kt).s7h = function (t, n) {}),