☰
Skia Job Analysis 指南:用 jq + Miller 审计 jobs.json,找出 CI 测试覆盖缺口
2026/9/25 3:30:32 网站建设 项目流程
  • 图形学
  • 图像处理

【免费下载链接】skia

Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.

项目地址:https://gitcode.com/gh_mirrors/skia1/skia
点击查看免费下载

本文围绕 Skia 仓库中 infra/bots/analysis/README.md 所描述的Job Analysis工具链展开,介绍如何通过一组 Shell 脚本与 Makefile 目标,对 infra/bots/jobs.json 中的 716 个 CI 任务进行结构化解析,从而快速找出"当前完全没有跑 Perf 基准、没有跑 Test、或者没有覆盖 Vulkan 后端"的 CPU / GPU 设备组合。读完本文,你将掌握该分析套件的运行环境要求、数据流水线原理(jobs.json → /tmp/alljobs.csv → 缺口报告),并能自定义 Miller 布尔过滤器,扩展出自己的覆盖度审计查询。

一、Job Analysis 是做什么的

infra/bots/analysis目录下存放着一套面向 Skia 基础设施团队的分析脚本,其目标非常明确:分析jobs.json,发现测试配置中的"空洞"(holes)。这里的"空洞"指的是某些 CPU 或 GPU 设备(即cpu_or_gpu_value维度上的取值)虽然出现在总设备清单中,却没有任何对应类型的 CI 任务在运行它们——例如某个 GPU 从未被跑过 Perf 基准测试,或从未以 Vulkan 后端跑过 Test。

这套分析的价值在于:Skia 的 CI 矩阵非常庞大,jobs.json中仅本仓库当前就包含约 716 条任务定义(可通过grep -c '"name"' infra/bots/jobs.json验证),覆盖 Android、iOS、Linux、Windows、Mac 等多个平台与数十种 CPU/GPU 型号。人工核对极易遗漏覆盖空白,而将任务名解析为结构化 CSV 后,用 Miller 做集合差(差集)运算即可自动列出所有缺口。

该目录包含 4 个文件,分工如下:

文件职责
README.md使用说明与快速上手
Makefile封装常见查询的 make 目标
create-alljobs.sh把jobs.json解析为/tmp/alljobs.csv
axis.sh生成指定列的"唯一值轴"CSV
missing.sh核心缺口查询脚本,对 CSV 做过滤 + 差集

二、环境要求与安装

根据 README.md 的要求,运行这套脚本需要两个命令行工具:

  • jq:用于解析jobs.json这个 JSON 文件;
  • mlr(Miller):用于对 CSV 做过滤(filter)、切列(cut)、排序(sort)、去重(uniq)与连接(join)等操作。

在 Debian / Ubuntu 系系统上,一条命令即可安装:

$ sudo apt install jq miller

