diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -747,6 +747,49 @@ translated from debug annotations. That translation can be lossy, which results in some remarks having no location information. +Options to Emit Resource Consumption Reports +-------------------------------------------- + +These are options that report execution time and consumed memory of different +compilations steps. + +.. option:: -fproc-stat-report= + + This option requests driver to print used memory and execution time of each + compilation step. The ``clang`` driver during execution calls different tools, + like compiler, assembler, linker etc. With this option the driver reports + total execution time, the execution time spent in user mode and peak memory + usage of each the called tool. Value of the option specifies where the report + is sent to. If it specifies a regular file, the data are saved to this file in + CSV format: + +.. code-block:: console + + $ clang -fproc-stat-report=abc foo.c + $ cat abc + clang-11,"/tmp/foo-123456.o",92000,84000,87536 + ld,"a.out",900,8000,53568 + + The data on each row represent: + + * file name of the tool executable, + * output file name in quotes, + * total execution time in microseconds, + * execution time in user mode in microseconds, + * peak memory usage in Kb. + + It is possible to specify ``-`` for the option value. In this case statistics + is printed on standard output in human readable format: + +.. code-block:: console + + $ clang -fproc-stat-report=- foo.c + clang-11: output=/tmp/foo-123456.o, total=84000, user=76000, mem=87496 + ld: output=a.out, total=8000, user=8000, mem=53548 + + The report file specified in the option is locked for write, so this option + can be used to collect statistics in parallel builds. + Other Options ------------- Clang options that don't fit neatly into other categories. diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/Option/Option.h" +#include "llvm/Support/Program.h" #include #include #include @@ -73,6 +74,9 @@ /// See Command::setEnvironment std::vector Environment; + /// Information on executable run provided by OS. + mutable llvm::sys::ProcessStatistics ProcStat; + /// When a response file is needed, we try to put most arguments in an /// exclusive file, while others remains as regular command line arguments. /// This functions fills a vector with the regular command line arguments, @@ -139,6 +143,10 @@ return OutputFilenames; } + const llvm::sys::ProcessStatistics &getProcessStatistics() const { + return ProcStat; + } + /// Print a command argument, and optionally quote it. static void printArg(llvm::raw_ostream &OS, StringRef Arg, bool Quote); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1909,6 +1909,8 @@ def ftime_trace_granularity_EQ : Joined<["-"], "ftime-trace-granularity=">, Group, HelpText<"Minimum time granularity (in microseconds) traced by time profiler">, Flags<[CC1Option, CoreOption]>; +def fproc_stat_report_EQ : Joined<["-"], "fproc-stat-report=">, Group, + Flags<[DriverOption]>, HelpText<"Save subprocess statistics to the given file">; def ftlsmodel_EQ : Joined<["-"], "ftls-model=">, Group, Flags<[CC1Option]>; def ftrapv : Flag<["-"], "ftrapv">, Group, Flags<[CC1Option]>, HelpText<"Trap on integer overflow">; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -3757,11 +3757,54 @@ /*TargetDeviceOffloadKind*/ Action::OFK_None); } - // If we have more than one job, then disable integrated-cc1 for now. - if (C.getJobs().size() > 1) + StringRef StatReportFile; + if (const Arg *A = C.getArgs().getLastArg(options::OPT_fproc_stat_report_EQ)) + StatReportFile = A->getValue(); + + // If we have more than one job, then disable integrated-cc1 for now. Do this + // also when we need to report process execution statistics. + if (C.getJobs().size() > 1 || !StatReportFile.empty()) for (auto &J : C.getJobs()) J.InProcess = false; + if (!StatReportFile.empty()) + C.setPostCallback([=](const Command &Cmd, int Res) { + const llvm::sys::ProcessStatistics &ProcStat = Cmd.getProcessStatistics(); + if (ProcStat.isSet()) { + if (StatReportFile.equals("-")) { + // Human readable output. + llvm::outs() << llvm::sys::path::filename(Cmd.getExecutable()) << ": " + << "output="; + if (Cmd.getOutputFilenames().empty()) + llvm::outs() << "\"\""; + else + llvm::outs() << Cmd.getOutputFilenames().front(); + llvm::outs() + << ", total=" << Cmd.getProcessStatistics().TotalTime.count() + << ", user=" << Cmd.getProcessStatistics().UserTime.count() + << ", mem=" << Cmd.getProcessStatistics().PeakMemory << "\n"; + } else { + // CSV format. + std::string Buffer; + llvm::raw_string_ostream Out(Buffer); + Out << llvm::sys::path::filename(Cmd.getExecutable()) << ','; + if (Cmd.getOutputFilenames().empty()) + Out << "\"\""; + else + Command::printArg(Out, Cmd.getOutputFilenames().front(), true); + Out << ',' + << Cmd.getProcessStatistics().TotalTime.count() << ',' + << Cmd.getProcessStatistics().UserTime.count() << ',' + << Cmd.getProcessStatistics().PeakMemory << '\n'; + Out.flush(); + std::error_code EC; + llvm::raw_fd_ostream OS(StatReportFile, EC, llvm::sys::fs::OF_Append); + if (!EC) + OS << Buffer; + } + } + }); + // If the user passed -Qunused-arguments or there were errors, don't warn // about any unused arguments. if (Diags.hasErrorOccurred() || diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -370,8 +370,8 @@ auto Args = llvm::toStringRefArray(Argv.data()); return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects, - /*secondsToWait*/ 0, - /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); + /*secondsToWait*/ 0, /*memoryLimit*/ 0, ErrMsg, ExecutionFailed, + &ProcStat); } CC1Command::CC1Command(const Action &Source, const Tool &Creator, diff --git a/clang/test/Driver/report-stat.c b/clang/test/Driver/report-stat.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/report-stat.c @@ -0,0 +1,6 @@ +// RUN: %clang -c -fproc-stat-report=- %s | FileCheck %s +// CHECK: clang{{.*}} output={{.*}}.o, total={{[0-9]+}}, user={{[0-9]+}}, mem={{[0-9]+}} + +// RUN: %clang -c -fproc-stat-report=%t %s +// RUN: cat %t | FileCheck --check-prefix=CSV %s +// CSV: clang{{.*}},"{{.*}}.o",{{[0-9]+}},{{[0-9]+}},{{[0-9]+}}