Files
cmake/Tests/RunCMake/Instrumentation/verify-snippet.cmake
T
Daksh Mamodiya bda67b82e7 instrumentation: Write cmakeBuild snippet when build is interrupted
The overall `cmakeBuild` snippet is written only after the native build
tool returns, so interrupting `cmake --build` with Ctrl+C terminated CMake
before it was recorded and lost the build's delineation.

When instrumentation is active, install a scoped, async-signal-safe handler
(POSIX SIGINT; Windows CTRL_C/CTRL_BREAK) that just flags the interrupt. The
existing write then runs during unwind, records the interrupting signal in a
new `interruptSignal` field, skips the post-build index hook, and re-raises so
the exit status still reflects the signal.

The handler lives in its own translation unit, keeping the platform-divergent
signal code out of the main instrumentation implementation.

Issue: #27859
2026-06-18 15:38:43 +02:00

126 lines
5.4 KiB
CMake

# Performs generic (non-project specific) validation of v1 Snippet File Contents
include_guard()
include(${CMAKE_CURRENT_LIST_DIR}/json.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/validate_schema.cmake)
function(snippet_has_fields snippet contents)
get_filename_component(filename "${snippet}" NAME)
json_has_key("${snippet}" "${contents}" role)
json_has_key("${snippet}" "${contents}" workingDir)
json_has_key("${snippet}" "${contents}" result)
# Only an interrupted build records this; completed commands must omit it.
json_missing_key("${snippet}" "${contents}" interruptSignal)
if (NOT filename MATCHES "^build-*")
json_has_key("${snippet}" "${contents}" command)
else()
json_missing_key("${snippet}" "${contents}" command)
endif()
if (filename MATCHES "^link-*")
json_has_key("${snippet}" "${contents}" target)
json_has_key("${snippet}" "${contents}" outputs)
json_has_key("${snippet}" "${contents}" outputSizes)
json_has_key("${snippet}" "${contents}" config)
json_has_key("${snippet}" "${contents}" language)
elseif (filename MATCHES "^compile-*")
json_has_key("${snippet}" "${contents}" target)
json_has_key("${snippet}" "${contents}" outputs)
json_has_key("${snippet}" "${contents}" outputSizes)
json_has_key("${snippet}" "${contents}" source)
json_has_key("${snippet}" "${contents}" language)
json_has_key("${snippet}" "${contents}" config)
elseif (filename MATCHES "^custom-*")
json_has_key("${snippet}" "${contents}" outputs)
json_has_key("${snippet}" "${contents}" outputSizes)
json_has_key("${snippet}" "${contents}" config)
elseif (filename MATCHES "^ctest-*")
json_has_key("${snippet}" "${contents}" showOnly)
elseif (filename MATCHES "^test-*")
json_has_key("${snippet}" "${contents}" testName)
json_has_key("${snippet}" "${contents}" config)
elseif (filename MATCHES "^install-*")
json_has_key("${snippet}" "${contents}" config)
endif()
if(ARGS_DYNAMIC_QUERY)
json_has_key("${snippet}" "${contents}" dynamicSystemInformation)
string(JSON dynamicSystemInfo ERROR_VARIABLE noInfo GET "${contents}" dynamicSystemInformation)
if (NOT noInfo)
json_has_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage)
json_has_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed)
json_has_key("${snippet}" ${dynamicSystemInfo} afterCPULoadAverage)
json_has_key("${snippet}" ${dynamicSystemInfo} afterHostMemoryUsed)
endif()
else()
json_missing_key("${snippet}" "${contents}" dynamicSystemInformation)
string(JSON dynamicSystemInfo ERROR_VARIABLE noInfo GET "${contents}" dynamicSystemInformation)
if (NOT noInfo)
json_missing_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage)
json_missing_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed)
json_missing_key("${snippet}" ${dynamicSystemInfo} afterCPULoadAverage)
json_missing_key("${snippet}" ${dynamicSystemInfo} afterHostMemoryUsed)
endif()
endif()
return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE)
endfunction()
function(snippet_valid_timing contents)
string(JSON start GET "${contents}" timeStart)
string(JSON duration GET "${contents}" duration)
if (start LESS 0)
snippet_error("${snippet}" "Negative time start: ${start}")
endif()
if (duration LESS 0)
json_error("${snippet}" "Negative duration: ${end}")
endif()
return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE)
endfunction()
function(verify_snippet_data snippet contents)
snippet_has_fields("${snippet}" "${contents}")
snippet_valid_timing("${contents}")
string(JSON version_major GET "${contents}" version major)
string(JSON version_minor GET "${contents}" version minor)
if (NOT version_major EQUAL 1 OR NOT version_minor LESS_EQUAL 2)
json_error("${snippet}" "Version must be <= 1.2, got: ${version_major}.${version_minor}")
endif()
get_filename_component(filename "${snippet}" NAME)
string(JSON result GET "${contents}" result)
if ("${filename}" MATCHES "^build-*" AND result)
json_error("${snippet}" "Result must be null for build snippets, got: ${result}")
elseif (NOT "${filename}" MATCHES "^build-*" AND NOT result MATCHES "^-?[0-9]+$")
json_error("${snippet}" "Result must be integer, got: ${result}")
endif()
string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs)
if (outputs)
string(JSON outputSizes ERROR_VARIABLE noOutputSizes GET "${contents}" outputSizes)
list(LENGTH outputs outputsLen)
list(LENGTH outputSizes outputSizesLen)
if (NOT outputSizes OR NOT outputsLen EQUAL outputSizesLen)
json_error("${snippet}" "outputs and outputSizes do not match")
endif()
endif()
return(PROPAGATE ERROR_MESSAGE RunCMake_TEST_FAILED role)
endfunction()
function(verify_snippet_file snippet contents)
verify_snippet_data("${snippet}" "${contents}")
string(JSON role GET "${contents}" role)
get_filename_component(filename "${snippet}" NAME)
if (NOT filename MATCHES "^${role}-")
json_error("${snippet}" "Role \"${role}\" doesn't match snippet filename")
endif()
validate_schema(
"${snippet}"
"${CMAKE_CURRENT_LIST_DIR}/../../../Help/manual/instrumentation/snippet-v1-schema.json"
# We expect to always generate valid snippet files.
0
)
if (RunCMake_TEST_FAILED)
add_error("${RunCMake_TEST_FAILED}")
endif()
return(PROPAGATE ERROR_MESSAGE RunCMake_TEST_FAILED role)
endfunction()