brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.7 KiB · ddff0d0 Raw
206 lines · cpp
1//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator  ----------===//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 is the entry point to the clang -cc1gen-reproducer functionality, which10// generates reproducers for invocations for clang-based tools.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Basic/Diagnostic.h"15#include "clang/Basic/LLVM.h"16#include "clang/Driver/Compilation.h"17#include "clang/Driver/Driver.h"18#include "llvm/ADT/ArrayRef.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/Support/FileSystem.h"21#include "llvm/Support/LLVMDriver.h"22#include "llvm/Support/TargetSelect.h"23#include "llvm/Support/VirtualFileSystem.h"24#include "llvm/Support/YAMLTraits.h"25#include "llvm/Support/raw_ostream.h"26#include "llvm/TargetParser/Host.h"27#include <optional>28 29using namespace clang;30 31namespace {32 33struct UnsavedFileHash {34  std::string Name;35  std::string MD5;36};37 38struct ClangInvocationInfo {39  std::string Toolchain;40  std::string LibclangOperation;41  std::string LibclangOptions;42  std::vector<std::string> Arguments;43  std::vector<std::string> InvocationArguments;44  std::vector<UnsavedFileHash> UnsavedFileHashes;45  bool Dump = false;46};47 48} // end anonymous namespace49 50LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)51 52namespace llvm {53namespace yaml {54 55template <> struct MappingTraits<UnsavedFileHash> {56  static void mapping(IO &IO, UnsavedFileHash &Info) {57    IO.mapRequired("name", Info.Name);58    IO.mapRequired("md5", Info.MD5);59  }60};61 62template <> struct MappingTraits<ClangInvocationInfo> {63  static void mapping(IO &IO, ClangInvocationInfo &Info) {64    IO.mapRequired("toolchain", Info.Toolchain);65    IO.mapOptional("libclang.operation", Info.LibclangOperation);66    IO.mapOptional("libclang.opts", Info.LibclangOptions);67    IO.mapRequired("args", Info.Arguments);68    IO.mapOptional("invocation-args", Info.InvocationArguments);69    IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);70  }71};72 73} // end namespace yaml74} // end namespace llvm75 76static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {77  std::string Result;78  llvm::raw_string_ostream OS(Result);79  OS << '{';80  bool NeedComma = false;81  auto EmitKey = [&](StringRef Key) {82    if (NeedComma)83      OS << ", ";84    NeedComma = true;85    OS << '"' << Key << "\": ";86  };87  auto EmitStringKey = [&](StringRef Key, StringRef Value) {88    if (Value.empty())89      return;90    EmitKey(Key);91    OS << '"' << Value << '"';92  };93  EmitStringKey("libclang.operation", Info.LibclangOperation);94  EmitStringKey("libclang.opts", Info.LibclangOptions);95  if (!Info.InvocationArguments.empty()) {96    EmitKey("invocation-args");97    OS << '[';98    for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {99      if (Arg.index())100        OS << ',';101      OS << '"' << Arg.value() << '"';102    }103    OS << ']';104  }105  OS << '}';106  // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.107  if (Info.Dump)108    llvm::outs() << "REPRODUCER METAINFO: " << Result << "\n";109  return Result;110}111 112/// Generates a reproducer for a set of arguments from a specific invocation.113static std::optional<driver::Driver::CompilationDiagnosticReport>114generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,115                                         const ClangInvocationInfo &Info,116                                         const llvm::ToolContext &ToolContext) {117  using namespace driver;118  auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);119 120  DiagnosticOptions DiagOpts;121 122  DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts,123                          new IgnoringDiagConsumer());124  auto VFS = llvm::vfs::getRealFileSystem();125  ProcessWarningOptions(Diags, DiagOpts, *VFS, /*ReportDiags=*/false);126  Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(), Diags,127                   /*Title=*/"clang LLVM compiler", VFS);128  TheDriver.setTargetAndMode(TargetAndMode);129  if (ToolContext.NeedsPrependArg)130    TheDriver.setPrependArg(ToolContext.PrependArg);131 132  std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));133  if (C && !C->containsError()) {134    for (const auto &J : C->getJobs()) {135      if (const Command *Cmd = dyn_cast<Command>(&J)) {136        Driver::CompilationDiagnosticReport Report;137        TheDriver.generateCompilationDiagnostics(138            *C, *Cmd, generateReproducerMetaInfo(Info), &Report);139        return Report;140      }141    }142  }143 144  return std::nullopt;145}146 147std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);148 149static void printReproducerInformation(150    llvm::raw_ostream &OS, const ClangInvocationInfo &Info,151    const driver::Driver::CompilationDiagnosticReport &Report) {152  OS << "REPRODUCER:\n";153  OS << "{\n";154  OS << R"("files":[)";155  for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {156    if (File.index())157      OS << ',';158    OS << '"' << File.value() << '"';159  }160  OS << "]\n}\n";161}162 163int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,164                           void *MainAddr,165                           const llvm::ToolContext &ToolContext) {166  if (Argv.size() < 1) {167    llvm::errs() << "error: missing invocation file\n";168    return 1;169  }170  // Parse the invocation descriptor.171  StringRef Input = Argv[0];172  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =173      llvm::MemoryBuffer::getFile(Input, /*IsText=*/true);174  if (!Buffer) {175    llvm::errs() << "error: failed to read " << Input << ": "176                 << Buffer.getError().message() << "\n";177    return 1;178  }179  llvm::yaml::Input YAML(Buffer.get()->getBuffer());180  ClangInvocationInfo InvocationInfo;181  YAML >> InvocationInfo;182  if (Argv.size() > 1 && Argv[1] == StringRef("-v"))183    InvocationInfo.Dump = true;184 185  // Create an invocation that will produce the reproducer.186  std::vector<const char *> DriverArgs;187  for (const auto &Arg : InvocationInfo.Arguments)188    DriverArgs.push_back(Arg.c_str());189  std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);190  DriverArgs[0] = Path.c_str();191  std::optional<driver::Driver::CompilationDiagnosticReport> Report =192      generateReproducerForInvocationArguments(DriverArgs, InvocationInfo,193                                               ToolContext);194 195  // Emit the information about the reproduce files to stdout.196  int Result = 1;197  if (Report) {198    printReproducerInformation(llvm::outs(), InvocationInfo, *Report);199    Result = 0;200  }201 202  // Remove the input file.203  llvm::sys::fs::remove(Input);204  return Result;205}206