instrumentation: Add Config value to snippet data
diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst
index c3ebc75..ae607ce 100644
--- a/Help/manual/cmake-instrumentation.7.rst
+++ b/Help/manual/cmake-instrumentation.7.rst
@@ -263,6 +263,10 @@
   ``testName``
     The name of the test being executed. Only included when ``role`` is ``test``.
 
+  ``config``
+    The type of build, such as ``Release`` or ``Debug``. Only included when
+    ``role`` is ``compile``, ``link`` or ``test``.
+
   ``dynamicSystemInformation``
     Specifies the dynamic information collected about the host machine
     CMake is being run from. Data is collected for every snippet file
@@ -294,7 +298,8 @@
     "language" : "C++",
     "outputs" : [ "CMakeFiles/main.dir/main.cxx.o" ],
     "outputSizes" : [ 0 ],
-    "source" : "<src>/main.cxx"
+    "source" : "<src>/main.cxx",
+    "config" : "Debug",
     "dynamicSystemInformation" :
     {
       "afterCPULoadAverage" : 2.3500000000000001,
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index 593adfb..2f25de7 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -70,7 +70,8 @@
     DoingBuildDir,
     DoingCurrentBuildDir,
     DoingCount,
-    DoingFilterPrefix
+    DoingFilterPrefix,
+    DoingConfig
   };
   Doing doing = DoingNone;
   int arg0 = 0;
@@ -100,6 +101,8 @@
       doing = DoingCurrentBuildDir;
     } else if (strcmp(arg, "--filter-prefix") == 0) {
       doing = DoingFilterPrefix;
+    } else if (strcmp(arg, "--config") == 0) {
+      doing = DoingConfig;
     } else if (doing == DoingOutput) {
       this->Reporter.OptionOutput = arg;
       doing = DoingNone;
@@ -136,6 +139,9 @@
     } else if (doing == DoingRole) {
       this->Reporter.OptionRole = arg;
       doing = DoingNone;
+    } else if (doing == DoingConfig) {
+      this->Reporter.OptionConfig = arg;
+      doing = DoingNone;
     }
   }
 
@@ -267,6 +273,7 @@
   options["language"] = this->Reporter.OptionLanguage;
   options["targetType"] = this->Reporter.OptionTargetType;
   options["role"] = this->Reporter.OptionRole;
+  options["config"] = this->Reporter.OptionConfig;
   std::map<std::string, std::string> arrayOptions;
   arrayOptions["outputs"] = this->Reporter.OptionOutput;
   arrayOptions["targetLabels"] = this->Reporter.OptionTargetLabels;
diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h
index 31cd9f2..0b9a08a 100644
--- a/Source/CTest/cmCTestLaunchReporter.h
+++ b/Source/CTest/cmCTestLaunchReporter.h
@@ -42,6 +42,7 @@
   std::string OptionFilterPrefix;
   std::string OptionCommandType;
   std::string OptionRole;
+  std::string OptionConfig;
 
   // The real command line appearing after launcher arguments.
   std::string CWD;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 98fd24a..42dc0af 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -1020,7 +1020,8 @@
     this->Instrumentation.InstrumentTest(
       this->TestProperties->Name, this->ActualCommand, this->Arguments,
       this->TestProcess->GetExitValue(), this->TestProcess->GetStartTime(),
-      this->TestProcess->GetSystemStartTime());
+      this->TestProcess->GetSystemStartTime(),
+      this->GetCTest()->GetConfigType());
   }
   this->MultiTestHandler.FinishTestProcess(this->TestProcess->GetRunner(),
                                            started);
diff --git a/Source/cmInstrumentation.cxx b/Source/cmInstrumentation.cxx
index c3f4488..774437d 100644
--- a/Source/cmInstrumentation.cxx
+++ b/Source/cmInstrumentation.cxx
@@ -312,7 +312,7 @@
   std::string const& name, std::string const& command,
   std::vector<std::string> const& args, int64_t result,
   std::chrono::steady_clock::time_point steadyStart,
-  std::chrono::system_clock::time_point systemStart)
+  std::chrono::system_clock::time_point systemStart, std::string config)
 {
   // Store command info
   Json::Value root(this->preTestStats);
@@ -323,6 +323,7 @@
   root["testName"] = name;
   root["binaryDir"] = this->binaryDir;
   root["result"] = static_cast<Json::Value::Int64>(result);
+  root["config"] = config;
 
   // Post-Command
   this->InsertTimingData(root, steadyStart, systemStart);
@@ -417,6 +418,13 @@
       }
     }
   }
