arphaman updated this revision to Diff 127935.
arphaman marked an inline comment as done.
arphaman added a comment.

Address review comments


https://reviews.llvm.org/D40983

Files:
  include/clang/Driver/Driver.h
  lib/Driver/Driver.cpp
  test/Index/create-libclang-completion-reproducer.c
  test/Index/create-libclang-parsing-reproducer.c
  tools/driver/CMakeLists.txt
  tools/driver/cc1gen_reproducer_main.cpp
  tools/driver/driver.cpp

Index: tools/driver/driver.cpp
===================================================================
--- tools/driver/driver.cpp
+++ tools/driver/driver.cpp
@@ -205,6 +205,8 @@
                     void *MainAddr);
 extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
                       void *MainAddr);
+extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
+                                  const char *Argv0, void *MainAddr);
 
 static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
                                     SmallVectorImpl<const char *> &ArgVector,
@@ -309,6 +311,8 @@
     return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP);
   if (Tool == "as")
     return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP);
+  if (Tool == "gen-reproducer")
+    return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP);
 
   // Reject unknown tools.
   llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n";
Index: tools/driver/cc1gen_reproducer_main.cpp
===================================================================
--- /dev/null
+++ tools/driver/cc1gen_reproducer_main.cpp
@@ -0,0 +1,196 @@
+//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator  ----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the entry point to the clang -cc1gen-reproducer functionality, which
+// generates reproducers for invocations for clang-based tools.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+namespace {
+
+struct UnsavedFileHash {
+  std::string Name;
+  std::string MD5;
+};
+
+struct ClangInvocationInfo {
+  std::string Toolchain;
+  std::string LibclangOperation;
+  std::string LibclangOptions;
+  std::vector<std::string> Arguments;
+  std::vector<std::string> InvocationArguments;
+  std::vector<UnsavedFileHash> UnsavedFileHashes;
+  bool Dump = false;
+};
+
+} // end anonymous namespace
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<UnsavedFileHash> {
+  static void mapping(IO &IO, UnsavedFileHash &Info) {
+    IO.mapRequired("name", Info.Name);
+    IO.mapRequired("md5", Info.MD5);
+  }
+};
+
+template <> struct MappingTraits<ClangInvocationInfo> {
+  static void mapping(IO &IO, ClangInvocationInfo &Info) {
+    IO.mapRequired("toolchain", Info.Toolchain);
+    IO.mapOptional("libclang.operation", Info.LibclangOperation);
+    IO.mapOptional("libclang.opts", Info.LibclangOptions);
+    IO.mapRequired("args", Info.Arguments);
+    IO.mapOptional("invocation-args", Info.InvocationArguments);
+    IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes);
+  }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) {
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  OS << '{';
+  bool NeedComma = false;
+  auto EmitKey = [&](StringRef Key) {
+    if (NeedComma)
+      OS << ", ";
+    NeedComma = true;
+    OS << '"' << Key << "\": ";
+  };
+  auto EmitStringKey = [&](StringRef Key, StringRef Value) {
+    if (Value.empty())
+      return;
+    EmitKey(Key);
+    OS << '"' << Value << '"';
+  };
+  EmitStringKey("libclang.operation", Info.LibclangOperation);
+  EmitStringKey("libclang.opts", Info.LibclangOptions);
+  if (!Info.InvocationArguments.empty()) {
+    EmitKey("invocation-args");
+    OS << '[';
+    for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) {
+      if (Arg.index())
+        OS << ',';
+      OS << '"' << Arg.value() << '"';
+    }
+    OS << ']';
+  }
+  OS << '}';
+  // FIXME: Compare unsaved file hashes and report mismatch in the reproducer.
+  if (Info.Dump)
+    llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n";
+  return std::move(OS.str());
+}
+
+/// Generates a reproducer for a set of arguments from a specific invocation.
+static llvm::Optional<driver::Driver::CompilationDiagnosticReport>
+generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
+                                         const ClangInvocationInfo &Info) {
+  using namespace driver;
+  auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]);
+
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions;
+
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+  DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
+  ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
+  Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags);
+  TheDriver.setTargetAndMode(TargetAndMode);
+
+  std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv));
+  if (C && !C->containsError()) {
+    for (const auto &J : C->getJobs()) {
+      if (const Command *Cmd = dyn_cast<Command>(&J)) {
+        Driver::CompilationDiagnosticReport Report;
+        TheDriver.generateCompilationDiagnostics(
+            *C, *Cmd, generateReproducerMetaInfo(Info), &Report);
+        return Report;
+      }
+    }
+  }
+
+  return None;
+}
+
+std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes);
+
+static void printReproducerInformation(
+    llvm::raw_ostream &OS, const ClangInvocationInfo &Info,
+    const driver::Driver::CompilationDiagnosticReport &Report) {
+  OS << "REPRODUCER:\n";
+  OS << "{\n";
+  OS << R"("files":[)";
+  for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) {
+    if (File.index())
+      OS << ',';
+    OS << '"' << File.value() << '"';
+  }
+  OS << "]\n}\n";
+}
+
+int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0,
+                           void *MainAddr) {
+  if (Argv.size() < 1) {
+    llvm::errs() << "error: missing invocation file\n";
+    return 1;
+  }
+  // Parse the invocation descriptor.
+  StringRef Input = Argv[0];
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
+      llvm::MemoryBuffer::getFile(Input);
+  if (!Buffer) {
+    llvm::errs() << "error: failed to read " << Input << ": "
+                 << Buffer.getError().message() << "\n";
+    return 1;
+  }
+  llvm::yaml::Input YAML(Buffer.get()->getBuffer());
+  ClangInvocationInfo InvocationInfo;
+  YAML >> InvocationInfo;
+  if (Argv.size() > 1 && Argv[1] == StringRef("-v"))
+    InvocationInfo.Dump = true;
+
+  // Create an invocation that will produce the reproducer.
+  std::vector<const char *> DriverArgs;
+  for (const auto &Arg : InvocationInfo.Arguments)
+    DriverArgs.push_back(Arg.c_str());
+  std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true);
+  DriverArgs[0] = Path.c_str();
+  llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report =
+      generateReproducerForInvocationArguments(DriverArgs, InvocationInfo);
+
+  // Emit the information about the reproduce files to stdout.
+  int Result = 1;
+  if (Report) {
+    printReproducerInformation(llvm::outs(), InvocationInfo, *Report);
+    Result = 0;
+  }
+
+  // Remove the input file.
+  llvm::sys::fs::remove(Input);
+  return Result;
+}
Index: tools/driver/CMakeLists.txt
===================================================================
--- tools/driver/CMakeLists.txt
+++ tools/driver/CMakeLists.txt
@@ -32,6 +32,7 @@
   driver.cpp
   cc1_main.cpp
   cc1as_main.cpp
