lebedev.ri created this revision.
lebedev.ri added reviewers: alexfh, sbenza.
Herald added subscribers: mgrang, xazax.hun.

Continuation of https://reviews.llvm.org/D46504.

Example output:

  $ clang-tidy -enable-check-profile -store-check-profile=. 
-store-check-profile-elide-prefix=. -checks=-*,readability-function-size 
source.cpp 2>&1 >/dev/null
  $ cat .csv
  "User Time","System Time","User+System","Wall Time","Name"
  
8.8420499999973945e-01,1.2026799999999757e-01,1.0044729999997370e+00,1.0031676292419434e+00,"readability-function-size"
  
8.8420499999973945e-01,1.2026799999999757e-01,1.0044729999997370e+00,1.0031676292419434e+00,"Total"

There are two arguments that control profile storage:

- `-store-check-profile=<prefix>` This option controls the prefix where these 
per-TU profiles are stored as CSV. If the prefix is not an absolute path, it is 
considered to be relative to the directory from where you have run 
`clang-tidy`. All `.` and `..` patterns in the path are collapsed, and symlinks 
are resolved.

  **Example**: Let's suppose you have a source file named `example.cpp`, 
located in `/source` directory.
  - If you specify `-store-check-profile=/tmp`, then the profile will be saved 
to `/tmp/source/example.cpp.csv`
  - If you run `clang-tidy` from within `/foo` directory, and specify 
`-store-check-profile=.`, then the profile will still be saved to 
`/foo/source/example.cpp.csv`
- `-store-check-profile-elide-prefix=<prefix>` When specified, this prefix will 
be elided from the source file name, before prepending it with the prefix 
specified by `-store-check-profile`. If the prefix is not an absolute path, it 
is considered to be relative to the directory from where you have run 
`clang-tidy`. All `.` and `..` patterns in the path are collapsed, and symlinks 
are resolved.

  **Example**: Let's suppose you have a source file named `example.cpp`, 
located in `/source` directory.
  - If you specify `-store-check-profile=/tmp 
-store-check-profile-elide-prefix=/source` , then the profile will be saved to 
`/tmp/example.cpp.csv`
  - If you run `clang-tidy` from within `/source` directory, and specify 
`-store-check-profile=/foo -store-check-profile-elide-prefix=.`, then the 
profile will be saved to `/foo/example.cpp.csv`


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D46602

Files:
  clang-tidy/ClangTidy.cpp
  clang-tidy/ClangTidy.h
  clang-tidy/ClangTidyDiagnosticConsumer.cpp
  clang-tidy/ClangTidyDiagnosticConsumer.h
  clang-tidy/ClangTidyProfiling.cpp
  clang-tidy/ClangTidyProfiling.h
  clang-tidy/tool/ClangTidyMain.cpp
  docs/ReleaseNotes.rst
  docs/clang-tidy/index.rst
  test/clang-tidy/clang-tidy-store-check-profile-one-tu.cpp

Index: test/clang-tidy/clang-tidy-store-check-profile-one-tu.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/clang-tidy-store-check-profile-one-tu.cpp
@@ -0,0 +1,17 @@
+// RUN: clang-tidy -enable-check-profile -checks='-*,readability-function-size' -store-check-profile=%T -store-check-profile-elide-prefix=%s %s 2>&1 | FileCheck --match-full-lines -implicit-check-not='{{warning:|error:}}' -check-prefix=CHECK-CONSOLE %s
+// RUN: FileCheck --match-full-lines -implicit-check-not='{{warning:|error:}}' -input-file=%T/.csv -check-prefix=CHECK-FILE %s
+
+// CHECK-CONSOLE: ===-------------------------------------------------------------------------===
+// CHECK-CONSOLE-NEXT: {{.*}}  --- Name ---
+// CHECK-CONSOLE-NEXT: {{.*}}  readability-function-size
+// CHECK-CONSOLE-NEXT: {{.*}}  Total
+// CHECK-CONSOLE-NEXT: ===-------------------------------------------------------------------------===
+
+// CHECK-FILE: {{.*}}"Wall Time","Name"
+// CHECK-FILE-NEXT: {{.*}}{{[0-9]}}.{{[0-9]+}}e{{[-+]}}{{[0-9]}}{{[0-9]}},"readability-function-size"
+// CHECK-FILE-NEXT: {{.*}}{{[0-9]}}.{{[0-9]+}}e{{[-+]}}{{[0-9]}}{{[0-9]}},"Total"
+
+class A {
+  A() {}
+  ~A() {}
+};
Index: docs/clang-tidy/index.rst
===================================================================
--- docs/clang-tidy/index.rst
+++ docs/clang-tidy/index.rst
@@ -99,114 +99,127 @@
 
 .. code-block:: console
 
