forked from OSchip/llvm-project
cpp11-migrate: Transforms collect timing data.
Using updated form of newFrontendActionFactory(), Transforms now automatically measure, if requested, how long it takes to apply a MatchFinder to a source file. Other per-transform overhead, e.g. applying replacements, is not currently measured. This behaviour is disabled for now and soon will be connected to a new command line arg. llvm-svn: 182942
This commit is contained in:
parent
8fe6d11b84
commit
b76a13eb4e
|
@ -45,7 +45,8 @@ int AddOverrideTransform::apply(const FileContentsByPath &InputStates,
|
|||
|
||||
Finder.addMatcher(makeCandidateForOverrideAttrMatcher(), &Fixer);
|
||||
|
||||
if (int result = AddOverrideTool.run(newFrontendActionFactory(&Finder))) {
|
||||
if (int result = AddOverrideTool.run(
|
||||
newFrontendActionFactory(&Finder, /*Callbacks=*/ this))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
/// member functions overriding base class virtual functions.
|
||||
class AddOverrideTransform : public Transform {
|
||||
public:
|
||||
AddOverrideTransform() : Transform("AddOverride") {}
|
||||
AddOverrideTransform(bool EnableTiming)
|
||||
: Transform("AddOverride", EnableTiming) {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
|
|
|
@ -35,3 +35,19 @@ void collectResults(clang::Rewriter &Rewrite,
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
|
||||
// For RewriterContainer
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
|
@ -104,9 +106,23 @@ private:
|
|||
};
|
||||
|
||||
/// \brief Abstract base class for all C++11 migration transforms.
|
||||
class Transform {
|
||||
///
|
||||
/// 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:
|
||||
Transform(llvm::StringRef Name) : Name(Name) {
|
||||
/// \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();
|
||||
}
|
||||
|
||||
|
@ -156,7 +172,31 @@ public:
|
|||
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;
|
||||
}
|
||||
|
@ -169,6 +209,8 @@ protected:
|
|||
|
||||
private:
|
||||
const std::string Name;
|
||||
bool EnableTiming;
|
||||
TimingVec Timings;
|
||||
unsigned AcceptedChanges;
|
||||
unsigned RejectedChanges;
|
||||
unsigned DeferredChanges;
|
||||
|
|
|
@ -35,11 +35,11 @@ void Transforms::registerTransform(llvm::StringRef OptName,
|
|||
new cl::opt<bool>(OptName.data(), cl::desc(Description.data())), Creator));
|
||||
}
|
||||
|
||||
void Transforms::createSelectedTransforms() {
|
||||
void Transforms::createSelectedTransforms(bool EnableTiming) {
|
||||
for (OptionVec::iterator I = Options.begin(),
|
||||
E = Options.end(); I != E; ++I) {
|
||||
if (*I->first) {
|
||||
ChosenTransforms.push_back(I->second());
|
||||
ChosenTransforms.push_back(I->second(EnableTiming));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@ class Option;
|
|||
} // namespace llvm
|
||||
class Transform;
|
||||
|
||||
typedef Transform *(*TransformCreator)();
|
||||
typedef Transform *(*TransformCreator)(bool);
|
||||
template <typename T>
|
||||
Transform *ConstructTransform() {
|
||||
return new T();
|
||||
Transform *ConstructTransform(bool EnableTiming) {
|
||||
return new T(EnableTiming);
|
||||
}
|
||||
|
||||
/// \brief Class encapsulating the creation of command line bool options
|
||||
|
@ -55,7 +55,7 @@ public:
|
|||
/// \brief Instantiate all transforms that were selected on the command line.
|
||||
///
|
||||
/// Call *after* parsing options.
|
||||
void createSelectedTransforms();
|
||||
void createSelectedTransforms(bool EnableTiming);
|
||||
|
||||
/// \brief Return an iterator to the start of a container of instantiated
|
||||
/// transforms.
|
||||
|
|
|
@ -64,7 +64,8 @@ int LoopConvertTransform::apply(const FileContentsByPath &InputStates,
|
|||
MaxRisk, LFK_PseudoArray);
|
||||
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
||||
|
||||
if (int result = LoopTool.run(newFrontendActionFactory(&Finder))) {
|
||||
if (int result = LoopTool.run(
|
||||
newFrontendActionFactory(&Finder, /*Callbacks=*/ this))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
/// for-loops where possible.
|
||||
class LoopConvertTransform : public Transform {
|
||||
public:
|
||||
LoopConvertTransform() : Transform("LoopConvert") {}
|
||||
LoopConvertTransform(bool EnableTiming)
|
||||
: Transform("LoopConvert", EnableTiming) {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
|
|
|
@ -43,7 +43,8 @@ int UseAutoTransform::apply(const FileContentsByPath &InputStates,
|
|||
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
|
||||
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
|
||||
|
||||
if (int Result = UseAutoTool.run(newFrontendActionFactory(&Finder))) {
|
||||
if (int Result = UseAutoTool.run(
|
||||
newFrontendActionFactory(&Finder, /*Callbacks=*/ this))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
/// p2 are not handled by this transform.
|
||||
class UseAutoTransform : public Transform {
|
||||
public:
|
||||
UseAutoTransform() : Transform("UseAuto") {}
|
||||
UseAutoTransform(bool EnableTiming) : Transform("UseAuto", EnableTiming) {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
|
|
|
@ -47,7 +47,8 @@ int UseNullptrTransform::apply(const FileContentsByPath &InputStates,
|
|||
|
||||
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
|
||||
|
||||
if (int result = UseNullptrTool.run(newFrontendActionFactory(&Finder))) {
|
||||
if (int result = UseNullptrTool.run(
|
||||
newFrontendActionFactory(&Finder, /*Callbacks=*/ this))) {
|
||||
llvm::errs() << "Error encountered during translation.\n";
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
/// C++11's nullptr keyword where possible.
|
||||
class UseNullptrTransform : public Transform {
|
||||
public:
|
||||
UseNullptrTransform() : Transform("UseNullptr") {}
|
||||
UseNullptrTransform(bool EnableTiming)
|
||||
: Transform("UseNullptr", EnableTiming) {}
|
||||
|
||||
/// \see Transform::run().
|
||||
virtual int apply(const FileContentsByPath &InputStates,
|
||||
|
|
|
@ -99,7 +99,7 @@ int main(int argc, const char **argv) {
|
|||
// This causes options to be parsed.
|
||||
CommonOptionsParser OptionsParser(argc, argv);
|
||||
|
||||
TransformManager.createSelectedTransforms();
|
||||
TransformManager.createSelectedTransforms(/*EnableTiming=*/false);
|
||||
|
||||
if (TransformManager.begin() == TransformManager.end()) {
|
||||
llvm::errs() << "No selected transforms\n";
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "Core/Transform.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/DeclGroup.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
class DummyTransform : public Transform {
|
||||
public:
|
||||
DummyTransform(llvm::StringRef Name) : Transform(Name) {}
|
||||
DummyTransform(llvm::StringRef Name, bool EnableTiming)
|
||||
: Transform(Name, EnableTiming) {}
|
||||
|
||||
virtual int apply(const FileContentsByPath &,
|
||||
RiskLevel ,
|
||||
const clang::tooling::CompilationDatabase &,
|
||||
const tooling::CompilationDatabase &,
|
||||
const std::vector<std::string> &,
|
||||
FileContentsByPath &) { return 0; }
|
||||
|
||||
|
@ -23,7 +30,7 @@ public:
|
|||
};
|
||||
|
||||
TEST(Transform, Interface) {
|
||||
DummyTransform T("my_transform");
|
||||
DummyTransform T("my_transform", /*EnableTiming=*/false);
|
||||
ASSERT_EQ("my_transform", T.getName());
|
||||
ASSERT_EQ(0u, T.getAcceptedChanges());
|
||||
ASSERT_EQ(0u, T.getRejectedChanges());
|
||||
|
@ -48,3 +55,83 @@ TEST(Transform, Interface) {
|
|||
T.setRejectedChanges(1);
|
||||
ASSERT_TRUE(T.getChangesNotMade());
|
||||
}
|
||||
|
||||
class FindTopLevelDeclConsumer : public ASTConsumer {
|
||||
public:
|
||||
FindTopLevelDeclConsumer(bool *Called) : Called(Called) {}
|
||||
|
||||
virtual bool HandleTopLevelDecl(DeclGroupRef DeclGroup) {
|
||||
llvm::sys::TimeValue UserStart;
|
||||
llvm::sys::TimeValue SystemStart;
|
||||
llvm::sys::TimeValue UserNow;
|
||||
llvm::sys::TimeValue SystemNow;
|
||||
llvm::sys::TimeValue Wall;
|
||||
|
||||
// Busy-wait until the user/system time combined is more than 1ms
|
||||
llvm::sys::TimeValue OneMS(0, 1000000);
|
||||
llvm::sys::Process::GetTimeUsage(Wall, UserStart, SystemStart);
|
||||
do {
|
||||
llvm::sys::Process::GetTimeUsage(Wall, UserNow, SystemNow);
|
||||
} while (UserNow - UserStart + SystemNow - SystemStart < OneMS);
|
||||
*Called = true;
|
||||
return true;
|
||||
}
|
||||
bool *Called;
|
||||
};
|
||||
|
||||
struct ConsumerFactory {
|
||||
ASTConsumer *newASTConsumer() {
|
||||
return new FindTopLevelDeclConsumer(&Called);
|
||||
}
|
||||
bool Called;
|
||||
};
|
||||
|
||||
TEST(Transform, Timings) {
|
||||
DummyTransform T("timing_transform", /*EnableTiming=*/true);
|
||||
|
||||
// All the path stuff is to make the test work independently of OS.
|
||||
|
||||
// The directory used is not important since the path gets mapped to a virtual
|
||||
// file anyway. What is important is that we have an absolute path with which
|
||||
// to use with mapVirtualFile().
|
||||
llvm::sys::Path FileA = llvm::sys::Path::GetCurrentDirectory();
|
||||
std::string CurrentDir = FileA.str();
|
||||
FileA.appendComponent("a.cc");
|
||||
std::string FileAName = FileA.str();
|
||||
llvm::sys::Path FileB = llvm::sys::Path::GetCurrentDirectory();
|
||||
FileB.appendComponent("b.cc");
|
||||
std::string FileBName = FileB.str();
|
||||
|
||||
tooling::FixedCompilationDatabase Compilations(CurrentDir, std::vector<std::string>());
|
||||
std::vector<std::string> Sources;
|
||||
Sources.push_back(FileAName);
|
||||
Sources.push_back(FileBName);
|
||||
tooling::ClangTool Tool(Compilations, Sources);
|
||||
|
||||
Tool.mapVirtualFile(FileAName, "void a() {}");
|
||||
Tool.mapVirtualFile(FileBName, "void b() {}");
|
||||
|
||||
ConsumerFactory Factory;
|
||||
Tool.run(newFrontendActionFactory(&Factory, &T));
|
||||
|
||||
EXPECT_TRUE(Factory.Called);
|
||||
Transform::TimingVec::const_iterator I = T.timing_begin();
|
||||
EXPECT_GT(I->second.getProcessTime(), 0.0);
|
||||
|
||||
// The success of the test shouldn't depend on the order of iteration through
|
||||
// timers.
|
||||
llvm::sys::Path FirstFile(I->first);
|
||||
if (FileA == FirstFile) {
|
||||
++I;
|
||||
EXPECT_EQ(FileB, llvm::sys::Path(I->first));
|
||||
EXPECT_GT(I->second.getProcessTime(), 0.0);
|
||||
} else if (FileB == FirstFile) {
|
||||
++I;
|
||||
EXPECT_EQ(FileA, llvm::sys::Path(I->first));
|
||||
EXPECT_GT(I->second.getProcessTime(), 0.0);
|
||||
} else {
|
||||
FAIL() << "Unexpected file name " << I->first << " in timing data.";
|
||||
}
|
||||
++I;
|
||||
EXPECT_EQ(T.timing_end(), I);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue