# PPT 补充资料 · 核实结论 + 数据导出

> 实验机: `/home/ubuntu/code/symcc`,Ubuntu 24.04,AFL++ 4.40c,clang/LLVM 18。
> 本目录内的文件即"图文报告"素材;下面是三个核实问题的结论 + 数据文件索引。

---

## Q2. Coverage 变体二进制怎么编译的?ShowmapCov 分母是否 collision-free?

**结论:是 collision-free。afl-showmap 用的是 PCGUARD 插桩的 `-afl` 二进制,分母 M = 该目标 PCGUARD 唯一 guard(边)数,向上取整到 64 的倍数,无 hash 碰撞膨胀。**

各变体编译方式(经构建脚本 + 实际二进制符号 + 现场探针三重核实):

| 变体 | 编译器 | 编译期 AFL 环境 | 插桩模式 | collision-free? | 谁在用 |
|---|---|---|---|---|---|
| `*-afl` | `afl-clang-fast -O2` | 无 | **PCGUARD**(LLVM-PCGUARD 默认) | ✅ 是 | **afl-showmap -C 边覆盖** |
| `*-cmplog` | `afl-clang-fast` + `AFL_LLVM_CMPLOG=1` | CmpLog | **PCGUARD** + CmpLog | ✅ 是 | AFL fuzzing(RedQueen) |
| `*-cov` | `gcc --coverage -O0 -g` | 无(非 AFL) | **gcov**(`.gcno/.gcda`) | N/A(非 AFL 图) | 源码级行/分支覆盖(lcov),**不进 afl-showmap** |

**关键证据**
- 构建:`benchmark/compile_public_benchmarks.sh`(`-afl` 用 `afl-clang-fast`,`-cov` 用 `gcc --coverage`);`benchmark/make_afl_targets_persistent.sh`(`-cmplog` 加 `AFL_LLVM_CMPLOG=1`)。全脚本无 `AFL_LLVM_INSTRUMENT=CLASSIC`、无 `AFL_LLVM_MAP_SIZE`、无 LTO、无 `afl-gcc`。
- 二进制符号:`-afl`/`-cmplog` 均含 `__sanitizer_cov_trace_pc_guard`、`__start___sancov_guards`,并内嵌运行期字符串 *"...compiled with non-colliding coverage instead of AFL_LLVM_INSTRUMENT=CLASSIC..."* → PCGUARD。`-cov` 零 sancov/零 `__afl_*`,`.comment` 仅 GCC 13.3 → 纯 gcov。
- afl-showmap 喂的是哪个二进制:`run_benchmark.py: measure_coverage_afl()` → `discover_afl_coverage_binaries()` 只取 `*-afl`(PCGUARD)。**gcov 的 `-cov` 二进制从不进 afl-showmap**。
- 分母 M 语义:afl-showmap 的 *"out of M existing"* = forkserver 上报的 guard 数取整到 64 的倍数(`(((n+63)>>6)<<6)`),**不是**固定 65536。现场实测:lava-base64→M=1088(真实 1038),png→3072,sqlite→31552,pcre2→**9728**,libarchive→**13696**——全是 64 的倍数、各目标不同,证实是"唯一边计数"而非碰撞 hash 图。

> ⚠️ 两点提醒写进 PPT 更严谨:①"Coverage 变体(`-cov`)"其实是 gcov 源码覆盖,与 afl-showmap 边覆盖是两条独立的度量;showmap 用的是 `-afl`。②M 向上取整到 64 的倍数,会比真实可达边数多至多 63 个恒零槽 → 覆盖率是极轻微**低估**,但**无碰撞误差**。

---

## Q3. SymCC 反馈环的 coverage map 是否与 AFL 二进制对齐?(issue #49)L1 判新准确吗?

**结论:issue #49 的 map 错位在 SymCC 内部【物理存在】,但设计【绕开了它对 L1 判新的影响】——判新是靠跑真实 AFL 二进制的 `afl-showmap` 决定的,不是靠 SymCC 内部 map。所以不会产生 L1 假新/假旧。内部 map 的错位只影响 QSYM 一个求解器侧优化。**

**证据链**
- SymCC 内部覆盖图 `AflTraceMap`(`runtime/.../qsym/pintool/afl_trace_map.cpp`):`kMapSize=65536`(与 AFL MAP_SIZE 同大小),但边 id 用 **SymCC 自己的** `XXH32(pc,taken) % 65536` 再 `(prev>>1)^h`——与 AFL 编译期随机 block id 的 `cur^(prev>>1)` **完全不同的函数**。→ 同一个下标 k,在 SymCC map 和 AFL map 里对应的**不是同一条程序边**。这就是 #49 的错位。
- 但这张 `trace_` map 只被 `Solver::isInterestingJcc → trace_.isInterestingBranch()`(`solver.cpp:809`)用来决定 **QSYM 要不要去翻转某个分支**(求解器侧优化),**不参与判新**。
- L1"这个测试用例是不是新的"由 **run 真实 AFL 目标二进制 + afl-showmap** 决定:worker `streaming_showmap.get_edges(content)` → `worker_coverage.merge(edges)` → `is_new`(`mpi_fuzzing_helper.py:653-667`);master 再 `coverage.merge()` 复核(`836-840`)。两侧都在**真实 afl-showmap 边空间**,"边 k"两边同义,判新对齐**由构造保证**。
- benchmark 模式下 `run_benchmark.py` **根本没设** `SYMCC_AFL_COVERAGE_MAP` → SymCC 的 `trace_` 从空图起步,连那个内部优化都跑在空图上(无错位下标读取)。

