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 "cmGeneratorTarget.h"
|
||||||
#include "cmGlobalCommonGenerator.h"
|
#include "cmGlobalCommonGenerator.h"
|
||||||
#include "cmGlobalFastbuildGenerator.h"
|
#include "cmGlobalFastbuildGenerator.h"
|
||||||
|
#include "cmInstrumentation.h"
|
||||||
|
#include "cmInstrumentationQuery.h"
|
||||||
#include "cmLinkLineComputer.h"
|
#include "cmLinkLineComputer.h"
|
||||||
#include "cmLinkLineDeviceComputer.h"
|
#include "cmLinkLineDeviceComputer.h"
|
||||||
#include "cmList.h"
|
#include "cmList.h"
|
||||||
@@ -1156,19 +1158,30 @@ void cmFastbuildNormalTargetGenerator::CollapseAllExecsIntoOneScriptfile(
|
|||||||
}
|
}
|
||||||
LogMessage("Writing collapsed Execs to " + scriptFileName);
|
LogMessage("Writing collapsed Execs to " + scriptFileName);
|
||||||
auto const shell = cmGlobalFastbuildGenerator::GetExternalShellExecutable();
|
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) {
|
for (auto const& exec : execs) {
|
||||||
if (exec.ScriptFile.empty()) {
|
if (exec.ScriptFile.empty()) {
|
||||||
scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable)
|
scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable)
|
||||||
<< " " << exec.ExecArguments << '\n';
|
<< " " << exec.ExecArguments << suffix;
|
||||||
} else {
|
} else {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
scriptFile << "call "
|
scriptFile << "call "
|
||||||
<< cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile)
|
<< cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile)
|
||||||
<< '\n';
|
<< suffix;
|
||||||
#else
|
#else
|
||||||
scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " "
|
scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " "
|
||||||
<< cmSystemTools::ConvertToOutputPath(exec.ScriptFile)
|
<< cmSystemTools::ConvertToOutputPath(exec.ScriptFile)
|
||||||
<< '\n';
|
<< suffix;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "cmGeneratorTarget.h"
|
#include "cmGeneratorTarget.h"
|
||||||
#include "cmGlobalCommonGenerator.h"
|
#include "cmGlobalCommonGenerator.h"
|
||||||
#include "cmGlobalFastbuildGenerator.h"
|
#include "cmGlobalFastbuildGenerator.h"
|
||||||
|
#include "cmInstrumentation.h"
|
||||||
|
#include "cmInstrumentationQuery.h"
|
||||||
#include "cmList.h"
|
#include "cmList.h"
|
||||||
#include "cmListFileCache.h"
|
#include "cmListFileCache.h"
|
||||||
#include "cmLocalCommonGenerator.h"
|
#include "cmLocalCommonGenerator.h"
|
||||||
@@ -38,6 +40,7 @@
|
|||||||
#include "cmTarget.h"
|
#include "cmTarget.h"
|
||||||
#include "cmTargetTypes.h"
|
#include "cmTargetTypes.h"
|
||||||
#include "cmValue.h"
|
#include "cmValue.h"
|
||||||
|
#include "cmake.h"
|
||||||
|
|
||||||
#define FASTBUILD_DOLLAR_TAG "FASTBUILD_DOLLAR_TAG"
|
#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% "
|
"echo Batch file failed at line %FAIL_LINE% "
|
||||||
"with errorcode %ERRORLEVEL%\n"
|
"with errorcode %ERRORLEVEL%\n"
|
||||||
"exit /b %ERROR_CODE%";
|
"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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
#include "cmGeneratorTarget.h"
|
#include "cmGeneratorTarget.h"
|
||||||
#include "cmGlobalGenerator.h"
|
#include "cmGlobalGenerator.h"
|
||||||
#include "cmGlobalNinjaGenerator.h"
|
#include "cmGlobalNinjaGenerator.h"
|
||||||
|
#include "cmInstrumentation.h"
|
||||||
|
#include "cmInstrumentationQuery.h"
|
||||||
#include "cmList.h"
|
#include "cmList.h"
|
||||||
#include "cmListFileCache.h"
|
#include "cmListFileCache.h"
|
||||||
#include "cmLocalGenerator.h"
|
#include "cmLocalGenerator.h"
|
||||||
@@ -476,6 +478,13 @@ std::string cmLocalNinjaGenerator::WriteCommandScript(
|
|||||||
"echo Batch file failed at line %FAIL_LINE% "
|
"echo Batch file failed at line %FAIL_LINE% "
|
||||||
"with errorcode %ERRORLEVEL%\n"
|
"with errorcode %ERRORLEVEL%\n"
|
||||||
"exit /b %ERROR_CODE%";
|
"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
|
#endif
|
||||||
|
|
||||||
return scriptPath;
|
return scriptPath;
|
||||||
@@ -567,6 +576,14 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
|
|||||||
}
|
}
|
||||||
cmd << *li;
|
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
|
#endif
|
||||||
return cmd.str();
|
return cmd.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "cmGlobalGenerator.h"
|
#include "cmGlobalGenerator.h"
|
||||||
#include "cmGlobalUnixMakefileGenerator3.h"
|
#include "cmGlobalUnixMakefileGenerator3.h"
|
||||||
#include "cmInstrumentation.h"
|
#include "cmInstrumentation.h"
|
||||||
|
#include "cmInstrumentationQuery.h"
|
||||||
#include "cmList.h"
|
#include "cmList.h"
|
||||||
#include "cmListFileCache.h"
|
#include "cmListFileCache.h"
|
||||||
#include "cmLocalGenerator.h"
|
#include "cmLocalGenerator.h"
|
||||||
@@ -631,7 +632,24 @@ void cmLocalUnixMakefileGenerator3::WriteMakeRule(
|
|||||||
|
|
||||||
if (!commands.empty()) {
|
if (!commands.empty()) {
|
||||||
// Write the list of commands.
|
// 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()) {
|
if (symbolic && !this->IsWatcomWMake()) {
|
||||||
os << ".PHONY : " << tgt << '\n';
|
os << ".PHONY : " << tgt << '\n';
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ if (NOT snippets)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(FOUND_SNIPPETS "")
|
set(FOUND_SNIPPETS "")
|
||||||
|
unset(process_metrics_chain_head_rss)
|
||||||
|
unset(process_metrics_chain_tail_rss)
|
||||||
foreach(snippet IN LISTS snippets)
|
foreach(snippet IN LISTS snippets)
|
||||||
get_filename_component(filename "${snippet}" NAME)
|
get_filename_component(filename "${snippet}" NAME)
|
||||||
|
|
||||||
@@ -42,7 +44,13 @@ foreach(snippet IN LISTS snippets)
|
|||||||
if (filename MATCHES "^custom-")
|
if (filename MATCHES "^custom-")
|
||||||
string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs)
|
string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs)
|
||||||
if (outputs MATCHES "process_metrics_custom\.stamp")
|
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")
|
elseif (outputs MATCHES "process_metrics_cpu_custom\.stamp")
|
||||||
set(process_metrics_case "cpu")
|
set(process_metrics_case "cpu")
|
||||||
endif()
|
endif()
|
||||||
@@ -254,6 +262,16 @@ foreach(snippet IN LISTS snippets)
|
|||||||
|
|
||||||
endforeach()
|
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
|
# Verify that listed snippets match expected roles
|
||||||
set(EXPECTED_SNIPPETS configure generate)
|
set(EXPECTED_SNIPPETS configure generate)
|
||||||
if (ARGS_BUILD OR ARGS_BUILD_MAKE_PROGRAM)
|
if (ARGS_BUILD OR ARGS_BUILD_MAKE_PROGRAM)
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ if (PROCESS_METRICS_QUERY)
|
|||||||
COMMAND ${Python_EXECUTABLE}
|
COMMAND ${Python_EXECUTABLE}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/process_metrics_memory.py
|
${CMAKE_CURRENT_LIST_DIR}/process_metrics_memory.py
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/process_metrics_custom.stamp
|
${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
|
VERBATIM
|
||||||
)
|
)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|||||||
Reference in New Issue
Block a user