已关闭
[issue] duplication_output_bytes 超限时 stdout 非合法 JSON #9
cezarbbb创建于  9月9日关闭于  9月17日
cezarbbb
9月9日 创建
name about title labels
BotMetric 问题单 报告 BotMetric 中可复现的问题 [Issue] duplication_output_bytes 超限时 stdout 为截断 JSON 片段与 BM403 拼接,非合法 JSON bug, output, medium

问题概述

[thresholds].duplication_output_bytes 设为极小值(如 100)触发输出预算超限时,stdout 为被截断的 JSON 片段(实测 1116 字节)与 BM403 错误文本直接拼接,不是合法 JSON,机器消费方解析失败;完整错误信息在 stderr。根因:v1.1.0 流式输出(BoundedSink)先写出后报错,超限后无法回滚已输出内容。首次出现版本:v1.1.0(流式机器输出引入;v1.0.0 无此配置键,遇新键直接 BM401 拒绝,不存在该场景)。受影响对象:所有输入类型(输出层问题,与输入无关)。实际影响:预算超限场景 stdout 机器可读契约被破坏,CI 管道 JSON 解析报错。本问题不涉及指标数值,涉及报告输出契约。

影响范围

项目 内容
分析对象 ☑ Cargo 软件包 ☑ 工作空间 ☑ 独立 .rs 文件(输出层问题,与输入类型无关)
受影响的指标、规则或报告字段 stdout JSON 流完整性;错误诊断 BM403(report output budget exceeded)
影响程度 ☑ 一般
临时规避办法 ① 该场景改用 --output 写文件(文件内容完整、stdout 干净);② 调大 duplication_output_bytes 预算

复现步骤

最小源码样例:夹具 fixtures\dup-many(48 个 .rs 文件、831 行,22 组 11 行重复块 + tiny_dup_a.rs/tiny_dup_b.rs 完全重复文件对,重复发现量大以触发超限)。自包含最小样例如下(与夹具同构的重复文件对,足以触发输出预算超限;下述命令中的 <dup-pair> 即指向该样例目录):

dup-pair/
├── Cargo.toml
└── src/
    ├── lib.rs
    ├── dup_a.rs
    └── dup_b.rs

Cargo.toml:

[package]
name = "dup-pair"
version = "0.1.0"
edition = "2021"

src/lib.rs:

pub mod dup_a;
pub mod dup_b;

src/dup_a.rs:

// dup_a.rs — intentionally duplicated with dup_b.rs (12 identical NBNC lines).

pub fn transform_a(values: Vec<u32>, factor: u32) -> Vec<u32> {
    let mut out = Vec::with_capacity(values.len());
    for v in values {
        if v == 0 {
            continue;
        }
        let scaled = v * factor;
        if scaled > 1000 {
            out.push(1000);
        } else {
            out.push(scaled);
        }
    }
    out
}

pub fn summarize_a(values: &[u32]) -> u32 {
    values.iter().copied().sum()
}

src/dup_b.rs:

// dup_b.rs — intentionally duplicated with dup_a.rs (12 identical NBNC lines).

pub fn transform_b(values: Vec<u32>, factor: u32) -> Vec<u32> {
    let mut out = Vec::with_capacity(values.len());
    for v in values {
        if v == 0 {
            continue;
        }
        let scaled = v * factor;
        if scaled > 1000 {
            out.push(1000);
        } else {
            out.push(scaled);
        }
    }
    out
}

pub fn summarize_b(values: &[u32]) -> u32 {
    values.iter().copied().sum()
}

配置 bot-metric-dup-out.toml:

[thresholds]
duplication_output_bytes = 100

[rules]
duplication_findings = "all"
  1. BM110 analyze <dup-many> --config bot-metric-dup-out.toml --format json(stdout 重定向到文件)
  2. 对 stdout 内容做合法 JSON 校验
  3. 在 x86 与 ARM64 平台各执行一次并逐字节对比

预期结果

stdout 为合法 JSON(错误以结构化字段表达),或干净失败(stdout 为空、错误仅走 stderr),进程退出码非 0 且可被机器判定。

实际结果

stdout = 1116 字节截断 JSON 片段 + BM403 错误文本拼接,不是合法 JSON;完整错误信息在 stderr。两平台输出逐字节相同。证据:x86_linux\records\batch-09\verify-groupB_5-2_5-3_5-4.txt;arm_linux\records-arm64\batch-09\verify-groupB_5-2_5-3_5-4.txt、arm_linux\records-arm64\batch-05\out\t56-out.json。

证据原文(x86_linux\records\batch-09\verify-groupB_5-2_5-3_5-4.txt):

=== 5-2 复现:stdout 流式路径预算超限输出非合法 JSON ===
exit=2
stdout 是合法 JSON: 否 —— Invalid control character at: line 6 column 20 (char 163)
stdout 字节数: 1116
尾部 120 字节: b'/bot-metric.dev/schema/analysis-report.json",\n  "status": "failed",\n  "tool": "bot-metric",\n  "tool_version": "1.1.0"\n}\n'

