diff options
-rw-r--r-- | clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt | 25 | ||||
-rw-r--r-- | clang-tools-extra/cpp11-migrate/Core/PerfSupport.cpp | 101 | ||||
-rw-r--r-- | clang-tools-extra/cpp11-migrate/Core/PerfSupport.h | 56 | ||||
-rw-r--r-- | clang-tools-extra/cpp11-migrate/Core/Transform.cpp | 110 | ||||
-rw-r--r-- | clang-tools-extra/cpp11-migrate/Core/Transform.h | 445 | ||||
-rw-r--r-- | clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp | 448 | ||||
-rw-r--r-- | clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt | 37 | ||||
-rw-r--r-- | clang-tools-extra/unittests/cpp11-migrate/PerfSupportTest.cpp | 89 |
8 files changed, 757 insertions, 554 deletions
diff --git a/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt b/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt index 58b35a47d50..e092eee5c86 100644 --- a/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt +++ b/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt @@ -1,12 +1,13 @@ -set(LLVM_LINK_COMPONENTS support) - -add_clang_library(migrateCore - Transforms.cpp - Transform.cpp - IncludeExcludeInfo.cpp - ) -target_link_libraries(migrateCore - clangTooling - clangBasic - clangASTMatchers - ) +set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(migrateCore
+ Transforms.cpp
+ Transform.cpp
+ IncludeExcludeInfo.cpp
+ PerfSupport.cpp
+ )
+target_link_libraries(migrateCore
+ clangTooling
+ clangBasic
+ clangASTMatchers
+ )
diff --git a/clang-tools-extra/cpp11-migrate/Core/PerfSupport.cpp b/clang-tools-extra/cpp11-migrate/Core/PerfSupport.cpp new file mode 100644 index 00000000000..0680747ae36 --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/Core/PerfSupport.cpp @@ -0,0 +1,101 @@ +//===-- cpp11-migrate/Cpp11Migrate.cpp - Main file C++11 migration tool ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides implementations for performance measuring helpers.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PerfSupport.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Path.h"
+
+void collectSourcePerfData(const Transform &T, SourcePerfData &Data) {
+ for (Transform::TimingVec::const_iterator I = T.timing_begin(),
+ E = T.timing_end();
+ I != E; ++I) {
+ SourcePerfData::iterator DataI = Data.insert(
+ SourcePerfData::value_type(I->first, std::vector<PerfItem>())).first;
+ DataI->second
+ .push_back(PerfItem(T.getName(), I->second.getProcessTime() * 1000.0));
+ }
+}
+
+void writePerfDataJSON(
+ const llvm::StringRef DirectoryName,
+ const SourcePerfData &TimingResults) {
+ // Create directory path if it doesn't exist
+ llvm::sys::Path P(DirectoryName);
+ P.createDirectoryOnDisk(true);
+
+ // Get PID and current time.
+ // FIXME: id_type on Windows is NOT a process id despite the function name.
+ // Need to call GetProcessId() providing it what get_id() returns. For now
+ // disabling PID-based file names until this is fixed properly.
+ //llvm::sys::self_process *SP = llvm::sys::process::get_self();
+ //id_type Pid = SP->get_id();
+ unsigned Pid = 0;
+ llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime();
+
+ std::string FileName;
+ llvm::raw_string_ostream SS(FileName);
+ SS << P.str() << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid
+ << ".json";
+
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo);
+ FileStream << "{\n";
+ FileStream << " \"Sources\" : [\n";
+ for (SourcePerfData::const_iterator I = TimingResults.begin(),
+ E = TimingResults.end();
+ I != E; ++I) {
+ // Terminate the last source with a comma before continuing to the next one.
+ if (I != TimingResults.begin())
+ FileStream << ",\n";
+
+ FileStream << " {\n";
+ FileStream << " \"Source \" : \"" << I->first << "\",\n";
+ FileStream << " \"Data\" : [\n";
+ for (std::vector<PerfItem>::const_iterator IE = I->second.begin(),
+ EE = I->second.end();
+ IE != EE; ++IE) {
+ // Terminate the last perf item with a comma before continuing to the next
+ // one.
+ if (IE != I->second.begin())
+ FileStream << ",\n";
+
+ FileStream << " {\n";
+ FileStream << " \"TimerId\" : \"" << IE->Label << "\",\n";
+ FileStream << " \"Time\" : " << llvm::format("%.2f", IE->Duration)
+ << "\n";
+
+ FileStream << " }";
+
+ }
+ FileStream << "\n ]\n";
+ FileStream << " }";
+ }
+ FileStream << "\n ]\n";
+ FileStream << "}";
+}
+
+void dumpPerfData(const SourcePerfData &Data) {
+ for (SourcePerfData::const_iterator I = Data.begin(), E = Data.end(); I != E;
+ ++I) {
+ llvm::errs() << I->first << ":\n";
+ for (std::vector<PerfItem>::const_iterator VecI = I->second.begin(),
+ VecE = I->second.end();
+ VecI != VecE; ++VecI) {
+ llvm::errs() << " " << VecI->Label << ": "
+ << llvm::format("%.1f", VecI->Duration) << "ms\n";
+ }
+ }
+}
diff --git a/clang-tools-extra/cpp11-migrate/Core/PerfSupport.h b/clang-tools-extra/cpp11-migrate/Core/PerfSupport.h new file mode 100644 index 00000000000..1596ee9c80c --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/Core/PerfSupport.h @@ -0,0 +1,56 @@ +//===-- cpp11-migrate/PerfSupport.h - Perf measurement helpers --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides helper functionality for measuring performance and
+/// recording data to file.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CPP11_MIGRATE_PERFSUPPORT_H
+#define CPP11_MIGRATE_PERFSUPPORT_H
+
+#include <map>
+#include <vector>
+#include "Transform.h"
+#include "llvm/ADT/StringRef.h"
+
+/// \brief A single piece of performance data: a duration in milliseconds and a
+/// label for that duration.
+struct PerfItem {
+ PerfItem(const llvm::StringRef Label, float Duration)
+ : Label(Label), Duration(Duration) {}
+
+ /// Label for this performance measurement.
+ std::string Label;
+
+ /// Duration in milliseconds.
+ float Duration;
+};
+
+/// Maps source file names to a vector of durations/labels.
+typedef std::map<std::string, std::vector<PerfItem> > SourcePerfData;
+
+/// Extracts durations collected by a Transform for all sources and adds them
+/// to a SourcePerfData map where data is organized by source file.
+extern void collectSourcePerfData(const Transform &T, SourcePerfData &Data);
+
+/// Write timing results to a JSON formatted file.
+///
+/// File is placed in the directory given by \p DirectoryName. File is named in
+/// a unique way with time and process ID to avoid naming collisions with
+/// existing files or files being generated by other migrator processes.
+void writePerfDataJSON(
+ const llvm::StringRef DirectoryName,
+ const SourcePerfData &TimingResults);
+
+/// Dump a SourcePerfData map to llvm::errs().
+extern void dumpPerfData(const SourcePerfData &Data);
+
+#endif // CPP11_MIGRATE_PERFSUPPORT_H
diff --git a/clang-tools-extra/cpp11-migrate/Core/Transform.cpp b/clang-tools-extra/cpp11-migrate/Core/Transform.cpp index c6bb835321d..f615867f89d 100644 --- a/clang-tools-extra/cpp11-migrate/Core/Transform.cpp +++ b/clang-tools-extra/cpp11-migrate/Core/Transform.cpp @@ -1,53 +1,57 @@ -#include "Core/Transform.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -void collectResults(clang::Rewriter &Rewrite, - const FileContentsByPath &InputStates, - FileContentsByPath &Results) { - // Copy the contents of InputStates to be modified. - Results = InputStates; - - for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(), - E = Rewrite.buffer_end(); - I != E; ++I) { - const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); - assert(Entry != 0 && "Expected a FileEntry"); - assert(Entry->getName() != 0 && - "Unexpected NULL return from FileEntry::getName()"); - - std::string ResultBuf; - - // Get a copy of the rewritten buffer from the Rewriter. - llvm::raw_string_ostream StringStream(ResultBuf); - I->second.write(StringStream); - - // Cause results to be written to ResultBuf. - StringStream.str(); - - // FIXME: Use move semantics to avoid copies of the buffer contents if - // benchmarking shows the copies are expensive, especially for large source - // files. - Results[Entry->getName()] = ResultBuf; - } -} - -bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) { - if (!EnableTiming) - return true; - - Timings.push_back(std::make_pair(Filename.str(), llvm::TimeRecord())); - Timings.back().second -= llvm::TimeRecord::getCurrentTime(true); - return true; -} - -void Transform::handleEndSource() { - if (!EnableTiming) - return; - - Timings.back().second += llvm::TimeRecord::getCurrentTime(false); -} +#include "Core/Transform.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+void collectResults(clang::Rewriter &Rewrite,
+ const FileContentsByPath &InputStates,
+ FileContentsByPath &Results) {
+ // Copy the contents of InputStates to be modified.
+ Results = InputStates;
+
+ for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
+ E = Rewrite.buffer_end();
+ I != E; ++I) {
+ const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
+ assert(Entry != 0 && "Expected a FileEntry");
+ assert(Entry->getName() != 0 &&
+ "Unexpected NULL return from FileEntry::getName()");
+
+ std::string ResultBuf;
+
+ // Get a copy of the rewritten buffer from the Rewriter.
+ llvm::raw_string_ostream StringStream(ResultBuf);
+ I->second.write(StringStream);
+
+ // Cause results to be written to ResultBuf.
+ StringStream.str();
+
+ // FIXME: Use move semantics to avoid copies of the buffer contents if
+ // benchmarking shows the copies are expensive, especially for large source
+ // files.
+ Results[Entry->getName()] = ResultBuf;
+ }
+}
+
+bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
+ if (!EnableTiming)
+ return true;
+
+ Timings.push_back(std::make_pair(Filename.str(), llvm::TimeRecord()));
+ Timings.back().second -= llvm::TimeRecord::getCurrentTime(true);
+ return true;
+}
+
+void Transform::handleEndSource() {
+ if (!EnableTiming)
+ return;
+
+ Timings.back().second += llvm::TimeRecord::getCurrentTime(false);
+}
+
+void Transform::addTiming(llvm::StringRef Label, llvm::TimeRecord Duration) {
+ Timings.push_back(std::make_pair(Label.str(), Duration));
+}
diff --git a/clang-tools-extra/cpp11-migrate/Core/Transform.h b/clang-tools-extra/cpp11-migrate/Core/Transform.h index 73a2a00a816..44d4d0559b6 100644 --- a/clang-tools-extra/cpp11-migrate/Core/Transform.h +++ b/clang-tools-extra/cpp11-migrate/Core/Transform.h @@ -1,219 +1,226 @@ -//===-- cpp11-migrate/Transform.h - Transform Base Class Def'n --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief This file provides the definition for the base Transform class from -/// which all transforms must subclass. -/// -//===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H -#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H - -#include <string> -#include <vector> -#include "clang/Tooling/Tooling.h" -#include "llvm/Support/Timer.h" - -// For RewriterContainer -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/DiagnosticOptions.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/Support/raw_ostream.h" -//// - - -/// \brief Description of the riskiness of actions that can be taken by -/// transforms. -enum RiskLevel { - /// Transformations that will not change semantics. - RL_Safe, - - /// Transformations that might change semantics. - RL_Reasonable, - - /// Transformations that are likely to change semantics. - RL_Risky -}; - -// Forward declarations -namespace clang { -namespace tooling { -class CompilationDatabase; -} // namespace tooling -} // namespace clang - -/// \brief The key is the path of a file, which is mapped to a -/// buffer with the possibly modified contents of that file. -typedef std::map<std::string, std::string> FileContentsByPath; - -/// \brief In \p Results place copies of the buffers resulting from applying -/// all rewrites represented by \p Rewrite. -/// -/// \p Results is made up of pairs {filename, buffer contents}. Pairs are -/// simply appended to \p Results. -void collectResults(clang::Rewriter &Rewrite, - const FileContentsByPath &InputStates, - FileContentsByPath &Results); - -/// \brief Class for containing a Rewriter instance and all of -/// its lifetime dependencies. -/// -/// Subclasses of Transform using RefactoringTools will need to create -/// Rewriters in order to apply Replacements and get the resulting buffer. -/// Rewriter requires some objects to exist at least as long as it does so this -/// class contains instances of those objects. -/// -/// FIXME: These objects should really come from somewhere more global instead -/// of being recreated for every Transform subclass, especially diagnostics. -class RewriterContainer { -public: - RewriterContainer(clang::FileManager &Files, - const FileContentsByPath &InputStates) - : DiagOpts(new clang::DiagnosticOptions()), - DiagnosticPrinter(llvm::errs(), DiagOpts.getPtr()), - Diagnostics(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>( - new clang::DiagnosticIDs()), - DiagOpts.getPtr(), &DiagnosticPrinter, false), - Sources(Diagnostics, Files), - Rewrite(Sources, DefaultLangOptions) { - - // Overwrite source manager's file contents with data from InputStates - for (FileContentsByPath::const_iterator I = InputStates.begin(), - E = InputStates.end(); - I != E; ++I) { - Sources.overrideFileContents(Files.getFile(I->first), - llvm::MemoryBuffer::getMemBuffer(I->second)); - } - } - - clang::Rewriter &getRewriter() { return Rewrite; } - -private: - clang::LangOptions DefaultLangOptions; - llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts; - clang::TextDiagnosticPrinter DiagnosticPrinter; - clang::DiagnosticsEngine Diagnostics; - clang::SourceManager Sources; - clang::Rewriter Rewrite; -}; - -/// \brief Abstract base class for all C++11 migration transforms. -/// -/// Per-source performance timing is handled by the callbacks -/// handleBeginSource() and handleEndSource() if timing is enabled. See -/// clang::tooling::newFrontendActionFactory() for how to register -/// a Transform object for callbacks. -class Transform : public clang::tooling::SourceFileCallbacks { -public: - /// \brief Constructor - /// \param Name Name of the transform for human-readable purposes (e.g. -help - /// text) - /// \param EnableTiming Enable the timing of the duration between calls to - /// handleBeginSource() and handleEndSource(). When a Transform object is - /// registered for FrontendAction source file callbacks, this behaviour can - /// be used to time the application of a MatchFinder by subclasses. Durations - /// are automatically stored in a TimingVec. - Transform(llvm::StringRef Name, bool EnableTiming) - : Name(Name), EnableTiming(EnableTiming) { - Reset(); - } - - virtual ~Transform() {} - - /// \brief Apply a transform to all files listed in \p SourcePaths. - /// - /// \p Database must contain information for how to compile all files in \p - /// SourcePaths. \p InputStates contains the file contents of files in \p - /// SourcePaths and should take precedence over content of files on disk. - /// Upon return, \p ResultStates shall contain the result of performing this - /// transform on the files listed in \p SourcePaths. - virtual int apply(const FileContentsByPath &InputStates, - RiskLevel MaxRiskLevel, - const clang::tooling::CompilationDatabase &Database, - const std::vector<std::string> &SourcePaths, - FileContentsByPath &ResultStates) = 0; - - /// \brief Query if changes were made during the last call to apply(). - bool getChangesMade() const { return AcceptedChanges > 0; } - - /// \brief Query if changes were not made due to conflicts with other changes - /// made during the last call to apply() or if changes were too risky for the - /// requested risk level. - bool getChangesNotMade() const { - return RejectedChanges > 0 || DeferredChanges > 0; - } - - /// \brief Query the number of accepted changes. - unsigned getAcceptedChanges() const { return AcceptedChanges; } - /// \brief Query the number of changes considered too risky. - unsigned getRejectedChanges() const { return RejectedChanges; } - /// \brief Query the number of changes not made because they conflicted with - /// early changes. - unsigned getDeferredChanges() const { return DeferredChanges; } - - /// \brief Query transform name. - llvm::StringRef getName() const { return Name; } - - /// \brief Reset internal state of the transform. - /// - /// Useful if calling apply() several times with one instantiation of a - /// transform. - void Reset() { - AcceptedChanges = 0; - RejectedChanges = 0; - DeferredChanges = 0; - } - - /// \brief Callback for notification of the start of processing of a source - /// file by a FrontendAction. Starts a performance timer if timing was - /// enabled. - virtual bool handleBeginSource(clang::CompilerInstance &CI, - llvm::StringRef Filename) LLVM_OVERRIDE; - - /// \brief Callback for notification of the end of processing of a source - /// file by a FrontendAction. Stops a performance timer if timing was enabled - /// and records the elapsed time. For a given source, handleBeginSource() and - /// handleEndSource() are expected to be called in pairs. - virtual void handleEndSource() LLVM_OVERRIDE; - - /// \brief Performance timing data is stored as a vector of pairs. Pairs are - /// formed of: - /// \li Name of source file. - /// \li Elapsed time. - typedef std::vector<std::pair<std::string, llvm::TimeRecord> > TimingVec; - - /// \brief Return an iterator to the start of collected timing data. - TimingVec::const_iterator timing_begin() const { return Timings.begin(); } - /// \brief Return an iterator to the start of collected timing data. - TimingVec::const_iterator timing_end() const { return Timings.end(); } - -protected: - - void setAcceptedChanges(unsigned Changes) { - AcceptedChanges = Changes; - } - void setRejectedChanges(unsigned Changes) { - RejectedChanges = Changes; - } - void setDeferredChanges(unsigned Changes) { - DeferredChanges = Changes; - } - -private: - const std::string Name; - bool EnableTiming; - TimingVec Timings; - unsigned AcceptedChanges; - unsigned RejectedChanges; - unsigned DeferredChanges; -}; - -#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H +//===-- cpp11-migrate/Transform.h - Transform Base Class Def'n --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the definition for the base Transform class from
+/// which all transforms must subclass.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
+#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
+
+#include <string>
+#include <vector>
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Timer.h"
+
+// For RewriterContainer
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/Support/raw_ostream.h"
+////
+
+
+/// \brief Description of the riskiness of actions that can be taken by
+/// transforms.
+enum RiskLevel {
+ /// Transformations that will not change semantics.
+ RL_Safe,
+
+ /// Transformations that might change semantics.
+ RL_Reasonable,
+
+ /// Transformations that are likely to change semantics.
+ RL_Risky
+};
+
+// Forward declarations
+namespace clang {
+namespace tooling {
+class CompilationDatabase;
+} // namespace tooling
+} // namespace clang
+
+/// \brief The key is the path of a file, which is mapped to a
+/// buffer with the possibly modified contents of that file.
+typedef std::map<std::string, std::string> FileContentsByPath;
+
+/// \brief In \p Results place copies of the buffers resulting from applying
+/// all rewrites represented by \p Rewrite.
+///
+/// \p Results is made up of pairs {filename, buffer contents}. Pairs are
+/// simply appended to \p Results.
+void collectResults(clang::Rewriter &Rewrite,
+ const FileContentsByPath &InputStates,
+ FileContentsByPath &Results);
+
+/// \brief Class for containing a Rewriter instance and all of
+/// its lifetime dependencies.
+///
+/// Subclasses of Transform using RefactoringTools will need to create
+/// Rewriters in order to apply Replacements and get the resulting buffer.
+/// Rewriter requires some objects to exist at least as long as it does so this
+/// class contains instances of those objects.
+///
+/// FIXME: These objects should really come from somewhere more global instead
+/// of being recreated for every Transform subclass, especially diagnostics.
+class RewriterContainer {
+public:
+ RewriterContainer(clang::FileManager &Files,
+ const FileContentsByPath &InputStates)
+ : DiagOpts(new clang::DiagnosticOptions()),
+ DiagnosticPrinter(llvm::errs(), DiagOpts.getPtr()),
+ Diagnostics(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(
+ new clang::DiagnosticIDs()),
+ DiagOpts.getPtr(), &DiagnosticPrinter, false),
+ Sources(Diagnostics, Files),
+ Rewrite(Sources, DefaultLangOptions) {
+
+ // Overwrite source manager's file contents with data from InputStates
+ for (FileContentsByPath::const_iterator I = InputStates.begin(),
+ E = InputStates.end();
+ I != E; ++I) {
+ Sources.overrideFileContents(Files.getFile(I->first),
+ llvm::MemoryBuffer::getMemBuffer(I->second));
+ }
+ }
+
+ clang::Rewriter &getRewriter() { return Rewrite; }
+
+private:
+ clang::LangOptions DefaultLangOptions;
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts;
+ clang::TextDiagnosticPrinter DiagnosticPrinter;
+ clang::DiagnosticsEngine Diagnostics;
+ clang::SourceManager Sources;
+ clang::Rewriter Rewrite;
+};
+
+/// \brief Abstract base class for all C++11 migration transforms.
+///
+/// Per-source performance timing is handled by the callbacks
+/// handleBeginSource() and handleEndSource() if timing is enabled. See
+/// clang::tooling::newFrontendActionFactory() for how to register
+/// a Transform object for callbacks.
+class Transform : public clang::tooling::SourceFileCallbacks {
+public:
+ /// \brief Constructor
+ /// \param Name Name of the transform for human-readable purposes (e.g. -help
+ /// text)
+ /// \param EnableTiming Enable the timing of the duration between calls to
+ /// handleBeginSource() and handleEndSource(). When a Transform object is
+ /// registered for FrontendAction source file callbacks, this behaviour can
+ /// be used to time the application of a MatchFinder by subclasses. Durations
+ /// are automatically stored in a TimingVec.
+ Transform(llvm::StringRef Name, bool EnableTiming)
+ : Name(Name), EnableTiming(EnableTiming) {
+ Reset();
+ }
+
+ virtual ~Transform() {}
+
+ /// \brief Apply a transform to all files listed in \p SourcePaths.
+ ///
+ /// \p Database must contain information for how to compile all files in \p
+ /// SourcePaths. \p InputStates contains the file contents of files in \p
+ /// SourcePaths and should take precedence over content of files on disk.
+ /// Upon return, \p ResultStates shall contain the result of performing this
+ /// transform on the files listed in \p SourcePaths.
+ virtual int apply(const FileContentsByPath &InputStates,
+ RiskLevel MaxRiskLevel,
+ const clang::tooling::CompilationDatabase &Database,
+ const std::vector<std::string> &SourcePaths,
+ FileContentsByPath &ResultStates) = 0;
+
+ /// \brief Query if changes were made during the last call to apply().
+ bool getChangesMade() const { return AcceptedChanges > 0; }
+
+ /// \brief Query if changes were not made due to conflicts with other changes
+ /// made during the last call to apply() or if changes were too risky for the
+ /// requested risk level.
+ bool getChangesNotMade() const {
+ return RejectedChanges > 0 || DeferredChanges > 0;
+ }
+
+ /// \brief Query the number of accepted changes.
+ unsigned getAcceptedChanges() const { return AcceptedChanges; }
+ /// \brief Query the number of changes considered too risky.
+ unsigned getRejectedChanges() const { return RejectedChanges; }
+ /// \brief Query the number of changes not made because they conflicted with
+ /// early changes.
+ unsigned getDeferredChanges() const { return DeferredChanges; }
+
+ /// \brief Query transform name.
+ llvm::StringRef getName() const { return Name; }
+
+ /// \brief Reset internal state of the transform.
+ ///
+ /// Useful if calling apply() several times with one instantiation of a
+ /// transform.
+ void Reset() {
+ AcceptedChanges = 0;
+ RejectedChanges = 0;
+ DeferredChanges = 0;
+ }
+
+ /// \brief Callback for notification of the start of processing of a source
+ /// file by a FrontendAction. Starts a performance timer if timing was
+ /// enabled.
+ virtual bool handleBeginSource(clang::CompilerInstance &CI,
+ llvm::StringRef Filename) LLVM_OVERRIDE;
+
+ /// \brief Callback for notification of the end of processing of a source
+ /// file by a FrontendAction. Stops a performance timer if timing was enabled
+ /// and records the elapsed time. For a given source, handleBeginSource() and
+ /// handleEndSource() are expected to be called in pairs.
+ virtual void handleEndSource() LLVM_OVERRIDE;
+
+ /// \brief Performance timing data is stored as a vector of pairs. Pairs are
+ /// formed of:
+ /// \li Name of source file.
+ /// \li Elapsed time.
+ typedef std::vector<std::pair<std::string, llvm::TimeRecord> > TimingVec;
+
+ /// \brief Return an iterator to the start of collected timing data.
+ TimingVec::const_iterator timing_begin() const { return Timings.begin(); }
+ /// \brief Return an iterator to the start of collected timing data.
+ TimingVec::const_iterator timing_end() const { return Timings.end(); }
+
+protected:
+
+ void setAcceptedChanges(unsigned Changes) {
+ AcceptedChanges = Changes;
+ }
+ void setRejectedChanges(unsigned Changes) {
+ RejectedChanges = Changes;
+ }
+ void setDeferredChanges(unsigned Changes) {
+ DeferredChanges = Changes;
+ }
+
+ /// \brief Allows subclasses to manually add performance timer data.
+ ///
+ /// \p Label should probably include the source file name somehow as the
+ /// duration info is simply added to the vector of timing data which holds
+ /// data for all sources processed by this transform.
+ void addTiming(llvm::StringRef Label, llvm::TimeRecord Duration);
+
+private:
+ const std::string Name;
+ bool EnableTiming;
+ TimingVec Timings;
+ unsigned AcceptedChanges;
+ unsigned RejectedChanges;
+ unsigned DeferredChanges;
+};
+
+#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
diff --git a/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp b/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp index 875d6a0c710..3a263fccc4c 100644 --- a/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp +++ b/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp @@ -1,252 +1,196 @@ -//===-- cpp11-migrate/Cpp11Migrate.cpp - Main file C++11 migration tool ---===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief This file implements the C++11 feature migration tool main function -/// and transformation framework. -/// -/// See user documentation for usage instructions. -/// -//===----------------------------------------------------------------------===// - -#include "Core/Transforms.h" -#include "Core/Transform.h" -#include "LoopConvert/LoopConvert.h" -#include "UseNullptr/UseNullptr.h" -#include "UseAuto/UseAuto.h" -#include "AddOverride/AddOverride.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Tooling/CommonOptionsParser.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/Timer.h" - -namespace cl = llvm::cl; -using namespace clang::tooling; - -static cl::opt<RiskLevel> MaxRiskLevel( - "risk", cl::desc("Select a maximum risk level:"), - cl::values(clEnumValN(RL_Safe, "safe", "Only safe transformations"), - clEnumValN(RL_Reasonable, "reasonable", - "Enable transformations that might change " - "semantics (default)"), - clEnumValN(RL_Risky, "risky", - "Enable transformations that are likely to " - "change semantics"), - clEnumValEnd), - cl::init(RL_Reasonable)); - -static cl::opt<bool> FinalSyntaxCheck( - "final-syntax-check", - cl::desc("Check for correct syntax after applying transformations"), - cl::init(false)); - -static cl::opt<bool> -SummaryMode("summary", cl::desc("Print transform summary"), - cl::init(false)); - -const char NoTiming[] = "no_timing"; -static cl::opt<std::string> TimingDirectoryName( - "report-times", cl::desc("Capture performance data and output to specified " - "directory. Default ./migrate_perf"), - cl::init(NoTiming), cl::ValueOptional, cl::value_desc("directory name")); - -// TODO: Remove cl::Hidden when functionality for acknowledging include/exclude -// options are implemented in the tool. -static cl::opt<std::string> -IncludePaths("include", cl::Hidden, - cl::desc("Comma seperated list of paths to consider to be " - "transformed")); -static cl::opt<std::string> -ExcludePaths("exclude", cl::Hidden, - cl::desc("Comma seperated list of paths that can not " - "be transformed")); -static cl::opt<std::string> -IncludeFromFile("include-from", cl::Hidden, cl::value_desc("filename"), - cl::desc("File containing a list of paths to consider to " - "be transformed")); -static cl::opt<std::string> -ExcludeFromFile("exclude-from", cl::Hidden, cl::value_desc("filename"), - cl::desc("File containing a list of paths that can not be " - "transforms")); - -class EndSyntaxArgumentsAdjuster : public ArgumentsAdjuster { - CommandLineArguments Adjust(const CommandLineArguments &Args) { - CommandLineArguments AdjustedArgs = Args; - AdjustedArgs.push_back("-fsyntax-only"); - AdjustedArgs.push_back("-std=c++11"); - return AdjustedArgs; - } -}; - -struct ExecutionTime { - std::string TimerId; - float Time; - ExecutionTime(const std::string &TimerId, float Time) - : TimerId(TimerId), Time(Time) {} -}; - -// Save execution times to a json formatted file. -void reportExecutionTimes( - const llvm::StringRef DirectoryName, - const std::map<std::string, std::vector<ExecutionTime> > &TimingResults) { - // Create directory path if it doesn't exist - llvm::sys::Path P(DirectoryName); - P.createDirectoryOnDisk(true); - - // Get PID and current time. - // FIXME: id_type on Windows is NOT a process id despite the function name. - // Need to call GetProcessId() providing it what get_id() returns. For now - // disabling PID-based file names until this is fixed properly. - //llvm::sys::self_process *SP = llvm::sys::process::get_self(); - //id_type Pid = SP->get_id(); - unsigned Pid = 0; - llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime(); - - std::string FileName; - llvm::raw_string_ostream SS(FileName); - SS << P.str() << "/" << static_cast<int>(T.getWallTime()) << Pid << ".json"; - - - std::string ErrorInfo; - llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo); - FileStream << "{\n"; - FileStream << " \"Sources\" : [\n"; - for (std::map<std::string, std::vector<ExecutionTime> >::const_iterator - I = TimingResults.begin(), - E = TimingResults.end(); - I != E; ++I) { - FileStream << " {\n"; - FileStream << " \"Source \" : \"" << I->first << "\",\n"; - FileStream << " \"Data\" : [\n"; - for (std::vector<ExecutionTime>::const_iterator IE = I->second.begin(), - EE = I->second.end(); - IE != EE; ++IE) { - FileStream << " {\n"; - FileStream << " \"TimerId\" : \"" << (*IE).TimerId << "\",\n"; - FileStream << " \"Time\" : " << llvm::format("%6.2f", (*IE).Time) - << "\n"; - - FileStream << " },\n"; - - } - FileStream << " ]\n"; - FileStream << " },\n"; - } - FileStream << " ]\n"; - FileStream << "}"; -} - -int main(int argc, const char **argv) { - llvm::sys::PrintStackTraceOnErrorSignal(); - Transforms TransformManager; - - TransformManager.registerTransform( - "loop-convert", "Make use of range-based for loops where possible", - &ConstructTransform<LoopConvertTransform>); - TransformManager.registerTransform( - "use-nullptr", "Make use of nullptr keyword where possible", - &ConstructTransform<UseNullptrTransform>); - TransformManager.registerTransform( - "use-auto", "Use of 'auto' type specifier", - &ConstructTransform<UseAutoTransform>); - TransformManager.registerTransform( - "add-override", "Make use of override specifier where possible", - &ConstructTransform<AddOverrideTransform>); - // Add more transform options here. - - // This causes options to be parsed. - CommonOptionsParser OptionsParser(argc, argv); - - // Since ExecutionTimeDirectoryName could be an empty string we compare - // against the default value when the command line option is not specified. - bool EnableTiming = (TimingDirectoryName != NoTiming); - std::map<std::string, std::vector<ExecutionTime> > TimingResults; - - TransformManager.createSelectedTransforms(EnableTiming); - - if (TransformManager.begin() == TransformManager.end()) { - llvm::errs() << "No selected transforms\n"; - return 1; - } - - FileContentsByPath FileStates1, FileStates2, - *InputFileStates = &FileStates1, *OutputFileStates = &FileStates2; - - // Apply transforms. - for (Transforms::const_iterator I = TransformManager.begin(), - E = TransformManager.end(); - I != E; ++I) { - if ((*I)->apply(*InputFileStates, MaxRiskLevel, - OptionsParser.getCompilations(), - OptionsParser.getSourcePathList(), *OutputFileStates) != - 0) { - // FIXME: Improve ClangTool to not abort if just one file fails. - return 1; - } - if (SummaryMode) { - llvm::outs() << "Transform: " << (*I)->getName() - << " - Accepted: " - << (*I)->getAcceptedChanges(); - if ((*I)->getChangesNotMade()) { - llvm::outs() << " - Rejected: " - << (*I)->getRejectedChanges() - << " - Deferred: " - << (*I)->getDeferredChanges(); - } - llvm::outs() << "\n"; - } - std::swap(InputFileStates, OutputFileStates); - OutputFileStates->clear(); - } - - // Final state of files is pointed at by InputFileStates. - - if (FinalSyntaxCheck) { - ClangTool EndSyntaxTool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); - - // Add c++11 support to clang. - EndSyntaxTool.setArgumentsAdjuster(new EndSyntaxArgumentsAdjuster); - - for (FileContentsByPath::const_iterator I = InputFileStates->begin(), - E = InputFileStates->end(); - I != E; ++I) { - EndSyntaxTool.mapVirtualFile(I->first, I->second); - } - - if (EndSyntaxTool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>()) - != 0) { - return 1; - } - } - - // Write results to file. - for (FileContentsByPath::const_iterator I = InputFileStates->begin(), - E = InputFileStates->end(); - I != E; ++I) { - std::string ErrorInfo; - llvm::raw_fd_ostream FileStream(I->first.c_str(), ErrorInfo, - llvm::raw_fd_ostream::F_Binary); - FileStream << I->second; - } - - // Report execution times. - if (EnableTiming && TimingResults.size() > 0) { - std::string DirectoryName = TimingDirectoryName; - // Use default directory name. - if (DirectoryName == "") - DirectoryName = "./migrate_perf"; - reportExecutionTimes(DirectoryName, TimingResults); - } - - return 0; -} +//===-- cpp11-migrate/Cpp11Migrate.cpp - Main file C++11 migration tool ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements the C++11 feature migration tool main function
+/// and transformation framework.
+///
+/// See user documentation for usage instructions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Core/Transforms.h"
+#include "Core/Transform.h"
+#include "Core/PerfSupport.h"
+#include "LoopConvert/LoopConvert.h"
+#include "UseNullptr/UseNullptr.h"
+#include "UseAuto/UseAuto.h"
+#include "AddOverride/AddOverride.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Signals.h"
+
+namespace cl = llvm::cl;
+using namespace clang::tooling;
+
+static cl::opt<RiskLevel> MaxRiskLevel(
+ "risk", cl::desc("Select a maximum risk level:"),
+ cl::values(clEnumValN(RL_Safe, "safe", "Only safe transformations"),
+ clEnumValN(RL_Reasonable, "reasonable",
+ "Enable transformations that might change "
+ "semantics (default)"),
+ clEnumValN(RL_Risky, "risky",
+ "Enable transformations that are likely to "
+ "change semantics"),
+ clEnumValEnd),
+ cl::init(RL_Reasonable));
+
+static cl::opt<bool> FinalSyntaxCheck(
+ "final-syntax-check",
+ cl::desc("Check for correct syntax after applying transformations"),
+ cl::init(false));
+
+static cl::opt<bool>
+SummaryMode("summary", cl::desc("Print transform summary"),
+ cl::init(false));
+
+const char NoTiming[] = "no_timing";
+static cl::opt<std::string> TimingDirectoryName(
+ "perf", cl::desc("Capture performance data and output to specified "
+ "directory. Default: ./migrate_perf"),
+ cl::init(NoTiming), cl::ValueOptional, cl::value_desc("directory name"));
+
+// TODO: Remove cl::Hidden when functionality for acknowledging include/exclude
+// options are implemented in the tool.
+static cl::opt<std::string>
+IncludePaths("include", cl::Hidden,
+ cl::desc("Comma seperated list of paths to consider to be "
+ "transformed"));
+static cl::opt<std::string>
+ExcludePaths("exclude", cl::Hidden,
+ cl::desc("Comma seperated list of paths that can not "
+ "be transformed"));
+static cl::opt<std::string>
+IncludeFromFile("include-from", cl::Hidden, cl::value_desc("filename"),
+ cl::desc("File containing a list of paths to consider to "
+ "be transformed"));
+static cl::opt<std::string>
+ExcludeFromFile("exclude-from", cl::Hidden, cl::value_desc("filename"),
+ cl::desc("File containing a list of paths that can not be "
+ "transforms"));
+
+class EndSyntaxArgumentsAdjuster : public ArgumentsAdjuster {
+ CommandLineArguments Adjust(const CommandLineArguments &Args) {
+ CommandLineArguments AdjustedArgs = Args;
+ AdjustedArgs.push_back("-fsyntax-only");
+ AdjustedArgs.push_back("-std=c++11");
+ return AdjustedArgs;
+ }
+};
+
+int main(int argc, const char **argv) {
+ llvm::sys::PrintStackTraceOnErrorSignal();
+ Transforms TransformManager;
+
+ TransformManager.registerTransform(
+ "loop-convert", "Make use of range-based for loops where possible",
+ &ConstructTransform<LoopConvertTransform>);
+ TransformManager.registerTransform(
+ "use-nullptr", "Make use of nullptr keyword where possible",
+ &ConstructTransform<UseNullptrTransform>);
+ TransformManager.registerTransform(
+ "use-auto", "Use of 'auto' type specifier",
+ &ConstructTransform<UseAutoTransform>);
+ TransformManager.registerTransform(
+ "add-override", "Make use of override specifier where possible",
+ &ConstructTransform<AddOverrideTransform>);
+ // Add more transform options here.
+
+ // This causes options to be parsed.
+ CommonOptionsParser OptionsParser(argc, argv);
+
+ // Since ExecutionTimeDirectoryName could be an empty string we compare
+ // against the default value when the command line option is not specified.
+ bool EnableTiming = (TimingDirectoryName != NoTiming);
+ TransformManager.createSelectedTransforms(EnableTiming);
+
+ if (TransformManager.begin() == TransformManager.end()) {
+ llvm::errs() << "No selected transforms\n";
+ return 1;
+ }
+
+ FileContentsByPath FileStates1, FileStates2,
+ *InputFileStates = &FileStates1, *OutputFileStates = &FileStates2;
+
+ SourcePerfData PerfData;
+
+ // Apply transforms.
+ for (Transforms::const_iterator I = TransformManager.begin(),
+ E = TransformManager.end();
+ I != E; ++I) {
+ if ((*I)->apply(*InputFileStates, MaxRiskLevel,
+ OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList(), *OutputFileStates) !=
+ 0) {
+ // FIXME: Improve ClangTool to not abort if just one file fails.
+ return 1;
+ }
+
+ if (EnableTiming)
+ collectSourcePerfData(**I, PerfData);
+
+ if (SummaryMode) {
+ llvm::outs() << "Transform: " << (*I)->getName()
+ << " - Accepted: "
+ << (*I)->getAcceptedChanges();
+ if ((*I)->getChangesNotMade()) {
+ llvm::outs() << " - Rejected: "
+ << (*I)->getRejectedChanges()
+ << " - Deferred: "
+ << (*I)->getDeferredChanges();
+ }
+ llvm::outs() << "\n";
+ }
+ std::swap(InputFileStates, OutputFileStates);
+ OutputFileStates->clear();
+ }
+
+ // Final state of files is pointed at by InputFileStates.
+
+ if (FinalSyntaxCheck) {
+ ClangTool EndSyntaxTool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+
+ // Add c++11 support to clang.
+ EndSyntaxTool.setArgumentsAdjuster(new EndSyntaxArgumentsAdjuster);
+
+ for (FileContentsByPath::const_iterator I = InputFileStates->begin(),
+ E = InputFileStates->end();
+ I != E; ++I) {
+ EndSyntaxTool.mapVirtualFile(I->first, I->second);
+ }
+
+ if (EndSyntaxTool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>())
+ != 0) {
+ return 1;
+ }
+ }
+
+ // Write results to file.
+ for (FileContentsByPath::const_iterator I = InputFileStates->begin(),
+ E = InputFileStates->end();
+ I != E; ++I) {
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream FileStream(I->first.c_str(), ErrorInfo,
+ llvm::raw_fd_ostream::F_Binary);
+ FileStream << I->second;
+ }
+
+ // Report execution times.
+ if (EnableTiming && !PerfData.empty()) {
+ std::string DirectoryName = TimingDirectoryName;
+ // Use default directory name.
+ if (DirectoryName.empty())
+ DirectoryName = "./migrate_perf";
+ writePerfDataJSON(DirectoryName, PerfData);
+ }
+
+ return 0;
+}
diff --git a/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt b/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt index bb0bfb104dc..4ea85d54073 100644 --- a/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt +++ b/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt @@ -1,18 +1,19 @@ -set(LLVM_LINK_COMPONENTS - support - ) - -get_filename_component(CPP11_MIGRATE_SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/../../cpp11-migrate REALPATH) -include_directories(${CPP11_MIGRATE_SOURCE_DIR}) - -add_extra_unittest(Cpp11MigrateTests - TransformTest.cpp - IncludeExcludeTest.cpp) - -target_link_libraries(Cpp11MigrateTests - migrateCore - clangTooling - clangBasic - clangASTMatchers - ) +set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(CPP11_MIGRATE_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../cpp11-migrate REALPATH)
+include_directories(${CPP11_MIGRATE_SOURCE_DIR})
+
+add_extra_unittest(Cpp11MigrateTests
+ TransformTest.cpp
+ IncludeExcludeTest.cpp
+ PerfSupportTest.cpp)
+
+target_link_libraries(Cpp11MigrateTests
+ migrateCore
+ clangTooling
+ clangBasic
+ clangASTMatchers
+ )
diff --git a/clang-tools-extra/unittests/cpp11-migrate/PerfSupportTest.cpp b/clang-tools-extra/unittests/cpp11-migrate/PerfSupportTest.cpp new file mode 100644 index 00000000000..8c362fd1a73 --- /dev/null +++ b/clang-tools-extra/unittests/cpp11-migrate/PerfSupportTest.cpp @@ -0,0 +1,89 @@ +#include "gtest/gtest.h"
+#include "Core/PerfSupport.h"
+
+using namespace llvm;
+using namespace clang;
+
+class TransformA : public Transform {
+public:
+ TransformA()
+ : Transform("TransformA", false) {}
+
+ virtual int apply(const FileContentsByPath &, RiskLevel,
+ const tooling::CompilationDatabase &,
+ const std::vector<std::string> &, FileContentsByPath &) {
+ return 0;
+ }
+
+ void addTiming(StringRef Label, TimeRecord Duration) {
+ Transform::addTiming(Label, Duration);
+ }
+};
+
+class TransformB : public Transform {
+public:
+ TransformB()
+ : Transform("TransformB", false) {}
+
+ virtual int apply(const FileContentsByPath &, RiskLevel,
+ const tooling::CompilationDatabase &,
+ const std::vector<std::string> &, FileContentsByPath &) {
+ return 0;
+ }
+
+ void addTiming(StringRef Label, TimeRecord Duration) {
+ Transform::addTiming(Label, Duration);
+ }
+};
+
+struct ExpectedResults {
+ const char *SourceName;
+ unsigned DataCount;
+ struct Datum {
+ const char *Label;
+ float Duration;
+ } Data[2];
+};
+
+TEST(PerfSupport, collectSourcePerfData) {
+ TransformA A;
+ TransformB B;
+
+ // The actual durations don't matter. Below only their relative ordering is
+ // tested to ensure times, labels, and sources all stay together properly.
+ A.addTiming("FileA.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+ A.addTiming("FileC.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+ B.addTiming("FileC.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+ B.addTiming("FileB.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+
+ SourcePerfData PerfData;
+ collectSourcePerfData(A, PerfData);
+
+ SourcePerfData::const_iterator FileAI = PerfData.find("FileA.cpp");
+ EXPECT_NE(FileAI, PerfData.end());
+ SourcePerfData::const_iterator FileCI = PerfData.find("FileC.cpp");
+ EXPECT_NE(FileCI, PerfData.end());
+ EXPECT_EQ(2u, PerfData.size());
+
+ EXPECT_EQ(1u, FileAI->second.size());
+ EXPECT_EQ("TransformA", FileAI->second[0].Label);
+ EXPECT_EQ(1u, FileCI->second.size());
+ EXPECT_EQ("TransformA", FileCI->second[0].Label);
+ EXPECT_LE(FileAI->second[0].Duration, FileCI->second[0].Duration);
+
+ collectSourcePerfData(B, PerfData);
+
+ SourcePerfData::const_iterator FileBI = PerfData.find("FileB.cpp");
+ EXPECT_NE(FileBI, PerfData.end());
+ EXPECT_EQ(3u, PerfData.size());
+
+ EXPECT_EQ(1u, FileAI->second.size());
+ EXPECT_EQ("TransformA", FileAI->second[0].Label);
+ EXPECT_EQ(2u, FileCI->second.size());
+ EXPECT_EQ("TransformA", FileCI->second[0].Label);
+ EXPECT_EQ("TransformB", FileCI->second[1].Label);
+ EXPECT_LE(FileCI->second[0].Duration, FileCI->second[1].Duration);
+ EXPECT_EQ(1u, FileBI->second.size());
+ EXPECT_EQ("TransformB", FileBI->second[0].Label);
+ EXPECT_LE(FileCI->second[1].Duration, FileBI->second[0].Duration);
+}
|