run_local_fuzz.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. #!/bin/bash
  2. set -euo pipefail
  3. # 本地一键 Fuzz(默认低内存并发,可通过环境变量覆盖)。
  4. # 用途:封装常用 fuzz 参数,避免每次手工传 FUZZ_WORKERS/FUZZ_JOBS/FUZZ_RSS_LIMIT_MB。
  5. # 默认行为:
  6. # 默认模式 nightly,默认生成覆盖率
  7. # workers/jobs 默认为 1/9(可覆盖)
  8. # 默认走固定轮次 FUZZ_RUNS=100000;若显式提供 FUZZ_MAX_TOTAL_TIME 且未设置 FUZZ_RUNS,则走时间预算模式
  9. # FUZZ_ENABLE_MERGE=1 时先将原始 corpus merge 到临时目录,再覆盖原始 corpus 后继续 fuzz
  10. # 可覆盖参数:
  11. # FUZZ_MODE/FUZZ_SKIP_COV/FUZZ_RUNS/FUZZ_MAX_TOTAL_TIME/FUZZ_TIMEOUT/FUZZ_MAX_LEN
  12. # FUZZ_WORKERS/FUZZ_JOBS/FUZZ_RSS_LIMIT_MB/FUZZ_MALLOC_LIMIT_MB
  13. # FUZZ_ENABLE_MERGE
  14. # FUZZ_LOG_PATH: 日志输出文件路径(默认 ./fuzz.log,覆盖写)
  15. # - 想保留“每次单独日志”可自行设置:FUZZ_LOG_PATH=./fuzz-$(date +%Y%m%d-%H%M%S).log
  16. # FUZZ_LOG_STRIP_ANSI=1: 写入日志时去掉 ANSI 颜色控制码(默认开启,确保日志为纯 UTF-8 文本)
  17. # 说明:
  18. # - 日志仅记录“运行阶段”,不包含编译输出
  19. # 三个语义宏默认值(可覆盖):
  20. # RYANJSON_STRICT_OBJECT_KEY_CHECK=false
  21. # RYANJSON_DEFAULT_ADD_AT_HEAD=false
  22. # RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC=false
  23. scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  24. # shellcheck source=scripts/lib/common.sh
  25. source "${scriptDir}/scripts/lib/common.sh"
  26. repoRoot="$(ryanjson_repo_root_from_source "${BASH_SOURCE[0]}" 0)"
  27. cd "${repoRoot}"
  28. # ================= 可手改默认值(不想传参就改这里) =================
  29. DEFAULT_FUZZ_MODE="nightly"
  30. DEFAULT_FUZZ_SKIP_COV="0"
  31. DEFAULT_FUZZ_RUNS="100000000"
  32. DEFAULT_FUZZ_MAX_TOTAL_TIME=""
  33. DEFAULT_FUZZ_TIMEOUT="4"
  34. DEFAULT_FUZZ_MAX_LEN="8192"
  35. DEFAULT_FUZZ_WORKERS="1"
  36. DEFAULT_FUZZ_JOBS="9"
  37. DEFAULT_FUZZ_RSS_LIMIT_MB="4096"
  38. DEFAULT_FUZZ_MALLOC_LIMIT_MB=""
  39. DEFAULT_FUZZ_ENABLE_MERGE="0"
  40. DEFAULT_FUZZ_LOG_PATH="./fuzz.log"
  41. DEFAULT_FUZZ_LOG_STRIP_ANSI="1"
  42. DEFAULT_RYANJSON_STRICT_OBJECT_KEY_CHECK="false"
  43. DEFAULT_RYANJSON_DEFAULT_ADD_AT_HEAD="false"
  44. DEFAULT_RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="false"
  45. # =================================================================
  46. fuzz_log_line() {
  47. # 输出一行日志(终端 + 可选落盘)
  48. local line="$1"
  49. if [[ -n "${FUZZ_LOG_PATH}" ]]; then
  50. if [[ "${FUZZ_LOG_STRIP_ANSI}" == "1" ]]; then
  51. echo "${line}" | tee >(sed -r 's/\x1B\[[0-?]*[ -/]*[@-~]//g' >> "${FUZZ_LOG_PATH}")
  52. else
  53. echo "${line}" | tee -a "${FUZZ_LOG_PATH}"
  54. fi
  55. else
  56. echo "${line}"
  57. fi
  58. }
  59. fuzz_log_begin() {
  60. # 初始化 fuzz 运行日志(覆盖写)
  61. local runId="$1"
  62. mkdir -p "$(dirname "${FUZZ_LOG_PATH}")"
  63. if [[ "${FUZZ_LOG_STRIP_ANSI}" == "1" ]]; then
  64. echo "[信息] Fuzz 日志将保存到:${FUZZ_LOG_PATH}(覆盖写,去除 ANSI 控制码,仅运行日志)"
  65. else
  66. echo "[信息] Fuzz 日志将保存到:${FUZZ_LOG_PATH}(覆盖写,仅运行日志)"
  67. fi
  68. : > "${FUZZ_LOG_PATH}"
  69. fuzz_log_line "==================== FUZZ BEGIN ${runId} ===================="
  70. local runsLabel="${FUZZ_RUNS:-默认}"
  71. local timeLabel="${FUZZ_MAX_TOTAL_TIME:-}"
  72. if [[ -z "${timeLabel}" ]]; then
  73. timeLabel="-"
  74. fi
  75. fuzz_log_line "[信息] 时间=$(date '+%Y-%m-%d %H:%M:%S') 模式=${FUZZ_MODE} runs=${runsLabel} max_total_time=${timeLabel} workers/jobs=${FUZZ_WORKERS}/${FUZZ_JOBS}"
  76. }
  77. fuzz_log_end() {
  78. # 结束标记,写入退出码
  79. local runId="$1"
  80. local status="$2"
  81. fuzz_log_line "==================== FUZZ END ${runId} rc=${status} ===================="
  82. }
  83. run_fuzz_flow() (
  84. set -euo pipefail
  85. # 读取参数与默认值(保持与旧脚本一致)
  86. local fuzzMode="${FUZZ_MODE:-quick}"
  87. local fuzzSkipCov="${FUZZ_SKIP_COV:-0}"
  88. local fuzzTimeout="${FUZZ_TIMEOUT:-4}"
  89. local fuzzMaxLen="${FUZZ_MAX_LEN:-8192}"
  90. local fuzzVerbosity="${FUZZ_VERBOSITY:-0}"
  91. local fuzzCorpusDir="${FUZZ_CORPUS_DIR:-./test/fuzzer/corpus}"
  92. local fuzzDictPath="${FUZZ_DICT_PATH:-./test/fuzzer/RyanJsonFuzzer.dict}"
  93. local fuzzRuns="${FUZZ_RUNS:-}"
  94. local fuzzMaxTotalTime="${FUZZ_MAX_TOTAL_TIME:-}"
  95. local fuzzWorkers="${FUZZ_WORKERS:-}"
  96. local fuzzJobs="${FUZZ_JOBS:-}"
  97. local fuzzRssLimitMb="${FUZZ_RSS_LIMIT_MB:-}"
  98. local fuzzMallocLimitMb="${FUZZ_MALLOC_LIMIT_MB:-}"
  99. local fuzzEnableMerge="${FUZZ_ENABLE_MERGE:-0}"
  100. local xmakeForceClean="${XMAKE_FORCE_CLEAN:-0}"
  101. local defaultRuns=100000
  102. local defaultWorkers=""
  103. local defaultJobs=""
  104. # 环境依赖检查
  105. ryanjson_require_cmd xmake
  106. # 模式校验与并发默认值
  107. case "${fuzzMode}" in
  108. quick)
  109. defaultWorkers=2
  110. defaultJobs=2
  111. ;;
  112. nightly)
  113. defaultWorkers=4
  114. defaultJobs=4
  115. ;;
  116. full)
  117. defaultWorkers=6
  118. defaultJobs=6
  119. ;;
  120. *)
  121. ryanjson_log_error "FUZZ_MODE 仅支持 quick/nightly/full,当前值:${fuzzMode}"
  122. return 1
  123. ;;
  124. esac
  125. # merge 参数校验
  126. if ! ryanjson_require_01 "FUZZ_ENABLE_MERGE" "${fuzzEnableMerge}"; then
  127. return 1
  128. fi
  129. # 0/1 与数值类参数校验
  130. if ! ryanjson_require_01 "FUZZ_SKIP_COV" "${fuzzSkipCov}"; then
  131. return 1
  132. fi
  133. if ! ryanjson_require_pos_int "FUZZ_TIMEOUT" "${fuzzTimeout}" "秒"; then
  134. return 1
  135. fi
  136. if ! ryanjson_require_pos_int "FUZZ_MAX_LEN" "${fuzzMaxLen}"; then
  137. return 1
  138. fi
  139. if ! ryanjson_require_nonneg_int "FUZZ_VERBOSITY" "${fuzzVerbosity}"; then
  140. return 1
  141. fi
  142. # 补齐未显式设置的默认值
  143. if [[ -z "${fuzzWorkers}" ]]; then
  144. fuzzWorkers="${defaultWorkers}"
  145. fi
  146. if [[ -z "${fuzzJobs}" ]]; then
  147. fuzzJobs="${defaultJobs}"
  148. fi
  149. if ! ryanjson_require_pos_int "FUZZ_WORKERS" "${fuzzWorkers}"; then
  150. return 1
  151. fi
  152. if ! ryanjson_require_pos_int "FUZZ_JOBS" "${fuzzJobs}"; then
  153. return 1
  154. fi
  155. if ! ryanjson_require_nonneg_int_optional "FUZZ_RSS_LIMIT_MB" "${fuzzRssLimitMb}"; then
  156. return 1
  157. fi
  158. if ! ryanjson_require_nonneg_int_optional "FUZZ_MALLOC_LIMIT_MB" "${fuzzMallocLimitMb}"; then
  159. return 1
  160. fi
  161. # runs / max_total_time 处理与校验
  162. if [[ -n "${fuzzRuns}" ]]; then
  163. if ! ryanjson_require_pos_int "FUZZ_RUNS" "${fuzzRuns}"; then
  164. return 1
  165. fi
  166. if [[ -n "${fuzzMaxTotalTime}" ]]; then
  167. ryanjson_log_warn "同时设置 FUZZ_RUNS 与 FUZZ_MAX_TOTAL_TIME,已优先使用 FUZZ_RUNS,忽略时间预算。"
  168. fuzzMaxTotalTime=""
  169. fi
  170. fi
  171. if [[ -z "${fuzzRuns}" && -n "${fuzzMaxTotalTime}" ]]; then
  172. if ! ryanjson_require_pos_int "FUZZ_MAX_TOTAL_TIME" "${fuzzMaxTotalTime}" "秒"; then
  173. return 1
  174. fi
  175. fi
  176. if [[ -z "${fuzzRuns}" && -z "${fuzzMaxTotalTime}" ]]; then
  177. fuzzRuns="${defaultRuns}"
  178. fi
  179. # 语义宏组合(用于 caseName 与构建配置)
  180. local strictKey="${RYANJSON_STRICT_OBJECT_KEY_CHECK:-false}"
  181. local addAtHead="${RYANJSON_DEFAULT_ADD_AT_HEAD:-true}"
  182. local scientific="${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC:-true}"
  183. local caseName=""
  184. caseName="$(ryanjson_semantic_case_name "${strictKey}" "${addAtHead}" "${scientific}")"
  185. # 覆盖率输出目录
  186. local coverageRoot="test/fuzzer/coverage"
  187. local profileRoot="${coverageRoot}/profiles"
  188. ryanjson_prepare_clean_dir "${coverageRoot}"
  189. mkdir -p "${profileRoot}"
  190. local profraw="${profileRoot}/coverage.profraw"
  191. local profdata="${coverageRoot}/coverage.profdata"
  192. local reportTxt="${coverageRoot}/report.txt"
  193. local reportHtml="${coverageRoot}/html"
  194. # 打印执行配置
  195. ryanjson_print_banner_begin "Fuzzer 执行配置"
  196. ryanjson_print_banner_kv "模式" "${fuzzMode}"
  197. ryanjson_print_banner_kv "配置" "${caseName}"
  198. ryanjson_print_banner_kv "Corpus" "${fuzzCorpusDir}"
  199. ryanjson_print_banner_kv "字典" "${fuzzDictPath}"
  200. ryanjson_print_banner_kv "merge" "${fuzzEnableMerge}"
  201. if [[ "${fuzzEnableMerge}" == "1" ]]; then
  202. ryanjson_print_banner_kv "merged_corpus" "${fuzzCorpusDir} (in-place)"
  203. fi
  204. ryanjson_print_banner_kv "timeout" "${fuzzTimeout}"
  205. ryanjson_print_banner_kv "max_len" "${fuzzMaxLen}"
  206. ryanjson_print_banner_kv "workers/jobs" "${fuzzWorkers}/${fuzzJobs}"
  207. if [[ -n "${fuzzRssLimitMb}" ]]; then
  208. ryanjson_print_banner_kv "rss_limit_mb" "${fuzzRssLimitMb}"
  209. fi
  210. if [[ -n "${fuzzMallocLimitMb}" ]]; then
  211. ryanjson_print_banner_kv "malloc_limit_mb" "${fuzzMallocLimitMb}"
  212. fi
  213. if [[ -n "${fuzzRuns}" ]]; then
  214. ryanjson_print_banner_kv "runs" "${fuzzRuns}"
  215. else
  216. ryanjson_print_banner_kv "max_total_time" "${fuzzMaxTotalTime}s"
  217. fi
  218. ryanjson_print_banner_end
  219. # 配置与构建
  220. ryanjson_run_xmake_config "${xmakeForceClean}" "${caseName}"
  221. ryanjson_run_xmake_build "RyanJsonFuzz" "${caseName}"
  222. ryanjson_log_info "xmake 构建完成(target=RyanJsonFuzz)"
  223. # 确保 corpus 存在
  224. if [[ ! -d "${fuzzCorpusDir}" ]]; then
  225. mkdir -p "${fuzzCorpusDir}"
  226. ryanjson_log_info "Corpus 目录不存在,已自动创建:${fuzzCorpusDir}"
  227. fi
  228. local actualFuzzCorpusDir="${fuzzCorpusDir}"
  229. # 可选:merge 输入 corpus,避免无序膨胀
  230. if [[ "${fuzzEnableMerge}" == "1" ]]; then
  231. local mergeTempDir="${coverageRoot}/merged-corpus.tmp"
  232. ryanjson_prepare_clean_dir "${mergeTempDir}"
  233. ryanjson_log_phase "正在 merge corpus..."
  234. ./build/linux/x86/release/RyanJsonFuzz -merge=1 "${mergeTempDir}" "${fuzzCorpusDir}"
  235. rm -rf "${fuzzCorpusDir}"
  236. mv "${mergeTempDir}" "${fuzzCorpusDir}"
  237. actualFuzzCorpusDir="${fuzzCorpusDir}"
  238. ryanjson_log_info "merge 完成,后续 fuzz 使用:${actualFuzzCorpusDir}"
  239. fi
  240. # 组装 libFuzzer 参数
  241. local -a dictArgs=()
  242. if [[ -f "${fuzzDictPath}" ]]; then
  243. dictArgs+=("-dict=${fuzzDictPath}")
  244. else
  245. ryanjson_log_warn "未找到字典文件 ${fuzzDictPath},已跳过 -dict 参数。"
  246. fi
  247. local -a fuzzArgs=()
  248. fuzzArgs+=("${actualFuzzCorpusDir}")
  249. fuzzArgs+=("${dictArgs[@]}")
  250. fuzzArgs+=("-timeout=${fuzzTimeout}")
  251. fuzzArgs+=("-verbosity=${fuzzVerbosity}")
  252. fuzzArgs+=("-max_len=${fuzzMaxLen}")
  253. fuzzArgs+=("-workers=${fuzzWorkers}")
  254. fuzzArgs+=("-jobs=${fuzzJobs}")
  255. if [[ -n "${fuzzRssLimitMb}" ]]; then
  256. fuzzArgs+=("-rss_limit_mb=${fuzzRssLimitMb}")
  257. fi
  258. if [[ -n "${fuzzMallocLimitMb}" ]]; then
  259. fuzzArgs+=("-malloc_limit_mb=${fuzzMallocLimitMb}")
  260. fi
  261. if [[ -n "${fuzzRuns}" ]]; then
  262. fuzzArgs+=("-runs=${fuzzRuns}")
  263. else
  264. fuzzArgs+=("-max_total_time=${fuzzMaxTotalTime}")
  265. fi
  266. if [[ -n "${FUZZ_EXTRA_ARGS:-}" ]]; then
  267. local -a extraArgs=()
  268. read -r -a extraArgs <<< "${FUZZ_EXTRA_ARGS}"
  269. fuzzArgs+=("${extraArgs[@]}")
  270. fi
  271. # 运行 fuzz(只记录运行阶段到日志)
  272. local runSummary=""
  273. if [[ -n "${fuzzRuns}" ]]; then
  274. runSummary="runs=${fuzzRuns}"
  275. else
  276. runSummary="max_total_time=${fuzzMaxTotalTime}"
  277. fi
  278. local limitSummary=""
  279. if [[ -n "${fuzzRssLimitMb}" ]]; then
  280. limitSummary="${limitSummary} rss_limit_mb=${fuzzRssLimitMb}"
  281. fi
  282. if [[ -n "${fuzzMallocLimitMb}" ]]; then
  283. limitSummary="${limitSummary} malloc_limit_mb=${fuzzMallocLimitMb}"
  284. fi
  285. fuzz_log_line "[阶段] 正在运行 fuzz 二进制..."
  286. fuzz_log_line "[信息] Fuzz 参数摘要: ${runSummary} workers/jobs=${fuzzWorkers}/${fuzzJobs} timeout=${fuzzTimeout} max_len=${fuzzMaxLen}${limitSummary}"
  287. if [[ -n "${FUZZ_LOG_PATH}" ]]; then
  288. if [[ "${FUZZ_LOG_STRIP_ANSI}" == "1" ]]; then
  289. LLVM_PROFILE_FILE="${profraw}" ./build/linux/x86/release/RyanJsonFuzz "${fuzzArgs[@]}" 2>&1 \
  290. | tee >(sed -r 's/\x1B\[[0-?]*[ -/]*[@-~]//g' >> "${FUZZ_LOG_PATH}")
  291. else
  292. LLVM_PROFILE_FILE="${profraw}" ./build/linux/x86/release/RyanJsonFuzz "${fuzzArgs[@]}" 2>&1 \
  293. | tee -a "${FUZZ_LOG_PATH}"
  294. fi
  295. else
  296. LLVM_PROFILE_FILE="${profraw}" ./build/linux/x86/release/RyanJsonFuzz "${fuzzArgs[@]}"
  297. fi
  298. # 覆盖率生成
  299. if [[ "${fuzzSkipCov}" == "1" ]]; then
  300. ryanjson_log_info "FUZZ_SKIP_COV=1,已跳过覆盖率生成。"
  301. ryanjson_log_info "输出目录:${coverageRoot}"
  302. return 0
  303. fi
  304. if ! command -v llvm-profdata >/dev/null 2>&1; then
  305. ryanjson_log_error "未找到 llvm-profdata,无法生成覆盖率。"
  306. return 1
  307. fi
  308. if ! command -v llvm-cov >/dev/null 2>&1; then
  309. ryanjson_log_error "未找到 llvm-cov,无法生成覆盖率。"
  310. return 1
  311. fi
  312. llvm-profdata merge -sparse "${profraw}" -o "${profdata}"
  313. echo "---------------- 覆盖率摘要(llvm-cov report) ----------------"
  314. llvm-cov report ./build/linux/x86/release/RyanJsonFuzz \
  315. -instr-profile="${profdata}" \
  316. -show-mcdc-summary \
  317. --use-color \
  318. -sources ./RyanJson
  319. llvm-cov report ./build/linux/x86/release/RyanJsonFuzz \
  320. -instr-profile="${profdata}" \
  321. -show-mcdc-summary \
  322. -sources ./RyanJson > "${reportTxt}"
  323. llvm-cov show ./build/linux/x86/release/RyanJsonFuzz \
  324. -instr-profile="${profdata}" \
  325. -format=html \
  326. -output-dir="${reportHtml}" \
  327. -show-mcdc-summary \
  328. -show-branches=count \
  329. -show-expansions \
  330. -show-regions \
  331. -show-line-counts-or-regions \
  332. -sources ./RyanJson
  333. ryanjson_log_done "Fuzzer 执行结束,覆盖率已生成。"
  334. ryanjson_log_info "输出目录:${coverageRoot}"
  335. ryanjson_log_info "覆盖率文本报告:${reportTxt}"
  336. ryanjson_log_info "覆盖率HTML目录:${reportHtml}"
  337. )
  338. main() {
  339. : "${FUZZ_MODE:=${DEFAULT_FUZZ_MODE}}"
  340. : "${FUZZ_SKIP_COV:=${DEFAULT_FUZZ_SKIP_COV}}"
  341. : "${FUZZ_RUNS:=${DEFAULT_FUZZ_RUNS}}"
  342. : "${FUZZ_MAX_TOTAL_TIME:=${DEFAULT_FUZZ_MAX_TOTAL_TIME}}"
  343. : "${FUZZ_TIMEOUT:=${DEFAULT_FUZZ_TIMEOUT}}"
  344. : "${FUZZ_MAX_LEN:=${DEFAULT_FUZZ_MAX_LEN}}"
  345. : "${FUZZ_WORKERS:=${DEFAULT_FUZZ_WORKERS}}"
  346. : "${FUZZ_JOBS:=${DEFAULT_FUZZ_JOBS}}"
  347. : "${FUZZ_RSS_LIMIT_MB:=${DEFAULT_FUZZ_RSS_LIMIT_MB}}"
  348. : "${FUZZ_MALLOC_LIMIT_MB:=${DEFAULT_FUZZ_MALLOC_LIMIT_MB}}"
  349. : "${FUZZ_ENABLE_MERGE:=${DEFAULT_FUZZ_ENABLE_MERGE}}"
  350. # 默认写入“单一日志文件”,便于判断上次 fuzz 是否完整结束。
  351. # 若需要每次单独文件,可通过 FUZZ_LOG_PATH 显式覆盖。
  352. : "${FUZZ_LOG_PATH:=${DEFAULT_FUZZ_LOG_PATH}}"
  353. : "${FUZZ_LOG_STRIP_ANSI:=${DEFAULT_FUZZ_LOG_STRIP_ANSI}}"
  354. : "${RYANJSON_STRICT_OBJECT_KEY_CHECK:=${DEFAULT_RYANJSON_STRICT_OBJECT_KEY_CHECK}}"
  355. : "${RYANJSON_DEFAULT_ADD_AT_HEAD:=${DEFAULT_RYANJSON_DEFAULT_ADD_AT_HEAD}}"
  356. : "${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC:=${DEFAULT_RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC}}"
  357. export RYANJSON_STRICT_OBJECT_KEY_CHECK
  358. export RYANJSON_DEFAULT_ADD_AT_HEAD
  359. export RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC
  360. export FUZZ_MODE FUZZ_SKIP_COV FUZZ_RUNS FUZZ_MAX_TOTAL_TIME FUZZ_TIMEOUT FUZZ_MAX_LEN
  361. export FUZZ_WORKERS FUZZ_JOBS FUZZ_RSS_LIMIT_MB FUZZ_MALLOC_LIMIT_MB FUZZ_ENABLE_MERGE
  362. # 记录一次运行的唯一标识,方便在总日志中定位开始/结束段。
  363. fuzzRunId="$(date +%Y%m%d-%H%M%S)"
  364. ryanjson_print_banner_begin "本地 Fuzz 启动"
  365. ryanjson_print_banner_kv "FUZZ_MODE" "${FUZZ_MODE}"
  366. ryanjson_print_banner_kv "FUZZ_SKIP_COV" "${FUZZ_SKIP_COV}"
  367. ryanjson_print_banner_kv_optional "FUZZ_RUNS" "${FUZZ_RUNS}"
  368. ryanjson_print_banner_kv_optional "FUZZ_MAX_TOTAL_TIME" "${FUZZ_MAX_TOTAL_TIME}"
  369. ryanjson_print_banner_kv "FUZZ_TIMEOUT" "${FUZZ_TIMEOUT}"
  370. ryanjson_print_banner_kv "FUZZ_MAX_LEN" "${FUZZ_MAX_LEN}"
  371. ryanjson_print_banner_kv "FUZZ_WORKERS" "${FUZZ_WORKERS}"
  372. ryanjson_print_banner_kv "FUZZ_JOBS" "${FUZZ_JOBS}"
  373. ryanjson_print_banner_kv_optional "FUZZ_RSS_LIMIT_MB" "${FUZZ_RSS_LIMIT_MB}"
  374. ryanjson_print_banner_kv_optional "FUZZ_MALLOC_LIMIT_MB" "${FUZZ_MALLOC_LIMIT_MB}"
  375. ryanjson_print_banner_kv "FUZZ_ENABLE_MERGE" "${FUZZ_ENABLE_MERGE}"
  376. ryanjson_print_banner_kv "FUZZ_LOG_PATH" "${FUZZ_LOG_PATH}"
  377. ryanjson_print_banner_kv "RYANJSON_STRICT_OBJECT_KEY_CHECK" "${RYANJSON_STRICT_OBJECT_KEY_CHECK}"
  378. ryanjson_print_banner_kv "RYANJSON_DEFAULT_ADD_AT_HEAD" "${RYANJSON_DEFAULT_ADD_AT_HEAD}"
  379. ryanjson_print_banner_kv "RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC" "${RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC}"
  380. ryanjson_print_banner_end
  381. if [[ -n "${FUZZ_LOG_PATH}" ]]; then
  382. local status=0
  383. fuzz_log_begin "${fuzzRunId}"
  384. set +e
  385. run_fuzz_flow
  386. status=$?
  387. set -e
  388. fuzz_log_end "${fuzzRunId}" "${status}"
  389. return "${status}"
  390. fi
  391. run_fuzz_flow
  392. }
  393. main "$@"