+  cc1gen_reproducer_main.cpp
 
   DEPENDS
   ${tablegen_deps}
Index: test/Index/create-libclang-parsing-reproducer.c
===================================================================
--- /dev/null
+++ test/Index/create-libclang-parsing-reproducer.c
@@ -0,0 +1,21 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: env CINDEXTEST_INVOCATION_EMISSION_PATH=%t not c-index-test -test-load-source all %s
+// RUN: %clang -cc1gen-reproducer %t/libclang-* -v | FileCheck %s
+
+// Invocation file must be removed by clang:
+// RUN: ls %t | count 0
+
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: env CINDEXTEST_INVOCATION_EMISSION_PATH=%t not c-index-test -test-load-source all "-remap-file=%s,%S/Inputs/record-parsing-invocation-remap.c" %s
+// RUN: %clang -cc1gen-reproducer %t/libclang-* -v | FileCheck %s
+
+#pragma clang __debug parser_crash
+
+// CHECK: REPRODUCER METAINFO: {"libclang.operation": "parse", "libclang.opts": "1"}
+
+// CHECK: REPRODUCER:
+// CHECK-NEXT: {
+// CHECK-NEXT: "files":["{{.*}}.c","{{.*}}.sh"]
+// CHECK-NEXT: }
Index: test/Index/create-libclang-completion-reproducer.c
===================================================================
--- /dev/null
+++ test/Index/create-libclang-completion-reproducer.c
@@ -0,0 +1,14 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: env CINDEXTEST_INVOCATION_EMISSION_PATH=%t not c-index-test -code-completion-at=%s:10:1 "-remap-file=%s,%S/Inputs/record-parsing-invocation-remap.c" %s
+// RUN: %clang -cc1gen-reproducer %t/libclang-* -v | FileCheck %s
+
+// Invocation file must be removed by clang:
+// RUN: ls %t | count 0
+
+// CHECK: REPRODUCER METAINFO: {"libclang.operation": "complete", "libclang.opts": "1", "invocation-args": ["-code-completion-at={{.*}}create-libclang-completion-reproducer.c:10:1"]}
+
+// CHECK: REPRODUCER:
+// CHECK-NEXT: {
+// CHECK-NEXT: "files":["{{.*}}.c","{{.*}}.sh"]
+// CHECK-NEXT: }
Index: lib/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -851,8 +851,9 @@
 // When clang crashes, produce diagnostic information including the fully
 // preprocessed source file(s).  Request that the developer attach the
 // diagnostic information to a bug report.