安装后可用jq --version与mlr --version验证。Miller 的完整查询语法可参考其官方参考文档(README 中给出的 https://miller.readthedocs.io/en/latest/reference-dsl.html ),本文后续也会结合脚本实际用到的 DSL 子集进行说明。需要注意:mlr --csv子命令对 CSV 的列名大小写敏感,分析脚本中统一使用Type、os、model、cpu_or_gpu_value等精确列名。

三、快速上手:运行第一个缺口查询

进入分析目录后,直接使用 Makefile 封装好的目标即可:

$ cd infra/bots/analysis $ make missing_perf_jobs

该命令会输出一份清单,列出所有当前没有运行 Perf 基准测试的cpu_or_gpu_value(以及对应的设备型号 model)。例如输出形如:

cpu_or_gpu_value,model Adreno540,Pixel2XL Adreno615,Pixel3a MaliG72,GalaxyS9 ...

以 infra/bots/jobs.json 中的真实任务名验证:Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android与Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android这类任务名会同时贡献 MaliT880 这一cpu_or_gpu_value;而如果某个 GPU 只出现在 Test 任务中、从未出现在 Perf 任务中,missing_perf_jobs就会把它报告出来。

Makefile 中总共封装了三个现成目标(Makefile):

missing_perf_jobs: ./missing.sh '$$Type == "Perf"' missing_test_jobs: ./missing.sh '$$Type == "Test"' missing_vulkan_jobs: ./missing.sh '$$vulkan == "true"'

三个目标本质都是调用missing.sh,只是传入的 Miller 布尔过滤器不同:

make 目标过滤器查询语义
missing_perf_jobs$Type == "Perf"找出没有 Perf 任务覆盖的设备
missing_test_jobs$Type == "Test"找出没有 Test 任务覆盖的设备
missing_vulkan_jobs$vulkan == "true"找出没有 Vulkan 后端任务覆盖的设备

注意 Makefile 中$$Type的写法:这是 make 的变量转义规则,$$在传给 Shell 时会展开成单个$,最终交给 Miller 的是$Type == "Perf"这样的 DSL 表达式。

四、数据流水线原理:从 jobs.json 到缺口报告

整个分析过程是一条清晰的数据流水线,共分三步:

  1. 解析:create-alljobs.sh 读取jobs.json,把每个任务名拆解为结构化 CSV 行,产出/tmp/alljobs.csv;
  2. 取轴:axis.sh 从全量任务中抽取所有cpu_or_gpu_value的唯一值及其型号,产出基准清单/tmp/cpu_or_gpu_value,model.csv;
  3. 求差:missing.sh 先按过滤器筛出"符合条件的任务",再与该基准清单做差集,最终输出"符合条件任务未覆盖的设备"。

下面逐层拆解每一步的源码实现。

4.1 create-alljobs.sh:任务名 → 结构化 CSV

create-alljobs.sh 是整个流水线的入口,核心逻辑如下:

# 写入起始表头 echo Type,os,compiler,model,cpu_or_gpu,cpu_or_gpu_value,arch,configuration,test_filter,extra > /tmp/alljobs.csv # 用 jq 提取每个 job 的 name,只保留 Perf / Test 开头者,并把 "-" 替换为 ",", # 同时把末尾的 All 补成 All,none,保证每列都有值 cat ../jobs.json | jq .[] -r | grep "^[Perf|Test]" | sed "s#-#,#g" | sed "s#All\$#All,none#g" >> /tmp/alljobs.csv # 追加 vulkan / metal 布尔列 mlr --csv -I put '$vulkan=$extra =~ "Vulkan"' /tmp/alljobs.csv mlr --csv -I put '$metal=$extra =~ "Metal"' /tmp/alljobs.csv # 校验输出是合法 CSV mlr --icsv check /tmp/alljobs.csv

结合 jobs.json 的实际结构(每条记录形如{"name": "Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android", ...}),可以还原其解析逻辑:

  • jq .[] -r逐条输出每个 job 的原始 JSON,-r保证输出为裸字符串(raw);
  • grep "^[Perf|Test]"只保留任务名以 Perf 或 Test 开头的条目,跳过 BazelBuild、BazelTest 等其它类型;
  • 第一个sed "s#-#,#g"把任务名中的所有-替换为,,从而把Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android这样的名字拆成 10 个字段,恰好对应表头的 10 列:Type=Perf、os=Android、compiler=Clang、model=GalaxyS7_G930FD、cpu_or_gpu=GPU、cpu_or_gpu_value=MaliT880、arch=arm64、configuration=Release、test_filter=All、extra=Android;
  • 第二个sed "s#All\$#All,none#g"处理test_filter为All的情况:因为任务名到All处已经结束,后面没有-可拆,所以补一个none占位,保证extra列不为空;
  • $vulkan=$extra =~ "Vulkan"与$metal=$extra =~ "Metal"是 Miller 的 put 语句:利用正则匹配extra列,新增vulkan、metal两个布尔列。这也是missing_vulkan_jobs能够用$vulkan == "true"查询的原因——任务名如Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android_Vulkan的extra段含有Vulkan,该行vulkan列即为 true;
  • 最后mlr --icsv check做完整性校验,若 CSV 格式不合法脚本会直接失败(set -e)。

4.2 axis.sh:生成设备"轴"清单

axis.sh 负责从全量任务中提取某个(或某几个)列的唯一取值组合:

mlr --csv uniq -f $1 /tmp/alljobs.csv | mlr --csv sort -f $1 > /tmp/$1.csv

其唯一参数是逗号分隔的列名列表,同时这个列表也用作输出文件名。例如./axis.sh cpu_or_gpu_value,model会:

  1. 用mlr --csv uniq -f cpu_or_gpu_value,model对两列组合去重;
  2. 用mlr --csv sort -f cpu_or_gpu_value,model排序;
  3. 输出到/tmp/cpu_or_gpu_value,model.csv。

这份文件就是"Skia 测试矩阵中已知的全部设备清单"——凡是没有出现在某类任务里的设备,都会被后续的差集运算揪出来。

4.3 missing.sh:过滤 + 差集,定位覆盖空洞

missing.sh 是查询核心,接收一个 Miller 布尔过滤器作为唯一参数,完整流程如下:

if [ $# -ne 1 ]; then echo "$0 <filter>" echo "Example: $0 '\$vulkan == \"true\"'" exit 1 fi FILTER=$1 # 确保 /tmp/alljobs.csv 已生成 ./create-alljobs.sh # 提取全部 cpu_or_gpu_value 与对应 model ./axis.sh cpu_or_gpu_value,model # 过滤出符合条件的任务 → 提取设备列 → 排序去重 → 与基准清单做差集 mlr --csv filter "${FILTER}" /tmp/alljobs.csv | \ mlr --csv cut -f cpu_or_gpu_value,model | \ mlr --csv sort -f cpu_or_gpu_value | \ mlr --csv uniq -f cpu_or_gpu_value | \ mlr --csv join -f /tmp/cpu_or_gpu_value,model.csv -j cpu_or_gpu_value --ul --np

这条五段式 Miller 管道是整套分析的精髓,逐段说明:

阶段作用
mlr --csv filter "${FILTER}"按传入的布尔表达式(如$Type == "Perf")筛选行,得到"符合条件的那类任务"
mlr --csv cut -f cpu_or_gpu_value,model只保留设备列,丢弃其它无关字段
mlr --csv sort -f cpu_or_gpu_value按键排序,保证后续 join 的效率与确定性
mlr --csv uniq -f cpu_or_gpu_value去重,得到"这类任务实际覆盖过的设备集合"
mlr --csv join -f /tmp/cpu_or_gpu_value,model.csv -j cpu_or_gpu_value --ul --np与设备全集做左连接,--np表示不打印匹配行,--ul表示打印左表(即设备全集)中未被匹配的行

--ul --np这两个参数值得单独说明(脚本注释中也有解释):--np(no print matches)让 join 不输出两边都命中的行;--ul(unmatched left)只输出左表——即/tmp/cpu_or_gpu_value,model.csv设备全集——中在右表找不到对应项的行。两者结合的效果,正是"设备全集 − 已覆盖设备 = 覆盖缺口"的集合差运算。

五、自定义查询:把分析扩展到任意维度

missing.sh的过滤器参数完全开放,任何能作用于alljobs.csv列的 Miller 布尔表达式都可以使用。结合 create-alljobs.sh 中定义的表头,可用列包括:

Type, os, compiler, model, cpu_or_gpu, cpu_or_gpu_value, arch, configuration, test_filter, extra, vulkan, metal

据此可以扩展出很多实用查询,例如:

# 找出没有跑 Metal 后端任务的设备(注意列名是 metal,布尔匹配) $ ./missing.sh '$metal == "true"' # 找出没有在 Release 配置下跑 Perf 的设备(组合条件) $ ./missing.sh '$Type == "Perf" && $configuration == "Release"' # 找出没有在 64 位 arm 架构下跑 Test 的设备 $ ./missing.sh '$Type == "Test" && $arch == "arm64"' # 找出连 Debug 配置的 Test 都没有的设备 $ ./missing.sh '$Type == "Test" && $configuration == "Debug"'

由于vulkan/metal列由extra列的子串匹配生成,凡是任务名中带有_Vulkan、_Metal后缀的条目都会自动归入对应布尔列,这一设计让"按图形后端审计覆盖"变得非常直接。若想固定下来复用,参照 Makefile 的写法新增一个目标即可,注意 DSL 中的$需要写成$$以通过 make 的转义。

六、与任务生成体系的衔接:jobs.json 从哪来

这套分析所依赖的 infra/bots/jobs.json 并不是手写的静态文件,而是与 Skia 的任务生成体系深度绑定的。在 infra/bots/gen_tasks_logic/gen_tasks_logic.go 中可以找到其定义与加载逻辑:

  • JobInfo类型(第 312 行附近)描述了 jobs.json 中每条记录的结构,即"任务名 + 可选的 cq_config";
  • GenTasks函数(第 365 行附近)负责重新生成tasks.json,其第一步就是通过LoadJson(filepath.Join(relpathBaseDir, "jobs.json"), &jobsWithInfo)加载任务清单;
  • 在任务展开过程中(第 961 行附近),生成逻辑会按cpu_or_gpu_value查找设备映射表,若遇到未知设备会直接log.Fatalf报错——这说明cpu_or_gpu_value是任务展开的关键键值,与本文分析脚本以它作为缺口判定维度完全一致。

因此,分析脚本与任务生成器共享同一份事实来源:当开发者在jobs.json中新增或删除任务后,重新运行make missing_*即可立刻看到新引入的覆盖缺口,形成"配置 → 生成 → 审计"的闭环。

七、常见问题与注意事项

  1. 输出文件路径固定:脚本约定所有中间产物都写在/tmp下(/tmp/alljobs.csv、/tmp/cpu_or_gpu_value,model.csv),多次运行会覆盖;若要保留历史结果,需自行拷贝。
  2. 任务名前缀过滤:create-alljobs.sh 中的grep "^[Perf|Test]"只纳入 Perf / Test 开头的任务,Bazel 系列任务(如BazelTest-...、BazelBuild-...)不会进入 CSV,因此jobs.json中约 716 条任务并非全部参与分析。
  3. 缺列的兜底:任务名末尾的All通过sed "s#All\$#All,none#g"补成All,none,保证extra列不为空,否则第 10 列会缺失导致列数错位。
  4. 大小写与引号:Miller 的filter表达式对列名大小写敏感,且 Bash 传参时要注意$的转义——命令行直接调用用单引号包住'$Type == "Perf"',写进 Makefile 则需写成$$Type。
  5. 依赖工具版本:脚本基于 jq 与 Miller 的--csv子命令与join(含--ul、--np选项)特性编写,建议使用较新的 Miller 版本以获得完整的 DSL 支持。

综上,infra/bots/analysis提供了一套轻量、可组合的 CI 覆盖审计工具:理解 README.md 中"jq + mlr"的定位后,通过 Makefile 的现成目标即可秒级定位测试空洞,而借助missing.sh的开放式过滤器,还能把审计扩展到任意架构、配置与图形后端的组合,是维护 Skia 庞大测试矩阵时值得常备的实用工具链。

  • 图形学
  • 图像处理

【免费下载链接】skia

Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.

项目地址:https://gitcode.com/gh_mirrors/skia1/skia
点击查看免费下载
上一篇:5大滤波算法实战指南:从噪声处理到状态估计
下一篇:ctxsync 完全指南:本地文件自动同步 Claude.ai Projects 的开源神器

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询