mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-07 22:59:56 -05:00
Merge topic 'profiling'
9aa4640792 cmake: add command line options to output script profiling data
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Ben Boeckel <ben.boeckel@kitware.com>
Acked-by: Pavel Solodovnikov <hellyeahdominate@gmail.com>
Acked-by: Leonid Pospelov <pospelovlm@yandex.ru>
Acked-by: Cristian Adam <cristian.adam@gmail.com>
Merge-request: !2760
This commit is contained in:
@@ -354,6 +354,7 @@ set(SRCS
|
||||
cmMakefileTargetGenerator.cxx
|
||||
cmMakefileExecutableTargetGenerator.cxx
|
||||
cmMakefileLibraryTargetGenerator.cxx
|
||||
cmMakefileProfilingData.cxx
|
||||
cmMakefileUtilityTargetGenerator.cxx
|
||||
cmMessageType.h
|
||||
cmMessenger.cxx
|
||||
|
||||
+14
-2
@@ -60,6 +60,7 @@
|
||||
#include "cmake.h"
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
# include "cmMakefileProfilingData.h"
|
||||
# include "cmVariableWatch.h"
|
||||
#endif
|
||||
|
||||
@@ -372,19 +373,30 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const
|
||||
class cmMakefileCall
|
||||
{
|
||||
public:
|
||||
cmMakefileCall(cmMakefile* mf, cmCommandContext const& cc,
|
||||
cmMakefileCall(cmMakefile* mf, cmListFileFunction const& lff,
|
||||
cmExecutionStatus& status)
|
||||
: Makefile(mf)
|
||||
{
|
||||
cmListFileContext const& lfc = cmListFileContext::FromCommandContext(
|
||||
cc, this->Makefile->StateSnapshot.GetExecutionListFile());
|
||||
lff, this->Makefile->StateSnapshot.GetExecutionListFile());
|
||||
this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc);
|
||||
++this->Makefile->RecursionDepth;
|
||||
this->Makefile->ExecutionStatusStack.push_back(&status);
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
if (this->Makefile->GetCMakeInstance()->IsProfilingEnabled()) {
|
||||
this->Makefile->GetCMakeInstance()->GetProfilingOutput().StartEntry(lff,
|
||||
lfc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
~cmMakefileCall()
|
||||
{
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
if (this->Makefile->GetCMakeInstance()->IsProfilingEnabled()) {
|
||||
this->Makefile->GetCMakeInstance()->GetProfilingOutput().StopEntry();
|
||||
}
|
||||
#endif
|
||||
this->Makefile->ExecutionStatusStack.pop_back();
|
||||
--this->Makefile->RecursionDepth;
|
||||
this->Makefile->Backtrace = this->Makefile->Backtrace.Pop();
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmMakefileProfilingData.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/SystemInformation.hxx"
|
||||
|
||||
#include "cm_jsoncpp_value.h"
|
||||
#include "cm_jsoncpp_writer.h"
|
||||
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
cmMakefileProfilingData::cmMakefileProfilingData(
|
||||
const std::string& profileStream)
|
||||
{
|
||||
std::ios::openmode omode = std::ios::out | std::ios::trunc;
|
||||
this->ProfileStream.open(profileStream.c_str(), omode);
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
this->JsonWriter =
|
||||
std::unique_ptr<Json::StreamWriter>(wbuilder.newStreamWriter());
|
||||
if (!this->ProfileStream.good()) {
|
||||
throw std::runtime_error(std::string("Unable to open: ") + profileStream);
|
||||
}
|
||||
|
||||
this->ProfileStream << "[";
|
||||
};
|
||||
|
||||
cmMakefileProfilingData::~cmMakefileProfilingData() noexcept
|
||||
{
|
||||
if (this->ProfileStream.good()) {
|
||||
try {
|
||||
this->ProfileStream << "]";
|
||||
this->ProfileStream.close();
|
||||
} catch (...) {
|
||||
cmSystemTools::Error("Error writing profiling output!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmMakefileProfilingData::StartEntry(const cmListFileFunction& lff,
|
||||
cmListFileContext const& lfc)
|
||||
{
|
||||
/* Do not try again if we previously failed to write to output. */
|
||||
if (!this->ProfileStream.good()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this->ProfileStream.tellp() > 1) {
|
||||
this->ProfileStream << ",";
|
||||
}
|
||||
cmsys::SystemInformation info;
|
||||
Json::Value v;
|
||||
v["ph"] = "B";
|
||||
v["name"] = lff.Name.Original;
|
||||
v["cat"] = "cmake";
|
||||
v["ts"] = uint64_t(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count());
|
||||
v["pid"] = static_cast<int>(info.GetProcessId());
|
||||
v["tid"] = 0;
|
||||
Json::Value argsValue;
|
||||
if (!lff.Arguments.empty()) {
|
||||
std::string args;
|
||||
for (const auto& a : lff.Arguments) {
|
||||
args += (args.empty() ? "" : " ") + a.Value;
|
||||
}
|
||||
argsValue["functionArgs"] = args;
|
||||
}
|
||||
argsValue["location"] = lfc.FilePath + ":" + std::to_string(lfc.Line);
|
||||
v["args"] = argsValue;
|
||||
|
||||
this->JsonWriter->write(v, &this->ProfileStream);
|
||||
} catch (std::ios_base::failure& fail) {
|
||||
cmSystemTools::Error(
|
||||
cmStrCat("Failed to write to profiling output: ", fail.what()));
|
||||
} catch (...) {
|
||||
cmSystemTools::Error("Error writing profiling output!");
|
||||
}
|
||||
}
|
||||
|
||||
void cmMakefileProfilingData::StopEntry()
|
||||
{
|
||||
/* Do not try again if we previously failed to write to output. */
|
||||
if (!this->ProfileStream.good()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this->ProfileStream << ",";
|
||||
cmsys::SystemInformation info;
|
||||
Json::Value v;
|
||||
v["ph"] = "E";
|
||||
v["ts"] = uint64_t(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count());
|
||||
v["pid"] = static_cast<int>(info.GetProcessId());
|
||||
v["tid"] = 0;
|
||||
this->JsonWriter->write(v, &this->ProfileStream);
|
||||
} catch (std::ios_base::failure& fail) {
|
||||
cmSystemTools::Error(
|
||||
cmStrCat("Failed to write to profiling output:", fail.what()));
|
||||
} catch (...) {
|
||||
cmSystemTools::Error("Error writing profiling output!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cmMakefileProfilingData_h
|
||||
#define cmMakefileProfilingData_h
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
|
||||
namespace Json {
|
||||
class StreamWriter;
|
||||
}
|
||||
|
||||
class cmListFileContext;
|
||||
struct cmListFileFunction;
|
||||
|
||||
class cmMakefileProfilingData
|
||||
{
|
||||
public:
|
||||
cmMakefileProfilingData(const std::string&);
|
||||
~cmMakefileProfilingData() noexcept;
|
||||
void StartEntry(const cmListFileFunction& lff, cmListFileContext const& lfc);
|
||||
void StopEntry();
|
||||
|
||||
private:
|
||||
cmsys::ofstream ProfileStream;
|
||||
std::unique_ptr<Json::StreamWriter> JsonWriter;
|
||||
};
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/memory>
|
||||
@@ -39,6 +40,9 @@
|
||||
#include "cmLinkLineComputer.h"
|
||||
#include "cmLocalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
# include "cmMakefileProfilingData.h"
|
||||
#endif
|
||||
#include "cmMessenger.h"
|
||||
#include "cmState.h"
|
||||
#include "cmStateDirectory.h"
|
||||
@@ -613,6 +617,10 @@ void cmake::SetArgs(const std::vector<std::string>& args)
|
||||
{
|
||||
bool haveToolset = false;
|
||||
bool havePlatform = false;
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
std::string profilingFormat;
|
||||
std::string profilingOutput;
|
||||
#endif
|
||||
for (unsigned int i = 1; i < args.size(); ++i) {
|
||||
std::string const& arg = args[i];
|
||||
if (arg.find("-H", 0) == 0 || arg.find("-S", 0) == 0) {
|
||||
@@ -839,6 +847,20 @@ void cmake::SetArgs(const std::vector<std::string>& args)
|
||||
return;
|
||||
}
|
||||
this->SetGlobalGenerator(std::move(gen));
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
} else if (arg.find("--profiling-format", 0) == 0) {
|
||||
profilingFormat = arg.substr(strlen("--profiling-format="));
|
||||
if (profilingFormat.empty()) {
|
||||
cmSystemTools::Error("No format specified for --profiling-format");
|
||||
}
|
||||
} else if (arg.find("--profiling-output", 0) == 0) {
|
||||
profilingOutput = arg.substr(strlen("--profiling-output="));
|
||||
profilingOutput = cmSystemTools::CollapseFullPath(profilingOutput);
|
||||
cmSystemTools::ConvertToUnixSlashes(profilingOutput);
|
||||
if (profilingOutput.empty()) {
|
||||
cmSystemTools::Error("No path specified for --profiling-output");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// no option assume it is the path to the source or an existing build
|
||||
else {
|
||||
@@ -856,6 +878,29 @@ void cmake::SetArgs(const std::vector<std::string>& args)
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
if (!profilingOutput.empty() || !profilingFormat.empty()) {
|
||||
if (profilingOutput.empty()) {
|
||||
cmSystemTools::Error(
|
||||
"--profiling-format specified but no --profiling-output!");
|
||||
return;
|
||||
}
|
||||
if (profilingFormat == "google-trace") {
|
||||
try {
|
||||
this->ProfilingOutput =
|
||||
cm::make_unique<cmMakefileProfilingData>(profilingOutput);
|
||||
} catch (std::runtime_error& e) {
|
||||
cmSystemTools::Error(
|
||||
cmStrCat("Could not start profiling: ", e.what()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
cmSystemTools::Error("Invalid format specified for --profiling-format");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const bool haveSourceDir = !this->GetHomeDirectory().empty();
|
||||
const bool haveBinaryDir = !this->GetHomeOutputDirectory().empty();
|
||||
|
||||
@@ -2952,3 +2997,15 @@ void cmake::SetDeprecatedWarningsAsErrors(bool b)
|
||||
" and functions.",
|
||||
cmStateEnums::INTERNAL);
|
||||
}
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
cmMakefileProfilingData& cmake::GetProfilingOutput()
|
||||
{
|
||||
return *(this->ProfilingOutput);
|
||||
}
|
||||
|
||||
bool cmake::IsProfilingEnabled() const
|
||||
{
|
||||
return static_cast<bool>(this->ProfilingOutput);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,9 @@ class cmFileTimeCache;
|
||||
class cmGlobalGenerator;
|
||||
class cmGlobalGeneratorFactory;
|
||||
class cmMakefile;
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
class cmMakefileProfilingData;
|
||||
#endif
|
||||
class cmMessenger;
|
||||
class cmVariableWatch;
|
||||
struct cmDocumentationEntry;
|
||||
@@ -549,6 +552,11 @@ public:
|
||||
|
||||
bool GetRegenerateDuringBuild() const { return this->RegenerateDuringBuild; }
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
cmMakefileProfilingData& GetProfilingOutput();
|
||||
bool IsProfilingEnabled() const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void RunCheckForUnusedVariables();
|
||||
int HandleDeleteCacheVariables(const std::string& var);
|
||||
@@ -657,6 +665,10 @@ private:
|
||||
|
||||
void AppendGlobalGeneratorsDocumentation(std::vector<cmDocumentationEntry>&);
|
||||
void AppendExtraGeneratorsDocumentation(std::vector<cmDocumentationEntry>&);
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
std::unique_ptr<cmMakefileProfilingData> ProfilingOutput;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define CMAKE_STANDARD_OPTIONS_TABLE \
|
||||
|
||||
@@ -93,6 +93,12 @@ const char* cmDocumentationOptions[][2] = {
|
||||
{ "--check-system-vars",
|
||||
"Find problems with variable usage in system "
|
||||
"files." },
|
||||
# if !defined(CMAKE_BOOTSTRAP)
|
||||
{ "--profiling-format=<fmt>", "Output data for profiling CMake scripts." },
|
||||
{ "--profiling-output=<file>",
|
||||
"Select an output path for the profiling data enabled through "
|
||||
"--profiling-format." },
|
||||
# endif
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user