Files
cobol-java-v3/docs/system-analysis.md
T

34 KiB
Raw Blame History

COBOL → Java/Spark 迁移验证平台 v3 — 系统分析


一、系统概览

职责概述

COBOL 测试数据生成 + 迁移验证平台:解析 COBOL 源码,生成分支全覆盖的测试数据,并行运行 COBOL 和 Java/Spark 版本,逐字段比对输出,判定迁移正确性并生成验证报告。

两条平行管道

系统包含两条完全独立的管道,根据源码是否包含 EXEC SQL 自动路由:

维度 非 DB 管道 DB 管道
入口 main.pyorchestrator.py python -m cobol_testgenorchestrator_db.py
适用程序 纯 flat file I-O 程序 含 EXEC SQL 的 DB 程序
编译器 cobc 标准编译 gixpp (ESQL 预处理) + cobc -l gixsql
存储 二进制 flat file SQLite + flat file
验证 二进制字节比对 SQLite 表行比对 + 中间 JSON
覆盖率 静态分支覆盖 gcov 动态覆盖(跨场景合并)

目录结构总览

cobol-java-v3/
├── cobol_testgen/          ← 核心引擎(18 个 .py 文件)
├── orchestrator.py         ← 非 DB 管道编排
├── orchestrator_db.py      ← DB 管道编排(1334 行)
├── main.py                 ← 非 DB CLI 入口
├── config/                 ← 配置系统(全局 + per-program YAML
├── runners/                ← 编译运行引擎(6 个 runner
├── gixsql/                 ← vendored ESQL 工具链
├── test-data/              ← 测试套件(61 个脚本)
├── docs/                   ← 文档
│── README.md / SETUP.md    ← 项目首页 + 搭建指南

二、cobol_testgen 核心引擎

职责概述

COBOL 源码解析器 + 测试数据生成器。四层架构(INPUT → CORE → CONDITION → DESIGN+ 两个正交层(OUTPUT, COVERAGE),依赖单向流动。

内部结构

cobol_testgen/
│
├── models.py (163 行)
│   数据模型层,零内部依赖
│   ├── PicInfo          — PIC 解析结果 (type, digits, decimal, length, signed)
│   ├── FieldDef         — DATA DIVISION 字段定义
│   ├── BrSeq/BrIf/BrEval/BrPerform/BrSearch — 分支树节点类型
│   ├── Assign           — 赋值语句 (MOVE/COMPUTE/SQL/INITIALIZE 等)
│   ├── CondLeaf/CondNot/CondAnd/CondOr — 条件表达式树节点
│   ├── Constraint       — 约束元组 (field, op, value, want_true)
│   └── ProcParseResult  — 解析结果容器
│
├── read.py (675 行)  ──── INPUT 层
│   预处理 + DATA DIVISION 解析
│   ├── preprocess()           — 主预处理: COPYBOOK 展开, EXEC 剥离, 注释清除
│   ├── resolve_copybooks()    — 递归 COPY 解析 (10 层深度保护)
│   ├── resolve_sql_includes() — EXEC SQL INCLUDE → COPY
│   ├── strip_exec_sql_from_data_div() — 从 DATA DIVISION 移除 SQL 块
│   ├── parse_data_division()  — Lark Earley parser → list[FieldDef]
│   ├── parse_pic()            — PIC 字符串 → PicInfo
│   ├── parse_file_control()   — FILE-CONTROL → {filename: {assign, org, mode}}
│   ├── parse_file_section()   — FILE SECTION → {FDname: [01-names]}
│   └── scan_open_statements() — OPEN 语句 → {filename: INPUT/OUTPUT/I-O}
│
├── core.py (2169 行)  ──── CORE 层
│   PROCEDURE DIVISION 解析 + 数据流追踪
│   ├── build_branch_tree()        — 主入口: 源码 → 分支树
│   ├── _BrParser (类)             — 行级状态机解析器
│   │   ├── parse_seq()            — 顺序语句序列
│   │   ├── _parse_if()            — IF ... END-IF
│   │   ├── _parse_evaluate()      — EVALUATE ... END-EVALUATE
│   │   ├── _parse_perform()       — PERFORM (UNTIL/VARYING/TIMES/THRU/行内)
│   │   ├── _parse_call()          — CALL ... USING
│   │   ├── _parse_search()        — SEARCH / SEARCH ALL
│   │   ├── _parse_sql()           — EXEC SQL (SELECT/INSERT/DELETE/UPDATE/DECLARE CURSOR/FETCH)
│   │   ├── _record_assignment()   — MOVE/COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE
│   │   ├── _parse_initialize()    — INITIALIZE
│   │   ├── _parse_string()        — STRING
│   │   └── _parse_unstring()      — UNSTRING
│   ├── propagate_assignments()    — 赋值传播 (20 轮收敛循环)
│   ├── trace_to_root()            — 追踪赋值链到根变量
│   ├── classify_field_roles()     — 字段角色分类 (input/output/inout/unused)
│   └── extract_sql_assignments()  — 提取 SQL 赋值
│
├── cond.py (413 行)  ──── CONDITION 层
│   条件表达式解析 + MC/DC 枚举
│   ├── parse_single_condition()   — 单条件 → (field, op, value)
│   ├── parse_compound_condition() — AND/OR/NOT/括号 → 条件树
│   ├── evaluate_tree()            — 条件树求值 (leaf→bool)
│   ├── collect_leaves()           — 收集所有叶子节点
│   ├── mcdc_sets()                — MC/DC 约束集生成
│   └── satisfying_value()         — 计算满足条件的值
│
├── design.py (1875 行)  ──── DESIGN 层
│   路径枚举 + 记录生成
│   ├── enum_paths()               — 递归树遍历路径枚举 (~500 行)
│   ├── make_base_record()         — 基础记录生成 (初值)
│   ├── apply_constraint()         — 单约束应用到记录 (~125 行)
│   ├── generate_records()         — 主生成入口: base → propagate → constrain → sync
│   ├── sync_redefined_fields()    — REDEFINES 字段同步
│   ├── apply_occurs_depending()   — OCCURS DEPENDING ON 处理
│   ├── _rebuild_r01line_csv()     — CSV 源字段重建
│   ├── _reconstruct_unstring_sources() — UNSTRING 源重建
│   └── _set_invalid_value()       — 无效值注入 (CALL ABEND 触发)
│
├── design_mcdc.py (286 行)
│   线性路径枚举 (O(2N) 替代 O(2^N),无爆炸风险)
│   └── enum_paths()               — 每决策点一条 T + 一条 F
│
├── pipeline_bridge.py (189 行)
│   新旧解析器桥接: BranchNode (procedure_parser) ↔ BrSeq 规范模型
│   └── build_branch_tree_fallback() — 新解析器优先 → 3s 超时回退到旧解析器
│
├── procedure_parser.py (610 行)
│   可选的 PROCEDURE DIVISION 解析器 (行级状态机)
│   └── extract_branch_tree()      — 源码 → BranchNode 树
│
├── output.py (213 行)  ──── OUTPUT 层
│   JSON 输出 + 二进制输入文件生成
│   ├── output_json()              — 按 FD 方向分 input/expected_output/working_storage
│   └── output_input_files()       — 写入 flat file
│
├── coverage.py (1375 行)  ──── COVERAGE 层
│   决策点收集 + 覆盖标记 + HTML 报告 (中文)
│   ├── collect_decision_points()  — 遍历分支树收集决策点
│   ├── mark_coverage()            — 从路径标记覆盖
│   ├── _mark_if() / _mark_eval() / _mark_search() / _mark_perform()
│   ├── generate_html_report()     — 中文 HTML 报告 (svg, 梯度)
│   ├── generate_coverage_index()  — 中文覆盖率索引页
│   └── run_coverage()             — 主覆盖入口
│
├── gcov.py (194 行)  ──── GCOV 层
│   gcov 解析 + 运行时覆盖
│   ├── parse_cbl_gcov()           — 解析 .cbl.gcov → {line: count}
│   ├── run_gcov()                 — 执行 gcov 子进程
│   └── mark_from_gcov()           — 从 gcov 数据标记覆盖
│
├── to_sql.py (481 行)  ──── SQL 层
│   SQL WHERE 约束解析 + DB 行生成 (仅 DB 管道)
│   ├── sql_extract_constraints()  — WHERE 子句 → 约束列表
│   ├── collect_sql_meta()         — 从 Assignments 收集 SQL 元数据
│   └── build_db_input()           — 按分支路径生成 DB 输入行
│
├── file_io.py (268 行)  ──── 工具层
│   COBOL 二进制 I/O (DISPLAY/COMP/COMP-3)
│
├── flatfile.py (327 行)  ──── 工具层
│   FD 布局分析 + flat file 写入
│
├── runner.py (587 行)  ──── 工具层
│   cobc 编译 + 运行 + 非 DB 验证
│   └── run_all() / run_and_compare()
│
├── data_merger.py (130 行)  ──── 工具层
│   白盒 + 功能 + 策略数据合并
│
├── __init__.py (~1300 行)  ──── 门面层
│   CLI 入口 main() + 公开 API
│   ├── main()                    — CLI 入口 (argparse, DB 自动路由)
│   ├── extract_structure()       — 公有 API: 静态分析
│   ├── generate_data()           — 公有 API: 数据生成
│   ├── expand_occurs()           — OCCURS 递归展开
│   ├── _chain_prev()             — 跨记录 PREV 链同步
│   ├── _inject_c01_coverage_records() — C01 决策点注入
│   └── incremental_supplement()  — 增量补充 (质量门)
│
└── __main__.py (4 行)
    python -m cobol_testgen → main()

数据流

COBOL 源码
  │
  ▼
read.py:preprocess()                ← COPYBOOK 展开, EXEC SQL 剥离, 注释清除
  │
  ▼
read.py:parse_data_division()       ← Lark parser → list[FieldDef]
  │
  ▼
__init__.py:expand_occurs()         ← OCCURS → 下标展开
  │
  ▼
core.py:build_branch_tree()         ← PROCEDURE DIVISION → 分支树
  │
  ▼
design.py:enum_paths()              ← 树遍历 → T/F 路径列表 (含约束)
  │
  ▼
design.py:generate_records()        ← 路径 → 记录
    ├── make_base_record()          ← 初值
    ├── propagate_assignments()     ← MOVE/COMPUTE 传播 (20 轮收敛)
    ├── apply_constraint()          ← 约束应用
    ├── sync_redefined_fields()     ← REDEFINES 同步
    └── _rebuild_r01line_csv()      ← CSV 重建
  │
  ▼
output.py:output_json()             ← JSON (input/expected/ws)
output.py:output_input_files()      ← 二进制 flat file
  │
  ▼
coverage.py:run_coverage()          ← HTML 覆盖率报告 (中文)

依赖关系

models.py  ← 零依赖, 被所有模块依赖
  │
  ├── read.py     ← lark (外部 parser)
  ├── cond.py     ← 纯 stdlib
  ├── core.py     ← cond.py
  ├── procedure_parser.py  ← 纯 stdlib
  │
  ├── design.py   ← models, cond, core
  ├── design_mcdc.py  ← models, cond, design
  ├── pipeline_bridge.py  ← models, procedure_parser, core
  │
  ├── output.py   ← file_io
  ├── coverage.py ← models, cond (optionally gcov)
  ├── gcov.py     ← 纯 stdlib
  ├── to_sql.py   ← 纯 stdlib
  ├── runner.py   ← file_io (optionally gcov)
  ├── flatfile.py ← read, file_io
  ├── data_merger.py ← cobol_testgen (自身)
  │
  └── __init__.py ← 所有上层模块 + japanese_data (外部)

无循环依赖。runner.py / gcov.py / to_sql.py 通过 try/except 门控导入(惰性依赖),允许核心管线在缺少可选依赖时正常工作。

关键注意事项

  1. OCCURS 展开只支持数值下标WS-CELL(WS-IDX) 这类变量下标在展开阶段不被支持,处理在 apply_constraint 中单独完成
  2. PREV 链硬编码 R01/W01 前缀_chain_prev() 只处理以 R01W01 开头的字段,是特定于 KIN/ZAN 程序集的假设
  3. C01 程序集硬编码SUB04CHKWRK-CSV-*C01CHKRRC 等字段名在多个位置硬编码(_inject_c01_coverage_records_rebuild_r01line_csvgenerate_records Pass B.13
  4. _MAX_PATHS = 50000 — 复杂程序可能达到上限导致路径多样性丢失;_cap_paths 用 T/F 多样性保护策略做公平裁剪,但仍可能丢失分支
  5. 算术约束启发式_apply_arith_constraint() 使用启发式(大/小值分配)而非精确求解器,复杂算术条件可能无法精确满足
  6. propagate_assignments 20 轮收敛 — 硬编码迭代上限,理论上自引用 COMPUTE 链可能不收敛
  7. 88-level 假设 — 目标程序没有 88-level VALUE,但解析器仍支持(用于测试程序 ALLCMDS.cbl
  8. False 值语义SET X TO FALSE 对 88-level 值的取反仅支持单字符值(Y→N, N→Y
  9. pipeline_bridge 3 秒超时 — 新解析器通过守护线程超时回退,可能在高负载下误触发
  10. gcov PERFORM 判断mark_from_gcov() 使用 count > 1 作为 PERFORM Enter 的启发式判断,不是精确判断

三、管道编排层

职责概述

两条管道的调度中枢。非 DB 管道(orchestrator.py)管理 9 阶段 LLM 驱动的验证流程;DB 管道(orchestrator_db.py)管理 6 步 ESQL 程序的编译→生成→运行→比对流程。

3.1 非 DB 管道 — orchestrator.py (203 行)

内部结构

run_pipeline(cfg, cpath, cbl, java, map_path)
│
├── 1. Agent1Parser(llm).parse()       — COPYBOOK → FieldTree (LLM)
├── 2. cobol_testgen.extract_structure() — 静态分析
│      cobol_testgen.generate_data()    — 测试数据生成
├── 3. hina.classify_program()          — 程序类型分类 (LLM)
│      hina.supplement()                — 策略补充
├── 4. cobol_testgen.check_coverage()   — 覆盖率检查
│      quality gate loop:               — 质量门循环 (最多 4 次)
│         gate_check() → incremental_supplement()
├── 5. Agent2Data(llm).design()         — LLM 测试设计(被 cobol_testgen 数据覆盖)
├── 6. DataWriter                       — 写入 COBOL 二进制 + JSON
├── 7. CobolRunner.compile()/run()      — COBOL 编译运行
├── 8. JavaRunner.compile()/run()       — Java/Spark 编译运行
├── 9. CobolBinaryReader → align_records → compare_field — 比对
├── 10. Agent3Diagnostic(llm).analyze() — LLM 诊断
└── 11. ReportGenerator                 — JSON + HTML + machine.json

依赖关系

agents/ (Agent1Parser, Agent2Data, Agent3Diagnostic, LLMClient) + hina/ (classify, gate, supplement) + cobol_testgen/ + runners/ (CobolRunner, JavaRunner, DataWriter) + comparator/ + report/ + storage/ + data/ + config/

关键注意事项

  1. Agent2Data 设计被覆盖 — line 110-114: LLM 生成的 TestSuite 被 cobol_testgen 的数据覆盖,实际只用了 LLM 的结构框架
  2. 质量门循环 — 最多 4 次迭代,每次调用 incremental_supplement() 补充未覆盖分支;达到 quality_gate_branch_threshold 或超限停止
  3. DB 管道不可达main.py 不连接 orchestrator_db.pyDB 程序必须通过 python -m cobol_testgen 入口运行

3.2 DB 管道 — orchestrator_db.py (1334 行)

内部结构

class GixsqlOrchestrator
│
├── __init__()                        — 加载 Config + ProgramSchema + GixsqlCobolRunner
│
├── run_all()                         — 主入口: Step 1→6 顺序执行
│   │
│   ├── Step 1: step1_setup_environment()
│   │   ├── _copy_sources_to_workdir()     — 复制到 ASCII-only 工作目录
│   │   └── GixsqlRunner.preprocess()+compile() — gixpp + cobc
│   │
│   ├── [For each scenario in schema.runs]:
│   │   │
│   │   ├── Step 2: step2_generate_inputs()
│   │   │   ├── cobol_testgen: extract_structure + generate_all_data
│   │   │   ├── _init_database()           — CREATE TABLE
│   │   │   ├── _populate_database()       — INSERT 覆盖分支数据
│   │   │   ├── _inject_sql_error_rows()   — 重复 PK (EXIT HANDLER 覆盖)
│   │   │   ├── _seed_matching_monthly_rows() — MONTHLY_ABSENCE 预填充
│   │   │   ├── flatfile.write_all_files() — 物理文件输出
│   │   │   └── output_json()              — JSON 输出
│   │   │
│   │   └── Step 3: step3_run_cobol()
│   │       ├── _scan_assign_to()          — 扫描 ASSIGN TO 方向
│   │       ├── GixsqlRunner.run()         — 执行 COBOL
│   │       └── .gcda/.gcno 收集
│   │
│   ├── Step 4: step4_extract_intermediate()  — SQLite → W01 JSON
│   ├── Step 5: step5_run_java()              — java -jar (可跳过)
│   ├── Step 6: step6_verify()                — COBOL vs Java 比对 (可跳过)
│   │
│   └── (可选) generate_coverage_report()
│       └── _merge_multi_run_gcov() + run_coverage() + HTML
│
├── _populate_database() (114 行)        — DB 行生成: build_db_input + INSERT
├── _create_tables()                     — CREATE TABLE IF NOT EXISTS
├── _inject_sql_error_rows()             — 重复 PK 注入
├── _seed_matching_monthly_rows()        — 预填充匹配行 (DP#27/#28)
├── _make_synthetic_error_rows()         — 兜底空表处理
└── _merge_multi_run_gcov()              — 多场景 gcov 合并

依赖关系

cobol_testgen (几乎所有子模块: read, core, design, design_mcdc, coverage, gcov, to_sql, flatfile, output, data_merger) + runners.gixsql_runner + config (Config + ProgramSchema) + data.diff_result

关键注意事项

  1. 单文件 1334 行 — 是项目中最大的文件,承担了生成、编排、DB 生命周期管理、gcov 合并等多种职责。函数间共享 self.* 状态,内聚但高耦合
  2. gixpp 中文路径 bug_copy_sources_to_workdir() 将源码复制到无中文路径的工作目录(C:\Users\... 含中文字符可能导致 gixpp 崩溃)
  3. 多场景 gcov 合并 — gcov 数据跨 normal/collision/abnormal 运行合并,但只累加不隔离,可能导致分支标记冲突
  4. skip_jvm=True 常态 — step5/step6 通常被跳过(Java 侧可能未实现)
  5. _scan_assign_to() 正则多行感知 — 跨越多行的 ASSIGN TO 语句通过多行正则会处理,但 OPEN 方向扫描仅检查单行
  6. INSERT OR IGNORE 使用 — 重复 PK 注入时使用 INSERT OR IGNORE 静默忽略失败,可能导致预期行未写入但无报错
  7. 硬编码的 COBOL 字段名WRK-R01-REC, R01INNFIL, WRK-EMP-ID, WRK-C01-* 等在多个位置直接引用
  8. EMP-ID 打补丁 — R01LINE 记录的 EMP-ID 在生成后会被 orchestrator_db.py 额外处理(line 340+),与 cobol_testgen 生成的初始值可能不一致

四、编译运行引擎 — runners/

职责概述

4 类 runnerCOBOL 非 DB / COBOL DB / Java Native / Spark+ 1 个 DataWriter。仅两个 Java runner 继承 Runner ABC,两个 COBOL runner 是独立类,没有多态派发。

内部结构

runners/
│
├── runner.py (40 行)                 ← ABC + 共享数据类型
│   ├── class Runner (ABC)
│   │   ├── compile(source_dir) → BuildResult
│   │   ├── run(artifact, input, output) → RunResult
│   │   └── get_coverage(artifact) → CoverageReport
│   ├── class BuildResult             — success, artifact_path, log
│   ├── class RunResult               — success, records, log
│   └── class CoverageReport          — branch_rate, verdict, detail
│
├── cobol_runner.py (115 行)          ← 非 DB COBOL runner (独立类)
│   ├── compile(src, dialect, gcov)                   — 旧 API: 单文件编译
│   ├── run(binary, input_path, output_path)           — 旧 API: 管道 I/O
│   ├── compile_with_links(src, work_dir, ...)         — 新 API: 多文件+子程序
│   └── run_file_based(binary, run_dir, input_files)   — 新 API: 文件 I/O
│
├── gixsql_runner.py (423 行)         ← DB COBOL runner (独立类)
│   ├── class GixsqlCobolRunner
│   │   ├── preprocess()       — gixpp ESQL 预处理 + COPY 展开 + 源码规范化
│   │   ├── compile()          — cobc -x -K GIXSQL* -l gixsql
│   │   ├── run()              — 部署 DLL + 执行 + SQLite
│   │   └── read_db_tables()   — 运行后回读 SQLite 表
│   ├── class GixsqlBuildResult
│   ├── class GixsqlRunResult
│   └── class GixsqlTableData
│
├── native_java_runner.py (30 行)     ← Java 本地 runner (继承 Runner)
│   └── compile() → mvn -B package
│       run() → java -jar
│       get_coverage() → JaCoCo (若有)
│
├── spark_java_runner.py (36 行)      ← Spark runner (继承 Runner)
│   └── compile() → mvn package
│       run() → spark-submit
│       get_coverage() → 硬编码 0.80
│
├── data_writer.py (33 行)            ← 数据序列化 (独立类)
│   ├── write_cobol_binary()          — struct.pack 二进制
│   ├── write_spark_json()            — part-00000.json
│   └── write_native_json()           — JSON lines
│
└── __init__.py (31 行)               — 门面导出

依赖关系

Runner 外部依赖
cobol_runner cobc 命令行 (GnuCOBOL)
gixsql_runner gixpp.exe + libgixsql.dll + 额外 DLL 链 (gixsql/lib/) + cobc
native_java_runner mvn + java
spark_java_runner mvn + spark-submit
data_writer data.test_case.TestCase / SparkConfig

关键注意事项

  1. Runner ABC 未覆盖 COBOL runnerCobolRunnerGixsqlCobolRunner 的 API 与 Runner ABC 不兼容,调用方必须知道具体类型
  2. gixsql_runner 源码规范化 — 预处理阶段做大量源码修改(CONNECT TO 转换、变量注入、FROM/INTO 重排、缺失数据变量补丁),这些修改是 per-program 级别的,耦合了特定程序的知识
  3. DLL 部署策略GixsqlCobolRunner.run() 搜索 DLL 的路径顺序:TEMP → lib_path → gixpp_dir → x86/gcc → 系统 PATHlibfmt.dll 有独立的搜索逻辑
  4. SparkJavaRunner.get_coverage() 硬编码 — 返回固定的 branch_rate=0.80,无论实际测试结果
  5. CobolRunner 新旧两套 API — 旧 APIstdin/stdout 管道)和新 API(文件 I/O + 子程序链接)并存,由不同调用方使用
  6. 退出码 0 或 1 都算成功gixsql_runner.run() 将退出码 1 也视为成功(DB 程序可能在非错误路径返回 1)

五、gixsql ESQL 工具链

职责概述

vendored 在项目 gixsql/ 目录下的 MinGW 编译版 GixSQL 工具链,提供 ESQL 预处理(gixpp.exe+ 运行时库(libgixsql.dll),用于将含 EXEC SQL 的 COBOL 程序转换为标准 COBOL + CALL 语句。

内部结构

gixsql/
├── bin/
│   ├── gixpp.exe              — ESQL 预处理器 (主入口)
│   ├── libgcc_s_dw2-1.dll     — GCC 运行时
│   ├── libstdc++-6.dll        — C++ 标准库
│   └── libwinpthread-1.dll    — POSIX 线程
│
└── lib/
    ├── libgixsql.dll           — 主运行时 (.dll)
    ├── libgixsql.a             — 静态库
    ├── libgixsql.dll.a         — MinGW import lib
    │
    ├── libgixsql-sqlite.dll    — SQLite 后端插件 (本项目使用)
    ├── libgixsql-mysql.dll     — MySQL 后端
    ├── libgixsql-pgsql.dll     — PostgreSQL 后端
    ├── libgixsql-oracle.dll    — Oracle 后端
    ├── libgixsql-odbc.dll      — ODBC 后端
    │
    ├── libgcc_s_dw2-1.dll      — GCC 运行时 (冗余)
    ├── libstdc++-6.dll         — C++ 标准库 (冗余)
    ├── libwinpthread-1.dll     — POSIX 线程 (冗余)
    ├── libiconv-2.dll          — 字符编码转换
    ├── libintl-8.dll           — gettext 国际化
    ├── zlib1.dll               — 压缩
    ├── libfmt.dll              — C++ fmt 库
    ├── libxml2-2.dll           — XML 解析
    ├── libssl-3.dll            — OpenSSL TLS
    ├── libcrypto-3.dll         — OpenSSL 加密原语
    ├── liblzma-5.dll           — LZMA 压缩
    ├── libpq.dll               — PostgreSQL 客户端
    └── libmariadb.dll          — MariaDB 客户端

数据流

含 EXEC SQL 的 COBOL 源码 (.cbl)
  │
  ▼
gixsql_runner.preprocess():
  1. EXEC SQL INCLUDE SQLCA → COPY SQLCA
  2. CONNECT TO 'literal' → CONNECT TO :WS-GIX-CONN USER :WS-GIX-USR
  3. 注入 WS-GIX-* 变量定义
  4. Python 端展开所有 COPY 语句 (替代 cobc -E)
  5. 清除注释,规范行首到第 8 列
  6. 调整 SQL FROM/INTO 顺序 → _norm.cbl
  7. gixpp -i _norm.cbl -o _pp.cbl   ← ESQL → 纯 COBOL (CALL gixsql*)
  │
  ▼
gixsql_runner.compile():
  cobc -x -L gixsql/lib/ -K GIXSQL* -l gixsql _pp.cbl → .exe
  │
  ▼
gixsql_runner.run():
  环境变量 GIXSQL_DB_PATH → .exe 运行时通过 libgixsql-sqlite
  操作 SQLite 数据库

依赖关系

  • gixpp.exe — 无外部 PATH 依赖(但规避中文路径)
  • libgixsql.dll + libgixsql-sqlite.dll — 运行时需要通过 COB_LIBRARY_PATH 或部署到 exe 目录加载
  • GnuCOBOL 3.2.0 (GC32-BDB-SP1) — 需要含 SQLite 支持的版本

关键注意事项

  1. 中文路径 Buggixpp.exe 在含中文的路径下崩溃,所有源码必须复制到纯 ASCII 路径
  2. DLL 部署 — 运行时需要 11+ 个 DLL 文件,gixsql_runner.run() 会尝试从 5 个位置搜索并复制到 exe 目录;缺少任一 DLL 都会导致启动失败
  3. -K 符号表 — 编译时链接 11 个 GIXSQL* 符号,这些符号名必须与 libgixsql.dll 导出的完全一致
  4. CONNECT TO 转化为宿主变量 — 源码硬编码的 CONNECT TO 'mydb' 被替换为 CONNECT TO :WS-GIX-CONN,运行时通过环境变量注入实际路径,这是运行时配置与源码强耦合的典型例子
  5. SQL 语法兼容性 — gixpp 支持标准 ESQL 语法,但不支持所有 DB2 COBOL 的 esoteric 特性(如某些嵌套 SQL 结构)
  6. 多后端共存 — 虽然本项目仅使用 SQLite,但 gixsql 包包含了 MySQL/PostgreSQL/Oracle/ODBC 后端,DLL 部署时会复制所有发现的 DLL

六、配置系统 — config/

职责概述

两级配置:全局级(Config dataclass,从 aurak.toml 加载)和 per-program 级(ProgramSchema,从 config/programs/*.yaml 加载)。

内部结构

config/
│
├── __init__.py (73 行)           ← 全局配置
│   └── class Config (dataclass, 21 字段)
│       ├── 项目: project_name, copybook_paths, dialect
│       ├── LLM: llm_model, llm_timeout, llm_cache_dir, max_llm_cost
│       ├── 覆盖率: coverage_default, branch_pass
│       ├── 比对: rounding_mode, tolerance
│       ├── 运行: runner_mode, spark_master, spark_input_format
│       ├── 质量门: quality_gate_mode, quality_gate_*_threshold, max_quality_retries
│       ├── GCOV: gcov_enabled, gcov_work_dir, gcov_threshold
│       └── gixsql: gixsql_path, gixsql_lib_path, gixsql_db_path, gixsql_compile_flags
│       └── from_toml()  — 从 aurak.toml 加载
│
├── mapping.py (39 行)            ← 字段映射
│   ├── class FieldMapping        — cobol_field, java_field, type, precision, trim, format
│   └── class MappingConfig       — program, dialect, field_mappings, redefines_strategy
│       └── from_yaml() / get_java_field()
│
└── program_schema.py (93 行)     ← per-program DB schema
    ├── class ColumnDef           — name, type, primary_key, nullable, default, cobol_field
    ├── class TableDef            — name, columns[], create_if_missing, sql_name
    ├── class SysinDef            — period, include_invalid_period, modes[]
    ├── class ScenarioDef         — id, sysin, inject_duplicate_pk
    ├── class ProgramSchema       — program_id, db_tables[], subprograms[], db_type, runs[]
    │   └── from_yaml()
    └── load_schema()             — 从 config/programs/ 按 ID 加载

Per-Program YAML 配置 (6 个 DB 程序)

ID DB 文件 子程序 场景数
KIN02UPD kin.db LEAVE_RECORDS SUB02MSG, SUB03END 0
KIN03EXP kin.db LEAVE_RECORDS, HOLIDAY_CALENDAR SUB01DAT, SUB02MSG, SUB03END 0
KIN06CLD kin.db HOLIDAY_CALENDAR, EMP_MASTER SUB02MSG, SUB03END 0
KIN08DBU kin.db DAILY_RECORDS, MONTHLY_ABSENCE SUB02MSG, SUB03END 3
KIN09CSV kin.db DAILY_RECORDS, MONTHLY_ABSENCE SUB02MSG, SUB03END 0
ZAN06UPD OVERTIME.DB ZANTBL01, ZANTBL02 SUB01DAT, SUB02MSG, SUB03END, SUB04CHK, SUB05TIM 3

关键注意事项

  1. 配置源不统一Configaurak.toml 加载,ProgramSchema 从 YAML 加载,映射配置从另一个 YAML 加载。三个配置系统用不同格式
  2. 4 个程序无运行场景 — KIN02UPD/KIN03EXP/KIN06CLD/KIN09CSV 的 runs: 为空,orchestrator_db.run_all() 跳过多场景循环但 stpe2/step3 仍会运行
  3. gixsql 配置翻倍gixsql_pathgixsql_lib_path 有默认值(相对于项目根),但 gixsql_compile_flagsConfiggixsql_runner 中都有定义,可能不一致

七、测试套件 — test-data/

职责概述

61 个测试脚本,覆盖从分支覆盖单元测试到端到端 DB 管道的全栈验证。

关键测试脚本

脚本 职责 验证内容
s15_coverage_verification.py 8 个合成 COBOL 片段的管线覆盖测试 IF/AND/NESTED/EVAL/PERFORM/ELSE-IF/VARYING/NOT
s25_per_program_report.py 全量 43 程序覆盖率报告 3178/3178 = 100%
s26_regression_check.py 回归快速检查 ALL 43/43 AT 100%
s30_db_e2e.py ZAN06UPD 6 步 DB 管线的端到端验证 编译→生成→运行→提取→Java→比对
s19_final_bridge_test.py pipeline_bridge 桥接器验证 新旧解析器一致性
s21_cond_fix_verify.py 条件解析修复验证 AND/OR/NOT 处理
s17_gcov_comparison.py gcov 覆盖率比对 运行时 vs 静态覆盖率一致性
s22_tna_e2e.py KIN/ZAN 程序端到端 TNA 系统全程序验证

注意事项

  1. 测试结果预记录test-report.json 包含预存的结果,可能在某些脚本中用于跳过实际执行(回归风险)
  2. 大量一次性脚本 — rXX 系列(r4_cond_coverage.pyr16_vuln_review.py)很可能是开发阶段的探索性脚本,维护状态未知
  3. benchmark-programs 目录 — 项目外也有独立的 README 文档集,用于 COBOL 语句/模式的基准测试,与核心管线关系较松散

八、入口点

职责概述

三个入口点,对应两条管道 + 程序化 API。

8.1 非 DB CLI — main.py (50 行)

python main.py --copybook COPY.cpy --cobol-src PROG.cbl --java-src PROG.java --mapping map.yaml
  │ --runner native|spark
  │ --coverage boundary|branch
  │ --quality-gate-mode warn|off
  │ --gcov
  │ --dry-run
  │
  └─ orchestrator.run_pipeline(config, copybook, cbl, java, mapping)

8.2 DB CLI — cobol_testgen/__init__.py:main() (~1300 行中的 ~500 行 CLI 代码)

python -m cobol_testgen [--gcov] [--temp-dir DIR] prog1.cbl prog2.cbl ... [outdir]
  │
  ├── 自动检测 EXEC SQL → GixsqlOrchestrator (DB 管道)
  └── 无 EXEC SQL → 行内管线 (非 DB,类似但独立于 orchestrator.py)

8.3 程序化 API

from cobol_testgen import extract_structure, generate_data

st = extract_structure(src, copybook_dirs=[...])
recs = generate_data(src, st, copybook_dirs=[...])
# st["coverage"] 包含覆盖率统计

关键注意事项

  1. 两个非 DB 管线入口main.py 走 LLM 代理管线(Agent1→Agent2→Agent3),python -m cobol_testgen 的行内管线不走 LLM。同一个"非 DB"语义有两种实现
  2. DB 自动路由是字符串检查 — 检测 EXEC SQL 的方式是简单的字符串包含匹配,可能被注释中的 EXEC SQL 误触发
  3. CLI 代码与门面层混合__init__.py 同时承担 CLI 解析、公开 API 导出、核心逻辑(OCCURS 展开、PREV 链、C01 注入),职责过重

九、跨层关注点

9.1 硬编码的领域知识

系统中散布着对 KIN/ZAN 程序集的假设:

  • R01* 前缀硬编码(_chain_prev, _rebuild_r01line_csv
  • W01* 前缀硬编码(_chain_prev, _rebuild_r01line_csv
  • SUB04CHK, WRK-C01*, C01CHKRRC, WRK-CSV-* — C01 相关硬编码
  • EMP-ID, APPL-ID, YEAR-MONTH — KIN 领域字段名硬编码
  • gixsql_runner.py 中的 KIN06CLD 缺失字段补丁

9.2 两条管道的隔离度

  • 共享 cobol_testgen/ 核心库,但使用方式不同(DB 管道直接导入子模块,非 DB 管道通过 __init__.py 门面)
  • 没有共享的编排基类
  • 两个 Java runner 通过 Runner ABC 有多态,COBOL runner 没有
  • 配置系统有重叠(Config.gixsql_* 只在 DB 管道使用,但存在全局 Config 中)

9.3 已知不合理标注

位置 问题 类型
orchestrator_db.py 1334 行 单文件承担生成/编排/DB/合并等多种职责 职责过重
__init__.py 1300 行 CLI 逻辑与核心 API 混合 职责过重
core.py 2169 行 包含解析、数据流追踪、SQL 提取等多种不相关功能 职责过重
spark_java_runner.py:35 get_coverage 返回硬编码 0.80 假覆盖率
gixsql_runner.py:237-247 per-program 缺失数据变量补丁(KIN06CLD 特有) 领域泄漏
Runner ABC 未被 COBOL runner 实现 无多态派发,调用方必须 isinstance 接口不完整
main.py__init__.py:main() 两个非 DB 入口 同样的"非 DB 测试"有两种不同实现 功能重复