+
+  // Create empty config entry if config not found
+  if (!root.isMember("config") &&
+      (command_type == "compile" || command_type == "link")) {
+    root["config"] = "";
+  }
+
   if (arrayOptions.has_value()) {
     for (auto const& item : arrayOptions.value()) {
       if (item.first == "targetLabels" && command_type != "link") {
diff --git a/Source/cmInstrumentation.h b/Source/cmInstrumentation.h
index d17e99c..3a2f9d9 100644
--- a/Source/cmInstrumentation.h
+++ b/Source/cmInstrumentation.h
@@ -32,7 +32,8 @@
   int InstrumentTest(std::string const& name, std::string const& command,
                      std::vector<std::string> const& args, int64_t result,
                      std::chrono::steady_clock::time_point steadyStart,
-                     std::chrono::system_clock::time_point systemStart);
+                     std::chrono::system_clock::time_point systemStart,
+                     std::string config);
   void GetPreTestStats();
   void LoadQueries();
   bool HasQuery() const;
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 3b3bbe4..85b2cfd 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -554,6 +554,7 @@
       cmOutputConverter::SHELL, useWatcomQuote);
     vars.Target = target.c_str();
     vars.TargetPDB = targetOutPathPDB.c_str();
+    vars.Config = this->GetConfigName().c_str();
 
     // Setup the target version.
     std::string targetVersionMajor;
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index 905a232..9774d12 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -809,8 +809,8 @@
       vars.TargetSOName = targetOutSOName.c_str();
     }
     vars.LinkFlags = linkFlags.c_str();
-
     vars.Manifests = manifests.c_str();
+    vars.Config = this->GetConfigName().c_str();
 
     // Compute the directory portion of the install_name setting.
     std::string install_name_dir;
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 0adffc6..ed232c8 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -952,6 +952,7 @@
   vars.ObjectFileDir = objectFileDir.c_str();
   vars.Flags = flags.c_str();
   vars.ISPCHeader = ispcHeaderForShell.c_str();
+  vars.Config = this->GetConfigName().c_str();
 
   std::string definesString = cmStrCat("$(", lang, "_DEFINES)");
 
@@ -1700,6 +1701,7 @@
   vars.Object = output.c_str();
   vars.Fatbinary = fatbinaryOutput.c_str();
   vars.RegisterFile = registerFile.c_str();
+  vars.Config = this->GetConfigName().c_str();
 
   std::string linkFlags;
   this->GetDeviceLinkFlags(linkFlags, "CUDA");
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 3c1d7e1..707e437 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -342,6 +342,7 @@
     vars.Flags = "$FLAGS";
     vars.LinkFlags = "$LINK_FLAGS";
     vars.Manifests = "$MANIFESTS";
+    vars.Config = "$CONFIG";
 
     vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS";
 
@@ -416,6 +417,7 @@
 
   std::string flags = this->GetFlags("CUDA", config);
   vars.Flags = flags.c_str();
+  vars.Config = "$CONFIG";
 
   std::string compileCmd = this->GetMakefile()->GetRequiredDefinition(
     "CMAKE_CUDA_DEVICE_LINK_COMPILE");
@@ -557,6 +559,7 @@
     vars.Flags = "$FLAGS";
     vars.LinkFlags = "$LINK_FLAGS";
     vars.Manifests = "$MANIFESTS";
