169 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements pp-trace, a tool for displaying a textual trace10// of the Clang preprocessor activity. It's based on a derivation of the11// PPCallbacks class, that once registerd with Clang, receives callback calls12// to its virtual members, and outputs the information passed to the callbacks13// in a high-level YAML format.14//15// The pp-trace tool also serves as the basis for a test of the PPCallbacks16// 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/Frontend/CompilerInstance.h"32#include "clang/Frontend/FrontendAction.h"33#include "clang/Frontend/FrontendActions.h"34#include "clang/Lex/Preprocessor.h"35#include "clang/Options/Options.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 52using namespace llvm;53 54namespace clang {55namespace pp_trace {56 57static cl::OptionCategory Cat("pp-trace options");58 59static 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 67static 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[[noreturn]] static void error(Twine Message) {73 WithColor::error() << Message << '\n';74 exit(1);75}76 77namespace {78 79class PPTraceAction : public ASTFrontendAction {80public:81 PPTraceAction(const FilterType &Filters, raw_ostream &OS)82 : Filters(Filters), OS(OS) {}83 84protected: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 105private:106 const FilterType &Filters;107 raw_ostream &OS;108 std::vector<CallbackCall> CallbackCalls;109};110 111class PPTraceFrontendActionFactory : public tooling::FrontendActionFactory {112public: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 120private:121 const FilterType &Filters;122 raw_ostream &OS;123};124} // namespace125} // namespace pp_trace126} // namespace clang127 128int 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 else147 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