From b7c1c0fec5191c0e8181ef130fa3567d15f7e5d4 Mon Sep 17 00:00:00 2001 From: Howard Su Date: Thu, 13 Aug 2026 22:29:59 +0800 Subject: [PATCH 1/3] Migrate standalone tests to CppUnitTestFramework --- server/test/test_feature_gate.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/test/test_feature_gate.cpp b/server/test/test_feature_gate.cpp index 404d7c4ee..d6e661237 100644 --- a/server/test/test_feature_gate.cpp +++ b/server/test/test_feature_gate.cpp @@ -600,7 +600,6 @@ void test_feature_warnings_report_inert_decode_tunables() { vw.verify_width = 8; CHECK(!warns_about(warn_result(vw, "laguna"), "--verify-width")); CHECK(warns_about(warn_result(vw, "qwen35"), "--verify-width")); - BackendArgs db; db.model_path = "/nonexistent/model.gguf"; db.draft_path = "/nonexistent/draft.gguf"; @@ -677,7 +676,6 @@ void test_model_capability_tables() { CHECK(arch_supports_paged_attention("qwen35", false)); CHECK(!arch_supports_paged_attention("qwen35", true)); CHECK(!arch_supports_paged_attention("qwen35moe", false)); - CHECK(arch_supports_draft_block_size("qwen35", false)); CHECK(!arch_supports_draft_block_size("qwen35", true)); CHECK(!arch_supports_draft_block_size("qwen35moe", false)); From 94214653a8651c6d102cec71daff8461d804829e Mon Sep 17 00:00:00 2001 From: Howard Su Date: Thu, 27 Aug 2026 09:51:48 +0800 Subject: [PATCH 2/3] Add host C++ coverage reporting --- server/CMakeLists.txt | 118 ++++++++++++++++++++++ server/cmake/GenerateCoverageReport.cmake | 69 +++++++++++++ server/cmake/RunCoverageTests.cmake | 26 +++++ server/src/deepseek4/deepseek4_graph.cpp | 6 ++ 4 files changed, 219 insertions(+) create mode 100644 server/cmake/GenerateCoverageReport.cmake create mode 100644 server/cmake/RunCoverageTests.cmake diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 925d5839d..43fb7b019 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -47,6 +47,39 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +option(DFLASH27B_COVERAGE + "Instrument C and C++ host code for LLVM source-based coverage" OFF) +if(DFLASH27B_COVERAGE) + if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang" OR + NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR + "DFLASH27B_COVERAGE requires Clang for both C and C++ " + "(configure with -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++)") + endif() + + file(GLOB _dflash_llvm_tool_dirs + "/usr/lib/llvm-*/bin" + "/usr/local/opt/llvm*/bin") + find_program(DFLASH27B_LLVM_COV_EXECUTABLE + NAMES llvm-cov + HINTS ${_dflash_llvm_tool_dirs} + REQUIRED) + find_program(DFLASH27B_LLVM_PROFDATA_EXECUTABLE + NAMES llvm-profdata + HINTS ${_dflash_llvm_tool_dirs} + REQUIRED) + unset(_dflash_llvm_tool_dirs) + + # CUDA/HIP device compilation is intentionally excluded: LLVM's + # source-based coverage records host-side C and C++ execution only. + add_compile_options( + $<$:-fprofile-instr-generate> + $<$:-fcoverage-mapping>) + add_link_options( + $<$:-fprofile-instr-generate> + $<$:-fprofile-instr-generate>) +endif() + # MSVC: treat source files as UTF-8 (the codebase contains UTF-8 string # literals such as tags). Without this, MSVC defaults to the system # code page and emits C4819 warnings or garbles multi-byte literals. @@ -1881,6 +1914,91 @@ if(DFLASH27B_TESTS) COMMENT "No unit-test binaries are enabled in this configuration" ) endif() + + if(DFLASH27B_COVERAGE) + set(_coverage_report_dir "${CMAKE_CURRENT_BINARY_DIR}/coverage") + set(_coverage_profile_dir "${_coverage_report_dir}/raw") + set(_coverage_object_list "${CMAKE_CURRENT_BINARY_DIR}/coverage_objects.txt") + set(_coverage_objects) + set(_coverage_deps) + set(_coverage_ctest_exclude) + foreach(_coverage_target IN LISTS _check_deps) + if(TARGET ${_coverage_target}) + get_target_property(_coverage_sources ${_coverage_target} SOURCES) + set(_coverage_has_device_source FALSE) + foreach(_coverage_source IN LISTS _coverage_sources) + get_source_file_property(_coverage_source_language + "${_coverage_source}" LANGUAGE) + if(_coverage_source MATCHES "\\.cu$" OR + _coverage_source_language STREQUAL "CUDA" OR + _coverage_source_language STREQUAL "HIP") + set(_coverage_has_device_source TRUE) + break() + endif() + endforeach() + unset(_coverage_source) + unset(_coverage_source_language) + + if(_coverage_has_device_source) + # Host-side LLVM coverage cannot instrument CUDA sources. + # Skip their standalone CTest entry and do not build them. + list(APPEND _coverage_ctest_exclude "^${_coverage_target}$") + else() + list(APPEND _coverage_deps ${_coverage_target}) + list(APPEND _coverage_objects "$") + endif() + unset(_coverage_has_device_source) + unset(_coverage_sources) + endif() + endforeach() + + if(NOT _coverage_objects) + message(FATAL_ERROR + "DFLASH27B_COVERAGE requires at least one registered test target") + endif() + + file(GENERATE + OUTPUT "${_coverage_object_list}" + CONTENT "$\n") + + if(_coverage_ctest_exclude) + list(JOIN _coverage_ctest_exclude "|" _coverage_ctest_exclude_regex) + else() + set(_coverage_ctest_exclude_regex "") + endif() + + # This target intentionally depends on the test binaries rather than + # `check`: CTest must run with LLVM_PROFILE_FILE set to capture data. + add_custom_target(coverage + COMMAND ${CMAKE_COMMAND} -E rm -rf "${_coverage_report_dir}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${_coverage_profile_dir}" + COMMAND ${CMAKE_COMMAND} + "-DCTEST_EXECUTABLE=${CMAKE_CTEST_COMMAND}" + "-DBUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}" + "-DCTEST_EXCLUDE_REGEX=${_coverage_ctest_exclude_regex}" + "-DLLVM_COV=${DFLASH27B_LLVM_COV_EXECUTABLE}" + "-DLLVM_PROFDATA=${DFLASH27B_LLVM_PROFDATA_EXECUTABLE}" + "-DPROFILE_DIR=${_coverage_profile_dir}" + "-DPROFILE_DATA=${_coverage_report_dir}/coverage.profdata" + "-DOBJECT_LIST_FILE=${_coverage_object_list}" + "-DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/src" + "-DHTML_DIR=${_coverage_report_dir}/html" + -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/RunCoverageTests.cmake" + DEPENDS ${_coverage_deps} + USES_TERMINAL + VERBATIM + COMMENT "Running CTest and generating LLVM coverage reports") + + unset(_coverage_report_dir) + unset(_coverage_profile_dir) + unset(_coverage_object_list) + unset(_coverage_objects) + unset(_coverage_deps) + unset(_coverage_ctest_exclude) + unset(_coverage_ctest_exclude_regex) + unset(_coverage_target) + endif() + unset(_check_deps) unset(_raw_unit_test_targets) diff --git a/server/cmake/GenerateCoverageReport.cmake b/server/cmake/GenerateCoverageReport.cmake new file mode 100644 index 000000000..a4f536e21 --- /dev/null +++ b/server/cmake/GenerateCoverageReport.cmake @@ -0,0 +1,69 @@ +foreach(_coverage_required_var IN ITEMS + LLVM_COV + LLVM_PROFDATA + PROFILE_DIR + PROFILE_DATA + OBJECT_LIST_FILE + SOURCE_DIR + HTML_DIR) + if(NOT DEFINED ${_coverage_required_var} OR + "${${_coverage_required_var}}" STREQUAL "") + message(FATAL_ERROR "${_coverage_required_var} is required") + endif() +endforeach() +unset(_coverage_required_var) + +file(GLOB _coverage_raw_profiles "${PROFILE_DIR}/*.profraw") +if(NOT _coverage_raw_profiles) + message(FATAL_ERROR + "CTest produced no LLVM profiles in ${PROFILE_DIR}") +endif() + +execute_process( + COMMAND "${LLVM_PROFDATA}" merge -sparse ${_coverage_raw_profiles} + -o "${PROFILE_DATA}" + RESULT_VARIABLE _coverage_merge_result +) +if(NOT _coverage_merge_result EQUAL 0) + message(FATAL_ERROR "llvm-profdata failed with exit code ${_coverage_merge_result}") +endif() + +file(STRINGS "${OBJECT_LIST_FILE}" _coverage_objects) +if(NOT _coverage_objects) + message(FATAL_ERROR "No coverage objects were generated in ${OBJECT_LIST_FILE}") +endif() + +list(GET _coverage_objects 0 _coverage_main_object) +list(REMOVE_AT _coverage_objects 0) +set(_coverage_object_args) +foreach(_coverage_object IN LISTS _coverage_objects) + list(APPEND _coverage_object_args -object "${_coverage_object}") +endforeach() +unset(_coverage_object) + +execute_process( + COMMAND "${LLVM_COV}" report "${_coverage_main_object}" + ${_coverage_object_args} + "-instr-profile=${PROFILE_DATA}" + "${SOURCE_DIR}" + RESULT_VARIABLE _coverage_report_result +) +if(NOT _coverage_report_result EQUAL 0) + message(FATAL_ERROR "llvm-cov report failed with exit code ${_coverage_report_result}") +endif() + +execute_process( + COMMAND "${LLVM_COV}" show "${_coverage_main_object}" + ${_coverage_object_args} + "-instr-profile=${PROFILE_DATA}" + -format=html + "-output-dir=${HTML_DIR}" + "${SOURCE_DIR}" + RESULT_VARIABLE _coverage_html_result +) +if(NOT _coverage_html_result EQUAL 0) + message(FATAL_ERROR "llvm-cov show failed with exit code ${_coverage_html_result}") +endif() + +message(STATUS "Coverage summary: ${PROFILE_DATA}") +message(STATUS "Coverage HTML report: ${HTML_DIR}/index.html") diff --git a/server/cmake/RunCoverageTests.cmake b/server/cmake/RunCoverageTests.cmake new file mode 100644 index 000000000..aa8db609a --- /dev/null +++ b/server/cmake/RunCoverageTests.cmake @@ -0,0 +1,26 @@ +foreach(_coverage_required_var IN ITEMS + CTEST_EXECUTABLE + BUILD_DIR + PROFILE_DIR + CTEST_EXCLUDE_REGEX) + if(NOT DEFINED ${_coverage_required_var}) + message(FATAL_ERROR "${_coverage_required_var} is required") + endif() +endforeach() +unset(_coverage_required_var) + +set(ENV{LLVM_PROFILE_FILE} "${PROFILE_DIR}/%m_%p.profraw") +set(_coverage_ctest_args --test-dir "${BUILD_DIR}" --output-on-failure) +if(NOT "${CTEST_EXCLUDE_REGEX}" STREQUAL "") + list(APPEND _coverage_ctest_args -E "${CTEST_EXCLUDE_REGEX}") +endif() +execute_process( + COMMAND "${CTEST_EXECUTABLE}" ${_coverage_ctest_args} + RESULT_VARIABLE _coverage_ctest_result) +unset(_coverage_ctest_args) + +include("${CMAKE_CURRENT_LIST_DIR}/GenerateCoverageReport.cmake") + +if(NOT _coverage_ctest_result EQUAL 0) + message(FATAL_ERROR "CTest failed with exit code ${_coverage_ctest_result}") +endif() diff --git a/server/src/deepseek4/deepseek4_graph.cpp b/server/src/deepseek4/deepseek4_graph.cpp index 8986328b1..8ac1c06a9 100644 --- a/server/src/deepseek4/deepseek4_graph.cpp +++ b/server/src/deepseek4/deepseek4_graph.cpp @@ -3427,7 +3427,13 @@ static bool ds4_cpu_has_f16c() { static int supported = -1; if (supported < 0) { __builtin_cpu_init(); +#if defined(__clang__) && __clang_major__ < 15 + // Clang 14 does not accept "f16c" in __builtin_cpu_supports(). + // AVX2-capable x86 CPUs also provide F16C. + supported = __builtin_cpu_supports("avx2") ? 1 : 0; +#else supported = (__builtin_cpu_supports("avx2") && __builtin_cpu_supports("f16c")) ? 1 : 0; +#endif } return supported == 1; } From 1ea74f80122ef836972f25c6d34de1f53461886c Mon Sep 17 00:00:00 2001 From: Howard Su Date: Thu, 27 Aug 2026 20:50:49 +0800 Subject: [PATCH 3/3] Document host coverage reporting --- server/docs/COVERAGE.md | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 server/docs/COVERAGE.md diff --git a/server/docs/COVERAGE.md b/server/docs/COVERAGE.md new file mode 100644 index 000000000..b5e195e64 --- /dev/null +++ b/server/docs/COVERAGE.md @@ -0,0 +1,50 @@ +# Host C/C++ Coverage + +The `coverage` target produces an LLVM source-based coverage report for host +C and C++ code. CUDA and HIP source files are intentionally excluded. + +## Prerequisites + +- Clang and matching `llvm-cov` and `llvm-profdata` executables +- The usual backend build prerequisites (CUDA or HIP) + +On Debian or Ubuntu, install the LLVM toolchain package matching the Clang +version used for the build. + +## Generate the report + +Configure a separate Debug build so coverage instrumentation does not affect +the normal build: + +```bash +cd server +cmake -S . -B build-coverage \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DDFLASH27B_COVERAGE=ON \ + -DDFLASH27B_TESTS=ON +cmake --build build-coverage --target coverage -j +``` + +For CUDA builds where `nvcc` is not on `PATH`, set `CUDACXX` before +configuring: + +```bash +CUDACXX=/usr/local/cuda/bin/nvcc cmake -S . -B build-coverage ... +``` + +The target runs CTest with `LLVM_PROFILE_FILE` configured, merges the emitted +profiles, prints a text summary, and writes the HTML report to: + +```text +server/build-coverage/coverage/html/index.html +``` + +The merged profile is `server/build-coverage/coverage/coverage.profdata`. + +## Test failures + +The HTML report is generated even when CTest fails, then the `coverage` target +returns CTest's failure status. Review the test failures before treating the +report as a passing coverage run.