Skip to content

Commit

Permalink
[Driver] Add option -fproc-stat-report
Browse files Browse the repository at this point in the history
The new option `-fproc-stat-info=<file>` can be used to generate report
about used memory and execution tile of each stage of compilation.
Documentation for this option can be found in `UserManual.rst`. The
option can be used in parallel builds.

Differential Revision: https://reviews.llvm.org/D78903
  • Loading branch information
spavloff committed Nov 13, 2020
1 parent 98f70e9 commit 92d7a84
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 4 deletions.
45 changes: 45 additions & 0 deletions clang/docs/UsersManual.rst
Expand Up @@ -747,6 +747,51 @@ Current limitations
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 this option without any 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-855a8e.o, total=68.000 ms, user=60.000 ms, mem=86920 Kb
ld: output=a.out, total=8.000 ms, user=4.000 ms, mem=52320 Kb
The report file specified in the option is locked for write, so this option
can be used to collect statistics in parallel builds. The report file is not
cleared, new data is appended to it, thus making posible to accumulate build
statistics.
Other Options
-------------
Clang options that don't fit neatly into other categories.
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Driver/Job.h
Expand Up @@ -140,6 +140,9 @@ class Command {
/// See Command::setEnvironment
std::vector<const char *> Environment;

/// Information on executable run provided by OS.
mutable Optional<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,
Expand Down Expand Up @@ -212,6 +215,10 @@ class Command {
return OutputFilenames;
}

Optional<llvm::sys::ProcessStatistics> getProcessStatistics() const {
return ProcStat;
}

protected:
/// Optionally print the filenames to be compiled
void PrintFileNames() const;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -1964,6 +1964,10 @@ can be analyzed with chrome://tracing or `Speedscope App
def ftime_trace_granularity_EQ : Joined<["-"], "ftime-trace-granularity=">, Group<f_Group>,
HelpText<"Minimum time granularity (in microseconds) traced by time profiler">,
Flags<[CC1Option, CoreOption]>;
def fproc_stat_report : Joined<["-"], "fproc-stat-report">, Group<f_Group>,
HelpText<"Print subprocess statistics">;
def fproc_stat_report_EQ : Joined<["-"], "fproc-stat-report=">, Group<f_Group>,
HelpText<"Save subprocess statistics to the given file">;
def ftlsmodel_EQ : Joined<["-"], "ftls-model=">, Group<f_Group>, Flags<[CC1Option]>;
def ftrapv : Flag<["-"], "ftrapv">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Trap on integer overflow">;
Expand Down
61 changes: 59 additions & 2 deletions clang/lib/Driver/Driver.cpp
Expand Up @@ -3926,11 +3926,68 @@ void Driver::BuildJobs(Compilation &C) const {
/*TargetDeviceOffloadKind*/ Action::OFK_None);
}

// If we have more than one job, then disable integrated-cc1 for now.
if (C.getJobs().size() > 1)
StringRef StatReportFile;
bool PrintProcessStat = false;
if (const Arg *A = C.getArgs().getLastArg(options::OPT_fproc_stat_report_EQ))
StatReportFile = A->getValue();
if (C.getArgs().hasArg(options::OPT_fproc_stat_report))
PrintProcessStat = true;

// 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() || PrintProcessStat)
for (auto &J : C.getJobs())
J.InProcess = false;

if (!StatReportFile.empty() || PrintProcessStat) {
C.setPostCallback([=](const Command &Cmd, int Res) {
Optional<llvm::sys::ProcessStatistics> ProcStat =
Cmd.getProcessStatistics();
if (!ProcStat)
return;
if (PrintProcessStat) {
using namespace llvm;
// Human readable output.
outs() << sys::path::filename(Cmd.getExecutable()) << ": "
<< "output=";
if (Cmd.getOutputFilenames().empty())
outs() << "\"\"";
else
outs() << Cmd.getOutputFilenames().front();
outs() << ", total="
<< format("%.3f", ProcStat->TotalTime.count() / 1000.) << " ms"
<< ", user="
<< format("%.3f", ProcStat->UserTime.count() / 1000.) << " ms"
<< ", mem=" << ProcStat->PeakMemory << " Kb\n";
}
if (!StatReportFile.empty()) {
// CSV format.
std::string Buffer;
llvm::raw_string_ostream Out(Buffer);
Out << llvm::sys::path::filename(Cmd.getExecutable()) << ',';
if (Cmd.getOutputFilenames().empty())
Out << "\"\"";
else
llvm::sys::printArg(Out, Cmd.getOutputFilenames().front(), true);
Out << ',' << ProcStat->TotalTime.count() << ','
<< ProcStat->UserTime.count() << ',' << ProcStat->PeakMemory
<< '\n';
Out.flush();
std::error_code EC;
llvm::raw_fd_ostream OS(StatReportFile, EC, llvm::sys::fs::OF_Append);
if (EC)
return;
auto L = OS.lock();
if (!L) {
llvm::errs() << "ERROR: Cannot lock file " << StatReportFile << ": "
<< toString(L.takeError()) << "\n";
return;
}
OS << Buffer;
}
});
}

// If the user passed -Qunused-arguments or there were errors, don't warn
// about any unused arguments.
if (Diags.hasErrorOccurred() ||
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Driver/Job.cpp
Expand Up @@ -352,8 +352,8 @@ int Command::Execute(ArrayRef<llvm::Optional<StringRef>> Redirects,

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,
Expand Down
6 changes: 6 additions & 0 deletions 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.]+}} ms, user={{[0-9.]+}} ms, mem={{[0-9]+}} Kb

// 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]+}}

0 comments on commit 92d7a84

Please sign in to comment.