| //===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements pp-trace, a tool for displaying a textual trace | |
| // of the Clang preprocessor activity. It's based on a derivation of the | |
| // PPCallbacks class, that once registerd with Clang, receives callback calls | |
| // to its virtual members, and outputs the information passed to the callbacks | |
| // in a high-level YAML format. | |
| // | |
| // The pp-trace tool also serves as the basis for a test of the PPCallbacks | |
| // mechanism. | |
| // | |
| // The pp-trace tool supports the following general command line format: | |
| // | |
| // pp-trace [pp-trace options] (source file) [compiler options] | |
| // | |
| // Basically you put the pp-trace options first, then the source file or files, | |
| // and then any options you want to pass to the compiler. | |
| // | |
| // These are the pp-trace options: | |
| // | |
| // -ignore (callback list) Don't display output for a comma-separated | |
| // list of callbacks, i.e.: | |
| // -ignore "FileChanged,InclusionDirective" | |
| // | |
| // -output (file) Output trace to the given file in a YAML | |
| // format, e.g.: | |
| // | |
| // --- | |
| // - Callback: Name | |
| // Argument1: Value1 | |
| // Argument2: Value2 | |
| // (etc.) | |
| // ... | |
| // | |
| // Future Directions: | |
| // | |
| // 1. Add option opposite to "-ignore" that specifys a comma-separated option | |
| // list of callbacs. Perhaps "-only" or "-exclusive". | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "PPCallbacksTracker.h" | |
| #include "clang/AST/ASTConsumer.h" | |
| #include "clang/AST/ASTContext.h" | |
| #include "clang/AST/RecursiveASTVisitor.h" | |
| #include "clang/Basic/SourceManager.h" | |
| #include "clang/Driver/Options.h" | |
| #include "clang/Frontend/CompilerInstance.h" | |
| #include "clang/Frontend/FrontendActions.h" | |
| #include "clang/Lex/Preprocessor.h" | |
| #include "clang/Tooling/CompilationDatabase.h" | |
| #include "clang/Tooling/Tooling.h" | |
| #include "llvm/Option/Arg.h" | |
| #include "llvm/Option/ArgList.h" | |
| #include "llvm/Option/OptTable.h" | |
| #include "llvm/Option/Option.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/Path.h" | |
| #include "llvm/Support/ToolOutputFile.h" | |
| #include <algorithm> | |
| #include <fstream> | |
| #include <iterator> | |
| #include <string> | |
| #include <vector> | |
| using namespace clang; | |
| using namespace clang::driver; | |
| using namespace clang::driver::options; | |
| using namespace clang::tooling; | |
| using namespace llvm; | |
| using namespace llvm::opt; | |
| // Options: | |
| // Collect the source files. | |
| static cl::list<std::string> SourcePaths(cl::Positional, | |
| cl::desc("<source0> [... <sourceN>]"), | |
| cl::OneOrMore); | |
| // Option to specify a list or one or more callback names to ignore. | |
| static cl::opt<std::string> IgnoreCallbacks( | |
| "ignore", cl::init(""), | |
| cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\".")); | |
| // Option to specify the trace output file name. | |
| static cl::opt<std::string> OutputFileName( | |
| "output", cl::init(""), | |
| cl::desc("Output trace to the given file name or '-' for stdout.")); | |
| // Collect all other arguments, which will be passed to the front end. | |
| static cl::list<std::string> | |
| CC1Arguments(cl::ConsumeAfter, | |
| cl::desc("<arguments to be passed to front end>...")); | |
| // Frontend action stuff: | |
| namespace { | |
| // Consumer is responsible for setting up the callbacks. | |
| class PPTraceConsumer : public ASTConsumer { | |
| public: | |
| PPTraceConsumer(SmallSet<std::string, 4> &Ignore, | |
| std::vector<CallbackCall> &CallbackCalls, Preprocessor &PP) { | |
| // PP takes ownership. | |
| PP.addPPCallbacks(llvm::make_unique<PPCallbacksTracker>(Ignore, | |
| CallbackCalls, PP)); | |
| } | |
| }; | |
| class PPTraceAction : public SyntaxOnlyAction { | |
| public: | |
| PPTraceAction(SmallSet<std::string, 4> &Ignore, | |
| std::vector<CallbackCall> &CallbackCalls) | |
| : Ignore(Ignore), CallbackCalls(CallbackCalls) {} | |
| protected: | |
| std::unique_ptr<clang::ASTConsumer> | |
| CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { | |
| return llvm::make_unique<PPTraceConsumer>(Ignore, CallbackCalls, | |
| CI.getPreprocessor()); | |
| } | |
| private: | |
| SmallSet<std::string, 4> &Ignore; | |
| std::vector<CallbackCall> &CallbackCalls; | |
| }; | |
| class PPTraceFrontendActionFactory : public FrontendActionFactory { | |
| public: | |
| PPTraceFrontendActionFactory(SmallSet<std::string, 4> &Ignore, | |
| std::vector<CallbackCall> &CallbackCalls) | |
| : Ignore(Ignore), CallbackCalls(CallbackCalls) {} | |
| PPTraceAction *create() override { | |
| return new PPTraceAction(Ignore, CallbackCalls); | |
| } | |
| private: | |
| SmallSet<std::string, 4> &Ignore; | |
| std::vector<CallbackCall> &CallbackCalls; | |
| }; | |
| } // namespace | |
| // Output the trace given its data structure and a stream. | |
| static int outputPPTrace(std::vector<CallbackCall> &CallbackCalls, | |
| llvm::raw_ostream &OS) { | |
| // Mark start of document. | |
| OS << "---\n"; | |
| for (std::vector<CallbackCall>::const_iterator I = CallbackCalls.begin(), | |
| E = CallbackCalls.end(); | |
| I != E; ++I) { | |
| const CallbackCall &Callback = *I; | |
| OS << "- Callback: " << Callback.Name << "\n"; | |
| for (auto AI = Callback.Arguments.begin(), AE = Callback.Arguments.end(); | |
| AI != AE; ++AI) { | |
| const Argument &Arg = *AI; | |
| OS << " " << Arg.Name << ": " << Arg.Value << "\n"; | |
| } | |
| } | |
| // Mark end of document. | |
| OS << "...\n"; | |
| return 0; | |
| } | |
| // Program entry point. | |
| int main(int Argc, const char **Argv) { | |
| // Parse command line. | |
| cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n"); | |
| // Parse the IgnoreCallbacks list into strings. | |
| SmallVector<StringRef, 32> IgnoreCallbacksStrings; | |
| StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",", | |
| /*MaxSplit=*/ -1, /*KeepEmpty=*/false); | |
| SmallSet<std::string, 4> Ignore; | |
| for (SmallVector<StringRef, 32>::iterator I = IgnoreCallbacksStrings.begin(), | |
| E = IgnoreCallbacksStrings.end(); | |
| I != E; ++I) | |
| Ignore.insert(*I); | |
| // Create the compilation database. | |
| SmallString<256> PathBuf; | |
| sys::fs::current_path(PathBuf); | |
| std::unique_ptr<CompilationDatabase> Compilations; | |
| Compilations.reset( | |
| new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); | |
| // Store the callback trace information here. | |
| std::vector<CallbackCall> CallbackCalls; | |
| // Create the tool and run the compilation. | |
| ClangTool Tool(*Compilations, SourcePaths); | |
| PPTraceFrontendActionFactory Factory(Ignore, CallbackCalls); | |
| int HadErrors = Tool.run(&Factory); | |
| // If we had errors, exit early. | |
| if (HadErrors) | |
| return HadErrors; | |
| // Do the output. | |
| if (!OutputFileName.size()) { | |
| HadErrors = outputPPTrace(CallbackCalls, llvm::outs()); | |
| } else { | |
| // Set up output file. | |
| std::error_code EC; | |
| llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::F_Text); | |
| if (EC) { | |
| llvm::errs() << "pp-trace: error creating " << OutputFileName << ":" | |
| << EC.message() << "\n"; | |
| return 1; | |
| } | |
| HadErrors = outputPPTrace(CallbackCalls, Out.os()); | |
| // Tell ToolOutputFile that we want to keep the file. | |
| if (HadErrors == 0) | |
| Out.keep(); | |
| } | |
| return HadErrors; | |
| } |