1 //===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements pp-trace, a tool for displaying a textual trace 10 // of the Clang preprocessor activity. It's based on a derivation of the 11 // PPCallbacks class, that once registerd with Clang, receives callback calls 12 // to its virtual members, and outputs the information passed to the callbacks 13 // in a high-level YAML format. 14 // 15 // The pp-trace tool also serves as the basis for a test of the PPCallbacks 16 // mechanism. 17 // 18 // The pp-trace tool supports the following general command line format: 19 // 20 // pp-trace [options] file... [-- compiler options] 21 // 22 // Basically you put the pp-trace options first, then the source file or files, 23 // and then -- followed by any options you want to pass to the compiler. 24 // 25 //===----------------------------------------------------------------------===// 26 27 #include "PPCallbacksTracker.h" 28 #include "clang/AST/ASTConsumer.h" 29 #include "clang/AST/ASTContext.h" 30 #include "clang/Basic/SourceManager.h" 31 #include "clang/Driver/Options.h" 32 #include "clang/Frontend/CompilerInstance.h" 33 #include "clang/Frontend/FrontendAction.h" 34 #include "clang/Frontend/FrontendActions.h" 35 #include "clang/Lex/Preprocessor.h" 36 #include "clang/Tooling/Execution.h" 37 #include "clang/Tooling/Tooling.h" 38 #include "llvm/Option/Arg.h" 39 #include "llvm/Option/ArgList.h" 40 #include "llvm/Option/OptTable.h" 41 #include "llvm/Option/Option.h" 42 #include "llvm/Support/CommandLine.h" 43 #include "llvm/Support/FileSystem.h" 44 #include "llvm/Support/GlobPattern.h" 45 #include "llvm/Support/InitLLVM.h" 46 #include "llvm/Support/Path.h" 47 #include "llvm/Support/ToolOutputFile.h" 48 #include "llvm/Support/WithColor.h" 49 #include <string> 50 #include <vector> 51 52 using namespace llvm; 53 54 namespace clang { 55 namespace pp_trace { 56 57 static cl::OptionCategory Cat("pp-trace options"); 58 59 static cl::opt<std::string> Callbacks( 60 "callbacks", cl::init("*"), 61 cl::desc("Comma-separated list of globs describing the list of callbacks " 62 "to output. Globs are processed in order of appearance. Globs " 63 "with the '-' prefix remove callbacks from the set. e.g. " 64 "'*,-Macro*'."), 65 cl::cat(Cat)); 66 67 static cl::opt<std::string> OutputFileName( 68 "output", cl::init("-"), 69 cl::desc("Output trace to the given file name or '-' for stdout."), 70 cl::cat(Cat)); 71 72 LLVM_ATTRIBUTE_NORETURN static void error(Twine Message) { 73 WithColor::error() << Message << '\n'; 74 exit(1); 75 } 76 77 namespace { 78 79 class PPTraceAction : public ASTFrontendAction { 80 public: 81 PPTraceAction(const FilterType &Filters, raw_ostream &OS) 82 : Filters(Filters), OS(OS) {} 83 84 protected: 85 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 86 StringRef InFile) override { 87 Preprocessor &PP = CI.getPreprocessor(); 88 PP.addPPCallbacks( 89 std::make_unique<PPCallbacksTracker>(Filters, CallbackCalls, PP)); 90 return std::make_unique<ASTConsumer>(); 91 } 92 93 void EndSourceFileAction() override { 94 OS << "---\n"; 95 for (const CallbackCall &Callback : CallbackCalls) { 96 OS << "- Callback: " << Callback.Name << "\n"; 97 for (const Argument &Arg : Callback.Arguments) 98 OS << " " << Arg.Name << ": " << Arg.Value << "\n"; 99 } 100 OS << "...\n"; 101 102 CallbackCalls.clear(); 103 } 104 105 private: 106 const FilterType &Filters; 107 raw_ostream &OS; 108 std::vector<CallbackCall> CallbackCalls; 109 }; 110 111 class PPTraceFrontendActionFactory : public tooling::FrontendActionFactory { 112 public: 113 PPTraceFrontendActionFactory(const FilterType &Filters, raw_ostream &OS) 114 : Filters(Filters), OS(OS) {} 115 116 std::unique_ptr<FrontendAction> create() override { 117 return std::make_unique<PPTraceAction>(Filters, OS); 118 } 119 120 private: 121 const FilterType &Filters; 122 raw_ostream &OS; 123 }; 124 } // namespace 125 } // namespace pp_trace 126 } // namespace clang 127 128 int main(int argc, const char **argv) { 129 using namespace clang::pp_trace; 130 InitLLVM X(argc, argv); 131 auto OptionsParser = clang::tooling::CommonOptionsParser::create( 132 argc, argv, Cat, llvm::cl::ZeroOrMore); 133 if (!OptionsParser) 134 error(toString(OptionsParser.takeError())); 135 // Parse the IgnoreCallbacks list into strings. 136 SmallVector<StringRef, 32> Patterns; 137 FilterType Filters; 138 StringRef(Callbacks).split(Patterns, ",", 139 /*MaxSplit=*/-1, /*KeepEmpty=*/false); 140 for (StringRef Pattern : Patterns) { 141 Pattern = Pattern.trim(); 142 bool Enabled = !Pattern.consume_front("-"); 143 Expected<GlobPattern> Pat = GlobPattern::create(Pattern); 144 if (Pat) 145 Filters.emplace_back(std::move(*Pat), Enabled); 146 else 147 error(toString(Pat.takeError())); 148 } 149 150 // Create the tool and run the compilation. 151 clang::tooling::ClangTool Tool(OptionsParser->getCompilations(), 152 OptionsParser->getSourcePathList()); 153 154 std::error_code EC; 155 llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::OF_TextWithCRLF); 156 if (EC) 157 error(EC.message()); 158 PPTraceFrontendActionFactory Factory(Filters, Out.os()); 159 int HadErrors = Tool.run(&Factory); 160 161 // If we had errors, exit early. 162 if (HadErrors) 163 return HadErrors; 164 165 Out.keep(); 166 167 return 0; 168 } 169