-  $ clang-tidy -help
+  $ clang-tidy --help
   USAGE: clang-tidy [options] <source0> [... <sourceN>]
 
   OPTIONS:
 
   Generic Options:
 
-    -help                        - Display available options (-help-hidden for more)
-    -help-list                   - Display list of available options (-help-list-hidden for more)
-    -version                     - Display the version of this program
+    -help                                      - Display available options (-help-hidden for more)
+    -help-list                                 - Display list of available options (-help-list-hidden for more)
+    -version                                   - Display the version of this program
 
   clang-tidy options:
 
-    -checks=<string>             -
-                                   Comma-separated list of globs with optional '-'
-                                   prefix. Globs are processed in order of
-                                   appearance in the list. Globs without '-'
-                                   prefix add checks with matching names to the
-                                   set, globs with the '-' prefix remove checks
-                                   with matching names from the set of enabled
-                                   checks. This option's value is appended to the
-                                   value of the 'Checks' option in .clang-tidy
-                                   file, if any.
-    -config=<string>             -
-                                   Specifies a configuration in YAML/JSON format:
-                                     -config="{Checks: '*',
-                                               CheckOptions: [{key: x,
-                                                               value: y}]}"
-                                   When the value is empty, clang-tidy will
-                                   attempt to find a file named .clang-tidy for
-                                   each source file in its parent directories.
-    -dump-config                 -
-                                   Dumps configuration in the YAML format to
-                                   stdout. This option can be used along with a
-                                   file name (and '--' if the file is outside of a
-                                   project with configured compilation database).
-                                   The configuration used for this file will be
-                                   printed.
-                                   Use along with -checks=* to include
-                                   configuration of all checks.
-    -enable-check-profile        -
-                                   Enable per-check timing profiles, and print a
-                                   report to stderr.
-    -explain-config              -
-                                   For each enabled check explains, where it is
-                                   enabled, i.e. in clang-tidy binary, command
-                                   line or a specific configuration file.
-    -export-fixes=<filename>     -
-                                   YAML file to store suggested fixes in. The
-                                   stored fixes can be applied to the input source
-                                   code with clang-apply-replacements.
-    -extra-arg=<string>          - Additional argument to append to the compiler command line
-    -extra-arg-before=<string>   - Additional argument to prepend to the compiler command line
-    -fix                         -
-                                   Apply suggested fixes. Without -fix-errors
-                                   clang-tidy will bail out if any compilation
-                                   errors were found.
-    -fix-errors                  -
-                                   Apply suggested fixes even if compilation
-                                   errors were found. If compiler errors have
-                                   attached fix-its, clang-tidy will apply them as
-                                   well.
-    -format-style=<string>       -
-                                   Style for formatting code around applied fixes:
-                                     - 'none' (default) turns off formatting
-                                     - 'file' (literally 'file', not a placeholder)
-                                       uses .clang-format file in the closest parent
-                                       directory
-                                     - '{ <json> }' specifies options inline, e.g.
-                                       -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
-                                     - 'llvm', 'google', 'webkit', 'mozilla'
-                                   See clang-format documentation for the up-to-date
-                                   information about formatting styles and options.
-                                   This option overrides the 'FormatStyle` option in
-                                   .clang-tidy file, if any.
-    -header-filter=<string>      -
-                                   Regular expression matching the names of the
-                                   headers to output diagnostics from. Diagnostics
-                                   from the main file of each translation unit are
-                                   always displayed.
-                                   Can be used together with -line-filter.
-                                   This option overrides the 'HeaderFilter' option
-                                   in .clang-tidy file, if any.
-    -line-filter=<string>        -
-                                   List of files with line ranges to filter the
-                                   warnings. Can be used together with
-                                   -header-filter. The format of the list is a
-                                   JSON array of objects:
-                                     [
-                                       {"name":"file1.cpp","lines":[[1,3],[5,7]]},
-                                       {"name":"file2.h"}
-                                     ]
-    -list-checks                 -
-                                   List all enabled checks and exit. Use with
-                                   -checks=* to list all available checks.
-    -p=<string>                  - Build path
-    -quiet                       -
-                                   Run clang-tidy in quiet mode. This suppresses
-                                   printing statistics about ignored warnings and
-                                   warnings treated as errors if the respective
-                                   options are specified.
-    -system-headers              - Display the errors from system headers.
-    -warnings-as-errors=<string> -
-                                   Upgrades warnings to errors. Same format as
-                                   '-checks'.
-                                   This option's value is appended to the value of
-                                   the 'WarningsAsErrors' option in .clang-tidy
-                                   file, if any.
+    -checks=<string>                           -
+                                                 Comma-separated list of globs with optional '-'
+                                                 prefix. Globs are processed in order of
+                                                 appearance in the list. Globs without '-'
+                                                 prefix add checks with matching names to the
+                                                 set, globs with the '-' prefix remove checks
+                                                 with matching names from the set of enabled
+                                                 checks. This option's value is appended to the
+                                                 value of the 'Checks' option in .clang-tidy
+                                                 file, if any.
+    -config=<string>                           -
+                                                 Specifies a configuration in YAML/JSON format:
+                                                   -config="{Checks: '*',
+                                                             CheckOptions: [{key: x,
+                                                                             value: y}]}"
+                                                 When the value is empty, clang-tidy will
+                                                 attempt to find a file named .clang-tidy for
+                                                 each source file in its parent directories.
+    -dump-config                               -
+                                                 Dumps configuration in the YAML format to
+                                                 stdout. This option can be used along with a
+                                                 file name (and '--' if the file is outside of a
+                                                 project with configured compilation database).
+                                                 The configuration used for this file will be
+                                                 printed.
+                                                 Use along with -checks=* to include
+                                                 configuration of all checks.
+    -enable-check-profile                      -
+                                                 Enable per-check timing profiles, and print a
+                                                 report to stderr.
+    -explain-config                            -
+                                                 For each enabled check explains, where it is
+                                                 enabled, i.e. in clang-tidy binary, command
+                                                 line or a specific configuration file.
+    -export-fixes=<filename>                   -
+                                                 YAML file to store suggested fixes in. The
+                                                 stored fixes can be applied to the input source
+                                                 code with clang-apply-replacements.
+    -extra-arg=<string>                        - Additional argument to append to the compiler command line
+    -extra-arg-before=<string>                 - Additional argument to prepend to the compiler command line
+    -fix                                       -
+                                                 Apply suggested fixes. Without -fix-errors
+                                                 clang-tidy will bail out if any compilation
+                                                 errors were found.
+    -fix-errors                                -
+                                                 Apply suggested fixes even if compilation
+                                                 errors were found. If compiler errors have
+                                                 attached fix-its, clang-tidy will apply them as
+                                                 well.
+    -format-style=<string>                     -
+                                                 Style for formatting code around applied fixes:
+                                                   - 'none' (default) turns off formatting
+                                                   - 'file' (literally 'file', not a placeholder)
+                                                     uses .clang-format file in the closest parent
+                                                     directory
+                                                   - '{ <json> }' specifies options inline, e.g.
+                                                     -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
+                                                   - 'llvm', 'google', 'webkit', 'mozilla'
+                                                 See clang-format documentation for the up-to-date
+                                                 information about formatting styles and options.
+                                                 This option overrides the 'FormatStyle` option in
+                                                 .clang-tidy file, if any.
+    -header-filter=<string>                    -
+                                                 Regular expression matching the names of the
+                                                 headers to output diagnostics from. Diagnostics
+                                                 from the main file of each translation unit are
+                                                 always displayed.
+                                                 Can be used together with -line-filter.
+                                                 This option overrides the 'HeaderFilter' option
+                                                 in .clang-tidy file, if any.
+    -line-filter=<string>                      -
+                                                 List of files with line ranges to filter the
+                                                 warnings. Can be used together with
+                                                 -header-filter. The format of the list is a
+                                                 JSON array of objects:
+                                                   [
+                                                     {"name":"file1.cpp","lines":[[1,3],[5,7]]},
+                                                     {"name":"file2.h"}
+                                                   ]
+    -list-checks                               -
+                                                 List all enabled checks and exit. Use with
+                                                 -checks=* to list all available checks.
+    -p=<string>                                - Build path
+    -quiet                                     -
+                                                 Run clang-tidy in quiet mode. This suppresses
+                                                 printing statistics about ignored warnings and
+                                                 warnings treated as errors if the respective
+                                                 options are specified.
+    -store-check-profile=<prefix>              -
+                                                 When -enable-check-profile option is enabled,
+                                                 this option controls the prefix where these
+                                                 per-TU profiles are stored, as CSV.
+                                                 By default reports are only printed
+                                                 in tabulated format to stderr.
+    -store-check-profile-elide-prefix=<prefix> -
+                                                 When specified, this prefix will be elided
+                                                 from the source file name, before prepending
+                                                 it with the prefix specified by -store-check-profile.
+    -system-headers                            - Display the errors from system headers.
+    -vfsoverlay=<filename>                     -
+                                                 Overlay the virtual filesystem described by file
+                                                 over the real file system.
+    -warnings-as-errors=<string>               -
+                                                 Upgrades warnings to errors. Same format as
+                                                 '-checks'.
+                                                 This option's value is appended to the value of
+                                                 the 'WarningsAsErrors' option in .clang-tidy
+                                                 file, if any.
 
   -p <build-path> is used to read a compile command database.
 
