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/FrontendActions.h" 34 #include "clang/Lex/Preprocessor.h" 35 #include "clang/Tooling/Execution.h" 36 #include "clang/Tooling/Tooling.h" 37 #include "llvm/Option/Arg.h" 38 #include "llvm/Option/ArgList.h" 39 #include "llvm/Option/OptTable.h" 40 #include "llvm/Option/Option.h" 41 #include "llvm/Support/CommandLine.h" 42 #include "llvm/Support/FileSystem.h" 43 #include "llvm/Support/GlobPattern.h" 44 #include "llvm/Support/InitLLVM.h" 45 #include "llvm/Support/Path.h" 46 #include "llvm/Support/ToolOutputFile.h" 47 #include "llvm/Support/WithColor.h" 48 #include <string> 49 #include <vector> 50 51 using namespace llvm; 52 53 namespace clang { 54 namespace pp_trace { 55 56 static cl::OptionCategory Cat("pp-trace options"); 57 58 static cl::opt<std::string> Callbacks( 59 "callbacks", cl::init("*"), 60 cl::desc("Comma-separated list of globs describing the list of callbacks " 61 "to output. Globs are processed in order of appearance. Globs " 62 "with the '-' prefix remove callbacks from the set. e.g. " 63 "'*,-Macro*'."), 64 cl::cat(Cat)); 65 66 static cl::opt<std::string> OutputFileName( 67 "output", cl::init("-"), 68 cl::desc("Output trace to the given file name or '-' for stdout."), 69 cl::cat(Cat)); 70 71 LLVM_ATTRIBUTE_NORETURN static void error(Twine Message) { 72 WithColor::error() << Message << '\n'; 73 exit(1); 74 } 75 76 namespace { 77 78 class PPTraceAction : public ASTFrontendAction { 79 public: 80 PPTraceAction(const FilterType &Filters, raw_ostream &OS) 81 : Filters(Filters), OS(OS) {} 82 83 protected: 84 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 85 StringRef InFile) override { 86 Preprocessor &PP = CI.getPreprocessor(); 87 PP.addPPCallbacks( 88 llvm::make_unique<PPCallbacksTracker>(Filters, CallbackCalls, PP)); 89 return llvm::make_unique<ASTConsumer>(); 90 } 91 92 void EndSourceFileAction() override { 93 OS << "---\n"; 94 for (const CallbackCall &Callback : CallbackCalls) { 95 OS << "- Callback: " << Callback.Name << "\n"; 96 for (const Argument &Arg : Callback.Arguments) 97 OS << " " << Arg.Name << ": " << Arg.Value << "\n"; 98 } 99 OS << "...\n"; 100 101 CallbackCalls.clear(); 102 } 103 104 private: 105 const FilterType &Filters; 106 raw_ostream &OS; 107 std::vector<CallbackCall> CallbackCalls; 108 }; 109 110 class PPTraceFrontendActionFactory : public tooling::FrontendActionFactory { 111 public: 112 PPTraceFrontendActionFactory(const FilterType &Filters, raw_ostream &OS) 113 : Filters(Filters), OS(OS) {} 114 115 PPTraceAction *create() override { return new PPTraceAction(Filters, OS); } 116 117 private: 118 const FilterType &Filters; 119 raw_ostream &OS; 120 }; 121 } // namespace 122 } // namespace pp_trace 123 } // namespace clang 124 125 int main(int argc, const char **argv) { 126 using namespace clang::pp_trace; 127 InitLLVM X(argc, argv); 128 auto OptionsParser = clang::tooling::CommonOptionsParser::create( 129 argc, argv, Cat, llvm::cl::ZeroOrMore); 130 if (!OptionsParser) 131 error(toString(OptionsParser.takeError())); 132 // Parse the IgnoreCallbacks list into strings. 133 SmallVector<StringRef, 32> Patterns; 134 FilterType Filters; 135 StringRef(Callbacks).split(Patterns, ",", 136 /*MaxSplit=*/-1, /*KeepEmpty=*/false); 137 for (StringRef Pattern : Patterns) { 138 Pattern = Pattern.trim(); 139 bool Enabled = !Pattern.consume_front("-"); 140 Expected<GlobPattern> Pat = GlobPattern::create(Pattern); 141 if (Pat) 142 Filters.emplace_back(std::move(*Pat), Enabled); 143 else 144 error(toString(Pat.takeError())); 145 } 146 147 // Create the tool and run the compilation. 148 clang::tooling::ClangTool Tool(OptionsParser->getCompilations(), 149 OptionsParser->getSourcePathList()); 150 151 std::error_code EC; 152 llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::F_Text); 153 if (EC) 154 error(EC.message()); 155 PPTraceFrontendActionFactory Factory(Filters, Out.os()); 156 int HadErrors = Tool.run(&Factory); 157 158 // If we had errors, exit early. 159 if (HadErrors) 160 return HadErrors; 161 162 Out.keep(); 163 164 return 0; 165 } 166