1 //===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements pp-trace, a tool for displaying a textual trace 11 // of the Clang preprocessor activity. It's based on a derivation of the 12 // PPCallbacks class, that once registerd with Clang, receives callback calls 13 // to its virtual members, and outputs the information passed to the callbacks 14 // in a high-level YAML format. 15 // 16 // The pp-trace tool also serves as the basis for a test of the PPCallbacks 17 // mechanism. 18 // 19 // The pp-trace tool supports the following general command line format: 20 // 21 // pp-trace [pp-trace options] (source file) [compiler options] 22 // 23 // Basically you put the pp-trace options first, then the source file or files, 24 // and then any options you want to pass to the compiler. 25 // 26 // These are the pp-trace options: 27 // 28 // -ignore (callback list) Don't display output for a comma-separated 29 // list of callbacks, i.e.: 30 // -ignore "FileChanged,InclusionDirective" 31 // 32 // -output (file) Output trace to the given file in a YAML 33 // format, e.g.: 34 // 35 // --- 36 // - Callback: Name 37 // Argument1: Value1 38 // Argument2: Value2 39 // (etc.) 40 // ... 41 // 42 // Future Directions: 43 // 44 // 1. Add option opposite to "-ignore" that specifys a comma-separated option 45 // list of callbacs. Perhaps "-only" or "-exclusive". 46 // 47 //===----------------------------------------------------------------------===// 48 49 #include "PPCallbacksTracker.h" 50 #include "clang/AST/ASTConsumer.h" 51 #include "clang/AST/ASTContext.h" 52 #include "clang/AST/RecursiveASTVisitor.h" 53 #include "clang/Basic/SourceManager.h" 54 #include "clang/Driver/Options.h" 55 #include "clang/Frontend/CompilerInstance.h" 56 #include "clang/Frontend/FrontendActions.h" 57 #include "clang/Lex/Preprocessor.h" 58 #include "clang/Tooling/CompilationDatabase.h" 59 #include "clang/Tooling/Tooling.h" 60 #include "llvm/Option/Arg.h" 61 #include "llvm/Option/ArgList.h" 62 #include "llvm/Option/OptTable.h" 63 #include "llvm/Option/Option.h" 64 #include "llvm/Support/CommandLine.h" 65 #include "llvm/Support/FileSystem.h" 66 #include "llvm/Support/MemoryBuffer.h" 67 #include "llvm/Support/Path.h" 68 #include "llvm/Support/ToolOutputFile.h" 69 #include <algorithm> 70 #include <fstream> 71 #include <iterator> 72 #include <string> 73 #include <vector> 74 75 using namespace clang; 76 using namespace clang::driver; 77 using namespace clang::driver::options; 78 using namespace clang::tooling; 79 using namespace llvm; 80 using namespace llvm::opt; 81 82 // Options: 83 84 // Collect the source files. 85 static cl::list<std::string> SourcePaths(cl::Positional, 86 cl::desc("<source0> [... <sourceN>]"), 87 cl::OneOrMore); 88 89 // Option to specify a list or one or more callback names to ignore. 90 static cl::opt<std::string> IgnoreCallbacks( 91 "ignore", cl::init(""), 92 cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\".")); 93 94 // Option to specify the trace output file name. 95 static cl::opt<std::string> OutputFileName( 96 "output", cl::init(""), 97 cl::desc("Output trace to the given file name or '-' for stdout.")); 98 99 // Collect all other arguments, which will be passed to the front end. 100 static cl::list<std::string> 101 CC1Arguments(cl::ConsumeAfter, 102 cl::desc("<arguments to be passed to front end>...")); 103 104 // Frontend action stuff: 105 106 namespace { 107 // Consumer is responsible for setting up the callbacks. 108 class PPTraceConsumer : public ASTConsumer { 109 public: 110 PPTraceConsumer(SmallSet<std::string, 4> &Ignore, 111 std::vector<CallbackCall> &CallbackCalls, Preprocessor &PP) { 112 // PP takes ownership. 113 PP.addPPCallbacks(llvm::make_unique<PPCallbacksTracker>(Ignore, 114 CallbackCalls, PP)); 115 } 116 }; 117 118 class PPTraceAction : public SyntaxOnlyAction { 119 public: 120 PPTraceAction(SmallSet<std::string, 4> &Ignore, 121 std::vector<CallbackCall> &CallbackCalls) 122 : Ignore(Ignore), CallbackCalls(CallbackCalls) {} 123 124 protected: 125 std::unique_ptr<clang::ASTConsumer> 126 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { 127 return llvm::make_unique<PPTraceConsumer>(Ignore, CallbackCalls, 128 CI.getPreprocessor()); 129 } 130 131 private: 132 SmallSet<std::string, 4> &Ignore; 133 std::vector<CallbackCall> &CallbackCalls; 134 }; 135 136 class PPTraceFrontendActionFactory : public FrontendActionFactory { 137 public: 138 PPTraceFrontendActionFactory(SmallSet<std::string, 4> &Ignore, 139 std::vector<CallbackCall> &CallbackCalls) 140 : Ignore(Ignore), CallbackCalls(CallbackCalls) {} 141 142 PPTraceAction *create() override { 143 return new PPTraceAction(Ignore, CallbackCalls); 144 } 145 146 private: 147 SmallSet<std::string, 4> &Ignore; 148 std::vector<CallbackCall> &CallbackCalls; 149 }; 150 } // namespace 151 152 // Output the trace given its data structure and a stream. 153 static int outputPPTrace(std::vector<CallbackCall> &CallbackCalls, 154 llvm::raw_ostream &OS) { 155 // Mark start of document. 156 OS << "---\n"; 157 158 for (std::vector<CallbackCall>::const_iterator I = CallbackCalls.begin(), 159 E = CallbackCalls.end(); 160 I != E; ++I) { 161 const CallbackCall &Callback = *I; 162 OS << "- Callback: " << Callback.Name << "\n"; 163 164 for (auto AI = Callback.Arguments.begin(), AE = Callback.Arguments.end(); 165 AI != AE; ++AI) { 166 const Argument &Arg = *AI; 167 OS << " " << Arg.Name << ": " << Arg.Value << "\n"; 168 } 169 } 170 171 // Mark end of document. 172 OS << "...\n"; 173 174 return 0; 175 } 176 177 // Program entry point. 178 int main(int Argc, const char **Argv) { 179 180 // Parse command line. 181 cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n"); 182 183 // Parse the IgnoreCallbacks list into strings. 184 SmallVector<StringRef, 32> IgnoreCallbacksStrings; 185 StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",", 186 /*MaxSplit=*/ -1, /*KeepEmpty=*/false); 187 SmallSet<std::string, 4> Ignore; 188 for (SmallVector<StringRef, 32>::iterator I = IgnoreCallbacksStrings.begin(), 189 E = IgnoreCallbacksStrings.end(); 190 I != E; ++I) 191 Ignore.insert(*I); 192 193 // Create the compilation database. 194 SmallString<256> PathBuf; 195 sys::fs::current_path(PathBuf); 196 std::unique_ptr<CompilationDatabase> Compilations; 197 Compilations.reset( 198 new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); 199 200 // Store the callback trace information here. 201 std::vector<CallbackCall> CallbackCalls; 202 203 // Create the tool and run the compilation. 204 ClangTool Tool(*Compilations, SourcePaths); 205 PPTraceFrontendActionFactory Factory(Ignore, CallbackCalls); 206 int HadErrors = Tool.run(&Factory); 207 208 // If we had errors, exit early. 209 if (HadErrors) 210 return HadErrors; 211 212 // Do the output. 213 if (!OutputFileName.size()) { 214 HadErrors = outputPPTrace(CallbackCalls, llvm::outs()); 215 } else { 216 // Set up output file. 217 std::error_code EC; 218 llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::F_Text); 219 if (EC) { 220 llvm::errs() << "pp-trace: error creating " << OutputFileName << ":" 221 << EC.message() << "\n"; 222 return 1; 223 } 224 225 HadErrors = outputPPTrace(CallbackCalls, Out.os()); 226 227 // Tell ToolOutputFile that we want to keep the file. 228 if (HadErrors == 0) 229 Out.keep(); 230 } 231 232 return HadErrors; 233 } 234