@@ -739,3 +752,69 @@
   all changes in a temporary directory and applies them. Passing ``-format``
   will run clang-format over changed lines.
 
+
+On checks profiling
+-------------------
+
+:program:`clang-tidy` can collect per-check profiling info, and output it
+for each processed source file (translation unit).
+
+To enable profiling info collection, use ``-enable-check-profile`` argument.
+The timings will be outputted to the ``stderr`` as a table. Example output:
+
+.. code-block:: console
+
+  $ clang-tidy -enable-check-profile -checks=-*,readability-function-size source.cpp
+  ===-------------------------------------------------------------------------===
+     ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---
+     0.8842 (100.0%)   0.1203 (100.0%)   1.0045 (100.0%)   1.0032 (100.0%)  readability-function-size
+     0.8842 (100.0%)   0.1203 (100.0%)   1.0045 (100.0%)   1.0032 (100.0%)  Total
+  ===-------------------------------------------------------------------------===
+
+It can also store that data as CSV files for further processing. Example output:
+
+.. code-block:: console
+
+  $ clang-tidy -enable-check-profile -store-check-profile=. -store-check-profile-elide-prefix=. -checks=-*,readability-function-size source.cpp 2>&1 >/dev/null
+  $ cat .csv
+  "User Time","System Time","User+System","Wall Time","Name"
+  8.8420499999973945e-01,1.2026799999999757e-01,1.0044729999997370e+00,1.0031676292419434e+00,"readability-function-size"
+  8.8420499999973945e-01,1.2026799999999757e-01,1.0044729999997370e+00,1.0031676292419434e+00,"Total"
+
+There are two arguments that control profile storage:
+
+* ``-store-check-profile=<prefix>``
+
+  This option controls the prefix where these per-TU profiles are stored as CSV.
+  If the prefix is not an absolute path, it is considered to be relative to the
+  directory from where you have run :program:`clang-tidy`. All ``.`` and ``..``
+  patterns in the path are collapsed, and symlinks are resolved.
+
+  Example:
+  Let's suppose you have a source file named ``example.cpp``, located in
+  ``/source`` directory.
+
+  * If you specify ``-store-check-profile=/tmp``, then the profile will be saved
+    to ``/tmp/source/example.cpp.csv``
+
+  * If you run :program:`clang-tidy` from within ``/foo`` directory, and specify
+    ``-store-check-profile=.``, then the profile will still be saved to
+    ``/foo/source/example.cpp.csv``
+* ``-store-check-profile-elide-prefix=<prefix>``
+
+  When specified, this prefix will be elided from the source file name,
+  before prepending it with the prefix specified by ``-store-check-profile``.
+  If the prefix is not an absolute path, it is considered to be relative to the
+  directory from where you have run :program:`clang-tidy`. All ``.`` and ``..``
+  patterns in the path are collapsed, and symlinks are resolved.
+
+  Example:
+  Let's suppose you have a source file named ``example.cpp``, located in
+  ``/source`` directory.
+
+  * If you specify ``-store-check-profile=/tmp -store-check-profile-elide-prefix=/source``
+    , then the profile will be saved to ``/tmp/example.cpp.csv``
+
+  * If you run :program:`clang-tidy` from within ``/source`` directory, and
+    specify ``-store-check-profile=/foo -store-check-profile-elide-prefix=.``,
+    then the profile will be saved to ``/foo/example.cpp.csv``
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -57,6 +57,8 @@
 Improvements to clang-tidy
 --------------------------
 
