instrumentation: Send processMetrics data to CDash

This commit is contained in:
Martin Duffy
2026-09-17 10:29:06 -04:00
parent 68e2a3e793
commit 01fa1984e5
6 changed files with 103 additions and 2 deletions
+21 -1
View File
@@ -3944,7 +3944,7 @@ bool cmCTest::ConvertInstrumentationJSONFileToXML(std::string const& fpath,
continue;
}
if (key == "role" || key == "target" || key == "targetType" ||
key == "targetLabels") {
key == "targetLabels" || key == "processMetrics") {
continue;
}
// Truncate the full command line if verbose instrumentation
@@ -3977,6 +3977,26 @@ bool cmCTest::ConvertInstrumentationJSONFileToXML(std::string const& fpath,
xml.EndElement(); // NamedMeasurement
}
// Record available processMetrics as integer measurements.
Json::Value const& process_metrics = root["processMetrics"];
if (process_metrics.isObject()) {
for (char const* key : { "maxRSS", "userTime", "systemTime" }) {
Json::Value const& value = process_metrics[key];
if (value.isNull()) {
continue;
}
std::string measurement_name = key;
measurement_name[0] =
static_cast<char>(cmsysString_toupper(measurement_name[0]));
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "numeric/integer");
xml.Attribute("name", measurement_name);
xml.Element("Value", value.asString());
xml.EndElement(); // NamedMeasurement
}
}
// Record information about outputs and their sizes if found.
if (root.isMember("outputs") && root.isMember("outputSizes")) {
Json::ArrayIndex num_outputs =
@@ -13,4 +13,12 @@ if($ENV{USE_INSTRUMENTATION_CMD})
CUSTOM_CONTENT exampleContent STRING exampleData
)
endif()
if($ENV{USE_PROCESS_METRICS})
cmake_instrumentation(
API_VERSION 1
DATA_VERSION 1.2
OPTIONS processMetrics cdashSubmit
CALLBACK "${CMAKE_COMMAND}" -P "@RunCMake_SOURCE_DIR@/save-process-metrics.cmake"
)
endif()
@CASE_CMAKELISTS_SUFFIX_CODE@
@@ -11,6 +11,10 @@ foreach(xml_type Configure Build Test)
file(GLOB xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/${xml_type}.xml")
if(xml_file)
file(READ "${xml_file}" xml_content)
if(NOT ARGS_USE_PROCESS_METRICS AND xml_content MATCHES
"name=\"(MaxRSS|UserTime|SystemTime)\"")
set(RunCMake_TEST_FAILED "Unexpected process metrics in ${xml_type}.xml")
endif()
if(NOT xml_content MATCHES "AfterHostMemoryUsed")
set(RunCMake_TEST_FAILED "'AfterHostMemoryUsed' not found in ${xml_type}.xml")
endif()
@@ -0,0 +1,41 @@
include("${RunCMake_SOURCE_DIR}/InstrumentationInCTestXML-check.cmake")
foreach(role compile link test)
if(role STREQUAL "test")
set(xml_type Test)
set(element Results)
else()
set(xml_type Build)
if(role STREQUAL "compile")
set(element Compile)
else()
set(element Link)
endif()
endif()
file(GLOB xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/${xml_type}.xml")
file(READ "${xml_file}" xml_content)
string(REGEX MATCH "<${element}( [^>]*)?>.*</${element}>" measurements "${xml_content}")
if(NOT measurements)
set(RunCMake_TEST_FAILED "Missing ${element} element in ${xml_type}.xml")
return()
endif()
file(READ "${RunCMake_TEST_BINARY_DIR}/metrics-${role}.json" snippet)
string(JSON metrics GET "${snippet}" processMetrics)
foreach(key maxRSS userTime systemTime)
string(SUBSTRING "${key}" 0 1 first)
string(TOUPPER "${first}" first)
string(SUBSTRING "${key}" 1 -1 rest)
set(name "${first}${rest}")
string(JSON value ERROR_VARIABLE unavailable GET "${metrics}" ${key})
if(unavailable)
if(measurements MATCHES "name=\"${name}\"")
set(RunCMake_TEST_FAILED "Unexpected ${name} measurement for ${role}")
return()
endif()
elseif(NOT measurements MATCHES
"<NamedMeasurement type=\"numeric/integer\" name=\"${name}\">[ \t\r\n]*<Value>${value}</Value>")
set(RunCMake_TEST_FAILED "Expected ${name}=${value} measurement for ${role}")
return()
endif()
endforeach()
endforeach()
@@ -1,7 +1,7 @@
include(RunCTest)
function(run_InstrumentationInCTestXML CASE_NAME)
cmake_parse_arguments(ARGS "USE_INSTRUMENTATION_ENV_VARS;USE_VERBOSE_INSTRUMENTATION;USE_INSTRUMENTATION_CMD;USE_LOCAL_INSTRUMENTATION;USE_STALE_CDASH" "" "" ${ARGN})
cmake_parse_arguments(ARGS "USE_INSTRUMENTATION_ENV_VARS;USE_VERBOSE_INSTRUMENTATION;USE_INSTRUMENTATION_CMD;USE_LOCAL_INSTRUMENTATION;USE_STALE_CDASH;USE_PROCESS_METRICS" "" "" ${ARGN})
if(ARGS_USE_VERBOSE_INSTRUMENTATION)
set(ENV{CTEST_USE_VERBOSE_INSTRUMENTATION} "1")
set(RunCMake_USE_VERBOSE_INSTRUMENTATION 1)
@@ -42,6 +42,12 @@ endforeach()
]=])
endif()
if(ARGS_USE_PROCESS_METRICS)
set(ENV{USE_PROCESS_METRICS} "1")
else()
set(ENV{USE_PROCESS_METRICS} "0")
endif()
configure_file(${RunCMake_SOURCE_DIR}/main.c
${RunCMake_BINARY_DIR}/${CASE_NAME}/main.c COPYONLY)
run_ctest("${CASE_NAME}")
@@ -65,3 +71,6 @@ run_InstrumentationInCTestXML(InstrumentationWithoutCDashSubmit
run_InstrumentationInCTestXML(InstrumentationWithoutCDashSubmitWithStaleData
USE_LOCAL_INSTRUMENTATION USE_STALE_CDASH
)
run_InstrumentationInCTestXML(ProcessMetricsInCTestXML
USE_PROCESS_METRICS
)
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 4.3)
# Preserve the source snippets before CDash staging consumes them.
math(EXPR last_arg "${CMAKE_ARGC} - 1")
file(READ "${CMAKE_ARGV${last_arg}}" index)
string(JSON data_dir GET "${index}" dataDir)
string(JSON build_dir GET "${index}" buildDir)
string(JSON count LENGTH "${index}" snippets)
if(count GREATER 0)
math(EXPR last "${count} - 1")
foreach(i RANGE ${last})
string(JSON filename GET "${index}" snippets ${i})
file(READ "${data_dir}/${filename}" snippet)
string(JSON role GET "${snippet}" role)
if(role MATCHES "^(compile|link|test)$")
file(COPY_FILE "${data_dir}/${filename}" "${build_dir}/metrics-${role}.json")
endif()
endforeach()
endif()