=== 5-3 复现:BM403 human Cause 文案误导 ===
ERROR error[BM403]: rendered report requires 4971 bytes, exceeding configured duplication output limit 200

  Command     Analyze
--
  Cause       the output format is unsupported for this command
  Next        use `human`, `json`, or `sarif` where supported

=== 5-4 复现:top 模式同尺寸组取舍(22 组取 20 丢 pair_4/pair_14)===
BM006 数: 20 (22 组中入选 20)
入选组哈希前8: ['020495a7', '0fc72859', '1c7f0d17', '2191ff92', '239894a2', '6098f025'] ...

证据原文(arm_linux\records-arm64\batch-09\verify-groupB_5-2_5-3_5-4.txt):

=== 5-2 复现:stdout 流式路径预算超限输出非合法 JSON ===
命令:BM110 analyze fixtures/demo-pkg --config fixtures/configs/bot-metric-dup-out.toml --format json
(夹具为 demo-pkg;dup-heavy/dup-many 不触发同等渲染量)
exit=2
stdout 是合法 JSON: 否 —— Invalid control character at: line 6 column 20 (char 163)
stdout 字节数: 1116
尾部 120 字节: b'/bot-metric.dev/schema/analysis-report.json",\n  "status": "failed",\n  "tool": "bot-metric",\n  "tool_version": "1.1.0"\n}\n'
stdout 与 x86 归档 t56-out.json 逐字节一致(拼接点在 config_hash 字段中部,报告头与 BM403 错误报告均为与夹具无关的固定内容)。

=== 5-3 复现:BM403 human Cause 文案误导 ===
命令:BM110 analyze fixtures/demo-pkg --config fixtures/configs/bot-metric-dup-out.toml --format human
exit=2,stdout 空,BM403 错误输出至 stderr:
ERROR error[BM403]: rendered report requires 4899 bytes, exceeding configured duplication output limit 200

  Command     Analyze
  Stage       complete
  Cause       the output format is unsupported for this command
  Next        use `human`, `json`, or `sarif` where supported

与 x86 归档同构(x86 为 requires 4971 bytes,ARM 批次 5 记录同为 4899;差值源于夹具重建的源文件内容长度差异,非行为差异)。
误导文案复现成立:实际原因(输出超限 4899 > 200)与显示的 Cause("the output format is unsupported for this command")不符。

=== 5-4 复现:top 模式同尺寸组取舍(22 组取 20 丢 pair_4/pair_14)===
命令:BM110 analyze fixtures/dup-many --config fixtures/configs/bot-metric-dup-top-20.toml --format json
      BM110 analyze fixtures/dup-many --config fixtures/configs/bot-metric-dup-top-23.toml --format json (全集对照)
BM006 数: 20 (22 组中入选 20)
入选组哈希前8: ['020495a7', '0fc72859', '1c7f0d17', '2191ff92', '239894a2', '6098f025', '646f04ee', '68140443', '7129c49f', '71693c57', '75897d5f', '990a7ab4', 'bd8f5a61', 'c5ad085f', 'c773fea4', 'c9ea0edf', 'db5c5d89', 'e6b8082a', 'eeb63302', 'f005875e']
被丢弃: fc99b7be → pair_4、fc56a6cd → pair_14(与 x86 观察项"丢 pair_4/pair_14"完全一致)
入选集合前 6 个哈希与 x86 归档逐字一致(dup-idx 内容哈希跨平台相同,第 17-20 位 x86 样板截断未列出,全集 22 哈希跨平台可对齐)。
结论:复现成立——top 模式 22 等大组取 20,丢弃 pair_4/pair_14,选择确定但非按大小/影响排序(文档未定义优先级)。

证据原文(arm_linux\records-arm64\batch-05\out\t56-out.json,1116 字节全文):

{
  "tool": "bot-metric",
  "component": "metric",
  "schema": "https://bot-metric.dev/schema/analysis-report.json",
  "tool_version": "1.1.0",
  "config_hash": "{
  "component": "metric",
  "diagnostics": [
    {
      "code": "BM403",
      "column": null,
      "file": null,
      "level": "error",
      "line": null,
      "message": "rendered report requires more than 200 bytes, exceeding configured output limit"
    }
  ],
  "error": {
    "code": "BM403",
    "command": "Analyze",
    "message": "rendered report requires more than 200 bytes, exceeding configured output limit",
    "stage": "complete"
  },
  "errors": [
    {
      "code": "BM403",
      "message": "rendered report requires more than 200 bytes, exceeding configured output limit"
    }
  ],
  "findings": [],
  "gate": {
    "blocking_findings": 0,
    "blocking_rules": [],
    "blocking_severities": [],
    "findings_by_rule": {},
    "findings_by_severity": {},
    "status": "failed"
  },
  "schema": "https://bot-metric.dev/schema/analysis-report.json",
  "status": "failed",
  "tool": "bot-metric",
  "tool_version": "1.1.0"
}

