//===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // 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 [options] file... [-- compiler options] // // Basically you put the pp-trace options first, then the source file or files, // and then -- followed by any options you want to pass to the compiler. // //===----------------------------------------------------------------------===// #include "PPCallbacksTracker.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" #include "clang/Driver/Options.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Execution.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/GlobPattern.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include #include using namespace llvm; namespace clang { namespace pp_trace { static cl::OptionCategory Cat("pp-trace options"); static cl::opt Callbacks( "callbacks", cl::init("*"), cl::desc("Comma-separated list of globs describing the list of callbacks " "to output. Globs are processed in order of appearance. Globs " "with the '-' prefix remove callbacks from the set. e.g. " "'*,-Macro*'."), cl::cat(Cat)); static cl::opt OutputFileName( "output", cl::init("-"), cl::desc("Output trace to the given file name or '-' for stdout."), cl::cat(Cat)); LLVM_ATTRIBUTE_NORETURN static void error(Twine Message) { WithColor::error() << Message << '\n'; exit(1); } namespace { class PPTraceAction : public ASTFrontendAction { public: PPTraceAction(const FilterType &Filters, raw_ostream &OS) : Filters(Filters), OS(OS) {} protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { Preprocessor &PP = CI.getPreprocessor(); PP.addPPCallbacks( std::make_unique(Filters, CallbackCalls, PP)); return std::make_unique(); } void EndSourceFileAction() override { OS << "---\n"; for (const CallbackCall &Callback : CallbackCalls) { OS << "- Callback: " << Callback.Name << "\n"; for (const Argument &Arg : Callback.Arguments) OS << " " << Arg.Name << ": " << Arg.Value << "\n"; } OS << "...\n"; CallbackCalls.clear(); } private: const FilterType &Filters; raw_ostream &OS; std::vector CallbackCalls; }; class PPTraceFrontendActionFactory : public tooling::FrontendActionFactory { public: PPTraceFrontendActionFactory(const FilterType &Filters, raw_ostream &OS) : Filters(Filters), OS(OS) {} std::unique_ptr create() override { return std::make_unique(Filters, OS); } private: const FilterType &Filters; raw_ostream &OS; }; } // namespace } // namespace pp_trace } // namespace clang int main(int argc, const char **argv) { using namespace clang::pp_trace; InitLLVM X(argc, argv); auto OptionsParser = clang::tooling::CommonOptionsParser::create( argc, argv, Cat, llvm::cl::ZeroOrMore); if (!OptionsParser) error(toString(OptionsParser.takeError())); // Parse the IgnoreCallbacks list into strings. SmallVector Patterns; FilterType Filters; StringRef(Callbacks).split(Patterns, ",", /*MaxSplit=*/-1, /*KeepEmpty=*/false); for (StringRef Pattern : Patterns) { Pattern = Pattern.trim(); bool Enabled = !Pattern.consume_front("-"); Expected Pat = GlobPattern::create(Pattern); if (Pat) Filters.emplace_back(std::move(*Pat), Enabled); else error(toString(Pat.takeError())); } // Create the tool and run the compilation. clang::tooling::ClangTool Tool(OptionsParser->getCompilations(), OptionsParser->getSourcePathList()); std::error_code EC; llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::OF_Text); if (EC) error(EC.message()); PPTraceFrontendActionFactory Factory(Filters, Out.os()); int HadErrors = Tool.run(&Factory); // If we had errors, exit early. if (HadErrors) return HadErrors; Out.keep(); return 0; }