-void Driver::generateCompilationDiagnostics(Compilation &C,
-                                            const Command &FailingCommand) {
+void Driver::generateCompilationDiagnostics(
+    Compilation &C, const Command &FailingCommand,
+    StringRef AdditionalInformation, CompilationDiagnosticReport *Report) {
   if (C.getArgs().hasArg(options::OPT_fno_crash_diagnostics))
     return;
 
@@ -978,6 +979,8 @@
   SmallString<128> ReproCrashFilename;
   for (const char *TempFile : TempFiles) {
     Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile;
+    if (Report)
+      Report->TemporaryFiles.push_back(TempFile);
     if (ReproCrashFilename.empty()) {
       ReproCrashFilename = TempFile;
       llvm::sys::path::replace_extension(ReproCrashFilename, ".crash");
@@ -1006,6 +1009,11 @@
     ScriptOS << "# Original command: ";
     Cmd.Print(ScriptOS, "\n", /*Quote=*/true);
     Cmd.Print(ScriptOS, "\n", /*Quote=*/true, &CrashInfo);
+    if (!AdditionalInformation.empty())
+      ScriptOS << "\n# Additional information: " << AdditionalInformation
+               << "\n";
+    if (Report)
+      Report->TemporaryFiles.push_back(Script);
     Diag(clang::diag::note_drv_command_failed_diag_msg) << Script;
   }
 
Index: include/clang/Driver/Driver.h
===================================================================
--- include/clang/Driver/Driver.h
+++ include/clang/Driver/Driver.h
@@ -388,11 +388,19 @@
   int ExecuteCompilation(Compilation &C,
      SmallVectorImpl< std::pair<int, const Command *> > &FailingCommands);
 
-  /// generateCompilationDiagnostics - Generate diagnostics information 
+  /// Contains the files in the compilation diagnostic report generated by
+  /// generateCompilationDiagnostics.
+  struct CompilationDiagnosticReport {
+    llvm::SmallVector<std::string, 4> TemporaryFiles;
+  };
+
+  /// generateCompilationDiagnostics - Generate diagnostics information
   /// including preprocessed source file(s).
-  /// 
-  void generateCompilationDiagnostics(Compilation &C,
-                                      const Command &FailingCommand);
+  ///
+  void generateCompilationDiagnostics(
+      Compilation &C, const Command &FailingCommand,
+      StringRef AdditionalInformation = "",
+      CompilationDiagnosticReport *GeneratedReport = nullptr);
 
   /// @}
   /// @name Helper Methods
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to