cc1gen_reproducer_main.cpp 6.3 KB

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