mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
instrumentation: Prevent inherited process metrics in chained commands
Shells may exec the final chained command instead of forking it, preserving resource usage accumulated from earlier children. When processMetrics is enabled, append a shell builtin to generated command chains and scripts for Ninja, Makefiles, and FASTBuild to prevent this optimization while preserving command exit status.
This commit is contained in:
@@ -33,6 +33,8 @@
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmGlobalCommonGenerator.h"
|
||||
#include "cmGlobalFastbuildGenerator.h"
|
||||
#include "cmInstrumentation.h"
|
||||
#include "cmInstrumentationQuery.h"
|
||||
#include "cmLinkLineComputer.h"
|
||||
#include "cmLinkLineDeviceComputer.h"
|
||||
#include "cmList.h"
|
||||
@@ -1156,19 +1158,30 @@ void cmFastbuildNormalTargetGenerator::CollapseAllExecsIntoOneScriptfile(
|
||||
}
|
||||
LogMessage("Writing collapsed Execs to " + scriptFileName);
|
||||
auto const shell = cmGlobalFastbuildGenerator::GetExternalShellExecutable();
|
||||
char const* suffix = "\n";
|
||||
#if !defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
|
||||
cmInstrumentation* instrumentation =
|
||||
this->GetLocalGenerator()->GetCMakeInstance()->GetInstrumentation();
|
||||
if (instrumentation->HasOption(
|
||||
cmInstrumentationQuery::Option::ProcessMetrics)) {
|
||||
// Prevent shell exec optimization from passing accumulated child resource
|
||||
// usage to the final instrumentation wrapper.
|
||||
suffix = " && :\n";
|
||||
}
|
||||
#endif
|
||||
for (auto const& exec : execs) {
|
||||
if (exec.ScriptFile.empty()) {
|
||||
scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable)
|
||||
<< " " << exec.ExecArguments << '\n';
|
||||
<< " " << exec.ExecArguments << suffix;
|
||||
} else {
|
||||
#if defined(_WIN32)
|
||||
scriptFile << "call "
|
||||
<< cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile)
|
||||
<< '\n';
|
||||
<< suffix;
|
||||
#else
|
||||
scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " "
|
||||
<< cmSystemTools::ConvertToOutputPath(exec.ScriptFile)
|
||||
<< '\n';
|
||||
<< suffix;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmGlobalCommonGenerator.h"
|
||||
#include "cmGlobalFastbuildGenerator.h"
|
||||
#include "cmInstrumentation.h"
|
||||
#include "cmInstrumentationQuery.h"
|
||||
#include "cmList.h"
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmLocalCommonGenerator.h"
|
||||
@@ -38,6 +40,7 @@
|
||||
#include "cmTarget.h"
|
||||
#include "cmTargetTypes.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmake.h"
|
||||
|
||||
#define FASTBUILD_DOLLAR_TAG "FASTBUILD_DOLLAR_TAG"
|
||||
|
||||
@@ -180,6 +183,15 @@ void cmFastbuildTargetGenerator::WriteScriptEpilog(cmsys::ofstream& file) const
|
||||
"echo Batch file failed at line %FAIL_LINE% "
|
||||
"with errorcode %ERRORLEVEL%\n"
|
||||
"exit /b %ERROR_CODE%";
|
||||
#elif !defined(CMAKE_BOOTSTRAP)
|
||||
cmInstrumentation* instrumentation =
|
||||
this->GetLocalGenerator()->GetCMakeInstance()->GetInstrumentation();
|
||||
if (instrumentation->HasOption(
|
||||
cmInstrumentationQuery::Option::ProcessMetrics)) {
|
||||
// Prevent shell exec optimization from passing accumulated child resource
|
||||
// usage to the final instrumentation wrapper.
|
||||
file << ":\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmGlobalNinjaGenerator.h"
|
||||
#include "cmInstrumentation.h"
|
||||
#include "cmInstrumentationQuery.h"
|
||||
#include "cmList.h"
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmLocalGenerator.h"
|
||||
@@ -476,6 +478,13 @@ std::string cmLocalNinjaGenerator::WriteCommandScript(
|
||||
"echo Batch file failed at line %FAIL_LINE% "
|
||||
"with errorcode %ERRORLEVEL%\n"
|
||||
"exit /b %ERROR_CODE%";
|
||||
#elif !defined(CMAKE_BOOTSTRAP)
|
||||
if (this->GetCMakeInstance()->GetInstrumentation()->HasOption(
|
||||
cmInstrumentationQuery::Option::ProcessMetrics)) {
|
||||
// Prevent shell exec optimization from passing accumulated child resource
|
||||
// usage to the final instrumentation wrapper.
|
||||
script << ":\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
return scriptPath;
|
||||
@@ -567,6 +576,14 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
|
||||
}
|
||||
cmd << *li;
|
||||
}
|
||||
# ifndef CMAKE_BOOTSTRAP
|
||||
if (this->GetCMakeInstance()->GetInstrumentation()->HasOption(
|
||||
cmInstrumentationQuery::Option::ProcessMetrics)) {
|
||||
// Prevent shell exec optimization from passing accumulated child resource
|
||||
// usage to the final instrumentation wrapper.
|
||||
cmd << " && :";
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
return cmd.str();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmGlobalUnixMakefileGenerator3.h"
|
||||
#include "cmInstrumentation.h"
|
||||
#include "cmInstrumentationQuery.h"
|
||||
#include "cmList.h"
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmLocalGenerator.h"
|
||||
@@ -631,7 +632,24 @@ void cmLocalUnixMakefileGenerator3::WriteMakeRule(
|
||||
|
||||
if (!commands.empty()) {
|
||||
// Write the list of commands.
|
||||
os << cmWrap("\t", commands, "", "\n") << '\n';
|
||||
bool protectProcessMetrics = false;
|
||||
#if !defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
|
||||
if (this->GetCMakeInstance()->GetInstrumentation()->HasOption(
|
||||
cmInstrumentationQuery::Option::ProcessMetrics)) {
|
||||
// Prevent shell exec optimization from passing accumulated child
|
||||
// resource usage to the final instrumentation wrapper.
|
||||
protectProcessMetrics = true;
|
||||
}
|
||||
#endif
|
||||
for (std::string const& command : commands) {
|
||||
os << '\t' << command;
|
||||
// Preserve the instrumentation daemon's explicit exec command.
|
||||
if (protectProcessMetrics && !command.empty() &&
|
||||
!cmHasLiteralPrefix(command, "exec ")) {
|
||||
os << " && :";
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
if (symbolic && !this->IsWatcomWMake()) {
|
||||
os << ".PHONY : " << tgt << '\n';
|
||||
|
||||
@@ -8,6 +8,8 @@ if (NOT snippets)
|
||||
endif()
|
||||
|
||||
set(FOUND_SNIPPETS "")
|
||||
unset(process_metrics_chain_head_rss)
|
||||
unset(process_metrics_chain_tail_rss)
|
||||
foreach(snippet IN LISTS snippets)
|
||||
get_filename_component(filename "${snippet}" NAME)
|
||||
|
||||
@@ -42,7 +44,13 @@ foreach(snippet IN LISTS snippets)
|
||||
if (filename MATCHES "^custom-")
|
||||
string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs)
|
||||
if (outputs MATCHES "process_metrics_custom\.stamp")
|
||||
set(process_metrics_case "memory")
|
||||
string(JSON command GET "${contents}" command)
|
||||
if (command MATCHES "process_metrics_memory\\.py")
|
||||
set(process_metrics_case "memory")
|
||||
string(JSON process_metrics_chain_head_rss GET "${process_metrics}" maxRSS)
|
||||
else()
|
||||
string(JSON process_metrics_chain_tail_rss GET "${process_metrics}" maxRSS)
|
||||
endif()
|
||||
elseif (outputs MATCHES "process_metrics_cpu_custom\.stamp")
|
||||
set(process_metrics_case "cpu")
|
||||
endif()
|
||||
@@ -254,6 +262,16 @@ foreach(snippet IN LISTS snippets)
|
||||
|
||||
endforeach()
|
||||
|
||||
# The final chained command must not inherit the preceding command's memory peak.
|
||||
# (SunOS does not populate maxRSS, so both commands report zero.)
|
||||
if (ARGS_PROCESS_METRICS_QUERY AND ARGS_BUILD AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "SunOS")
|
||||
if (NOT DEFINED process_metrics_chain_head_rss OR NOT DEFINED process_metrics_chain_tail_rss)
|
||||
add_error("Missing process metrics for one or both chained commands")
|
||||
elseif (process_metrics_chain_head_rss EQUAL process_metrics_chain_tail_rss)
|
||||
add_error("Chained commands reported identical maxRSS: ${process_metrics_chain_tail_rss} KiB")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Verify that listed snippets match expected roles
|
||||
set(EXPECTED_SNIPPETS configure generate)
|
||||
if (ARGS_BUILD OR ARGS_BUILD_MAKE_PROGRAM)
|
||||
|
||||
@@ -30,6 +30,8 @@ if (PROCESS_METRICS_QUERY)
|
||||
COMMAND ${Python_EXECUTABLE}
|
||||
${CMAKE_CURRENT_LIST_DIR}/process_metrics_memory.py
|
||||
${CMAKE_CURRENT_BINARY_DIR}/process_metrics_custom.stamp
|
||||
# The last command must not inherit the preceding command's memory peak.
|
||||
COMMAND ${CMAKE_COMMAND} -E true
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
|
||||
Reference in New Issue
Block a user