From 2299b2fcab26dc5d7d4e82da7068da23af17623a Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Mon, 10 Feb 2025 16:39:35 -0500 Subject: instrumentation: Add build snippet Adds a new snippet generation for recording the full time spent waiting for `ninja` or `make` invocations to finish. --- Help/manual/cmake-instrumentation.7.rst | 3 +- Source/cmInstrumentation.cxx | 17 ++++-- .../Instrumentation/check-make-program-hooks.cmake | 10 ++++ Tests/RunCMake/Instrumentation/hook.cmake | 61 ++++++++++++---------- .../RunCMake/Instrumentation/verify-snippet.cmake | 19 ++++--- 5 files changed, 68 insertions(+), 42 deletions(-) diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index 3a70843..677e639 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst @@ -243,7 +243,7 @@ and contain the following data: always ``1``. ``command`` - The full command executed. + The full command executed. Excluded when ``role`` is ``build``. ``result`` The exit-value of the command, an integer. @@ -255,6 +255,7 @@ and contain the following data: * ``link`` * ``custom`` * ``cmakeBuild`` + * ``build`` * ``install`` * ``ctest`` * ``test`` diff --git a/Source/cmInstrumentation.cxx b/Source/cmInstrumentation.cxx index 87f59a8..6dffc4c 100644 --- a/Source/cmInstrumentation.cxx +++ b/Source/cmInstrumentation.cxx @@ -365,7 +365,9 @@ int cmInstrumentation::InstrumentCommand( Json::Value commandInfo(Json::objectValue); std::string command_str = GetCommandStr(command); - root["command"] = command_str; + if (!command_str.empty()) { + root["command"] = command_str; + } root["version"] = 1; // Pre-Command @@ -533,8 +535,15 @@ int cmInstrumentation::SpawnBuildDaemon() */ int cmInstrumentation::CollectTimingAfterBuild(int ppid) { - while (0 == uv_kill(ppid, 0)) { - cmSystemTools::Delay(100); + std::function waitForBuild = [ppid]() -> int { + while (0 == uv_kill(ppid, 0)) { + cmSystemTools::Delay(100); + }; + return 0; }; - return this->CollectTimingData(cmInstrumentationQuery::Hook::PostBuild); + int ret = this->InstrumentCommand( + "build", {}, [waitForBuild]() { return waitForBuild(); }, cm::nullopt, + cm::nullopt, false); + this->CollectTimingData(cmInstrumentationQuery::Hook::PostBuild); + return ret; } diff --git a/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake b/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake index 60b2f7b..239af3b 100644 --- a/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake +++ b/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake @@ -33,3 +33,13 @@ endif() if (NOT dataDirClean) string(APPEND RunCMake_TEST_FAILED "Snippet files not fully removed post build\n") endif() + +file(READ ${v1}/postBuild.hook postBuildErrors) +if (NOT postBuildErrors MATCHES "^$") + string(APPEND RunCMake_TEST_FAILED "Errors found in data during postBuild hook:\n${postBuildErrors}\n") +endif() + +file(READ ${v1}/preBuild.hook preBuildErrors) +if (NOT preBuildErrors MATCHES "^$") + string(APPEND RunCMake_TEST_FAILED "Errors found in data during preBuild hook:\n${preBuildErrors}\n") +endif() diff --git a/Tests/RunCMake/Instrumentation/hook.cmake b/Tests/RunCMake/Instrumentation/hook.cmake index cef088d..a139a37 100644 --- a/Tests/RunCMake/Instrumentation/hook.cmake +++ b/Tests/RunCMake/Instrumentation/hook.cmake @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.30) include(${CMAKE_CURRENT_LIST_DIR}/json.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/verify-snippet.cmake) # Test CALLBACK script. Prints output information and verifies index file # Called as: cmake -P hook.cmake [CheckForStaticQuery?] [index.json] set(index ${CMAKE_ARGV4}) @@ -19,7 +20,7 @@ function(add_error error) return(PROPAGATE ERROR_MESSAGE) endfunction() -function(has_key key json) +function(has_key_index key json) cmake_parse_arguments(ARG "UNEXPECTED" "" "" ${ARGN}) unset(missingKey) string(JSON ${key} ERROR_VARIABLE missingKey GET "${json}" ${key}) @@ -28,13 +29,13 @@ function(has_key key json) elseif(ARG_UNEXPECTED AND missingKey MATCHES NOTFOUND) add_error("\nUnexpected key \"${key}\" in index:\n${json}") endif() - return(PROPAGATE RunCMake_TEST_FAILED ${key}) + return(PROPAGATE ERROR_MESSAGE ${key}) endfunction() -has_key(version "${contents}") -has_key(buildDir "${contents}") -has_key(dataDir "${contents}") -has_key(snippets "${contents}") +has_key_index(version "${contents}") +has_key_index(buildDir "${contents}") +has_key_index(dataDir "${contents}") +has_key_index(snippets "${contents}") if (NOT version EQUAL 1) add_error("Version must be 1, got: ${version}") @@ -47,32 +48,34 @@ foreach(i RANGE ${length}) if (NOT EXISTS ${dataDir}/${filename}) add_error("Listed snippet: ${dataDir}/${filename} does not exist") endif() + read_json(${dataDir}/${filename} snippet_contents) + verify_snippet(${dataDir}/${filename} "${snippet_contents}") endforeach() -has_key(staticSystemInformation "${contents}" ${hasStaticInfo}) -has_key(OSName "${staticSystemInformation}" ${hasStaticInfo}) -has_key(OSPlatform "${staticSystemInformation}" ${hasStaticInfo}) -has_key(OSRelease "${staticSystemInformation}" ${hasStaticInfo}) -has_key(OSVersion "${staticSystemInformation}" ${hasStaticInfo}) -has_key(familyId "${staticSystemInformation}" ${hasStaticInfo}) -has_key(hostname "${staticSystemInformation}" ${hasStaticInfo}) -has_key(is64Bits "${staticSystemInformation}" ${hasStaticInfo}) -has_key(modelId "${staticSystemInformation}" ${hasStaticInfo}) -has_key(numberOfLogicalCPU "${staticSystemInformation}" ${hasStaticInfo}) -has_key(numberOfPhysicalCPU "${staticSystemInformation}" ${hasStaticInfo}) -has_key(processorAPICID "${staticSystemInformation}" ${hasStaticInfo}) -has_key(processorCacheSize "${staticSystemInformation}" ${hasStaticInfo}) -has_key(processorClockFrequency "${staticSystemInformation}" ${hasStaticInfo}) -has_key(processorName "${staticSystemInformation}" ${hasStaticInfo}) -has_key(totalPhysicalMemory "${staticSystemInformation}" ${hasStaticInfo}) -has_key(totalVirtualMemory "${staticSystemInformation}" ${hasStaticInfo}) -has_key(vendorID "${staticSystemInformation}" ${hasStaticInfo}) -has_key(vendorString "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(staticSystemInformation "${contents}" ${hasStaticInfo}) +has_key_index(OSName "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(OSPlatform "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(OSRelease "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(OSVersion "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(familyId "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(hostname "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(is64Bits "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(modelId "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(numberOfLogicalCPU "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(numberOfPhysicalCPU "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(processorAPICID "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(processorCacheSize "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(processorClockFrequency "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(processorName "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(totalPhysicalMemory "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(totalVirtualMemory "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(vendorID "${staticSystemInformation}" ${hasStaticInfo}) +has_key_index(vendorString "${staticSystemInformation}" ${hasStaticInfo}) + +get_filename_component(dataDir ${index} DIRECTORY) +get_filename_component(v1 ${dataDir} DIRECTORY) +file(WRITE ${v1}/${hook}.hook "${ERROR_MESSAGE}") if (NOT ERROR_MESSAGE MATCHES "^$") message(FATAL_ERROR ${ERROR_MESSAGE}) endif() - -get_filename_component(dataDir ${index} DIRECTORY) -get_filename_component(v1 ${dataDir} DIRECTORY) -file(TOUCH ${v1}/${hook}.hook) diff --git a/Tests/RunCMake/Instrumentation/verify-snippet.cmake b/Tests/RunCMake/Instrumentation/verify-snippet.cmake index d64f686..ec997e3 100644 --- a/Tests/RunCMake/Instrumentation/verify-snippet.cmake +++ b/Tests/RunCMake/Instrumentation/verify-snippet.cmake @@ -2,12 +2,13 @@ function(add_error error) string(APPEND RunCMake_TEST_FAILED " ${error}\n") - return(PROPAGATE RunCMake_TEST_FAILED) + string(APPEND ERROR_MESSAGE " ${error}\n") + return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) endfunction() function(snippet_error snippet error) add_error("Error in snippet file ${snippet}:\n${error}") - return(PROPAGATE RunCMake_TEST_FAILED) + return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) endfunction() function(has_key snippet json key) @@ -15,7 +16,7 @@ function(has_key snippet json key) if (NOT missingKey MATCHES NOTFOUND) snippet_error("${snippet}" "Missing ${key}") endif() - return(PROPAGATE RunCMake_TEST_FAILED) + return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) endfunction() function(has_not_key snippet json key) @@ -23,14 +24,16 @@ function(has_not_key snippet json key) if (missingKey MATCHES NOTFOUND) snippet_error("${snippet}" "Has unexpected ${key}") endif() - return(PROPAGATE RunCMake_TEST_FAILED) + return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) endfunction() function(snippet_has_fields snippet contents) get_filename_component(filename "${snippet}" NAME) - has_key("${snippet}" "${contents}" command) has_key("${snippet}" "${contents}" role) has_key("${snippet}" "${contents}" result) + if (NOT filename MATCHES "^build-*") + has_key("${snippet}" "${contents}" command) + endif() if (filename MATCHES "^link-*") has_key("${snippet}" "${contents}" target) has_key("${snippet}" "${contents}" outputs) @@ -72,7 +75,7 @@ function(snippet_has_fields snippet contents) has_not_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed) endif() endif() - return(PROPAGATE RunCMake_TEST_FAILED) + return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) endfunction() function(snippet_valid_timing contents) @@ -84,7 +87,7 @@ function(snippet_valid_timing contents) if (duration LESS 0) snippet_error("${snippet}" "Negative duration: ${end}") endif() - return(PROPAGATE RunCMake_TEST_FAILED) + return(PROPAGATE RunCMake_TEST_FAILED ERROR_MESSAGE) endfunction() function(verify_snippet snippet contents) @@ -108,5 +111,5 @@ function(verify_snippet snippet contents) snippet_error("${snippet}" "outputs and outputSizes do not match") endif() endif() - return(PROPAGATE RunCMake_TEST_FAILED role) + return(PROPAGATE ERROR_MESSAGE RunCMake_TEST_FAILED role) endfunction() -- cgit v0.12 From f777af7734f712e420159e099cbc96c3582c038c Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Tue, 11 Feb 2025 10:47:12 -0500 Subject: instrumentation: Update docs for available snippets --- Help/manual/cmake-instrumentation.7.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index 677e639..b5431f7 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst @@ -251,14 +251,17 @@ and contain the following data: ``role`` The type of command executed, which will be one of the following values: - * ``compile`` - * ``link`` - * ``custom`` - * ``cmakeBuild`` - * ``build`` - * ``install`` - * ``ctest`` - * ``test`` + * ``configure``: the CMake configure step + * ``generate``: the CMake generate step + * ``compile``: an individual compile step invoked during the build + * ``link``: an individual link step invoked during the build + * ``custom``: an individual custom command invoked during the build + * ``build``: a complete ``make`` or ``ninja`` invocation. Only generated if ``preBuild`` or ``postBuild`` hooks are enabled. + * ``cmakeBuild``: a complete ``cmake --build`` invocation + * ``cmakeInstall``: a complete ``cmake --install`` invocation + * ``install``: an individual ``cmake -P cmake_install.cmake`` invocation + * ``ctest``: a complete ``ctest`` invocation + * ``test``: a single test executed by CTest ``target`` The CMake target associated with the command. Only included when ``role`` is -- cgit v0.12 From c8e319d08cc0b9d88e434f3fd0f79af8fc6395c9 Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Tue, 11 Feb 2025 11:53:19 -0500 Subject: instrumentation: Add experimental notes to docs --- Help/command/cmake_instrumentation.rst | 5 +++++ Help/manual/cmake-instrumentation.7.rst | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Help/command/cmake_instrumentation.rst b/Help/command/cmake_instrumentation.rst index a859ff3..22e77ea 100644 --- a/Help/command/cmake_instrumentation.rst +++ b/Help/command/cmake_instrumentation.rst @@ -3,6 +3,11 @@ cmake_instrumentation .. versionadded:: 4.0 +.. note:: + + This command is only available when experimental support for instrumentation + has been enabled by the ``CMAKE_EXPERIMENTAL_INSTRUMENTATION`` gate. + Enables interacting with the :manual:`CMake Instrumentation API `. diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index b5431f7..7b2d1e6 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst @@ -12,6 +12,11 @@ cmake-instrumentation(7) Introduction ============ +.. note:: + + This feature is only available when experimental support for instrumentation + has been enabled by the ``CMAKE_EXPERIMENTAL_INSTRUMENTATION`` gate. + The CMake Instrumentation API allows for the collection of timing data, target information and system diagnostic information during the configure, generate, build, test and install steps for a CMake project. -- cgit v0.12