+    vars.Config = "$CONFIG";
 
     std::string langFlags;
     if (targetType != cmStateEnums::EXECUTABLE) {
@@ -1313,6 +1316,7 @@
   vars["AIX_EXPORTS"] = this->GetAIXExports(config);
 
   vars["LINK_PATH"] = frameworkPath + linkPath;
+  vars["CONFIG"] = config;
 
   // Compute architecture specific link flags.  Yes, these go into a different
   // variable for executables, probably due to a mistake made when duplicating
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 59ab999..99ab466 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -630,6 +630,7 @@
   scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE";
   scanVars.DependencyFile = rule.DepFile.c_str();
   scanVars.DependencyTarget = "$out";
+  scanVars.Config = vars.Config;
 
   // Scanning needs the same preprocessor settings as direct compilation would.
   scanVars.Source = vars.Source;
@@ -695,6 +696,7 @@
   vars.ObjectFileDir = "$OBJECT_FILE_DIR";
   vars.CudaCompileMode = "$CUDA_COMPILE_MODE";
   vars.ISPCHeader = "$ISPC_HEADER_FILE";
+  vars.Config = "$CONFIG";
 
   cmMakefile* mf = this->GetMakefile();
 
@@ -1440,6 +1442,7 @@
     this->ComputeFlagsForObject(source, language, config, objectFileName);
   vars["DEFINES"] = this->ComputeDefines(source, language, config);
   vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
+  vars["CONFIG"] = config;
 
   auto compilerLauncher = this->GetCompilerLauncher(language, config);
 
@@ -1793,6 +1796,7 @@
     this->ComputeFlagsForObject(source, language, config, bmiFileName);
   vars["DEFINES"] = this->ComputeDefines(source, language, config);
   vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
+  vars["CONFIG"] = config;
 
   if (this->GetMakefile()->GetSafeDefinition(
         cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) {
@@ -2035,6 +2039,7 @@
                                     this->GetFlags(language, config));
   vars["DEFINES"] = this->GetDefines(language, config);
   vars["INCLUDES"] = this->GetIncludes(language, config);
+  vars["CONFIG"] = config;
 
   // target-level object filename
   std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat(
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index c21f5fe..9c3c1ed 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -283,6 +283,12 @@
     }
     return "";
   }
+  if (variable == "CONFIG") {
+    if (this->ReplaceValues->Config) {
+      return this->ReplaceValues->Config;
+    }
+    return "";
+  }
 
   auto compIt = this->Compilers.find(variable);
 
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 46d4be9..df382dd 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -75,6 +75,7 @@
     char const* RegisterFile = nullptr;
     char const* Launcher = nullptr;
     char const* Role = nullptr;
+    char const* Config = nullptr;
   };
 
   // Expand rule variables in CMake of the type found in language rules
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 2055167..62f0d18 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -2690,13 +2690,13 @@
     this->State->SetGlobalProperty(
       "RULE_LAUNCH_COMPILE",
       cmStrCat(
-        launcher, "--command-type compile", common_args,
+        launcher, "--command-type compile", common_args, "--config <CONFIG> ",
         "--output <OBJECT> --source <SOURCE> --language <LANGUAGE> -- "));
     this->State->SetGlobalProperty(
       "RULE_LAUNCH_LINK",
       cmStrCat(
         launcher, "--command-type link", common_args,
-        "--output <TARGET> --target-type <TARGET_TYPE> ",
+        "--output <TARGET> --target-type <TARGET_TYPE> --config <CONFIG> ",
         "--language <LANGUAGE> --target-labels \"<TARGET_LABELS>\" -- "));
     this->State->SetGlobalProperty(
       "RULE_LAUNCH_CUSTOM",
diff --git a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake
index c270b71..1e86d44 100644
--- a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake
@@ -52,7 +52,10 @@
     list(APPEND ARGS_CONFIGURE_ARG "-Wno-dev")
   endif()
   set(RunCMake_TEST_SOURCE_DIR ${RunCMake_SOURCE_DIR}/project)
-  run_cmake_with_options(${test} ${ARGS_CONFIGURE_ARG})
+  if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+  run_cmake_with_options(${test} ${ARGS_CONFIGURE_ARG} ${maybe_CMAKE_BUILD_TYPE})
 
   # Follow-up Commands
   if (ARGS_BUILD)
diff --git a/Tests/RunCMake/Instrumentation/check-data-dir.cmake b/Tests/RunCMake/Instrumentation/check-data-dir.cmake
index 0c93d7a..f2e8235 100644
--- a/Tests/RunCMake/Instrumentation/check-data-dir.cmake
+++ b/Tests/RunCMake/Instrumentation/check-data-dir.cmake
@@ -99,6 +99,14 @@
       snippet_error("${snippet}" "Unexpected testName: ${testName}")
     endif()
   endif()
+
+  # Verify that Config is Debug
+  if (filename MATCHES "^test|^compile|^link")
+    string(JSON config GET "${contents}" config)
+    if (NOT config STREQUAL "Debug")
+      snippet_error(${snippet} "Unexpected config: ${config}")
+    endif()
+  endif()
 endforeach()
 
 # Verify that listed snippets match expected roles
diff --git a/Tests/RunCMake/Instrumentation/verify-snippet.cmake b/Tests/RunCMake/Instrumentation/verify-snippet.cmake
index dfc6332..d64f686 100644
--- a/Tests/RunCMake/Instrumentation/verify-snippet.cmake
+++ b/Tests/RunCMake/Instrumentation/verify-snippet.cmake
@@ -37,18 +37,21 @@
     has_key("${snippet}" "${contents}" outputSizes)
     has_key("${snippet}" "${contents}" targetType)
     has_key("${snippet}" "${contents}" targetLabels)
+    has_key("${snippet}" "${contents}" config)
   elseif (filename MATCHES "^compile-*")
     has_key("${snippet}" "${contents}" target)
     has_key("${snippet}" "${contents}" outputs)
     has_key("${snippet}" "${contents}" outputSizes)
     has_key("${snippet}" "${contents}" source)
     has_key("${snippet}" "${contents}" language)
+    has_key("${snippet}" "${contents}" config)
   elseif (filename MATCHES "^custom-*")
     has_key("${snippet}" "${contents}" target)
     has_key("${snippet}" "${contents}" outputs)
     has_key("${snippet}" "${contents}" outputSizes)
   elseif (filename MATCHES "^test-*")
     has_key("${snippet}" "${contents}" testName)
+    has_key("${snippet}" "${contents}" config)
   endif()
   if(ARGS_DYNAMIC_QUERY)
     has_key("${snippet}" "${contents}" dynamicSystemInformation)