+- clang-tidy learned to store checks profiling info as CSV files.
+
 - New module `abseil` for checks related to the `Abseil <https://abseil.io>`_
   library.
 
Index: clang-tidy/tool/ClangTidyMain.cpp
===================================================================
--- clang-tidy/tool/ClangTidyMain.cpp
+++ clang-tidy/tool/ClangTidyMain.cpp
@@ -181,6 +181,24 @@
                                         cl::init(false),
                                         cl::cat(ClangTidyCategory));
 
+static cl::opt<std::string> StoreCheckProfile("store-check-profile",
+                                              cl::desc(R"(
+When -enable-check-profile option is enabled,
+this option controls the prefix where these
+per-TU profiles are stored, as CSV.
+By default reports are only printed
+in tabulated format to stderr.)"),
+                                              cl::value_desc("prefix"),
+                                              cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> StoreCheckProfileElidePrefix(
+    "store-check-profile-elide-prefix", cl::desc(R"(
+When specified, this prefix will be elided
+from the source file name, before prepending
+it with the prefix specified by -store-check-profile.
+)"),
+    cl::value_desc("prefix"), cl::cat(ClangTidyCategory));
+
 static cl::opt<std::string> ExportFixes("export-fixes", cl::desc(R"(
 YAML file to store suggested fixes in. The
 stored fixes can be applied to the input source
@@ -323,6 +341,26 @@
   if (!OptionsProvider)
     return 1;
 
+  auto processPrefix = [](std::string &input, SmallVectorImpl<char> &dest) {
+    dest.clear();
+    if (input.empty())
+      return;
+    SmallString<256> ScratchPath(input);
+    if (std::error_code EC = llvm::sys::fs::make_absolute(ScratchPath)) {
+      llvm::errs() << "Can't make absolute path from " << ScratchPath << ": "
+                   << EC.message() << "\n";
+    }
+    if (std::error_code EC = llvm::sys::fs::real_path(ScratchPath, dest)) {
+      llvm::errs() << "Can't make real path from " << ScratchPath << ": "
+                   << EC.message() << "\n";
+    }
+  };
+
+  SmallString<256> ProfilePrefix;
+  processPrefix(StoreCheckProfile, ProfilePrefix);
+  SmallString<256> ProfileElidePrefix;
+  processPrefix(StoreCheckProfileElidePrefix, ProfileElidePrefix);
+
   StringRef FileName("dummy");
   auto PathList = OptionsParser.getSourcePathList();
   if (!PathList.empty()) {
@@ -392,7 +430,7 @@
 
   ClangTidyContext Context(std::move(OwningOptionsProvider));
   runClangTidy(Context, OptionsParser.getCompilations(), PathList, BaseFS,
-               EnableCheckProfile);
+               EnableCheckProfile, ProfilePrefix, ProfileElidePrefix);
   ArrayRef<ClangTidyError> Errors = Context.getErrors();
   bool FoundErrors = llvm::find_if(Errors, [](const ClangTidyError &E) {
                        return E.DiagLevel == ClangTidyError::Error;
Index: clang-tidy/ClangTidyProfiling.h
===================================================================
--- clang-tidy/ClangTidyProfiling.h
+++ clang-tidy/ClangTidyProfiling.h
@@ -10,28 +10,41 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H
 
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
+#include <string>
 #include <utility>
 #include <vector>
 
 namespace clang {
 namespace tidy {
 
 class ClangTidyProfiling {
+  llvm::Optional<std::string> StoreCSVTo;
+
   // Time is first to allow for sorting by it.
   std::vector<std::pair<llvm::TimeRecord, llvm::StringRef>> Timers;
+
+  // The Total needs to be here, not in preprocess().
   llvm::TimeRecord Total;
 
   void preprocess();
 
-  void printProfileData(llvm::raw_ostream &OS) const;
+  void printUserFriendlyTable(llvm::raw_ostream &OS) const;
+  void printCSVTable(llvm::raw_ostream &OS) const;
+
+  void storeProfileData() const;
 
 public:
   llvm::StringMap<llvm::TimeRecord> Records;
 
+  ClangTidyProfiling() = default;
+
+  ClangTidyProfiling(llvm::Optional<std::string> StoreCSVTo);
+
   ~ClangTidyProfiling();
 };
 
Index: clang-tidy/ClangTidyProfiling.cpp
===================================================================
--- clang-tidy/ClangTidyProfiling.cpp
+++ clang-tidy/ClangTidyProfiling.cpp
@@ -9,27 +9,41 @@
 
 #include "ClangTidyProfiling.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <system_error>
+#include <utility>
 
 #define DEBUG_TYPE "clang-tidy-profiling"
 
+namespace {
+static const char *const TotalStr = "Total";
+}
+
 namespace clang {
 namespace tidy {
 
 void ClangTidyProfiling::preprocess() {
+  assert(Timers.empty() && "Don't preprocess twice");
+
   // Convert from a insertion-friendly map to sort-friendly vector.
-  Timers.clear();
-  Timers.reserve(Records.size());
+  Timers.reserve(1UL + Records.size());
   for (const auto &P : Records) {
     Timers.emplace_back(P.getValue(), P.getKey());
     Total += P.getValue();
   }
-  assert(Timers.size() == Records.size() && "Size mismatch after processing");
 
   // We want the measurements to be sorted by decreasing time spent.
-  llvm::sort(Timers.begin(), Timers.end());
+  llvm::sort(Timers.rbegin(), Timers.rend());
+
+  // And the total at the end.
+  Timers.emplace_back(Total, TotalStr);
+  assert(Timers.size() == 1UL + Records.size() && "Size mismatch afterwards");
 }
 
-void ClangTidyProfiling::printProfileData(llvm::raw_ostream &OS) const {
+void ClangTidyProfiling::printUserFriendlyTable(llvm::raw_ostream &OS) const {
   std::string Line = "===" + std::string(73, '-') + "===\n";
   OS << Line;
 
@@ -45,20 +59,79 @@
   OS << "  --- Name ---\n";
 
   // Loop through all of the timing data, printing it out.
-  for (auto I = Timers.rbegin(), E = Timers.rend(); I != E; ++I) {
-    I->first.print(Total, OS);
-    OS << I->second << '\n';
+  for (const auto &I : Timers) {
+    I.first.print(Total, OS);
+    OS << I.second << '\n';
   }
 
-  Total.print(Total, OS);
-  OS << "Total\n";
   OS << Line << "\n";
   OS.flush();
 }
 
+void ClangTidyProfiling::printCSVTable(llvm::raw_ostream &OS) const {
+  int Column = 0;
+  auto comma = [&Column, &OS]() {
+    if (Column)
+      OS << ','; // FIXME: can ',' be a decimal separator?
+    ++Column;
+  };
+  auto printColumn = [comma, &OS](bool print, const char *str) {
+    if (!print)
+      return;
+    comma();
+    OS << "\"";
+    OS << str;
+    OS << "\"";
+  };
+  printColumn(Total.getUserTime(), "User Time");
+  printColumn(Total.getSystemTime(), "System Time");
+  printColumn(Total.getProcessTime(), "User+System");
+  printColumn(true, "Wall Time");
+  printColumn(Total.getMemUsed(), "Mem");
+  printColumn(true, "Name");
+  OS << "\n";
+
+  // Loop through all of the timing data, printing it out.
+  for (const auto &I : Timers) {
+    I.first.printCSV(Total, OS);
+    comma();
+    OS << "\"" << I.second << "\"\n";
+  }
+
+  OS.flush();
+}
+
+void ClangTidyProfiling::storeProfileData() const {
+  assert(StoreCSVTo.hasValue() && "We should have a filename.");
+
+  llvm::SmallString<256> OutputDirectory(*StoreCSVTo);
+  llvm::sys::path::remove_filename(OutputDirectory);
+  if (std::error_code EC = llvm::sys::fs::create_directories(OutputDirectory)) {
+    llvm::errs() << "Unable to create output directory '" << OutputDirectory
+                 << "': " << EC.message() << "\n";
+    return;
+  }
+
+  std::error_code EC;
+  llvm::raw_fd_ostream OS(*StoreCSVTo, EC, llvm::sys::fs::F_None);
+  if (EC) {
+    llvm::errs() << "Error opening output file: " << EC.message() << "\n";
+    return;
+  }
+
+  printCSVTable(OS);
+}
+
+ClangTidyProfiling::ClangTidyProfiling(llvm::Optional<std::string> StoreCSV)
+    : StoreCSVTo(std::move(StoreCSV)) {}
+
 ClangTidyProfiling::~ClangTidyProfiling() {
   preprocess();
-  printProfileData(llvm::errs());
+
+  printUserFriendlyTable(llvm::errs());
+
+  if (StoreCSVTo.hasValue())
+    storeProfileData();
 }
 
 } // namespace tidy
Index: clang-tidy/ClangTidyDiagnosticConsumer.h
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.h
+++ clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -168,6 +168,10 @@
   void setEnableProfiling(bool Profile);
   bool getEnableProfiling() const { return Profile; }
 
+  /// \brief Control storage of profile date.
+  void setStoreProfile(StringRef ProfilePrefix, StringRef PrefixElide);
+  llvm::Optional<std::string> getStoreProfileFilename() const;
+
   /// \brief Should be called when starting to process new translation unit.
   void setCurrentBuildDirectory(StringRef BuildDirectory) {
     CurrentBuildDirectory = BuildDirectory;
@@ -209,6 +213,8 @@
   llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
 
   bool Profile;
+  std::string ProfilePrefix;
+  std::string ProfilePrefixElide;
 };
 
 /// \brief A diagnostic consumer that turns each \c Diagnostic into a
Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -23,6 +23,7 @@
 #include "clang/Frontend/DiagnosticRenderer.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
 #include <tuple>
 #include <vector>
 using namespace clang;
@@ -235,6 +236,28 @@
 
 void ClangTidyContext::setEnableProfiling(bool P) { Profile = P; }
 
+void ClangTidyContext::setStoreProfile(StringRef Prefix, StringRef Elide) {
+  ProfilePrefix = Prefix;
+  ProfilePrefixElide = Elide;
+}
+
+llvm::Optional<std::string> ClangTidyContext::getStoreProfileFilename() const {
+  if (ProfilePrefix.empty())
+    return llvm::None;
+
+  SmallString<256> OutputFile(CurrentFile);
+
+  // If the current file starts with ProfilePrefixElide, drop that prefix.
+  llvm::sys::path::replace_path_prefix(OutputFile, ProfilePrefixElide, "");
+
+  // Now, append the current file name (after possibly eliding prefix)
+  // to the output prefix.
+  SmallString<256> FinalName(ProfilePrefix);
+  llvm::sys::path::append(FinalName, OutputFile);
+
+  return Twine(FinalName + ".csv").str();
+}
+
 bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
   assert(CheckFilter != nullptr);
   return CheckFilter->contains(CheckName);
Index: clang-tidy/ClangTidy.h
===================================================================
--- clang-tidy/ClangTidy.h
+++ clang-tidy/ClangTidy.h
@@ -228,7 +228,9 @@
                   const tooling::CompilationDatabase &Compilations,
                   ArrayRef<std::string> InputFiles,
                   llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,
-                  bool EnableCheckProfile = false);
+                  bool EnableCheckProfile = false,
+                  llvm::StringRef StoreCheckProfile = StringRef(),
+                  llvm::StringRef StoreCheckProfileElide = StringRef());
 
 // FIXME: This interface will need to be significantly extended to be useful.
 // FIXME: Implement confidence levels for displaying/fixing errors.
Index: clang-tidy/ClangTidy.cpp
===================================================================
--- clang-tidy/ClangTidy.cpp
+++ clang-tidy/ClangTidy.cpp
@@ -362,7 +362,8 @@
 
   std::unique_ptr<ClangTidyProfiling> Profiling;
   if (Context.getEnableProfiling()) {
-    Profiling = llvm::make_unique<ClangTidyProfiling>();
+    Profiling = llvm::make_unique<ClangTidyProfiling>(
+        Context.getStoreProfileFilename());
     FinderOptions.CheckProfiling.emplace(Profiling->Records);
   }
 
@@ -483,7 +484,8 @@
                   const CompilationDatabase &Compilations,
                   ArrayRef<std::string> InputFiles,
                   llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,
-                  bool EnableCheckProfile) {
+                  bool EnableCheckProfile, llvm::StringRef StoreCheckProfile,
+                  llvm::StringRef StoreCheckProfileElide) {
   ClangTool Tool(Compilations, InputFiles,
                  std::make_shared<PCHContainerOperations>(), BaseFS);
 
@@ -524,6 +526,7 @@
   Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
   Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
   Context.setEnableProfiling(EnableCheckProfile);
+  Context.setStoreProfile(StoreCheckProfile, StoreCheckProfileElide);
 
   ClangTidyDiagnosticConsumer DiagConsumer(Context);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to