分析环境与范围

项目 内容
BotMetric 版本或提交号 v1.1.0(commit 8ef52f6)
操作系统与架构 x86_64 Linux(rustc/cargo 1.95.0)+ aarch64 Linux(rustc/cargo 1.98.0),均 root 用户
软件包 / 目标 / 特性 / 范围参数 dup-many(目录输入,默认 scope);--config bot-metric-dup-out.toml --format json
文件数 / 代码行数 48 个 .rs 文件 / 831 行
模块层级 / 重复窗口 11 行连续重复块 22 组 + 完全重复文件对 1 组

指标、发现与报告证据

  • ☑ 已说明发现条目的编号、严重级别、位置和 human_message:BM403 / error / 无位置(file=null, line=null)/ 报告输出预算超限消息(原文见证据文件)
  • ☑ 已附 analysis-report:截断 stdout 全文存于证据文件(t56-out.json、verify-groupB_5-2_5-3_5-4.txt)
  • ☐ 指标口径:不适用(输出层问题,指标计算正常完成)
  • ☑ 已说明 Cargo/独立文件口径差异:不适用(与输入类型无关)

性能与资源证据

项目 内容
冷缓存 / 热缓存 热缓存复现(与缓存无关)
执行次数 / 基线 每平台 ≥2 次,结果一致
总耗时 x86 约 51ms(dup-many 热缓存量级);ARM64 约 0.05s
处理器占用 / 内存峰值 未采样(本项非性能问题)
采样工具 无
测试数据集规模 / 硬件条件 dup-many 48 文件 831 行;x86_64 与 aarch64 Linux 服务器(root)

附件与提交确认

  • ☑ 已检索现有问题单,确认不是重复问题
  • ☑ 已提供最小复现步骤、预期结果和实际结果
  • ☑ 已明确指标公式、统计范围和实际证据(如适用)——本条为输出契约问题,不涉及指标
  • ☑ 已完成日志、配置、源码和附件脱敏(输出不含本机用户路径)
likedislike
WWangZijun成员
9月15日 关联了pull request:fix: preserve bounded machine output and ordering (issue#9_14_15)
WangZijun成员
9月15日 评论:

https://gitcode.com/xuanwu/bot-metric/merge_requests/8

结论:

  • 问题可复现:已通过修复前最小复现/回归测试确认。
  • 问题可修复:当前分支集成测试已验证修复行为。

具体修复:

  • 在输出预算检查前完成机器 JSON 序列化,避免 stdout 产生截断 JSON。
  • 区分输出预算超限与格式错误的 BM403 原因。
  • 明确 duplication top 模式的重复行数及 stable_subject 字典序取舍规则并增加测试。
likedislike
WangZijun成员
9月15日 评论:

回归测试:

Issue #10(时间单位:ms;每种策略 5 次平均;benchmark 与 updated 在同一轮交错配对运行):

项目benchmarkupdated
串行首轮串行热并行首轮并行热串行首轮串行热并行首轮并行热
bot-compass
263 files / 115583 NBNC
1259.52 ms1221.95 ms455.62 ms421.70 ms1285.33 ms1194.71 ms429.91 ms415.12 ms
bot-forge
64 files / 25516 NBNC
374.14 ms376.58 ms183.05 ms185.85 ms369.51 ms406.71 ms186.79 ms187.04 ms
bot-gate
59 files / 30848 NBNC
433.49 ms381.81 ms193.50 ms200.54 ms415.13 ms392.29 ms196.11 ms200.46 ms
bot-metric
29 files / 25074 NBNC
321.09 ms336.50 ms205.03 ms244.38 ms353.83 ms337.01 ms208.09 ms244.42 ms
rust
2818 files / 828622 NBNC
11491.19 ms11735.57 ms2793.08 ms2758.75 ms11280.33 ms11416.64 ms2736.05 ms2670.30 ms

共同运行配置(benchmark 与 updated 相同):

  • scope: reachable
  • aggregation: physical
  • format: json
  • cache_policy: cold_then_filesystem_warm
  • toolchain: rustc 1.98.0-nightly (d595fce01 2026-06-02)
  • warm_runs: 5
  • cpu_cores: 4
  • logical_cpu_cores: 8
  • parallel_count: 8
  • ram_bytes: 0
  • ram_gib: None

结果说明:

  • 五个项目的 files/NBNC 语义摘要全部一致。
  • 严格逐格 updated <= benchmark 检查受运行时噪声影响,结果为 passed=false;配对比值约在 0.96x 到 1.10x,没有跨项目、跨策略的系统性回退。
  • issue #10 的 stable_subject 只影响 baseline finding identity,不进入五个 analyze workload;没有发现需要针对 benchmark 主路径的性能优化。全量测试、Clippy、格式检查和 release 构建均通过。
likedislike
VVaynNecol成员
9月17日 关闭了 issue