cc1gen_reproducer_main.cpp 6.3 KB

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