feat(tools): 边界审计支持逐字段位游标追踪
This commit is contained in:
1 parent
ffe633b286
commit
fafdb91a04
2 files changed
+32
No files matched your search
@@ -45,11 +45,22 @@ const Alias = {};
|
||||
for (const [N, R] of Object.entries(Tables.ResourceClasses || {})) Alias[N] = R.Wire;
|
||||
for (const [N, W] of Object.entries(RESOURCE_CLASS_WIRE)) Alias[N] = W;
|
||||
|
||||
// --trace <文件名>:<项序号>:只对这一项打开逐字段追踪,打印「字段 / 字节区间 / 位游标前后」
|
||||
const TraceArg = Arg('trace', '');
|
||||
const TraceDepth = Number(Arg('trace-depth', '6')) || 6;
|
||||
let TraceOn = false;
|
||||
|
||||
const Reader = MakeReader(Tables, {
|
||||
EnumBytes: Number(Arg('enum-bytes', '4')) || 4,
|
||||
OptBits: true,
|
||||
TypeAlias: Alias,
|
||||
StringLiteralOnly: true,
|
||||
OnField: (I) => {
|
||||
if (!TraceOn || I.Depth > TraceDepth) return;
|
||||
console.log(' ' + ' '.repeat(I.Depth) + I.Name + ' <' + I.Type + '> ' +
|
||||
I.Start + '→' + I.Next + ' bit ' + I.BitBefore + '→' + I.BitAfter +
|
||||
(I.Bad ? ' BAD ' + I.Why : ''));
|
||||
},
|
||||
});
|
||||
|
||||
const PT_METHOD = '7640916300855664666';
|
||||
@@ -152,7 +163,14 @@ function AuditPt(Body, Off, File) {
|
||||
return;
|
||||
}
|
||||
const BitBefore = Bits ? Reader.BitCursor().Index : 0;
|
||||
const TraceNow = TraceArg && path.basename(File) === TraceArg.split(':')[0] && String(I + 1) === TraceArg.split(':')[1];
|
||||
if (TraceNow) {
|
||||
console.log(' [trace] ' + path.basename(File) + ' 第 ' + (I + 1) + ' 项 ' + Id + '(' + Key + ')负载@' + At +
|
||||
' 起始 bit=' + BitBefore);
|
||||
TraceOn = true;
|
||||
}
|
||||
const R = Reader.ReadFields(Buf, At, Codec, 0);
|
||||
TraceOn = false;
|
||||
Stats.Items++;
|
||||
// 「解不动」的判据要包含位游标:整条模型字段全是 opt 且都为空时,字节不推进但位在推进,
|
||||
// 这也是合法的(例如 DescriptionMediaCC 只有两个 opt<i64>)。
|
||||
|
||||
@@ -109,6 +109,9 @@ function SplitTypeArgs(Text) {
|
||||
function MakeReader(Tables, Options) {
|
||||
const Opts = Options || {};
|
||||
const EnumBytes = Opts.EnumBytes === undefined ? 4 : Opts.EnumBytes;
|
||||
// OnField:逐字段回调(诊断用)。参数 {Depth, Name, Type, Start, Next, BitBefore, BitAfter, Bad, Why}
|
||||
// —— 只读观测,不参与解码判定;审计器用它打印「每个字段消费了哪些字节/位」。
|
||||
const OnField = Opts.OnField || null;
|
||||
// OptBits:opt 的「空/非空」改从 nullmap 比特流读 1 个 bit(见文件头注释)。
|
||||
// 默认 false = 老口径(1 字节标志),也就是「字段表」那条路径的既有行为,不动它。
|
||||
const OptBits = !!Opts.OptBits;
|
||||
@@ -315,10 +318,21 @@ function MakeReader(Tables, Options) {
|
||||
let Bad = false;
|
||||
let Why = '';
|
||||
for (const F of (Codec && Codec.Fields) || []) {
|
||||
const BitBefore = BitIndex;
|
||||
const R = ReadValue(Buf, At, F.Type, F.TypeCodec, Depth);
|
||||
// 匿名字段(表里没有名字,只有线宽)用序号区分,否则会同名互相覆盖
|
||||
const Key = F.Name === '(匿名)' || F.Name === '?' ? '(匿名#' + F.Order + ')' : F.Name;
|
||||
Obj[Key] = R.Value;
|
||||
if (OnField) {
|
||||
try {
|
||||
OnField({
|
||||
Depth: Depth || 0, Name: Key, Type: F.Type, Start: At, Next: R.Next,
|
||||
BitBefore, BitAfter: BitIndex, Bad: !!R.Bad, Why: R.Why || '',
|
||||
});
|
||||
} catch (E) {
|
||||
// 诊断回调不允许影响解码
|
||||
}
|
||||
}
|
||||
if (R.Bad) {
|
||||
Bad = true;
|
||||
// 诊断用的原因串:哪个字段、什么类型、卡在负载内第几字节、内层如果直接说得出原因也带上
|
||||
|
||||
Reference in new issue
Block a user