Browse Source

Merge topic 'instrumentation-build-snippet' into release-4.0

c8e319d08c instrumentation: Add experimental notes to docs
f777af7734 instrumentation: Update docs for available snippets
2299b2fcab instrumentation: Add build snippet

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !10328
Brad King 8 months ago
parent
commit
1d274d34b9

+ 5 - 0
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 <cmake-instrumentation(7)>`.
 

+ 17 - 8
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.
@@ -243,7 +248,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.
@@ -251,13 +256,17 @@ and contain the following data:
   ``role``
     The type of command executed, which will be one of the following values:
 
-    * ``compile``
-    * ``link``
-    * ``custom``
-    * ``cmakeBuild``
-    * ``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

+ 13 - 4
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<int()> 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;
 }

+ 10 - 0
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()

+ 32 - 29
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)

+ 11 - 8
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()