> ⚠️ 代码注释瑕疵(可在 PPT 里作为"已知并已定性"提一句):`mpi_fuzzing_helper.py:1743-1748` 注释称共享 bitmap 与 SymCC 的 coverage map "同属 afl-showmap 边空间"。对 Python 侧 `worker_cov`/`coverage` 播种是**对的**;但对 SymCC 的 C++ `AflTraceMap` **不成立**(它会用 XXH32 重新索引)。因为 SymCC 那侧对判新非权威,不影响正确性,但注释措辞不精确。

---

## Q4. Worker per-phase 计时

**结论:原先没有(只有一个 `elapsed`=concolic 子进程墙钟,且连 showmap/dedup 后处理都没算进去)。现已实现(opt-in),可产出瓶颈分析数据。**

- 原状:worker 每工作项只记 `elapsed`(相位 E=concolic 执行);相位 F(afl-showmap 取边 + 本地 dedup)虽在同一函数里却**被排除在 elapsed 之外**;无跨 worker 聚合、无 CSV。master 侧有 `SYMCC_MASTER_PROFILE`(仅 master 相位,非 per-worker)。
- 已实现:`util/mpi_fuzzing_helper.py` 加了 **opt-in `SYMCC_WORKER_PROFILE=1`** 的每-worker 分相位计时,6 个相位:
  `wait`(等待/取件·MPI idle)、`bmsync`(位图同步)、`import`(输入拷入)、`exec`(concolic 执行)、`showmap_dedup`(showmap 取边+本地去重)、`send`(回传 master)。
  热路径开销:每工作项约 6 次 `time.monotonic()`(~ns 级,对 0.5–30s 的执行是 sub-ppm)。
  结束时 `comm.gather` 汇总所有 worker,rank 0 写 `<output_dir>/<name>/phase_timing.csv`(每 worker 一行 + TOTAL + MEAN_PCT 行),并打印一行 `[WPROF]` 占比汇总。
- 用法:`SYMCC_WORKER_PROFILE=1`(可选 `SYMCC_WPROF_DIR=<目录>` 指定落盘位置)下跑 hybrid,
  各 worker 落 `phase_timing_rank*.csv`;跑完 `python3 -c "import sys;sys.path.insert(0,'util');
  from mpi_fuzzing_helper import aggregate_phase_timing as a;a('<目录>')"` 合并为 `phase_timing.csv`。
  (因编排层用 SIGTERM 杀 mpirun,进程内 MPI gather 来不及,故各 worker 各自落盘、事后合并。)

**实测:快目标 vs 慢目标对比(hybrid,np=12,各 6 个 SymCC worker;可直接做成堆叠柱状图)**

| 目标 | 类型 | 工作项 | exec(执行) | **showmap_dedup(后处理)** | 其它 |
|---|---|---|---|---|---|
| lava-base64(`05a_phase_timing_lava_fast.csv`) | 快 | 37 | 34.2% | **65.7%** | <0.2% |
| sqlite(`05b_phase_timing_sqlite_slow.csv`) | 慢/复杂 | 6 | 0.3% | **99.7%** | <0.1% |

**瓶颈结论:两类目标上,worker 时间的主导成本都是 afl-showmap 取边 + coverage 去重的【后处理】,
而非 concolic 执行本身;且【目标越复杂,后处理越极端】。** 原因:复杂目标(sqlite)单次 SymCC 执行
会生成【大量】输出,后处理要对**每个输出各跑一次 afl-showmap**(sqlite 的 showmap 执行本身还慢),
于是后处理吃掉几乎全部时间(99.7%);快目标(lava)输出少、执行也快,后处理占比降到 ~2/3。

> 这直接解释了 ~12 worker 饱和点:瓶颈是**每输出一次 showmap 调用 + 覆盖合并的后处理**,它随
> SymCC 输出数量放大,而不是 concolic 求解或 worker 数量。**优化方向应指向后处理**:批量 showmap、
> 对海量输出先粗筛再 showmap、或更廉价的 dedup——而非一味加 worker / 优化求解器。

> 计时说明:worker 被编排层 SIGTERM 打断时,在途时长按【正确相位】归账(exec 完成的部分单独补计),
> 故慢目标即使单 worker 只跑完 1 个工作项,exec/showmap_dedup 的拆分依然准确(见各 worker 明细一致)。

---

## 数据文件索引(本目录)

| 文件 | 内容 | 对应 PPT 用途 |
|---|---|---|
| `01_afl-showmap-C.txt` | 两个目标 `afl-showmap -C` 完整终端输出("coverage of N edges out of M") | 覆盖率度量截图 |
| `02_fuzzer_stats_pcre2.txt` | pcre2 目标 `fuzzer_stats`(edges_found/total_edges/bitmap_cvg/execs_per_sec) | fuzzer 状态截图 |
| `03_benchmark_report_scaling_hybrid.txt` + `.csv` | 各 np · afl-only/hybrid/mpi/serial 的 EdgeCov 对照 | 覆盖对照表/图 |
| `03b_benchmark_report_public_v4.txt` + `.csv` | 另一组 public 目标基准 | 补充数据 |
| `04_binary_variants_instrumentation.txt` | 各目标二进制变体清单 + 每个的插桩类型判定 | 变体/插桩说明 |
| `05_phase_timing.csv` | worker 分相位计时(本次实现的产物) | 瓶颈分析图 |
